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/>.
31 #include "build/debug.h"
33 #include "drivers/dma.h"
34 #include "drivers/dma_reqmap.h"
35 #include "drivers/io.h"
36 #include "drivers/nvic.h"
37 #include "drivers/rcc.h"
38 #include "drivers/time.h"
39 #include "drivers/timer.h"
41 #include "stm32f4xx.h"
44 #include "pwm_output.h"
45 #include "drivers/dshot.h"
46 #include "drivers/dshot_dpwm.h"
47 #include "drivers/dshot_command.h"
48 #include "drivers/motor.h"
50 #include "pwm_output_dshot_shared.h"
52 FAST_DATA_ZERO_INIT
uint8_t dmaMotorTimerCount
= 0;
54 FAST_DATA_ZERO_INIT motorDmaTimer_t dmaMotorTimers
[MAX_DMA_TIMERS
];
55 FAST_DATA_ZERO_INIT motorDmaOutput_t dmaMotors
[MAX_SUPPORTED_MOTORS
];
57 motorDmaTimer_t dmaMotorTimers
[MAX_DMA_TIMERS
];
58 motorDmaOutput_t dmaMotors
[MAX_SUPPORTED_MOTORS
];
61 #ifdef USE_DSHOT_TELEMETRY
62 FAST_DATA_ZERO_INIT
uint32_t inputStampUs
;
64 FAST_DATA_ZERO_INIT dshotDMAHandlerCycleCounters_t dshotDMAHandlerCycleCounters
;
67 motorDmaOutput_t
*getMotorDmaOutput(uint8_t index
)
69 return &dmaMotors
[index
];
72 uint8_t getTimerIndex(TIM_TypeDef
*timer
)
74 for (int i
= 0; i
< dmaMotorTimerCount
; i
++) {
75 if (dmaMotorTimers
[i
].timer
== timer
) {
79 dmaMotorTimers
[dmaMotorTimerCount
++].timer
= timer
;
80 return dmaMotorTimerCount
- 1;
84 * Prepare to send dshot data for one motor
86 * Formats the value into the appropriate dma buffer and enables the dma channel.
87 * The packet won't start transmitting until later since the dma requests from the timer
88 * are disabled when this function is called.
90 * @param index index of the motor that the data is to be sent to
91 * @param value the dshot value to be sent
93 FAST_CODE
void pwmWriteDshotInt(uint8_t index
, uint16_t value
)
95 motorDmaOutput_t
*const motor
= &dmaMotors
[index
];
97 if (!motor
->configured
) {
101 /*If there is a command ready to go overwrite the value and send that instead*/
102 if (dshotCommandIsProcessing()) {
103 value
= dshotCommandGetCurrent(index
);
105 motor
->protocolControl
.requestTelemetry
= true;
109 motor
->protocolControl
.value
= value
;
111 uint16_t packet
= prepareDshotPacket(&motor
->protocolControl
);
114 #ifdef USE_DSHOT_DMAR
116 bufferSize
= loadDmaBuffer(&motor
->timer
->dmaBurstBuffer
[timerLookupChannelIndex(motor
->timerHardware
->channel
)], 4, packet
);
117 motor
->timer
->dmaBurstLength
= bufferSize
* 4;
121 bufferSize
= loadDmaBuffer(motor
->dmaBuffer
, 1, packet
);
123 motor
->timer
->timerDmaSources
|= motor
->timerDmaSource
;
125 #ifdef USE_FULL_LL_DRIVER
126 xLL_EX_DMA_SetDataLength(motor
->dmaRef
, bufferSize
);
127 xLL_EX_DMA_EnableResource(motor
->dmaRef
);
129 xDMA_SetCurrDataCounter(motor
->dmaRef
, bufferSize
);
131 // XXX we can remove this ifdef if we add a new macro for the TRUE/ENABLE constants
133 xDMA_Cmd(motor
->dmaRef
, TRUE
);
135 xDMA_Cmd(motor
->dmaRef
, ENABLE
);
138 #endif // USE_FULL_LL_DRIVER
144 #ifdef USE_DSHOT_TELEMETRY
146 void dshotEnableChannels(uint8_t motorCount
);
149 static uint32_t decodeTelemetryPacket(uint32_t buffer
[], uint32_t count
)
152 uint32_t oldValue
= buffer
[0];
155 for (uint32_t i
= 1; i
<= count
; i
++) {
157 int diff
= buffer
[i
] - oldValue
;
161 len
= (diff
+ 8) / 16;
167 value
|= 1 << (len
- 1);
168 oldValue
= buffer
[i
];
175 static const uint32_t decode
[32] = {
176 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 10, 11, 0, 13, 14, 15,
177 0, 0, 2, 3, 0, 5, 6, 7, 0, 0, 8, 1, 0, 4, 12, 0 };
179 uint32_t decodedValue
= decode
[value
& 0x1f];
180 decodedValue
|= decode
[(value
>> 5) & 0x1f] << 4;
181 decodedValue
|= decode
[(value
>> 10) & 0x1f] << 8;
182 decodedValue
|= decode
[(value
>> 15) & 0x1f] << 12;
184 uint32_t csum
= decodedValue
;
185 csum
= csum
^ (csum
>> 8); // xor bytes
186 csum
= csum
^ (csum
>> 4); // xor nibbles
188 if ((csum
& 0xf) != 0xf) {
189 return DSHOT_TELEMETRY_INVALID
;
192 return decodedValue
>> 4;
197 #ifdef USE_DSHOT_TELEMETRY
199 * Process dshot telemetry packets before switching the channels back to outputs
202 FAST_CODE_NOINLINE
bool pwmTelemetryDecode(void)
204 if (!useDshotTelemetry
) {
208 #ifdef USE_DSHOT_TELEMETRY_STATS
209 const timeMs_t currentTimeMs
= millis();
211 const timeUs_t currentUs
= micros();
213 for (int i
= 0; i
< dshotPwmDevice
.count
; i
++) {
214 timeDelta_t usSinceInput
= cmpTimeUs(currentUs
, inputStampUs
);
215 if (usSinceInput
>= 0 && usSinceInput
< dmaMotors
[i
].dshotTelemetryDeadtimeUs
) {
218 if (dmaMotors
[i
].isInput
) {
219 #ifdef USE_FULL_LL_DRIVER
220 uint32_t edges
= GCR_TELEMETRY_INPUT_LEN
- xLL_EX_DMA_GetDataLength(dmaMotors
[i
].dmaRef
);
222 uint32_t edges
= GCR_TELEMETRY_INPUT_LEN
- xDMA_GetCurrDataCounter(dmaMotors
[i
].dmaRef
);
225 #ifdef USE_FULL_LL_DRIVER
226 LL_EX_TIM_DisableIT(dmaMotors
[i
].timerHardware
->tim
, dmaMotors
[i
].timerDmaSource
);
227 #elif defined(AT32F435)
228 tmr_dma_request_enable(dmaMotors
[i
].timerHardware
->tim
, dmaMotors
[i
].timerDmaSource
, FALSE
);
230 TIM_DMACmd(dmaMotors
[i
].timerHardware
->tim
, dmaMotors
[i
].timerDmaSource
, DISABLE
);
235 if (edges
> MIN_GCR_EDGES
) {
236 dshotTelemetryState
.readCount
++;
238 rawValue
= decodeTelemetryPacket(dmaMotors
[i
].dmaBuffer
, edges
);
240 if (rawValue
!= DSHOT_TELEMETRY_INVALID
) {
241 // Check EDT enable or store raw value
242 if ((rawValue
== 0x0E00) && (dshotCommandGetCurrent(i
) == DSHOT_CMD_EXTENDED_TELEMETRY_ENABLE
)) {
243 dshotTelemetryState
.motorState
[i
].telemetryTypes
= 1 << DSHOT_TELEMETRY_TYPE_STATE_EVENTS
;
245 dshotTelemetryState
.motorState
[i
].rawValue
= rawValue
;
248 dshotTelemetryState
.invalidPacketCount
++;
250 memcpy(dshotTelemetryState
.inputBuffer
, dmaMotors
[i
].dmaBuffer
, sizeof(dshotTelemetryState
.inputBuffer
));
253 #ifdef USE_DSHOT_TELEMETRY_STATS
254 updateDshotTelemetryQuality(&dshotTelemetryQuality
[i
], rawValue
!= DSHOT_TELEMETRY_INVALID
, currentTimeMs
);
258 pwmDshotSetDirectionOutput(&dmaMotors
[i
]);
261 dshotTelemetryState
.rawValueState
= DSHOT_RAW_VALUE_STATE_NOT_PROCESSED
;
263 dshotEnableChannels(dshotPwmDevice
.count
);
267 #endif // USE_DSHOT_TELEMETRY