이전에 쓴 글([STM32F4xx] I2C DMA 설정 (CUBEMX))에 이어서, 

프로그램에 대해서 설명해 보겠습니다.
I2C DMA 및 인터럽트, 추가로 시퀀셜 제어까지 모두 설명해 보겠습니다.

이번에도, 보안칩 회사의 철통같은 보안으로 메뉴얼 조차도 보안적으로 설명을 좀 빼먹어서,
I2C 시퀀셜 송/수신 함수까지 써 보게 되었습니다. ^^

열심이 공부해서, 보안 칩의 보안을 깨 버리겠습니다. ^^
이미 I2C DEVICE ADDRESS 안 알려줘서 찾아내 버렸습니다. ㅋㅋ

이 보안칩은 저희 사장님의 친구인 사장님이 만든 거라는데, 가장 싼 모델이랍니다.
그래서 인지, 이름도 없고 핀 번호 표시도 하얀 페인트로 칠해져 있어서 알았습니다.
보안상 그런게 아니라, 진짜 이름도 모릅니다. ^^

I2C 의 전송 HAL LIB 함수를 사용했는데, 여기서 사용한 함수는 다음과 같습니다.
모두 인터럽트를 사용하고, 같은 콜백함수(Tx 함수는 Tx 콜백,Rx 함수는 Rx 콜백)를 호출합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// DMA Function
// DMA Transmit Function
HAL_StatusTypeDef HAL_I2C_Master_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size)
// DMA Receive Function
HAL_StatusTypeDef HAL_I2C_Master_Receive_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size)
 
// Interrupt Function
// Interrupt Transmit Function
HAL_StatusTypeDef HAL_I2C_Master_Transmit_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size)
// Interrupt Receive Function
HAL_StatusTypeDef HAL_I2C_Master_Receive_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size)
 
// Interrupt Sequential Function
// Interrupt Sequential Transmit Function
HAL_StatusTypeDef HAL_I2C_Master_Sequential_Transmit_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions)
// Interrupt Sequential Receive Function
HAL_StatusTypeDef HAL_I2C_Master_Sequential_Receive_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions)
cs

지금까지 Sequential 함수는 사용할 일이 없었는데, 보안칩 메뉴얼 때문에 반드시 사용해야 하는 줄 알고 쓰게 됐습니다. ㅠㅠ

보안 칩 메뉴얼에 Transmit 함수는 그냥 IT 함수나 DMA 함수를 쓰면 되는데,
Receive 할 때 Device Address 와 Sub Address를 쓰고 나서 STOP 을 하지 말라고 해서, 방법을 찾아보니 Sequential 함수라는 놈이 있었습니다.
다음은 제가 참고로 본 보안 칩 메뉴얼 중에서, Tx/Rx 송/수신 정보 입니다.





여기 아랫 쪽에, Non STOP 보이시죠. 젠장 욕나온다. ㅜㅜ
나중에 테스트를 해보니까, STOP 하고 읽어도 되는 것이었습니다. DMA 나 일반 IT 함수 쓰면 되는 것이죠.





아뭏든 DMA 함수의 사용 방법은 다음과 같습니다.

송신시, HAL_I2C_Master_Transmit_DMA() 함수를 사용합니다.
함수 프로토타입은 다음과 같습니다.
1
HAL_StatusTypeDef HAL_I2C_Master_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size)
cs
사용예는 다음과 같습니다.
1
HAL_I2C_Master_Transmit_DMA(&hi2c1,(uint16_t)(SECU_SL_ADD<<1),i2c_wr_buff,9);
cs

수신시, HAL_I2C_Master_Receive_DMA() 함수를 사용합니다.
함수 프로토타입은 다음과 같습니다.
1
HAL_StatusTypeDef HAL_I2C_Master_Receive_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size)
cs
사용예는 다음과 같습니다.
1
HAL_I2C_Master_Receive_DMA(&hi2c1,(uint16_t)(SECU_SL_ADD<<1),i2c_rd_buff,8);
cs


