AM32开源代码之代码分析 - DSHOT/BDSHOT

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传输

实际上并不是直接将这个值发送回飞控器。

  1. 应用了GCR编码,并且通过将半字节(4位一组)按照以下表格进行映射,将16位值映射为20位值
    在这里插入图片描述
  2. 将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
  1. 检查 armed && dshot_telemetry 是否同时为真

    • 条件: out_put == 0/1 判断
    • 操作:
    • 0:调用sendDshotDma接收报文;待处理标识compute_dshot_flag = 1
    • 1:调用receiveDshotDma接收报文;待处理标识compute_dshot_flag = 2
  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
  1. 检查 compute_dshot_flag 是否为 1

    • 条件: compute_dshot_flag == 1
    • 操作:
      • 调用 computeDshotDMA() 函数。这通常会处理与 DShot 相关的数据计算或操作。
      • compute_dshot_flag 设置为 0,以重置标志位,表示处理已完成。
  2. 检查 compute_dshot_flag 是否为 2

    • 条件: compute_dshot_flag == 2
    • 操作:
      • 调用 make_dshot_package(e_com_time) 函数。这通常会生成 DShot 数据包,并可能涉及到对信号或数据的打包处理。
      • compute_dshot_flag 设置为 0,以重置标志位,表示处理已完成。
      • 使用 return 语句跳出函数,防止执行后续的 setInput() 调用。
  3. 调用 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()
  1. 初始化变量

    • smallestnumber 设置为 20000
    • average_signal_pulse 设置为 0
    • lastnumber 设为 dma_buffer[0]
  2. 循环计算信号脉冲

    • 循环范围:j = 1j < 31 (共 30 次)
    • 操作:
      • 如果 dma_buffer[j] - lastnumber 大于 0(即当前值大于前一个值):
        • 如果当前脉冲宽度 dma_buffer[j] - lastnumber 小于 smallestnumber,则更新 smallestnumber
        • 累加当前脉冲宽度到 average_signal_pulse
      • 更新 lastnumber 为当前 dma_buffer[j]
  3. 计算平均信号脉冲

    • average_signal_pulse 除以 32 以获取平均值
  4. 根据标志位执行不同的检查

    • 如果 dshot == 1,调用 checkDshot() 函数
    • 如果 servoPwm == 1,调用 checkServo() 函数
    • 如果既 dshotservoPwm 都为 false,则分别调用 checkDshot()checkServo() 函数

3.3.2 checkDshot

  • 根据信号特征启用 DShot 模式: 根据信号的最小脉冲宽度和平均脉冲宽度来决定是否启用 DShot 模式,并设置相关的计时器和缓冲区参数。
  • 动态配置计时器: 根据 CPU 频率来调整计时器的预分频器,以确保适当的计时精度。
  • 配置缓冲区: 设置 buffer_paddingbuffersize,这些参数影响信号的处理和存储。
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
  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
      • 设置 dshot 标志为 1(启用 DShot 模式)
      • buffer_padding 设为 14
      • buffersize 设为 32
      • inputSet 设为 1(表示输入设置已完成)
  2. 第二个条件判断

    • 条件:
      • 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
      • 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
  1. 条件判断

    • 条件:
      • smallestnumber 大于 200 且小于 20000
  2. 操作:

    • 启用伺服 PWM 模式:servoPwm 设为 1,表示启用伺服 PWM 模式。
    • 设置计时器预分频器:ic_timer_prescaler 设为 CPU_FREQUENCY_MHZ - 1,以便适应 CPU 频率的要求。
    • 配置缓冲区大小:buffersize 设为 2,以适应伺服 PWM 信号的处理。
    • 标记输入设置完成:inputSet 设为 1,表示输入设置已完成。

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++
  1. 初始化与计算

    • dshot_frametime 计算 DSHOT 帧的时间。
    • halfpulsetime 是帧时间的一半,右移 5 位。
  2. Dshot 信号验证

    • 条件: 如果 dshot_frametime 在指定范围内(dshot_frametime_lowdshot_frametime_high)。
      • 设置超时计数器为 0
      • 计算脉冲数组: 用于 CRC 校验的 dpulse 数组,根据脉冲宽度判断高低电平。
      • 计算 CRC:
        • calcCRC 计算实际的 CRC 值。
        • checkCRC 计算从数据中提取的 CRC 值。
      • 校验 CRC:
        • 如果 dshot_telemetry 为 1,反转并加 16 到 checkCRC
  3. 处理 Dshot 数据

    • 条件: 如果 CRC 校验成功。
      • 设置超时计数器为 0,增加 dshot_goodcounts
      • 处理数据:
        • 发送遥测数据: 如果 dpulse[11] 为 1,设置 send_telemetry
        • 处理命令:
          • 如果 tocheck 大于 47 且 EDT_ARMED,更新 newinputdshotcommand
          • 如果 tocheck 在 0 和 47 之间,设置 newinput 为 0 并更新 dshotcommand
          • 如果 tocheck 为 0,根据 EDT_ARM_ENABLE 更新状态并重置命令。
      • 处理 Dshot 命令:
        • 如果 dshotcommand 大于 0 且 running 为 0 且 armed,处理 Dshot 命令,根据 dshotcommand 更新状态或执行操作。
    • CRC 校验失败: 增加 dshot_badcounts

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
|
|-- 结束
  1. 处理扩展 Dshot 数据

    • 条件: 如果 send_extended_dshot 大于 0。
      • 设置 dshot_full_numbersend_extended_dshot
      • 重置 send_extended_dshot 为 0。
  2. 处理常规 Dshot 数据

    • 条件: 如果 running 为假或 com_time 大于 65535。
      • com_time 设置为 65535。
    • 计算 shift_amount
      • 循环从 15 到 9(即 Dshot 数据的最高位到最低位),确定数据的位移量。
      • 如果 com_time 的第 i 位为 1,计算 shift_amount 并跳出循环。
    • 计算 dshot_full_number
      • shift_amountcom_time 进行位移并组合为 dshot_full_number
  3. 计算校验和

    • 初始化 csum 为 0 和 csum_datadshot_full_number
    • 循环 3 次:
      • csum_data 进行异或操作,更新 csum
    • csum 进行取反并截断到 4 位。
    • 将校验和添加到 dshot_full_number 的末尾。
  4. GCR RLL 编码

    • 计算 gcrnumber
      • 使用 GCR 编码表将 dshot_full_number 编码为 20 位数字。
    • 根据不同 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。

4. 总结

从目前库上代码看,还有很多DSHOT命令相关的代码上没有合入am32-firmware/AM32, AM32的代码可能更多的还是需要从历史版本中查询AlkaMotors/AM32-MultiRotor-ESC-firmware

5. 参考资料

【1】BLDC ESC 无刷直流电子调速器简介
【2】BLDC ESC 无刷直流电子调速器工作原理
【3】BLDC ESC 无刷直流电子调速器工作过程
【4】BLDC ESC 无刷直流电子调速器驱动方式
【5】AM32开源代码之工程结构

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值