AM32开源代码之代码分析 - DSHOT/BDSHOT
- 1. 源由
- 2. 协议设计
- 2.1 位格式
- 2.2 帧结构
- 2.3 CRC计算
- 2.4 帧格式(eRPM)
- 2.4.1 DSHOT
- 2.4.2 BDSHOT
- 2.5 EDT编码策略
- 2.5 eRPM传输
- 3. 框架设计
- 3.1 初始化
- 3.2 动态过程
- 3.2.1 飞控触发
- 3.2.2 定时触发
- 3.3 协议检测
- 3.3.1 detectInput
- 3.3.2 checkDshot
- 3.3.3 checkServo
- 3.4 报文处理
- 3.4.1 computeDshotDMA
- 3.4.2 make_dshot_package
- 4. 总结
- 5. 参考资料
1. 源由
现在的ESC电子调速器,除了ESC电传通过数字形式反馈当前工作状态。
控制和数据反馈也离不开DSHOT和BDSHOT协议,因此,本章我们研读下关于DSHOT和BDSHOT在AM32代码上的视线。
2. 协议设计
2.1 位格式
DSHOT
注:BDSHOT正好是DSHOT的反向。
2.2 帧结构
- 11 bit throttle: 2048 possible values. 0 is reserved for disarmed. 1-47 are reserved for special commands. Leaving 48 to 2047 (2000 steps) for the actual throttle value
- 1 bit telemetry request - if this is set, telemetry data is sent back via a separate channel
- 4 bit CRC: (Cyclic Redundancy) Check to validate data (throttle and telemetry request bit)
2.3 CRC计算
计算公式:
crc = (value ^ (value >> 4) ^ (value >> 8)) & 0x0F
举例:
value = 100000101100
(>>4) = 000010000010 # right shift value by 4
(^) = 100010101110 # XOR with value
(>>8) = 000000001000 # right shift value by 8
(^) = 100010100110 # XOR with previous XOR
(0x0F) = 000000001111 # Mask 0x0F
(&) = 000000000110 # CRC
2.4 帧格式(eRPM)
在双向DSHOT和DSHOT模式下,电子调速器(ESC)发送的eRPM遥测帧都是16位值。
eeemmmmmmmmmcccc
- 12位:eRPM数据
- 3位:需要左移的位数,以获取以微秒为单位的周期
- 9位:周期基数
- 4位:CRC
2.4.1 DSHOT
2.4.2 BDSHOT
2.5 EDT编码策略
扩展DSHOT遥测(EDT, Extended DShot Telemetry)是DSHOT“标准”中相对较晚添加的一项功能。它允许在eRPM帧内传输额外的遥测数据,这意味着不需要额外的线来从电子调速器(ESC)传输额外的遥测数据到飞控器。
pppp mmmmmmmm
注:这个功能需要ESC和飞控器双方都支持。Bleujay、BLHeli_32、AM32在它们的最新版本中都支持这一功能。
其主要方法就是通过左移的方式将RPM值最高位不为零(不存在如下帧,若存在该帧则是遥测帧):
eee 0mmmmmmmm
2.5 eRPM传输
实际上并不是直接将这个值发送回飞控器。
- 应用了GCR编码,并且通过将半字节(4位一组)按照以下表格进行映射,将16位值映射为20位值
- 将GCR映射为21位值,这个新值以0开始,剩余的位由以下两条规则设置:
如果GCR数据中的当前位是1:新值中的当前位是前一个新位的反转
如果GCR数据中的当前位是0:新值中的当前位与前一个新位相同
e.g.
16 bit: 1000001011000110
Nibbles: 1000 0010 1100 0110
Hex: 0x8 0x2 0xC 0x6
Mapped: 0x1A 0x12 0x1E 0x16
GCR: 11010 10010 11110 10110
mapped: 010011 00011 01011 00100
解码方式:
gcr = (value ^ (value >> 1));
3. 框架设计
3.1 初始化
首先,进行IO&Timer初始化(G071代码有部分DMA初始化)
main
└──> initCorePeripherals
└──> MX_TIM3_Init
#define INPUT_PIN LL_GPIO_PIN_6
#define INPUT_PIN_PORT GPIOA
然后,DMA挂中断钩子函数
main
└──> initCorePeripherals
└──> MX_DMA_Init
└──> DMA1_Channel1_IRQHandler
最后,ADC相关报文钩子函数
main
└──> enableCorePeripherals
└──> EXTI4_15_IRQHandler
3.2 动态过程
3.2.1 飞控触发
DMA1_Channel1_IRQHandler
└──> transfercomplete
-
检查
armed && dshot_telemetry
是否同时为真- 条件:
out_put == 0/1
判断 - 操作:
- 0:调用
sendDshotDma
接收报文;待处理标识compute_dshot_flag = 1
- 1:调用
receiveDshotDma
接收报文;待处理标识compute_dshot_flag = 2
- 条件:
-
检查
inputSet
是否为 1- 条件:
inputSet == 0/1
判断 - 操作:
- 0:调用
detectInput
检查输入协议 - 1:根据协议进行相关报文处理
- 条件:
transfercomplete
|
|-- 如果 (armed && dshot_telemetry)
| |-- 如果 (out_put)
| | |-- 调用 receiveDshotDma()
| | |-- compute_dshot_flag = 2
| | |-- 返回
| |-- 否则
| | |-- 调用 sendDshotDma()
| | |-- compute_dshot_flag = 1
| | |-- 返回
|
|-- 如果 (inputSet == 0)
| |-- 调用 detectInput()
| |-- 调用 receiveDshotDma()
| |-- 返回
|
|-- 如果 (inputSet == 1)
| |-- 如果 (dshot_telemetry)
| | |-- 如果 (out_put)
| | | |-- 调用 make_dshot_package(e_com_time)
| | | |-- 调用 computeDshotDMA()
| | | |-- 调用 receiveDshotDma()
| | | |-- 返回
| | |-- 否则
| | | |-- 调用 sendDshotDma()
| | | |-- 返回
| |
| |-- 否则 (dshot_telemetry 为 false)
| | |-- 如果 (dshot == 1)
| | | |-- 调用 computeDshotDMA()
| | | |-- 调用 receiveDshotDma()
| | |
| | |-- 如果 (servoPwm == 1)
| | |-- 如果 (getInputPinState())
| | | |-- buffersize = 3
| | |-- 否则
| | |-- buffersize = 2
| | |-- 调用 computeServoInput()
| | |-- 调用 receiveDshotDma()
| |
| |-- 如果 (!armed)
| |-- 如果 (dshot && (average_count < 8) && (zero_input_count > 5))
| | |-- average_count++
| | |-- average_packet_length += (dma_buffer[31] - dma_buffer[0])
| | |-- 如果 (average_count == 8)
| | |-- dshot_frametime_high = (average_packet_length >> 3) + (average_packet_length >> 7)
| | |-- dshot_frametime_low = (average_packet_length >> 3) - (average_packet_length >> 7)
| |
| |-- 如果 (adjusted_input < 0)
| | |-- adjusted_input = 0
| |
| |-- 如果 (adjusted_input == 0 && calibration_required == 0)
| | |-- zero_input_count++
| |-- 否则
| |-- zero_input_count = 0
| |-- 如果 (adjusted_input > 1500)
| | |-- 如果 (getAbsDif(adjusted_input, last_input) > 50)
| | | |-- enter_calibration_count = 0
| | |-- 否则
| | |-- enter_calibration_count++
| |
| | |-- 如果 (enter_calibration_count > 50 && (!high_calibration_set))
| | |-- 调用 playBeaconTune3()
| | |-- calibration_required = 1
| | |-- enter_calibration_count = 0
| |-- last_input = adjusted_input
|
|-- 结束
3.2.2 定时触发
EXTI4_15_IRQHandler
└──> processDshot
-
检查
compute_dshot_flag
是否为 1- 条件:
compute_dshot_flag == 1
- 操作:
- 调用
computeDshotDMA()
函数。这通常会处理与 DShot 相关的数据计算或操作。 - 将
compute_dshot_flag
设置为 0,以重置标志位,表示处理已完成。
- 调用
- 条件:
-
检查
compute_dshot_flag
是否为 2- 条件:
compute_dshot_flag == 2
- 操作:
- 调用
make_dshot_package(e_com_time)
函数。这通常会生成 DShot 数据包,并可能涉及到对信号或数据的打包处理。 - 将
compute_dshot_flag
设置为 0,以重置标志位,表示处理已完成。 - 使用
return
语句跳出函数,防止执行后续的setInput()
调用。
- 调用
- 条件:
-
调用
setInput()
- 仅在
compute_dshot_flag
不为 1 或 2 时执行。该函数的作用是设置输入数据或处理其他与输入相关的操作。由于return
已在第二个条件中使用,因此setInput()
只在没有其他标志位处理的情况下执行。
- 仅在
processDshot()
|
|-- if (compute_dshot_flag == 1)
| |
| |-- computeDshotDMA()
| |-- compute_dshot_flag = 0
|
|-- if (compute_dshot_flag == 2)
| |
| |-- make_dshot_package(e_com_time)
| |-- compute_dshot_flag = 0
| |-- return
|
|-- setInput()
3.3 协议检测
3.3.1 detectInput
- 计算最小脉冲宽度 和 平均脉冲宽度:这些值可以用于检测和分析输入信号的特性。
- 条件检查:根据不同的标志位,调用相应的函数进行 DShot 或伺服 PWM 的检查。这确保了根据输入模式适当地处理信号。
detectInput()
|
|-- smallestnumber = 20000
|-- average_signal_pulse = 0
|-- lastnumber = dma_buffer[0]
|-- for (int j = 1; j < 31; j++)
| |
| |-- if (dma_buffer[j] - lastnumber > 0)
| | |
| | |-- if ((dma_buffer[j] - lastnumber) < smallestnumber)
| | | |-- smallestnumber = dma_buffer[j] - lastnumber
| | |
| | |-- average_signal_pulse += (dma_buffer[j] - lastnumber)
| |
| |-- lastnumber = dma_buffer[j]
|-- average_signal_pulse = average_signal_pulse / 32
|
|-- if (dshot == 1)
| |-- checkDshot()
|
|-- if (servoPwm == 1)
| |-- checkServo()
|
|-- if (!dshot && !servoPwm)
| |-- checkDshot()
| |-- checkServo()
-
初始化变量
smallestnumber
设置为 20000average_signal_pulse
设置为 0lastnumber
设为dma_buffer[0]
-
循环计算信号脉冲
- 循环范围: 从
j = 1
到j < 31
(共 30 次) - 操作:
- 如果
dma_buffer[j] - lastnumber
大于 0(即当前值大于前一个值):- 如果当前脉冲宽度
dma_buffer[j] - lastnumber
小于smallestnumber
,则更新smallestnumber
- 累加当前脉冲宽度到
average_signal_pulse
- 如果当前脉冲宽度
- 更新
lastnumber
为当前dma_buffer[j]
- 如果
- 循环范围: 从
-
计算平均信号脉冲
- 将
average_signal_pulse
除以 32 以获取平均值
- 将
-
根据标志位执行不同的检查
- 如果
dshot == 1
,调用checkDshot()
函数 - 如果
servoPwm == 1
,调用checkServo()
函数 - 如果既
dshot
又servoPwm
都为false
,则分别调用checkDshot()
和checkServo()
函数
- 如果
3.3.2 checkDshot
- 根据信号特征启用 DShot 模式: 根据信号的最小脉冲宽度和平均脉冲宽度来决定是否启用 DShot 模式,并设置相关的计时器和缓冲区参数。
- 动态配置计时器: 根据 CPU 频率来调整计时器的预分频器,以确保适当的计时精度。
- 配置缓冲区: 设置
buffer_padding
和buffersize
,这些参数影响信号的处理和存储。
checkDshot()
|
|-- if ((smallestnumber >= 1) && (smallestnumber < 4) && (average_signal_pulse < 60))
| |
| |-- ic_timer_prescaler = 0
| |-- if (CPU_FREQUENCY_MHZ > 100)
| | |-- output_timer_prescaler = 1
| |-- else
| | |-- output_timer_prescaler = 0
| |-- dshot = 1
| |-- buffer_padding = 14
| |-- buffersize = 32
| |-- inputSet = 1
|
|-- if ((smallestnumber >= 4) && (smallestnumber <= 8) && (average_signal_pulse < 100))
| |
| |-- dshot = 1
| |-- ic_timer_prescaler = 1
| |-- if (CPU_FREQUENCY_MHZ > 100)
| | |-- output_timer_prescaler = 3
| |-- else
| | |-- output_timer_prescaler = 1
| |-- buffer_padding = 7
| |-- buffersize = 32
| |-- inputSet = 1
-
第一个条件判断
- 条件:
smallestnumber
在 1 到 4 之间(包括 1,不包括 4)average_signal_pulse
小于 60
- 操作:
- 将
ic_timer_prescaler
设为 0 - 根据
CPU_FREQUENCY_MHZ
判断:- 如果 CPU 频率大于 100 MHz,
output_timer_prescaler
设为 1 - 否则,
output_timer_prescaler
设为 0
- 如果 CPU 频率大于 100 MHz,
- 设置
dshot
标志为 1(启用 DShot 模式) - 将
buffer_padding
设为 14 - 将
buffersize
设为 32 - 将
inputSet
设为 1(表示输入设置已完成)
- 将
- 条件:
-
第二个条件判断
- 条件:
smallestnumber
在 4 到 8 之间(包括 4 和 8)average_signal_pulse
小于 100
- 操作:
- 将
dshot
标志设为 1(启用 DShot 模式) - 将
ic_timer_prescaler
设为 1 - 根据
CPU_FREQUENCY_MHZ
判断:- 如果 CPU 频率大于 100 MHz,
output_timer_prescaler
设为 3 - 否则,
output_timer_prescaler
设为 1
- 如果 CPU 频率大于 100 MHz,
- 将
buffer_padding
设为 7 - 将
buffersize
设为 32 - 将
inputSet
设为 1(表示输入设置已完成)
- 将
- 条件:
3.3.3 checkServo
- 根据信号特征启用伺服 PWM 模式: 根据信号的最小脉冲宽度来决定是否启用伺服 PWM 模式,并配置相关参数。
- 调整计时器设置: 动态设置计时器预分频器,以匹配 CPU 频率和信号要求。
- 配置处理参数: 设置
buffersize
为 2,这可能意味着伺服信号只需要少量数据进行处理和存储。
checkServo()
|
|-- if (smallestnumber > 200 && smallestnumber < 20000)
| |
| |-- servoPwm = 1
| |-- ic_timer_prescaler = CPU_FREQUENCY_MHZ - 1
| |-- buffersize = 2
| |-- inputSet = 1
-
条件判断
- 条件:
smallestnumber
大于 200 且小于 20000
- 条件:
-
操作:
- 启用伺服 PWM 模式: 将
servoPwm
设为 1,表示启用伺服 PWM 模式。 - 设置计时器预分频器: 将
ic_timer_prescaler
设为CPU_FREQUENCY_MHZ - 1
,以便适应 CPU 频率的要求。 - 配置缓冲区大小: 将
buffersize
设为 2,以适应伺服 PWM 信号的处理。 - 标记输入设置完成: 将
inputSet
设为 1,表示输入设置已完成。
- 启用伺服 PWM 模式: 将
3.4 报文处理
3.4.1 computeDshotDMA
注:貌似关于指令部分的代码对照命令表格尚未都完成。关于16bit长度脉冲宽度方面的内容尚没有完全明白,后续再跟进!!!
computeDshotDMA()
|
|-- dshot_frametime = dma_buffer[31] - dma_buffer[0]
|-- halfpulsetime = dshot_frametime >> 5
|
|-- if ((dshot_frametime > dshot_frametime_low) && (dshot_frametime < dshot_frametime_high))
| |
| |-- signaltimeout = 0
| |-- for (int i = 0; i < 16; i++)
| | |
| | |-- dpulse[i] = ((dma_buffer[j + (i << 1) + 1] - dma_buffer[j + (i << 1)]) > (halfpulsetime))
| |
| |-- uint8_t calcCRC = ((dpulse[0] ^ dpulse[4] ^ dpulse[8]) << 3 | (dpulse[1] ^ dpulse[5] ^ dpulse[9]) << 2 | (dpulse[2] ^ dpulse[6] ^ dpulse[10]) << 1 | (dpulse[3] ^ dpulse[7] ^ dpulse[11]))
| |-- uint8_t checkCRC = (dpulse[12] << 3 | dpulse[13] << 2 | dpulse[14] << 1 | dpulse[15])
| |
| |-- if (!armed)
| | |
| | |-- if (dshot_telemetry == 0)
| | |
| | |-- if (getInputPinState())
| | |
| | |-- high_pin_count++
| | |-- if (high_pin_count > 100)
| | |
| | |-- dshot_telemetry = 1
| |
| |-- if (dshot_telemetry)
| | |
| | |-- checkCRC = ~checkCRC + 16
| |
| |-- int tocheck = (dpulse[0] << 10 | dpulse[1] << 9 | dpulse[2] << 8 | dpulse[3] << 7 | dpulse[4] << 6 | dpulse[5] << 5 | dpulse[6] << 4 | dpulse[7] << 3 | dpulse[8] << 2 | dpulse[9] << 1 | dpulse[10])
| |
| |-- if (calcCRC == checkCRC)
| | |
| | |-- signaltimeout = 0
| | |-- dshot_goodcounts++
| | |
| | |-- if (dpulse[11] == 1)
| | | |
| | | |-- send_telemetry = 1
| |
| | |-- if (tocheck > 47)
| | | |
| | | |-- if (EDT_ARMED)
| | | |
| | | |-- newinput = tocheck
| | | |-- dshotcommand = 0
| | | |-- command_count = 0
| | |
| | |-- if ((tocheck <= 47) && (tocheck > 0))
| | | |
| | | |-- newinput = 0
| | | |-- dshotcommand = tocheck
| | |
| | |-- if (tocheck == 0)
| | |
| | |-- if (EDT_ARM_ENABLE == 1)
| | |
| | |-- EDT_ARMED = 0
| | |-- newinput = 0
| | |-- dshotcommand = 0
| | |-- command_count = 0
| |
| |-- if ((dshotcommand > 0) && (running == 0) && armed)
| | |
| | |-- if (dshotcommand != last_command)
| | | |
| | | |-- last_command = dshotcommand
| | | |-- command_count = 0
| | |
| | |-- if (dshotcommand < 5)
| | | |
| | | |-- command_count = 6
| | |
| | |-- command_count++
| | |-- if (command_count >= 6)
| | |
| | |-- command_count = 0
| | |
| | |-- switch (dshotcommand)
| | |
| | |-- case 1: play_tone_flag = 1
| | |-- case 2: play_tone_flag = 2
| | |-- case 3: play_tone_flag = 3
| | |-- case 4: play_tone_flag = 4
| | |-- case 5: play_tone_flag = 5
| | |-- case 7: dir_reversed = 0; forward = 1 - dir_reversed
| | |-- case 8: dir_reversed = 1; forward = 1 - dir_reversed
| | |-- case 9: bi_direction = 0
| | |-- case 10: bi_direction = 1
| | |-- case 12: saveEEpromSettings(); play_tone_flag = 1 + dir_reversed
| | |-- case 13: dshot_extended_telemetry = 1; send_extended_dshot = 0b111000000000; if (EDT_ARM_ENABLE == 1) { EDT_ARMED = 1; }
| | |-- case 14: dshot_extended_telemetry = 0; send_extended_dshot = 0b111011111111
| | |-- case 20: forward = 1 - dir_reversed
| | |-- case 21: forward = dir_reversed
| | |-- last_dshot_command = dshotcommand
| | |-- dshotcommand = 0
| |
| |-- else
| |
| |-- dshot_badcounts++
-
初始化与计算
dshot_frametime
计算 DSHOT 帧的时间。halfpulsetime
是帧时间的一半,右移 5 位。
-
Dshot 信号验证
- 条件: 如果
dshot_frametime
在指定范围内(dshot_frametime_low
和dshot_frametime_high
)。- 设置超时计数器为 0
- 计算脉冲数组: 用于 CRC 校验的
dpulse
数组,根据脉冲宽度判断高低电平。 - 计算 CRC:
calcCRC
计算实际的 CRC 值。checkCRC
计算从数据中提取的 CRC 值。
- 校验 CRC:
- 如果
dshot_telemetry
为 1,反转并加 16 到checkCRC
。
- 如果
- 条件: 如果
-
处理 Dshot 数据
- 条件: 如果 CRC 校验成功。
- 设置超时计数器为 0,增加
dshot_goodcounts
。 - 处理数据:
- 发送遥测数据: 如果
dpulse[11]
为 1,设置send_telemetry
。 - 处理命令:
- 如果
tocheck
大于 47 且EDT_ARMED
,更新newinput
和dshotcommand
。 - 如果
tocheck
在 0 和 47 之间,设置newinput
为 0 并更新dshotcommand
。 - 如果
tocheck
为 0,根据EDT_ARM_ENABLE
更新状态并重置命令。
- 如果
- 发送遥测数据: 如果
- 处理 Dshot 命令:
- 如果
dshotcommand
大于 0 且running
为 0 且armed
,处理 Dshot 命令,根据dshotcommand
更新状态或执行操作。
- 如果
- 设置超时计数器为 0,增加
- CRC 校验失败: 增加
dshot_badcounts
。
- 条件: 如果 CRC 校验成功。
3.4.2 make_dshot_package
make_dshot_package(uint16_t com_time)
|
|-- 如果 (send_extended_dshot > 0)
| |-- dshot_full_number = send_extended_dshot
| |-- send_extended_dshot = 0
|-- 否则
| |-- 如果 (!running || (com_time > 65535))
| | |-- com_time = 65535
| |
| |-- 计算数据的位移量
| | |-- 从 15 到 9 反向循环
| | | |-- 如果 (com_time >> i == 1)
| | | | |-- shift_amount = i + 1 - 9
| | | | |-- 退出循环
| | | |-- 否则
| | | |-- shift_amount = 0
| |
| |-- 将通信时间左移并设置位移量
| | |-- dshot_full_number = ((shift_amount << 9) | (com_time >> shift_amount))
| |
| |-- 计算校验和
| | |-- uint16_t csum = 0
| | |-- uint16_t csum_data = dshot_full_number
| | |-- 循环 3 次
| | | |-- csum ^= csum_data // 按半字节异或数据
| | | |-- csum_data >>= 4
| | |-- csum = ~csum // 取反
| | |-- csum &= 0xf
| |
| |-- 将校验和添加到 dshot_full_number
| | |-- dshot_full_number = (dshot_full_number << 4) | csum
| |
| |-- GCR RLL 编码 16 位到 20 位
| | |-- gcrnumber = gcr_encode_table[(dshot_full_number >> 12)] << 15
| | | |-- gcr_encode_table[(((1 << 4) - 1) & (dshot_full_number >> 8))] << 10
| | | |-- gcr_encode_table[(((1 << 4) - 1) & (dshot_full_number >> 4))] << 5
| | | |-- gcr_encode_table[(((1 << 4) - 1) & (dshot_full_number >> 0))]
| |
| |-- GCR RLL 编码 20 位到 21 位输出
| | |-- #if defined(MCU_F051) || defined(MCU_F031)
| | | |-- gcr[1 + buffer_padding] = 64
| | | |-- 循环 19 到 0
| | | | |-- gcr[buffer_padding + 20 - i + 1] = ((((gcrnumber & 1 << i)) >> i) ^ (gcr[buffer_padding + 20 - i] >> 6)) << 6
| | | |-- gcr[buffer_padding] = 0
| | |
| | |-- #else
| | | |-- gcr[1 + buffer_padding] = 128
| | | |-- 循环 19 到 0
| | | | |-- gcr[buffer_padding + 20 - i + 1] = ((((gcrnumber & 1 << i)) >> i) ^ (gcr[buffer_padding + 20 - i] >> 7)) << 7
| | | |-- gcr[buffer_padding] = 0
|
|-- 结束
-
处理扩展 Dshot 数据
- 条件: 如果
send_extended_dshot
大于 0。- 设置
dshot_full_number
为send_extended_dshot
。 - 重置
send_extended_dshot
为 0。
- 设置
- 条件: 如果
-
处理常规 Dshot 数据
- 条件: 如果
running
为假或com_time
大于 65535。- 将
com_time
设置为 65535。
- 将
- 计算
shift_amount
:- 循环从 15 到 9(即 Dshot 数据的最高位到最低位),确定数据的位移量。
- 如果
com_time
的第i
位为 1,计算shift_amount
并跳出循环。
- 计算
dshot_full_number
:- 将
shift_amount
和com_time
进行位移并组合为dshot_full_number
。
- 将
- 条件: 如果
-
计算校验和
- 初始化
csum
为 0 和csum_data
为dshot_full_number
。 - 循环 3 次:
- 对
csum_data
进行异或操作,更新csum
。
- 对
- 对
csum
进行取反并截断到 4 位。 - 将校验和添加到
dshot_full_number
的末尾。
- 初始化
-
GCR RLL 编码
- 计算
gcrnumber
:- 使用 GCR 编码表将
dshot_full_number
编码为 20 位数字。
- 使用 GCR 编码表将
- 根据不同 MCU 定义,处理 GCR 编码输出。
- 对于 MCU_F051 或 MCU_F031:
- 设置
gcr[1 + buffer_padding]
为 64。 - 将
gcrnumber
的每位数字编码到gcr
中,应用适当的位移和异或操作。 - 将
gcr[buffer_padding]
设置为 0。
- 设置
- 对于其他 MCU:
- 设置
gcr[1 + buffer_padding]
为 128。 - 将
gcrnumber
的每位数字编码到gcr
中,应用适当的位移和异或操作。 - 将
gcr[buffer_padding]
设置为 0。
- 设置
- 对于 MCU_F051 或 MCU_F031:
- 计算
4. 总结
从目前库上代码看,还有很多DSHOT命令相关的代码上没有合入am32-firmware/AM32, AM32的代码可能更多的还是需要从历史版本中查询AlkaMotors/AM32-MultiRotor-ESC-firmware。
- Is there any link to bootloader? #62
- What kind of situation will use second set of parameters from eeprom_address(0x0800F800), not default EEPROM_START_ADD(0x08007C00)? #55
5. 参考资料
【1】BLDC ESC 无刷直流电子调速器简介
【2】BLDC ESC 无刷直流电子调速器工作原理
【3】BLDC ESC 无刷直流电子调速器工作过程
【4】BLDC ESC 无刷直流电子调速器驱动方式
【5】AM32开源代码之工程结构