ProtoCore v0.0.2
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
dshot.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file dshot.cpp
6 * @brief DShot ESC throttle protocol codec (see dshot.h).
7 */
8
10
11#if PC_ENABLE_DSHOT
12
13namespace
14{
15// The DShot CRC: xor of the three 4-bit nibbles of the 12-bit (value<<1 | telemetry) word.
16uint8_t dshot_crc(uint16_t v12, bool bidirectional)
17{
18 uint8_t crc = (uint8_t)((v12 ^ (v12 >> 4) ^ (v12 >> 8)) & 0x0F);
19 if (bidirectional)
20 {
21 crc = (uint8_t)((~crc) & 0x0F); // bidirectional/extended DShot inverts the CRC
22 }
23 return crc;
24}
25} // namespace
26
27uint16_t pc_dshot_encode(uint16_t value11, bool telemetry, bool bidirectional)
28{
29 value11 &= 0x07FF; // 11-bit value field
30 uint16_t v12 = (uint16_t)((value11 << 1) | (telemetry ? 1 : 0));
31 uint8_t crc = dshot_crc(v12, bidirectional);
32 return (uint16_t)((v12 << 4) | crc);
33}
34
35bool pc_dshot_decode(uint16_t frame, uint16_t *value11, bool *telemetry, bool bidirectional)
36{
37 uint16_t v12 = (uint16_t)(frame >> 4);
38 uint8_t got = (uint8_t)(frame & 0x0F);
39 if (got != dshot_crc(v12, bidirectional))
40 {
41 return false;
42 }
43 if (value11)
44 {
45 *value11 = (uint16_t)(v12 >> 1);
46 }
47 if (telemetry)
48 {
49 *telemetry = (v12 & 1) != 0;
50 }
51 return true;
52}
53
54uint32_t pc_dshot_bit_ns(uint16_t rate_kbit, bool bit)
55{
56 uint32_t period_ns; // one bit-period, in ns, at rate_kbit kbit/s
57 switch (rate_kbit)
58 {
59 case 150:
60 period_ns = 6667;
61 break;
62 case 300:
63 period_ns = 3333;
64 break;
65 case 600:
66 period_ns = 1667;
67 break;
68 case 1200:
69 period_ns = 833;
70 break;
71 default:
72 return 0;
73 }
74 // A "1" holds high ~3/4 of the period, a "0" ~3/8 (T1H = 2 * T0H); the ESC samples the pulse width.
75 return bit ? (period_ns * 3 / 4) : (period_ns * 3 / 8);
76}
77
78uint32_t pc_esc_pwm_ns(uint16_t throttle_1000, pc_esc_pwm mode)
79{
80 uint32_t lo;
81 uint32_t hi; // pulse width range in ns
82 switch (mode)
83 {
84 case pc_esc_pwm::PC_ESC_ONESHOT125:
85 lo = 125000;
86 hi = 250000;
87 break;
88 case pc_esc_pwm::PC_ESC_ONESHOT42:
89 lo = 42000;
90 hi = 84000;
91 break;
92 case pc_esc_pwm::PC_ESC_MULTISHOT:
93 lo = 5000;
94 hi = 25000;
95 break;
96 case pc_esc_pwm::PC_ESC_PWM:
97 default:
98 lo = 1000000;
99 hi = 2000000;
100 break;
101 }
102 if (throttle_1000 > 1000)
103 {
104 throttle_1000 = 1000;
105 }
106 // Linear map, computed in 64-bit to avoid overflow (hi-lo up to 1e6, * 1000).
107 return lo + (uint32_t)(((uint64_t)(hi - lo) * throttle_1000) / 1000);
108}
109
110#endif // PC_ENABLE_DSHOT
DShot ESC digital throttle protocol codec (PC_ENABLE_DSHOT).