CubeMX 配置PWM使用DMA,生成Dshot600的协议

看到电调支持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

  • 7
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值