본문 바로가기
뻘소리

드론 dshot 프로토콜 직접 구현

by mokhwasomssi 2021. 2. 19.

개발환경 : stm32cubeide, stm32f4 개발보드, hal 라이브러리

https://github.com/mokhwasomssi/stm32f4_hal_dshot

 

mokhwasomssi/stm32f4_hal_dshot

stm32f4 hal dshot(drone esc protocol) source code. Contribute to mokhwasomssi/stm32f4_hal_dshot development by creating an account on GitHub.

github.com

 

매우 간단하게 코드를 짰다.

원래는 이것저것 넣으려고 했지만

모터는 잘 돌아가는 것 같아서 나중에 필요할 때 넣기로 했다.

디샷 패킷 변하는 것도 찍었어야 했는데ㅠ

 

dshot600

속도 제어만 가능 (48 - 2000 throttle)

다른 명령 안됨 (led, 방향 바꾸기, 등등등)

값 넣으면 속도는 잘 바뀌는데 다른 명령이 적용이 안되는 걸로 봐서

아마 다른 절차가 있는 것 같다.

 

소스코드

 

dshot.h

#ifndef _DSHOT_H_
#define _DSHOT_H_

#include "stm32f4xx_hal.h"
#include <stdbool.h>


#define MOTOR_BIT_0           7
#define MOTOR_BIT_1           14
#define MOTOR_BITLENGTH       20

#define DSHOT_FRAME_SIZE 18    // 0-15 : dshot frame, 16-17 : reset


void dshot600(uint32_t *motor, uint16_t value);


#endif /* _DSHOT_H_ */

dshot.c

 #include "dshot.h"

void dshot600(uint32_t *motor, uint16_t value)
{
  uint16_t packet = value << 1;

  // compute checksum
  int csum = 0;
  int csum_data = packet;
  for (int i = 0; i < 3; i++) {
    csum ^=  csum_data;   // xor data by nibbles
    csum_data >>= 4;
  }
  csum &= 0xf;

  // append checksum
  packet = (packet << 4) | csum;

  // encoding
  int i;
  for (i = 0; i < 16; i++)
  {
      motor[i] = (packet & 0x8000) ? MOTOR_BIT_1 : MOTOR_BIT_0;  // MSB first
      packet <<= 1;
  }

  motor[i++] = 0;
  motor[i++] = 0;
}

main.c

...
#include "dshot.h"
...
uint32_t motor1[DSHOT_FRAME_SIZE];    // dshot data frame array transmitted through DMA
uint16_t value = 0;					  // need to keep value = 0 for a while after power on
...
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  if (htim == &htim11)
  {
	  dshot600(motor1, value);
	  HAL_TIM_PWM_Start_DMA(&htim2, TIM_CHANNEL_1, motor1, 18);
  }
}
...
int main(void)
{
    ...
    HAL_TIM_Base_Start_IT(&htim11);    // timer interrupt 1kHz
    ...
}
...
​

 

STM32CubeMX TIM, DMA 설정

TIM

베타플라이트 디샷 코드의 프리스케일러는 계산은 이렇다.

// src > main > drivers > pwm_output_dshot_hal.c 
Prescaler = (uint16_t)(lrintf((float) timerClock(timer) / getDshotHz(pwmProtocolType) + 0.01f) - 1);

대충 이렇게 구하면 된다.

프리스케일러 = 사용하는 타이머 클럭 속도 / 디샷 클럭 속도 - 1

TIM2 클럭 속도 : 100Mhz

Dshot600 클럭 속도 : 12Mhz

프리스케일러 : 8.33333 ≒ 8 - 1

DMA

왜 이런 설정을 쓰는지는 잘 모르겠지만 나중에 알아보도록 하자.

 

 

코드는 베타플라이트 깃헙과 MGeo 씨의 코드를 참고했다.

https://github.com/betaflight/betaflight

 

betaflight/betaflight

Open Source Flight Controller Firmware. Contribute to betaflight/betaflight development by creating an account on GitHub.

github.com

https://forum.arduino.cc/index.php?topic=624454.0

 

Dshot ESC Protocol on STM32 using DMA with Arduino IDE

 

forum.arduino.cc

MGeo씨가 tim, dma 설정 다 찾아준 덕에 수월하게 할 수 있었다.