기본 IT 함수의 사용 방법은 다음과 같습니다.
송신시, HAL_I2C_Master_Transmit_IT() 함수를 사용합니다.
함수 프로토타입은 다음과 같습니다.
1
HAL_StatusTypeDef HAL_I2C_Master_Transmit_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size)
cs
사용예는 다음과 같습니다.
1
HAL_I2C_Master_Transmit_IT(&hi2c1,(uint16_t)(SECU_SL_ADD<<1),i2c_wr_buff,1);
cs

수신시, HAL_I2C_Master_Receive_IT() 함수를 사용합니다.
함수 프로토타입은 다음과 같습니다.
1
HAL_StatusTypeDef HAL_I2C_Master_Receive_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size)
cs
사용예는 다음과 같습니다.
1
HAL_I2C_Master_Receive_IT(&hi2c1,(uint16_t)(SECU_SL_ADD<<1),i2c_rd_buff,8);
cs


Sequential IT 함수의 사용 방법은 다음과 같습니다.
송신시, HAL_I2C_Master_Sequential_Transmit_IT() 함수를 사용합니다.
함수 프로토타입은 다음과 같습니다.
1
HAL_StatusTypeDef HAL_I2C_Master_Sequential_Transmit_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions)
cs
사용예는 다음과 같습니다.
1
HAL_I2C_Master_Sequential_Transmit_IT(&hi2c1,(uint16_t)(SECU_SL_ADD<<1),i2c_wr_buff,1,I2C_FIRST_FRAME);
cs

송신시, HAL_I2C_Master_Sequential_Receive_IT() 함수를 사용합니다.
함수 프로토타입은 다음과 같습니다.
1
HAL_StatusTypeDef HAL_I2C_Master_Sequential_Receive_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions)
cs
사용예는 다음과 같습니다.
1
HAL_I2C_Master_Sequential_Receive_IT(&hi2c1,(uint16_t)(SECU_SL_ADD<<1),i2c_rd_buff,8,I2C_LAST_FRAME);
cs

Sequential IT 함수에서 좀 설명할게 있는데, 함수의 마지막 입력 파라메터인 XferOptions 가 이 함수의 특징을 좌우합니다.
이 변수의 정의는 다음과 같습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/** @defgroup I2C_XferOptions_definition I2C XferOptions definition
  * @{
  */
#define  I2C_FIRST_FRAME                0x00000001U
#define  I2C_NEXT_FRAME                 0x00000002U
#define  I2C_FIRST_AND_LAST_FRAME       0x00000004U
#define  I2C_LAST_FRAME                 0x00000008U
 
/*
(+) A specific option field manage the different steps of a sequential transfer
(+) Option field values are defined through @ref I2C_XFEROPTIONS and are listed below:
(++) I2C_FIRST_AND_LAST_FRAME: No sequential usage, functionnal is same as associated interfaces in no sequential mode 
(++) I2C_FIRST_FRAME: Sequential usage, this option allow to manage a sequence with start condition, address
                      and data to transfer without a final stop condition
(++) I2C_NEXT_FRAME: Sequential usage, this option allow to manage a sequence with a restart condition, address
                     and with new data to transfer if the direction change or manage only the new data to transfer
                     if no direction change and without a final stop condition in both cases
(++) I2C_LAST_FRAME: Sequential usage, this option allow to manage a sequance with a restart condition, address
                     and with new data to transfer if the direction change or manage only the new data to transfer
                     if no direction change and with a final stop condition in both cases
*/
cs

I2C_FIRST_AND_LAST_FRAME은 시퀀스 없이 사용하는 것과 마찮가지란 말입니다. 그냥 일반 IT 함수죠.
I2C_FIRST_FRAME은 처음 일반 IT 처럼 시작하지만, 마지막에 STOP 이 아닌 상태로 됩니다.
I2C_NEXT_FRAME은 중간에 Restart 상태로 시작하고, 마지막에 STOP 이 아닌상태로 됩니다.
I2C_LAST_FRAME은 중간에 Restart 상태로 시작하고, 마지막에 STOP 상태로 됩니다.


