STM32F4

[STM32F4xx] I2C DMA 프로그램 (CUBEMX)

트라이문 2018. 9. 6. 20:34

이전에 쓴 글([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