看到电调支持Dshot125-600的协议,想自己做一个支持Dshot协议的驱动,所以研究了一下,如何利用精确的PWM产生Dshot协议。
先看结果!图中为油门值为1500的时候的输出的Dshot600的一个协议帧
长的代表1 短的代表0,一共18个数据,包括最后连个低电平表示的帧间隔。
=============================分隔符================================
【Dshot协议】
主要用于飞控和电调之间的通讯,根据通讯速录不同可以分为Dshot150,Dshot300,...等等
•DShot600 – 1200k bits/Sec
•DShot600 – 600k bits/Sec
•DShot300 – 300k bits/Sec
•DShot150 – 150k bits/Sec
DSHOT协议一帧信号由16位组成:
1、油门值:
前11位二级制数表示油门的大小,其范围为0-2047。
2、回传标志位:
第12位,由0和1表示,0为不回传数据,1位回传数据,相交于传统的电调这种电调多了回传信息,更有利于开发者进行开发。
3、校验位:
第13-16位组成, 其数值为是将前12位分成3组,每组4位,将三组数据进行亦或计算得出的数值即为校验位。
每一位的数值均由0和1组成,由PWM波的占空比进行数值的表示。
拿Dshot600协议来说:每600k b/s ,每包数据有18个bit ,所以每包数据要30us。每个bit大概要1.67us。 上图表示bit1时的脉宽为1.22us 周期1.66us。 表示bit0时的脉宽为610ns。
其实我也不确定表示1和0时,高电平的时间是怎么分割的,感觉是单个bit周期的70%和30%左右即可,等我电调回来了实验一下。
所以这个图中表示的就是101 1101 1100 0 100 0 - -
前11位就是1500的二进制表示,下一个0代表不需要回传,100 0代表CRC校验。 - -代表两个低电平 帧分割 。
=============================分隔符================================
首先进行CubeMX的配置:我使用的是F103VET6开发板
Time2的配置,注意合适的分频系数以及重装载值。
DMA的配置主要注意数据宽度,以及方向,还有内存地址递增。要和程序进行结合
实际函数使用 HAL_TIM_PWM_Start_DMA进行发送
HAL_TIM_PWM_Start_DMA(&htim2,TIM_CHANNEL_3,(uint32_t *)esc_cmd,ESC_CMD_BUF_LEN);
CubeMX中配置外设的数据宽度位16位(半字),因为PWM对应的存放定时值的寄存器长度是16。
初始化数组的时候也要uint16的数组,从这个地址每次取16bit。
uint16_t ESC_CMD[18]={0};
HAL_TIM_PWM_Start_DMA的第三个参数是一个地址,不用理会(uint32_t)。只要你设置了两个数据宽度为Half Word, DMA就会从这个地址开始每次拿16位数据,然后地址递增,拿够你设置的个数。
这样就可以输出希望个数的 占空比不同的 PWM波了(不要忘记在DMA输出完后 ,回调中关闭DMA)。
其他的CubeMX配置都是比较简单的了,时钟也贴出来了。最后调用pwmWriteDigital函数就可以输出希望的Dshot协议了。
=============================分隔符================================
【程序部分】【程序参考bf开源固件中的部分】
//定时器 4分频72/4=18mhz ;分频不固定,可自行调整
//pwm波周期 1.67us ;对应的pwm分辨率 1.67us /(1/18)= 30;
// 0.625us/(1/18) = 11; 0的占空比11/30
// 1.250us/(1/18) = 22; 1的占空比22/30
#define ESC_BIT_0 11
#define ESC_BIT_1 22
#define ESC_CMD_BUF_LEN 18
uint16_t ESC_CMD[ESC_CMD_BUF_LEN]={0};
//定时器 1分频32/1=32mhz ;分频不固定,可自行调整 记一次数就是1/32 us
//pwm波周期 1.67us ;对应的pwm分辨率 1.67us /(1/32)= 53.44;
// 0.625us/(1/32) = 20; 0的占空比20/53
// 1.250us/(1/32) = 40; 1的占空比40/53
// #define ESC_BIT_0 20
// #define ESC_BIT_1 40
/*************************************************************
** Function name: prepareDshotPacket
** Descriptions: CRC校验以及要不要回传信息
** Input parameters: value: 油门大小 注意取值范围, requestTelemetry 是否请求回传数据
** Output parameters: None
** Returned value: None
** Remarks: None
*************************************************************/
static uint16_t prepareDshotPacket(const uint16_t value, bool requestTelemetry)
{
// 油门大小为11位 所以这里先左移一位 添加上请求回传标志共12位
uint16_t packet = (value << 1) | (requestTelemetry ? 1 : 0);
// 将12位数据分为3组 每组4位, 进行异或
// 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 将CRC添加到后四位
packet = (packet << 4) | csum;
return packet;
}
/*************************************************************
** Function name: pwmWriteDigital
** Descriptions: 根据输入 填充esc_cmd,填充的数值代表每一位的高低电平
** Input parameters: None
** Output parameters: None
** Returned value: None
** Remarks: None
*************************************************************/
static void pwmWriteDigital(uint16_t *esc_cmd, uint16_t value)
{
value = ( (value > 2047) ? 2047 : value );
value = prepareDshotPacket(value, 0);
esc_cmd[0] = (value & 0x8000) ? ESC_BIT_1 : ESC_BIT_0;
esc_cmd[1] = (value & 0x4000) ? ESC_BIT_1 : ESC_BIT_0;
esc_cmd[2] = (value & 0x2000) ? ESC_BIT_1 : ESC_BIT_0;
esc_cmd[3] = (value & 0x1000) ? ESC_BIT_1 : ESC_BIT_0;
esc_cmd[4] = (value & 0x0800) ? ESC_BIT_1 : ESC_BIT_0;
esc_cmd[5] = (value & 0x0400) ? ESC_BIT_1 : ESC_BIT_0;
esc_cmd[6] = (value & 0x0200) ? ESC_BIT_1 : ESC_BIT_0;
esc_cmd[7] = (value & 0x0100) ? ESC_BIT_1 : ESC_BIT_0;
esc_cmd[8] = (value & 0x0080) ? ESC_BIT_1 : ESC_BIT_0;
esc_cmd[9] = (value & 0x0040) ? ESC_BIT_1 : ESC_BIT_0;
esc_cmd[10] = (value & 0x0020) ? ESC_BIT_1 : ESC_BIT_0;
esc_cmd[11] = (value & 0x0010) ? ESC_BIT_1 : ESC_BIT_0;
esc_cmd[12] = (value & 0x8) ? ESC_BIT_1 : ESC_BIT_0;
esc_cmd[13] = (value & 0x4) ? ESC_BIT_1 : ESC_BIT_0;
esc_cmd[14] = (value & 0x2) ? ESC_BIT_1 : ESC_BIT_0;
esc_cmd[15] = (value & 0x1) ? ESC_BIT_1 : ESC_BIT_0;
HAL_TIM_PWM_Start_DMA(&htim2,TIM_CHANNEL_3,(uint32_t *)esc_cmd,ESC_CMD_BUF_LEN);
}
/*************************************************************
** Function name: HAL_TIM_PWM_PulseFinishedCallback
** Descriptions: 每次DMA发送完后会进这个回调函数,可以做标记。要记得关闭DMA
** Input parameters: None
** Output parameters: None
** Returned value: None
** Remarks: None
*************************************************************/
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
HAL_TIM_PWM_Stop_DMA(&htim2, TIM_CHANNEL_3);
}
参考文章:
https://blog.csdn.net/qq_43650722/article/details/116402588?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522162728504716780261987921%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=162728504716780261987921&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_v2~rank_v29-5-116402588.first_rank_v2_pc_rank_v29&utm_term=DSHOT&spm=1018.2226.3001.4187
https://blog.csdn.net/qq_35081072/article/details/107747996?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522162728504716780265454079%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=162728504716780265454079&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-4-107747996.first_rank_v2_pc_rank_v29&utm_term=DSHOT&spm=1018.2226.3001.4187
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)