그래서 저는 보안칩 메뉴얼 상, 수신시 패킷 구조가 1번 전송하고 NO STOP 으로 중간에 읽고 끝내기 위해서,
HAL_I2C_Master_Sequential_Transmit_IT() 함수는 I2C_FIRST_FRAME ,
HAL_I2C_Master_Sequential_Receive_IT() 함수는 I2C_LAST_FRAME으로 사용했습니다.
즉 다음과 같은 예처럼 사용했습니다.
아래 내용을 참고하실 때, // wait for Tx Callbackfunction process이 있는데, 
저는 귀찮아서 HAL_Delay()를 써버렸는데, Ready 를 체크한다던지, 콜백함수로 부터 플레그 변수가 변하는 것을 감지해서
완료가 된는지를 검사하셔야 합니다. (그냥 똑같이 주석만 처리하시까봐 알려 드립니다)
저는 테스트 용도로 코드를 만들었기 때문에 HAL_Delay(10); 을 넣었었습니다. ^^ 
1
2
3
4
HAL_I2C_Master_Sequential_Transmit_IT(&hi2c1,(uint16_t)(SECU_SL_ADD<<1),i2c_wr_buff,1,I2C_FIRST_FRAME);
// wait for Tx Callbackfunction process
HAL_I2C_Master_Sequential_Receive_IT(&hi2c1,(uint16_t)(SECU_SL_ADD<<1),i2c_rd_buff,8,I2C_LAST_FRAME);
// wait for Rx Callbackfunction process
cs

그런데, 나중에 테스트 해보니까.. 중간 상태가 반드시 NO STOP 이 아니라, STOP 이어도 무방했습니다.
그래서 DMA 와 일반 IT 함수를 사용해도 상관이 없었습니다. 아휴.. 회사 일이 좀 늦어졌고, 업무상 필요없는 짓을 한거죠.
그래도 앞으로 쓸 일이 있겠죠. 뭐. ^^
보안 칩에서, 위의 예와 같은 동작을 하는 DMA 코드는 다음과 같습니다.
1
2
3
4
HAL_I2C_Master_Transmit_DMA(&hi2c1,(uint16_t)(SECU_SL_ADD<<1),i2c_wr_buff,1);
// wait for Tx Callbackfunction process
HAL_I2C_Master_Receive_DMA(&hi2c1,(uint16_t)(SECU_SL_ADD<<1),i2c_rd_buff,8);
// wait for Rx Callbackfunction process
cs
보안 칩에서, 위의 예와 같은 동작을 하는 IT 코드는 다음과 같습니다.
1
2
3
4
HAL_I2C_Master_Transmit_IT(&hi2c1,(uint16_t)(SECU_SL_ADD<<1),i2c_wr_buff,1);
// wait for Tx Callbackfunction process
HAL_I2C_Master_Receive_IT(&hi2c1,(uint16_t)(SECU_SL_ADD<<1),i2c_rd_buff,8);
// wait for Rx Callbackfunction process
cs


그리고 인터럽트는 (송/수신이 완료된 시점에서) 일반 IT 함수,시퀀셜 IT 함수DMA 함수 모두 걸리고,
송/수신 완료시 실행되는 콜벡함수는 다음과 같습니다. stm32f4xx_hal_i2c.c 파일 안에 __weak 으로 되어 있는데,
main.c 에다 카피한 후, __weak 지우고 인터럽트에 의해 콜되었을 때 실행할 코드를 넣어 주면 됩니다.
1
2
3
4
5
6
7
8
9
10
11
// Tx INT Complete
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
// insert user code
}
 
// Rx INT Complete
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
// insert user code
}
cs


이번에 사용하는 보안 칩이 I2C로 되어 있어서, I2C 통신에 대해서 써 보겠습니다.


예전에 적은 글이 너무 허접해서, 다른분들이 봐도 도움이 안될 듯 해서 다시 한번 써 봅니다.

