드론을 만들 때 mpu9250, mpu6050과 같은 자이로 센서와 지자기 센서, 기압 센서와 통신하기 위해서 I2C 통신을 사용해야 한다.

 

HAL드라이버로 쿼드 콥터를 만들때는 주로 HAL_I2C_Master_Transmit(), HAL_I2C_Master_Receive(), HAL_I2C_Mem_Read() 3개의 함수로 통신을 했기 때문에 간단하게 코드를 만들 수 있었다.

 

하지만 LL드라이버로 통신을 하기 위해서는 데이터시트를 보면서 I2C 통신이 어떤 과정을 통해 이루어지고 레지스터 설정은 어떻게 해야 하는지 알아야 한다.

 

이 글에서는 I2C Master TX와 Master RX 두 가지의 코드를 작성하겠다.

 

테스트는 ds1307 i2c통신을 하는 rtc칩을 사용하겠다.

 

 

I2C 코드 생성 (0)

Parameter Settings
PIN

I2C Speed의 경우 fast mode를 사용해도 작동한다.

 

I2C 역시 코드를 생성하면 I2C에 관한 기본적인 레지스터 세팅 코드를 생성한다.

 

 

코드 - TX (1)

849p

데이터시트에 I2C에 관한 내용이 많지만 위 사진의 Sequence diagram이 중요하다고 생각한다.

 

7-bit transmit을 사용할 것이다.

 

diagram을 보고 코드를 작성해 보았다.

 

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
void I2C_Transmit(I2C_TypeDef * I2C, uint8_t address, uint8_t* data,uint16_t size){
 
    LL_I2C_DisableIT_TX(I2C1);
 
    if(!LL_I2C_IsEnabled(I2C)){
        LL_I2C_Enable(I2C);
    }
    LL_I2C_DisableBitPOS(I2C);
 
    while(LL_I2C_IsActiveFlag_BUSY(I2C));
 
    LL_I2C_GenerateStartCondition(I2C);
 
 
    while!LL_I2C_IsActiveFlag_SB(I2C) );
    LL_I2C_TransmitData8(I2C,address);
 
 
    while!LL_I2C_IsActiveFlag_ADDR(I2C));
    LL_I2C_ClearFlag_ADDR(I2C);
 
    for(uint16_t i=0;i<size;i++){
        LL_I2C_TransmitData8(I2C,data[i]);
        while(!LL_I2C_IsActiveFlag_TXE(I2C));
    }
    while(!LL_I2C_IsActiveFlag_BTF(I2C));
 
    LL_I2C_GenerateStopCondition(I2C);
}
 

 

 

인터럽트를 사용하지 않기 때문에 인터럽트를 disable 하였다. 

 

POS bit의 경우 TX일 때 사용하지 않는다.

 

그 외 Sequence diagram에 맞게 코드를 작성하였다.

 

TX코드만 가지고 실제 데이터가 전송되는지 모르기 때문에 RX코드 또한 작성해 보겠다.

 

 

코드 - RX (2)

850p
849

데이터시트를 참고하여 코드를 작성해보았다.

 

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
void I2C_Receive(I2C_TypeDef * I2C, uint8_t address, uint8_t* outputdata,uint16_t size){
    address |=(0x01);
 
    LL_I2C_DisableIT_RX(I2C1);
 
    if(!LL_I2C_IsEnabled(I2C)){
        LL_I2C_Enable(I2C);
    }
    LL_I2C_DisableBitPOS(I2C);
 
 
    while(LL_I2C_IsActiveFlag_BUSY(I2C));
 
    if(size==1)
        LL_I2C_AcknowledgeNextData(I2C,LL_I2C_NACK);
    else
        LL_I2C_AcknowledgeNextData(I2C,LL_I2C_ACK);
 
    LL_I2C_GenerateStartCondition(I2C);
 
    while!LL_I2C_IsActiveFlag_SB(I2C) );
    LL_I2C_TransmitData8(I2C,address);
 
    while!LL_I2C_IsActiveFlag_ADDR(I2C));
    LL_I2C_ClearFlag_ADDR(I2C);
 
    for(uint16_t i=0;i<size;i++){
        if(i==(size-1))
            LL_I2C_AcknowledgeNextData(I2C,LL_I2C_NACK);
        while(!LL_I2C_IsActiveFlag_RXNE(I2C));
        outputdata[i]=LL_I2C_ReceiveData8(I2C);
    }
    LL_I2C_GenerateStopCondition(I2C);
}
 

 

기본 구성은 TX 코드와 많이 비슷하게 생겼다.

 

device address 부분에서 LSB를 1로 SET 하는 코드가 추가 돼있는데 대부분의 I2C 칩들은 데이터를 읽는 주소 device address의 LSB 1이 SET 되어 있다.

 

FLAG가 SET 될 때까지 기다리고 다음 단계로 넘어가는 Polling방식이다.

 

위에 TX, RX코드들을 가지고 실제 DS1307 칩에 데이터를 쓰고 데이터를 읽어보겠다.

 

 

코드 - DS1307 (3)

DS1307의 기본적인 레지스터 주소와 데이터 통신 과정이다. device 주소는 0xd0이다.

 

대부분의 I2C 통신과 같은 방식으로 통신할 수 있다.

 

main.c

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
  /* USER CODE BEGIN 2 */
 
  uint8_t data[8];
 
  data[0]=0x00;        //register address
  data[1]=0x27;        //sec
  data[2]=0x55;        //min
  data[3]=0x05;        //hour
  data[4]=0x03;        //day
  data[5]=0x27;        //date
  data[6]=0x07;        //month
  data[7]=0x76;        //year
  I2C_Transmit(I2C1,0xd0,data,8);
 
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
      LL_mDelay(200);
      data[0]=0x00;
      I2C_Transmit(I2C1,0xd0,data,1);
      I2C_Receive(I2C1, 0xd0,data,7);
      printf("year:%x\tmonth:%x\tdate:%x\r\n",data[6],data[5],data[4]);
      printf("day:%x\thour:%x\tmin:%x\tsec:%x\n\n\r",data[3],data[2],data[1],data[0]);
      data[0]=0x00;
      data[1]=0x00;
      data[2]=0x00;
      data[3]=0x00;
      data[4]=0x00;
      data[5]=0x00;
      data[6]=0x00;
 
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
 

실제 연결

 

위에서 작성한 I2C_Transmit(), I2C_Receive() 함수를 사용하여 실제 DS1307 칩과 통신하면서 시간을 받아 봤다.

 

처음 sec에서 year까지 데이터를 전송하여 DS1307 내부의 데이터의 값들을 변경 후 while문 내부에서 200ms마다 통신을 하여 실제 데이터가 어떻게 나오는지 printf로 출력하였다.

 

 

 

-

위의 코드로도 I2C 통신은 잘되며 실제 헥사콥터에 적용할 때는 RX에서 DMA를 사용하여 Polling 방식을 사용하지 않고 데이터를 받을 때 다른 연산을 하도록 할 것이다.

 

DMA코드는 글의  양을 줄이기 위해 헥사콥터를 제작할 때 따로 글을 쓸 생각이다.

Posted by DDTXRX
,