main.c
0.02MB
stm32f4xx_it.c
0.01MB

슬슬 부품들이 도착하고 기본 통신 코드들을 작성했기 때문에 헥사콥터 코드를 작성하는 중이며 헥사콥터를 조종하기 위한 기본 코드인 Receiver에서 데이터를 받는 코드를 작성했다.

 

이번에 작성된 코드들은 Transceiver와 Receiver 두 개가 있어야 데이터가 받아지는 것을 확인할 수 있다.

 

 

Receiver - IBUS (0)

Transceiver

 

TGY-IA6B

 

드론에 사용할 Receiver이며 PWM 출력뿐만 아니라 IBUS 출력이 가능하다.

 

실제 드론에 사용할 방식은 PWM이 아닌 IBUS이고 PWM을 사용하면 PWM을 검출하기 위한 핀들이 추가적으로 사용되므로 핀 낭비가 심해진다.

 

IBUS를 사용하면 VCC, GND, IBUS로 핀을 3개로 줄일 수 있다.

 

IBUS 핀에서 SERVO 핀은 데이터를 출력하고 SENS 핀은 데이터를 입력받을 때 쓴다고 하는데 확실한 건지는 모르겠다.

 

UART 115200 Bits/s

IBUS는 UART 통신이며 115200 Bits/s의 Baud Rate로 통신한다. 또한 약 7ms 정도의 주기로 데이터를 보낸다.

 

표에서는 Check byte가 문제가 없을 거라고 하지만 Check byte 또한 0x40을 지켜주는 게 나을 것 같다.

 

 

코드 생성 - (1)

 

Parameter Settings
NVIC Settings
DMA Settings

UART4를 사용했다.

 

Baud Rate는 115200 Bits/s로 맞췄으며, IDLE interrupt를 사용할 것이다.

 

DMA는 Normal Mode로 사용한다.

 

 

코드 - main.c (2)

 

1
2
3
4
5
6
7
/* USER CODE BEGIN 0 */
 
uint8_t uart4_dma_buf[33];
 
uint8_t condition_uart4=0;
 
/* USER CODE END 0 */

 

DMA로 데이터를 받을 버퍼와 데이터 전송이 끝났다는 것을 알려줄 변수를 선언한다.

 

IBUS의 데이터 길이는 32byte 지만 33byte를 선언한 이유는 데이터가 밀리는 현상 때문에 일부로 1byte를 여유로 선언했다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* USER CODE BEGIN 2 */
 
  uint32_t timer[6]={0,};
//----------------------------------------------------------------------------------- ibus receiver
  LL_DMA_SetMemoryAddress(DMA1,LL_DMA_STREAM_2,(uint32_t)uart4_dma_buf);
  LL_DMA_SetPeriphAddress(DMA1,LL_DMA_STREAM_2,(uint32_t)(&UART4->DR));
 
  LL_DMA_SetDataLength(DMA1,LL_DMA_STREAM_2,33);
  LL_DMA_ClearFlag_TE2(DMA1);
  LL_DMA_ClearFlag_HT2(DMA1);
  LL_DMA_ClearFlag_TC2(DMA1);
  LL_DMA_ClearFlag_DME2(DMA1);
  LL_DMA_ClearFlag_FE2(DMA1);
  LL_DMA_EnableStream(DMA1,LL_DMA_STREAM_2);
 
  LL_USART_EnableDMAReq_RX(UART4);
  LL_USART_EnableIT_IDLE(UART4);
 
/* USER CODE END 2 */
 

 

timer 변수에 Receiver에서 받은 데이터들이 저장된다. 현재 가지고 있는 트랜시버가 6ch을 지원하기 때문에 6개의 배열을 선언했다.

 