회사에 아주 많이 굴러다니는 F4 개발 키트중에 골라 잡으니, STM32F411(NUCLEO-F411RE) 이네요.
일단 요놈에다가 I2C 로 보안칩을 연결했습니다.

다음은 STM32F411 NUCLEO 보드의 핀팹을 CUBEMX에서 설정한 내용입니다.


 
다음은 NUCLEO 보드의 I2C 포트의 위치를 표시한 그림입니다. 개발 보드에서 최대한 모여있는 위치로 , CUBEMX 에서 핀을 선택하고 이동했습니다. UART2 는 USB-to-UART 로 NUCLEO 보드와 연결되어 있어서 디버깅 하기가 좋습니다. ^^





이렇게 I2C 에 보안칩을 연결해 봤습니다. 작아서 , 큰 선들을 연결하기 힘들었습니다.




보안칩 핀 넘버 .





다음은 CUBEMX 의 CLK 설정입니다. NUCLEO 보드의 입력 클럭이 8MHz 이므로, HSE 를 선택하고, 최종 HCLK는 100MHz





이제 I2C 설정을 해 보겠습니다. configuration 에서 I2C1을 클릭.





I2C1 설정 중, Parameter Setting 에서 수정한 것은 Clock speed 입니다. 보안 칩이 400KHz 까지 지원을 한다는데, 
오실로 스코프 파형이 별로라서 50KHz로 설정. ^^





다음은 DMA를 설정 합니다.
I2C1_TX , I2C1_RX 둘 다 선택합니다.




다음은 인터럽트설정 입니다. 여기서 주의할 점은 , I2C1_event interrupt 와 I2C1_error interrupt 2개도 선택해 줘야,
나중에 s/w 에서 DMA 함수가 동작합니다. 왜냐하면, DMA 함수에서 인터럽트 함수를 쓰고 있습니다.





다음은, GPIO 설정입니다. 포트를 PULL UP 해 줍니다. 이렇게 하면, 따로 풀업 저항이 필요 없습니다.




다음은 인터럽트를 설정해 보겠습니다. NVIC 를 클릭 합니다.





인터럽트 설정의 우선 순위를 정합니다. 아직 아무엏게나 번호를 붙이고 있습니다. 
나중에 된통 당해봐야 , 어떻게 번호를 붙이는 줄 알 것 같은데.. 막 나눠 주는 중입니다. ^^





마지막으로 프로젝트 설정입니다. 그냥 저장 위치하고, 프로젝트 이름, 컴파일러 종류 등을 설정해 줍니다.




이렇게 H/W 와 I2C DMA 프로그램의 설정은 완료 되었고, 글이 길어지니까.. I2C S/W 테스트는 다음에 올리겠습니다.


오늘 보안관련 칩 회사에서 I2C I/F 로 된 IC를 받았는데,
I2C Address 도 보안인가 봅니다. ㅠㅠ

저한테는 분명히 Device Address 가 0x7A 라고 해서, 아무리 해도 안되서,
보안(이게 보안이냐?, Device Address 를 찾아내는 작업)을 뚫어 봤습니다. ^^

원리는 I2C 칩은 항상 Device Address 를 보내서 해당 Address가 맞으면,  ACK를 보냅니다.
STM32Fx 의 I2C Tx 인터럽트는 
ACK를 받으면    인터럽트 콜백 함수를 호출하고,
ACK를 못 받으면 인터럽트 콜백 함수를 호출하지 않습니다.
그리고 주소는 7비트이므로, 0x00~0x7F 범위에 있습니다.

이 원리를 이용해서, 0x00~0x7F 범위의 Device Address를 1개씩 보내고 조금 기다리다 ACK 안오면,
다시 Device Address를 1개씩 보내고를 반복 하다보면 ACK가 오는 경우가 바로 이 칩의 Device Address 입니다.

다음 번에 I2C 에 대해서 다시 자세히 설명할 것이라 간단하게 어드레스 찾는 함수 쪽하고 콜백함수만 적어 봅니다.

Tx 콜백 함수 내용
1
2
3
4
5
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
    f_add_cmpl = 1;
    printf("Tx compleate~!!\r\n");
}
cs

