2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
30 #include "build/debug.h"
32 #include "drivers/dma.h"
33 #include "drivers/dma_reqmap.h"
34 #include "drivers/io.h"
35 #include "drivers/nvic.h"
36 #include "drivers/rcc.h"
37 #include "drivers/time.h"
38 #include "drivers/timer.h"
40 #include "stm32f4xx.h"
43 #include "pwm_output.h"
44 #include "drivers/dshot.h"
45 #include "drivers/dshot_dpwm.h"
46 #include "drivers/dshot_command.h"
47 #include "drivers/motor.h"
49 #include "pwm_output_dshot_shared.h"
51 FAST_DATA_ZERO_INIT
uint8_t dmaMotorTimerCount
= 0;
53 FAST_DATA_ZERO_INIT motorDmaTimer_t dmaMotorTimers
[MAX_DMA_TIMERS
];
54 FAST_DATA_ZERO_INIT motorDmaOutput_t dmaMotors
[MAX_SUPPORTED_MOTORS
];
56 motorDmaTimer_t dmaMotorTimers
[MAX_DMA_TIMERS
];
57 motorDmaOutput_t dmaMotors
[MAX_SUPPORTED_MOTORS
];
60 #ifdef USE_DSHOT_TELEMETRY
61 FAST_DATA_ZERO_INIT
uint32_t inputStampUs
;
63 FAST_DATA_ZERO_INIT dshotDMAHandlerCycleCounters_t dshotDMAHandlerCycleCounters
;
66 motorDmaOutput_t
*getMotorDmaOutput(uint8_t index
)
68 return &dmaMotors
[index
];
71 uint8_t getTimerIndex(TIM_TypeDef
*timer
)
73 for (int i
= 0; i
< dmaMotorTimerCount
; i
++) {
74 if (dmaMotorTimers
[i
].timer
== timer
) {
78 dmaMotorTimers
[dmaMotorTimerCount
++].timer
= timer
;
79 return dmaMotorTimerCount
- 1;
83 * Prepare to send dshot data for one motor
85 * Formats the value into the appropriate dma buffer and enables the dma channel.
86 * The packet won't start transmitting until later since the dma requests from the timer
87 * are disabled when this function is called.
89 * @param index index of the motor that the data is to be sent to
90 * @param value the dshot value to be sent
92 FAST_CODE
void pwmWriteDshotInt(uint8_t index
, uint16_t value
)
94 motorDmaOutput_t
*const motor
= &dmaMotors
[index
];
96 if (!motor
->configured
) {
100 /*If there is a command ready to go overwrite the value and send that instead*/
101 if (dshotCommandIsProcessing()) {
102 value
= dshotCommandGetCurrent(index
);
104 motor
->protocolControl
.requestTelemetry
= true;
108 motor
->protocolControl
.value
= value
;
110 uint16_t packet
= prepareDshotPacket(&motor
->protocolControl
);
113 #ifdef USE_DSHOT_DMAR
115 bufferSize
= loadDmaBuffer(&motor
->timer
->dmaBurstBuffer
[timerLookupChannelIndex(motor
->timerHardware
->channel
)], 4, packet
);
116 motor
->timer
->dmaBurstLength
= bufferSize
* 4;
120 bufferSize
= loadDmaBuffer(motor
->dmaBuffer
, 1, packet
);
122 motor
->timer
->timerDmaSources
|= motor
->timerDmaSource
;
124 #ifdef USE_FULL_LL_DRIVER
125 xLL_EX_DMA_SetDataLength(motor
->dmaRef
, bufferSize
);
126 xLL_EX_DMA_EnableResource(motor
->dmaRef
);
128 xDMA_SetCurrDataCounter(motor
->dmaRef
, bufferSize
);
130 // XXX we can remove this ifdef if we add a new macro for the TRUE/ENABLE constants
132 xDMA_Cmd(motor
->dmaRef
, TRUE
);
134 xDMA_Cmd(motor
->dmaRef
, ENABLE
);
137 #endif // USE_FULL_LL_DRIVER
142 #ifdef USE_DSHOT_TELEMETRY
144 void dshotEnableChannels(uint8_t motorCount
);
146 static uint32_t decodeTelemetryPacket(const uint32_t buffer
[], uint32_t count
)
149 uint32_t oldValue
= buffer
[0];
152 for (uint32_t i
= 1; i
<= count
; i
++) {
154 int diff
= buffer
[i
] - oldValue
;
158 len
= (diff
+ 8) / 16;
164 value
|= 1 << (len
- 1);
165 oldValue
= buffer
[i
];
172 static const uint32_t decode
[32] = {
173 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 10, 11, 0, 13, 14, 15,
174 0, 0, 2, 3, 0, 5, 6, 7, 0, 0, 8, 1, 0, 4, 12, 0 };
176 uint32_t decodedValue
= decode
[value
& 0x1f];
177 decodedValue
|= decode
[(value
>> 5) & 0x1f] << 4;
178 decodedValue
|= decode
[(value
>> 10) & 0x1f] << 8;
179 decodedValue
|= decode
[(value
>> 15) & 0x1f] << 12;
181 uint32_t csum
= decodedValue
;
182 csum
= csum
^ (csum
>> 8); // xor bytes
183 csum
= csum
^ (csum
>> 4); // xor nibbles
185 if ((csum
& 0xf) != 0xf) {
186 return DSHOT_TELEMETRY_INVALID
;
189 return decodedValue
>> 4;
194 #ifdef USE_DSHOT_TELEMETRY
196 * Process dshot telemetry packets before switching the channels back to outputs
199 FAST_CODE_NOINLINE
bool pwmTelemetryDecode(void)
201 if (!useDshotTelemetry
) {
205 #ifdef USE_DSHOT_TELEMETRY_STATS
206 const timeMs_t currentTimeMs
= millis();
208 const timeUs_t currentUs
= micros();
210 for (int i
= 0; i
< dshotPwmDevice
.count
; i
++) {
211 timeDelta_t usSinceInput
= cmpTimeUs(currentUs
, inputStampUs
);
212 if (usSinceInput
>= 0 && usSinceInput
< dmaMotors
[i
].dshotTelemetryDeadtimeUs
) {
215 if (dmaMotors
[i
].isInput
) {
216 #ifdef USE_FULL_LL_DRIVER
217 uint32_t edges
= GCR_TELEMETRY_INPUT_LEN
- xLL_EX_DMA_GetDataLength(dmaMotors
[i
].dmaRef
);
219 uint32_t edges
= GCR_TELEMETRY_INPUT_LEN
- xDMA_GetCurrDataCounter(dmaMotors
[i
].dmaRef
);
222 #ifdef USE_FULL_LL_DRIVER
223 LL_EX_TIM_DisableIT(dmaMotors
[i
].timerHardware
->tim
, dmaMotors
[i
].timerDmaSource
);
224 #elif defined(AT32F435)
225 tmr_dma_request_enable(dmaMotors
[i
].timerHardware
->tim
, dmaMotors
[i
].timerDmaSource
, FALSE
);
227 TIM_DMACmd(dmaMotors
[i
].timerHardware
->tim
, dmaMotors
[i
].timerDmaSource
, DISABLE
);
232 if (edges
> MIN_GCR_EDGES
) {
233 dshotTelemetryState
.readCount
++;
235 rawValue
= decodeTelemetryPacket(dmaMotors
[i
].dmaBuffer
, edges
);
237 if (rawValue
!= DSHOT_TELEMETRY_INVALID
) {
238 // Check EDT enable or store raw value
239 if ((rawValue
== 0x0E00) && (dshotCommandGetCurrent(i
) == DSHOT_CMD_EXTENDED_TELEMETRY_ENABLE
)) {
240 dshotTelemetryState
.motorState
[i
].telemetryTypes
= 1 << DSHOT_TELEMETRY_TYPE_STATE_EVENTS
;
242 dshotTelemetryState
.motorState
[i
].rawValue
= rawValue
;
245 dshotTelemetryState
.invalidPacketCount
++;
247 memcpy(dshotTelemetryState
.inputBuffer
, dmaMotors
[i
].dmaBuffer
, sizeof(dshotTelemetryState
.inputBuffer
));
250 #ifdef USE_DSHOT_TELEMETRY_STATS
251 updateDshotTelemetryQuality(&dshotTelemetryQuality
[i
], rawValue
!= DSHOT_TELEMETRY_INVALID
, currentTimeMs
);
255 pwmDshotSetDirectionOutput(&dmaMotors
[i
]);
258 dshotTelemetryState
.rawValueState
= DSHOT_RAW_VALUE_STATE_NOT_PROCESSED
;
260 dshotEnableChannels(dshotPwmDevice
.count
);
264 #endif // USE_DSHOT_TELEMETRY