UART 데이터를 DMA로 저장하기 위한 설정을 하는 코드이며, LL_DMA_SetDataLength()에서 데이터의 크기가 33인 이유는 위에서 말한 것처럼 데이터가 밀리기 때문에 1byte를 여유로 잡아준 것이다.

 

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
/* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
//========================================================================================================================= ibus receiver
      if(condition_uart4){
          LL_DMA_DisableStream(DMA1,LL_DMA_STREAM_2);
          LL_DMA_ClearFlag_TE2(DMA1);
          LL_DMA_ClearFlag_HT2(DMA1);
          LL_DMA_ClearFlag_TC2(DMA1);
          LL_DMA_ClearFlag_DME2(DMA1);
          LL_DMA_ClearFlag_FE2(DMA1);
          LL_DMA_EnableStream(DMA1,LL_DMA_STREAM_2);
 
          if(uart4_dma_buf[0]==0x20 && uart4_dma_buf[1]==0x40){
              timer[0]=(uint32_t)(uart4_dma_buf[7]<<8+ uart4_dma_buf[6];
              timer[1]=(uint32_t)(uart4_dma_buf[5]<<8+ uart4_dma_buf[4];
              timer[2]=(uint32_t)(uart4_dma_buf[9]<<8+ uart4_dma_buf[8];
              timer[3]=(uint32_t)(uart4_dma_buf[3]<<8+ uart4_dma_buf[2];
              timer[4]=(uint32_t)(uart4_dma_buf[11]<<8+ uart4_dma_buf[10];
              timer[5]=(uint32_t)(uart4_dma_buf[13]<<8+ uart4_dma_buf[12];
          }
          uart_counter++;
          if(uart_counter>50){
              printf("%d\t%d\t%d\r\n%d\t%d\t%d\n\n\r",timer[0],timer[1],timer[2],timer[3],timer[4],timer[5]);
              uart_counter=0;
          }
          condition_uart4=0;
      }
 
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
 

 

데이터를 받게 되면 stm32f4xx_it.c 내부의 코드에서 condition_uart4를 1로 바꿔준다.

 

while문 내부의 코드이며 DMA_SxNDTR를 다시 33으로 세팅하기 위해 DMA를 Disable 한 후 Enable 하는 코드가 처음에 나온다.

 

uart4_dma_buf의 0번째와 1번째 데이터들이 0x20, 0x40으로 제대로 받았다면 받은 데이터들을 timer 배열에 저장한다.

 

timer 배열이 순서대로 안돼있는데 이유는 throttle, pitch, roll, yaw에 맞는 채널의 순서 때문이고  timer[0]은 throttle, timer[1]은 pitch, timer[2]는 yaw, timer[3]은 roll로 사용할 것이다. 나머지 timer[4], timer[5]는 다른 용도로 사용한다.

 

uart_counter는 터미널 프로그램에 데이터를 출력할 때 일정 주기로 데이터를 보여주기 위해 넣은 것으로 실제 드론에 적용할 때는 지운 후 빌드할 것이다.

 

 

코드 - stm32f4xx_it.c (3)

 

1
2
3
4
5
/* USER CODE BEGIN EV */
 
extern uint8_t condition_uart4;
 
/* USER CODE END EV */

 

main.c에서 Receiver 데이터를 받았다는 것을 알려주기 위한 변수 condition_uart4를 extern으로 선언한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
void UART4_IRQHandler(void)
{
  /* USER CODE BEGIN UART4_IRQn 0 */
    //receiver data
    if(LL_USART_IsActiveFlag_IDLE(UART4)){
        LL_USART_ClearFlag_IDLE(UART4);
        condition_uart4=1;
    }
  /* USER CODE END UART4_IRQn 0 */
  /* USER CODE BEGIN UART4_IRQn 1 */
 
  /* USER CODE END UART4_IRQn 1 */
}
 

 

UART4_IRQHandler() 코드에 IDLE인터럽트가 발생했을 때 interrupt flag를 지우고 condition_uart4를 1로 변경하여 데이터의 전송이 끝났다는 것을 알린다.

 

DMA Interrupt는 사용하지 않았다.

 

 

결과 - (4)

 

첨부되어 있는 파일을 빌드하고 터미널 프로그램으로 결과를 보면 받은 데이터를 출력하는 것을 볼 수 있다.

 

첨부되어있는 파일에는 mpu6050 코드 또한 존재하기 때문에 pitch, roll 각도의 데이터도 출력이 되는 모습을 볼 수 있다.

 

 

 

-

UART와 interrupt, DMA를 사용하여 Receiver에서 전송되는 데이터를 읽어 봤다.

 

리시버는 계속해서 데이터를 보내기 때문에 UART에서 DMA가 실행되어 데이터를 버퍼에 넣는 시간의 싱크가 안 맞을 수 있다. 그렇게 되면 데이터가 한 칸씩 밀리는 현상이 일어나는데 그것을 바로잡는 해결방법 중 한 가지가 버퍼의 크기에 1byte의 여유분을 주는 방법이 있기 때문에 그 방식을 사용했다. 다른 효과적인 방법이 있을 경우 코드를 변경할 것이다.

 

 

코드에서 기본적인 MPU6050 자이로 센서에 관한 코드는 블로그에 작성해둔 글 그대로 사용했고 I2C, SPI 코드들 또한 기존에 블로그에 작성한 글에 나오는 코드들을 사용했고 사용할 것이다.

 

통신 관련 코드들에 에러가 생기면 그때그때 코드를 수정할 것이고 그 외에는 변경할 부분이 거의 없을 것이다.

 

 

 

모든 코드는 LL드라이버를 사용한다.

Posted by DDTXRX
,