Device address 찾는 함수.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void I2C_Add_serch(void)
{
    uint8_t i;
    for(i=0;i<0x80;i++)
    {
        f_add_cmpl=0;
        HAL_I2C_Master_Transmit_DMA(&hi2c1,(uint16_t)(i<<1),i2c_wr_buff,1);
        HAL_Delay(10);    
        if (f_add_cmpl == 1)
        {
            printf("Device Address search complete. [%02X]\r\n",i);
            return;
        }
    }
    printf("Device Address search fail.\r\n");
}
cs

이 두개의 함수와 printf() 함수 사용하는 내용 외에는 CUBEMX가 자동으로 만들어 준 것을 썼는데,
몇 줄 안되네요. ^^

이 방법으로 찾은 어드레스는 0x3D 였습니다. 1초도 안걸립니다. ^^





다음은 보안칩 에 잘못된 주소의 응답과 맞게 입력한 주소의 파형입니다.




다음은 전체 소스 입니다. DMA를 사용해 봤습니다. 자세한 내용은 다음에 ..
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
/**
  ******************************************************************************
  * File Name          : main.c
  * Description        : Main program body
  ******************************************************************************
  ** This notice applies to any and all portions of this file
  * that are not between comment pairs USER CODE BEGIN and
  * USER CODE END. Other portions of this file, whether 
  * inserted by the user or by software development tools
  * are owned by their respective copyright owners.
  *
  * COPYRIGHT(c) 2017 STMicroelectronics
  *
  * Redistribution and use in source and binary forms, with or without modification,
  * are permitted provided that the following conditions are met:
  *   1. Redistributions of source code must retain the above copyright notice,
  *      this list of conditions and the following disclaimer.
  *   2. Redistributions in binary form must reproduce the above copyright notice,
  *      this list of conditions and the following disclaimer in the documentation
  *      and/or other materials provided with the distribution.
  *   3. Neither the name of STMicroelectronics nor the names of its contributors
  *      may be used to endorse or promote products derived from this software
  *      without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  ******************************************************************************
  */
 
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f4xx_hal.h"
 
/* USER CODE BEGIN Includes */
#include "string.h"
/* USER CODE END Includes */
 
/* Private variables ---------------------------------------------------------*/
I2C_HandleTypeDef hi2c1;
DMA_HandleTypeDef hdma_i2c1_tx;
DMA_HandleTypeDef hdma_i2c1_rx;
 
UART_HandleTypeDef huart2;
 
/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
uint8_t f_add_cmpl=0;
uint8_t i2c_wr_buff[20];
 
/* USER CODE END PV */
 
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_I2C1_Init(void);
 
/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/
 
/* USER CODE END PFP */
 
/* USER CODE BEGIN 0 */
#ifdef __GNUC__
 #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
 #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
 
PUTCHAR_PROTOTYPE
{
 HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 11);
 return ch;
}
 
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
    f_add_cmpl = 1;
    printf("Tx compleate~!!\r\n");
}
 
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
}
 
void I2C_Add_serch(void)
{
    uint8_t i;
    for(i=0;i<0x80;i++)
    {
        f_add_cmpl=0;
        HAL_I2C_Master_Transmit_DMA(&hi2c1,(uint16_t)(i<<1),i2c_wr_buff,1);
        HAL_Delay(10);    
        if (f_add_cmpl == 1)
        {
            printf("Device Address search complete. [%02X]\r\n",i);
            return;
        }
    }
    printf("Device Address search fail.\r\n");
}
/* USER CODE END 0 */
 
int main(void)
{
 
  /* USER CODE BEGIN 1 */
 
  /* USER CODE END 1 */
 
  /* MCU Configuration----------------------------------------------------------*/
 
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
 
  /* USER CODE BEGIN Init */
 
  /* USER CODE END Init */
 
  /* Configure the system clock */
  SystemClock_Config();
 
  /* USER CODE BEGIN SysInit */
 
  /* USER CODE END SysInit */
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART2_UART_Init();
  MX_I2C1_Init();
 
  /* USER CODE BEGIN 2 */
    I2C_Add_serch();
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while(1)
  {
  /* USER CODE END WHILE */
 
  /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
 
}
 
/** System Clock Configuration
*/
void SystemClock_Config(void)
{
 
  RCC_OscInitTypeDef RCC_OscInitStruct;
  RCC_ClkInitTypeDef RCC_ClkInitStruct;
 
    /**Configure the main internal regulator output voltage 
    */
  __HAL_RCC_PWR_CLK_ENABLE();
 
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
 
    /**Initializes the CPU, AHB and APB busses clocks 
    */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_BYPASS;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 4;
  RCC_OscInitStruct.PLL.PLLN = 100;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
 
    /**Initializes the CPU, AHB and APB busses clocks 
    */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
 
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
 
    /**Configure the Systick interrupt time 
    */
  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
 
    /**Configure the Systick 
    */
  HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
 
  /* SysTick_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(SysTick_IRQn, 00);
}
 
/* I2C1 init function */
static void MX_I2C1_Init(void)
{
 
  hi2c1.Instance = I2C1;
  hi2c1.Init.ClockSpeed = 50000;
  hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
  hi2c1.Init.OwnAddress1 = 244;
  hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2c1.Init.OwnAddress2 = 0;
  hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  if (HAL_I2C_Init(&hi2c1) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
 
}
 
/* USART2 init function */
static void MX_USART2_UART_Init(void)
{
 
  huart2.Instance = USART2;
  huart2.Init.BaudRate = 115200;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
 
}
 
/** 
  * Enable DMA controller clock
  */
static void MX_DMA_Init(void
{
  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();
 
  /* DMA interrupt init */
  /* DMA1_Stream0_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 10);
  HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);
  /* DMA1_Stream1_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 10);
  HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);
 
}
 
/** Configure pins as 
        * Analog 
        * Input 
        * Output
        * EVENT_OUT
        * EXTI
*/
static void MX_GPIO_Init(void)
{
 
  GPIO_InitTypeDef GPIO_InitStruct;
 
  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
 
  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);
 
  /*Configure GPIO pin : B1_Pin */
  GPIO_InitStruct.Pin = B1_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);
 
  /*Configure GPIO pin : LD2_Pin */
  GPIO_InitStruct.Pin = LD2_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(LD2_GPIO_Port, &GPIO_InitStruct);
 
}
 
/* USER CODE BEGIN 4 */
 
/* USER CODE END 4 */
 
/**
  * @brief  This function is executed in case of error occurrence.
  * @param  None
  * @retval None
  */
void _Error_Handler(char * file, int line)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  while(1
  {
  }
  /* USER CODE END Error_Handler_Debug */ 
}
 
#ifdef USE_FULL_ASSERT
 
/**
   * @brief Reports the name of the source file and the source line number
   * where the assert_param error has occurred.
   * @param file: pointer to the source file name
   * @param line: assert_param error line source number
   * @retval None
   */
void assert_failed(uint8_t* file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
    ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
 
}
 
#endif
 
/**
  * @}
  */ 
 
/**
  * @}
*/ 
 
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
cs

i2c_test.ioc


I2C 통신으로 간단하게 EEPROM(AT24C04) 를 읽기/쓰기 테스트 해 봤습니다.


예전에 PSOC4로 해 놓은 자료가 있으니까, 자세한 설명은 빼고 동작 테스트만 해 보겠습니다.

먼저 I2C 인터럽트를 사용하여 데이터를 보내고 받는 함수들은 다음과 같습니다. 
1. HAL_I2C_Master_Transmit_IT()
   : I2C 인터럽트를 사용하여 데이터를 보내는(Write) 함수
2. HAL_I2C_MasterTxCpltCallback()
   : I2C 인터럽트를 사용하여 데이터를 보내고 다 보내면 인터럽트에 의해 호출되는 함수
3. HAL_I2C_Master_Receive_IT()
   : I2C 인터럽트를 사용하여 데이터를 받는(Receive) 함수
4. HAL_I2C_MasterRxCpltCallback()
   : I2C 인터럽트를 사용하여 정해진 개수의 데이터를 다 받으면 인터럽트에 의해 호출되는 함수

위의 함수들은 stm32f4xx_hal_i2c.c 파일에 정의되어 있습니다. 
HAL_I2C_MasterTxCpltCallback(), HAL_I2C_MasterRxCpltCallback() 과 같은 콜백함수는 __weak 키워드를 없애고
main.c 파일에 복사해서 내부 내용을 바꿔서 쓰면 인터럽트가 걸릴때 마다 콜벡함수를 호출하게 됩니다.

이 파일의 위치는 다음과 같습니다.



친구 돌잔치를 가봐야 해서 main.c 에서 EEPROM 읽고 쓰는 부분을 그림으로 올려 놓겠습니다.







[16 Byte Write 오실로 스코프 파형]





[Word Address Write 오실로 스코프 파형]





[16 Byte Read 오실로 스코프 파형]



소스 파일 첨부합니다.

I2C_test (2).zip


이제 I2C 통신 테스트를 해 보려고 합니다.


먼저 Cube 툴에서 I2C1 을 선택했더니, PB6(I2C1_SDA) , PB7(I2C_SCL) 로 핀아웃이 배정되는군요.



Nucleo 보드의 핀위치는 다음과 같습니다.



현재 제가 갖고 있는 I2C 테스트하기 가장 좋은 놈이 하나 있군요. 예전에 PSOC4로 I2C를 테스트하려고 산 EEPROM 모듈(AT24C04)가 있어서 Nucleo 보드와 위의 핀에 연결해 봤습니다.



I2C 테스트 코드를 IRQ 용으로 작성을 했는데, 아직은 IRQ 처럼 사용하지는 않았습니다.
그저 Write 테스트를 해 보려했습니다.

I2C Write 방식은 
'시작 + 7비트 DEVICE ADDRESS + R/W + ACK(EEPROM 에서 응답) + 끝' 이 기본이고,

여기서 추가로 데이터를 몇개 더 보낼 때는,
'시작 + 7비트 DEVICE ADDRESS + R/W + ACK(EEPROM 에서 응답) + 8비트 데이터#1 + ACK(EEPROM 에서 응답)
 + 8비트 데이터#2 + ACK(EEPROM 에서 응답) + ....+ 8비트 데이터#n + ACK(EEPROM 에서 응답) + 끝' 과 같이 하면 됩니다.

I2C 디바이스는 각각 고유의 DEVICE ADDRESS가 있는데, 위의 AT24C04 모듈의 DEV ADDR 은 0x50 입니다.
따라서 main.c 에 어드레스를 다음과 같이 정의했습니다.
#define I2C_ADDRESS        0x50

그리고 main() 함수에 다음을 추가해서 오실로 스코프로 찍어볼 준비를 했구요.
(I2C 설정은 Cube 툴에서 해주니 설정에 관한 설명은 생략합니다)

   i2c_tx_buf[0] = 0x01;
   i2c_tx_buf[1] = 0x02;
   i2c_tx_buf[2] = 0x03;
    if(HAL_I2C_Master_Transmit_IT(&hi2c1, (uint16_t)I2C_ADDRESS, i2c_tx_buf, 3)!= HAL_OK)
    {
      /* Error_Handler() function is called in case of error. */
      Error_Handler();
    }

그런데 스코프로 찍어 보니, 7비트 어드레스만 우측으로 1비트 밀려서 나가고 더이상 데이터가 나오지 않아서
자세히 보니, 어드레스를 좌측으로 1비트 밀어야 ACK가 응답으로 오면서 후속 데이터가 제대로 나가겠구나 .. 라는 생각이 드네요.

그래서 DEVICE Address 를 다음과 같이 수정했고,
#define I2C_ADDRESS        (0x50 << 1)

오실로 스코프의 파형도 정상으로 나왔습니다.



소스 파일 첨부합니다.

I2C_test.zip


+ Recent posts