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/>.
27 #ifdef USE_DSHOT_BITBANG
29 #include "build/atomic.h"
30 #include "build/debug.h"
32 #include "drivers/io.h"
33 #include "drivers/io_impl.h"
34 #include "drivers/dma.h"
35 #include "drivers/dma_reqmap.h"
36 #include "drivers/dshot.h"
37 #include "drivers/dshot_bitbang_impl.h"
38 #include "drivers/dshot_command.h"
39 #include "drivers/motor.h"
40 #include "drivers/nvic.h"
41 #include "drivers/pwm_output.h" // XXX for pwmOutputPort_t motors[]; should go away with refactoring
42 #include "drivers/time.h"
43 #include "drivers/timer.h"
47 // Setup GPIO_MODER and GPIO_ODR register manipulation values
49 void bbGpioSetup(bbMotor_t
*bbMotor
)
51 bbPort_t
*bbPort
= bbMotor
->bbPort
;
52 int pinIndex
= bbMotor
->pinIndex
;
55 bbPort
->gpioModeMask
|= (GPIO_MODER_MODE0
<< (pinIndex
* 2)); // A minor name change in H7 CMSIS
57 bbPort
->gpioModeMask
|= (GPIO_MODER_MODER0
<< (pinIndex
* 2));
59 bbPort
->gpioModeInput
|= (LL_GPIO_MODE_INPUT
<< (pinIndex
* 2));
60 bbPort
->gpioModeOutput
|= (LL_GPIO_MODE_OUTPUT
<< (pinIndex
* 2));
62 #ifdef USE_DSHOT_TELEMETRY
63 if (useDshotTelemetry
) {
64 bbPort
->gpioIdleBSRR
|= (1 << pinIndex
); // BS (lower half)
68 bbPort
->gpioIdleBSRR
|= (1 << (pinIndex
+ 16)); // BR (higher half)
71 #ifdef USE_DSHOT_TELEMETRY
72 if (useDshotTelemetry
) {
73 IOWrite(bbMotor
->io
, 1);
77 IOWrite(bbMotor
->io
, 0);
80 #if defined(STM32F7) || defined(STM32G4) || defined(STM32H7)
81 IOConfigGPIO(bbMotor
->io
, IO_CONFIG(GPIO_MODE_OUTPUT_PP
, GPIO_SPEED_FREQ_HIGH
, bbPuPdMode
));
83 #error MCU dependent code required
87 void bbTimerChannelInit(bbPort_t
*bbPort
)
89 const timerHardware_t
*timhw
= bbPort
->timhw
;
91 switch (bbPort
->timhw
->channel
) {
92 case TIM_CHANNEL_1
: bbPort
->llChannel
= LL_TIM_CHANNEL_CH1
; break;
93 case TIM_CHANNEL_2
: bbPort
->llChannel
= LL_TIM_CHANNEL_CH2
; break;
94 case TIM_CHANNEL_3
: bbPort
->llChannel
= LL_TIM_CHANNEL_CH3
; break;
95 case TIM_CHANNEL_4
: bbPort
->llChannel
= LL_TIM_CHANNEL_CH4
; break;
98 LL_TIM_OC_InitTypeDef ocInit
;
99 LL_TIM_OC_StructInit(&ocInit
);
100 ocInit
.OCMode
= LL_TIM_OCMODE_PWM1
;
101 ocInit
.OCIdleState
= LL_TIM_OCIDLESTATE_HIGH
;
102 ocInit
.OCState
= LL_TIM_OCSTATE_ENABLE
;
103 ocInit
.OCPolarity
= LL_TIM_OCPOLARITY_LOW
;
104 ocInit
.CompareValue
= 10; // Duty doesn't matter, but too value small would make monitor output invalid
106 //TIM_Cmd(bbPort->timhw->tim, DISABLE);
107 LL_TIM_DisableCounter(bbPort
->timhw
->tim
);
109 //timerOCInit(timhw->tim, timhw->channel, &TIM_OCStruct);
110 LL_TIM_OC_Init(timhw
->tim
, bbPort
->llChannel
, &ocInit
);
112 //timerOCPreloadConfig(timhw->tim, timhw->channel, TIM_OCPreload_Enable);
113 LL_TIM_OC_EnablePreload(timhw
->tim
, bbPort
->llChannel
);
115 #ifdef DEBUG_MONITOR_PACER
117 IO_t io
= IOGetByTag(timhw
->tag
);
118 IOConfigGPIOAF(io
, IOCFG_AF_PP
, timhw
->alternateFunction
);
119 IOInit(io
, OWNER_DSHOT_BITBANG
, 0);
120 //TIM_CtrlPWMOutputs(timhw->tim, ENABLE);
121 LL_TIM_EnableAllOutputs(timhw
->tim
);
125 // Enable and keep it running
127 //TIM_Cmd(bbPort->timhw->tim, ENABLE);
128 LL_TIM_EnableCounter(bbPort
->timhw
->tim
);
131 #ifdef USE_DMA_REGISTER_CACHE
132 void bbLoadDMARegs(dmaResource_t
*dmaResource
, dmaRegCache_t
*dmaRegCache
)
134 #if defined(STM32F7) || defined(STM32H7)
135 ((DMA_ARCH_TYPE
*)dmaResource
)->CR
= dmaRegCache
->CR
;
136 ((DMA_ARCH_TYPE
*)dmaResource
)->FCR
= dmaRegCache
->FCR
;
137 ((DMA_ARCH_TYPE
*)dmaResource
)->NDTR
= dmaRegCache
->NDTR
;
138 ((DMA_ARCH_TYPE
*)dmaResource
)->PAR
= dmaRegCache
->PAR
;
139 ((DMA_ARCH_TYPE
*)dmaResource
)->M0AR
= dmaRegCache
->M0AR
;
140 #elif defined(STM32G4)
141 ((DMA_ARCH_TYPE
*)dmaResource
)->CCR
= dmaRegCache
->CCR
;
142 ((DMA_ARCH_TYPE
*)dmaResource
)->CNDTR
= dmaRegCache
->CNDTR
;
143 ((DMA_ARCH_TYPE
*)dmaResource
)->CPAR
= dmaRegCache
->CPAR
;
144 ((DMA_ARCH_TYPE
*)dmaResource
)->CMAR
= dmaRegCache
->CMAR
;
146 #error MCU dependent code required
150 static void bbSaveDMARegs(dmaResource_t
*dmaResource
, dmaRegCache_t
*dmaRegCache
)
152 #if defined(STM32F7) || defined(STM32H7)
153 dmaRegCache
->CR
= ((DMA_ARCH_TYPE
*)dmaResource
)->CR
;
154 dmaRegCache
->FCR
= ((DMA_ARCH_TYPE
*)dmaResource
)->FCR
;
155 dmaRegCache
->NDTR
= ((DMA_ARCH_TYPE
*)dmaResource
)->NDTR
;
156 dmaRegCache
->PAR
= ((DMA_ARCH_TYPE
*)dmaResource
)->PAR
;
157 dmaRegCache
->M0AR
= ((DMA_ARCH_TYPE
*)dmaResource
)->M0AR
;
158 #elif defined(STM32G4)
159 dmaRegCache
->CCR
= ((DMA_ARCH_TYPE
*)dmaResource
)->CCR
;
160 dmaRegCache
->CNDTR
= ((DMA_ARCH_TYPE
*)dmaResource
)->CNDTR
;
161 dmaRegCache
->CPAR
= ((DMA_ARCH_TYPE
*)dmaResource
)->CPAR
;
162 dmaRegCache
->CMAR
= ((DMA_ARCH_TYPE
*)dmaResource
)->CMAR
;
164 #error MCU dependent code required
169 void bbSwitchToOutput(bbPort_t
* bbPort
)
171 // Output idle level before switching to output
172 // Use BSRR register for this
173 // Normal: Use BR (higher half)
174 // Inverted: Use BS (lower half)
176 WRITE_REG(bbPort
->gpio
->BSRR
, bbPort
->gpioIdleBSRR
);
178 // Set GPIO to output
180 ATOMIC_BLOCK(NVIC_PRIO_TIMER
) {
181 MODIFY_REG(bbPort
->gpio
->MODER
, bbPort
->gpioModeMask
, bbPort
->gpioModeOutput
);
184 // Reinitialize port group DMA for output
186 dmaResource_t
*dmaResource
= bbPort
->dmaResource
;
187 #ifdef USE_DMA_REGISTER_CACHE
188 bbDMA_Cmd(bbPort
, DISABLE
);
189 bbLoadDMARegs(dmaResource
, &bbPort
->dmaRegOutput
);
191 //xDMA_DeInit(dmaResource);
192 xLL_EX_DMA_Deinit(dmaResource
);
193 //xDMA_Init(dmaResource, &bbPort->outputDmaInit);
194 xLL_EX_DMA_Init(dmaResource
, &bbPort
->outputDmaInit
);
195 // Needs this, as it is DeInit'ed above...
196 //xDMA_ITConfig(dmaResource, DMA_IT_TC, ENABLE);
197 xLL_EX_DMA_EnableIT_TC(dmaResource
);
200 // Reinitialize pacer timer for output
202 bbPort
->timhw
->tim
->ARR
= bbPort
->outputARR
;
204 bbPort
->direction
= DSHOT_BITBANG_DIRECTION_OUTPUT
;
207 #ifdef USE_DSHOT_TELEMETRY
208 void bbSwitchToInput(bbPort_t
*bbPort
)
212 ATOMIC_BLOCK(NVIC_PRIO_TIMER
) {
213 MODIFY_REG(bbPort
->gpio
->MODER
, bbPort
->gpioModeMask
, bbPort
->gpioModeInput
);
216 // Reinitialize port group DMA for input
218 dmaResource_t
*dmaResource
= bbPort
->dmaResource
;
219 #ifdef USE_DMA_REGISTER_CACHE
220 bbLoadDMARegs(dmaResource
, &bbPort
->dmaRegInput
);
222 //xDMA_DeInit(dmaResource);
223 xLL_EX_DMA_Deinit(dmaResource
);
224 //xDMA_Init(dmaResource, &bbPort->inputDmaInit);
225 xLL_EX_DMA_Init(dmaResource
, &bbPort
->inputDmaInit
);
227 // Needs this, as it is DeInit'ed above...
228 //xDMA_ITConfig(dmaResource, DMA_IT_TC, ENABLE);
229 xLL_EX_DMA_EnableIT_TC(dmaResource
);
232 // Reinitialize pacer timer for input
234 bbPort
->timhw
->tim
->ARR
= bbPort
->inputARR
;
236 bbDMA_Cmd(bbPort
, ENABLE
);
238 bbPort
->direction
= DSHOT_BITBANG_DIRECTION_INPUT
;
242 void bbDMAPreconfigure(bbPort_t
*bbPort
, uint8_t direction
)
244 LL_DMA_InitTypeDef
*dmainit
= (direction
== DSHOT_BITBANG_DIRECTION_OUTPUT
) ? &bbPort
->outputDmaInit
: &bbPort
->inputDmaInit
;
246 LL_DMA_StructInit(dmainit
);
248 dmainit
->Mode
= LL_DMA_MODE_NORMAL
;
249 #if defined(STM32G4) || defined(STM32H7)
250 dmainit
->PeriphRequest
= bbPort
->dmaChannel
;
252 dmainit
->Channel
= bbPort
->dmaChannel
;
253 dmainit
->FIFOMode
= LL_DMA_FIFOMODE_ENABLE
;
256 dmainit
->PeriphOrM2MSrcIncMode
= LL_DMA_PERIPH_NOINCREMENT
;
257 dmainit
->MemoryOrM2MDstIncMode
= LL_DMA_MEMORY_INCREMENT
;
259 if (direction
== DSHOT_BITBANG_DIRECTION_OUTPUT
) {
260 dmainit
->Priority
= LL_DMA_PRIORITY_VERYHIGH
;
261 dmainit
->Direction
= LL_DMA_DIRECTION_MEMORY_TO_PERIPH
;
262 dmainit
->NbData
= bbPort
->portOutputCount
;
263 dmainit
->PeriphOrM2MSrcAddress
= (uint32_t)&bbPort
->gpio
->BSRR
;
264 dmainit
->PeriphOrM2MSrcDataSize
= LL_DMA_PDATAALIGN_WORD
;
265 dmainit
->MemoryOrM2MDstAddress
= (uint32_t)bbPort
->portOutputBuffer
;
266 dmainit
->MemoryOrM2MDstDataSize
= LL_DMA_MDATAALIGN_WORD
;
268 #ifdef USE_DMA_REGISTER_CACHE
269 xLL_EX_DMA_Init(bbPort
->dmaResource
, dmainit
);
270 bbSaveDMARegs(bbPort
->dmaResource
, &bbPort
->dmaRegOutput
);
273 dmainit
->Priority
= LL_DMA_PRIORITY_HIGH
;
274 dmainit
->Direction
= LL_DMA_DIRECTION_PERIPH_TO_MEMORY
;
275 dmainit
->NbData
= bbPort
->portInputCount
;
277 dmainit
->PeriphOrM2MSrcAddress
= (uint32_t)&bbPort
->gpio
->IDR
;
278 dmainit
->PeriphOrM2MSrcDataSize
= LL_DMA_PDATAALIGN_HALFWORD
;
279 dmainit
->MemoryOrM2MDstAddress
= (uint32_t)bbPort
->portInputBuffer
;
282 // XXX G4 seems to require 16-bit transfer
283 dmainit
->MemoryOrM2MDstDataSize
= LL_DMA_MDATAALIGN_HALFWORD
;
285 dmainit
->MemoryOrM2MDstDataSize
= LL_DMA_MDATAALIGN_WORD
;
288 #ifdef USE_DMA_REGISTER_CACHE
289 xLL_EX_DMA_Init(bbPort
->dmaResource
, dmainit
);
290 bbSaveDMARegs(bbPort
->dmaResource
, &bbPort
->dmaRegInput
);
295 void bbTIM_TimeBaseInit(bbPort_t
*bbPort
, uint16_t period
)
297 LL_TIM_InitTypeDef
*init
= &bbPort
->timeBaseInit
;
299 init
->Prescaler
= 0; // Feed raw timerClock
300 init
->ClockDivision
= LL_TIM_CLOCKDIVISION_DIV1
;
301 init
->CounterMode
= LL_TIM_COUNTERMODE_UP
;
302 init
->Autoreload
= period
;
303 //TIM_TimeBaseInit(bbPort->timhw->tim, &bbPort->timeBaseInit);
304 LL_TIM_Init(bbPort
->timhw
->tim
, init
);
305 MODIFY_REG(bbPort
->timhw
->tim
->CR1
, TIM_CR1_ARPE
, TIM_AUTORELOAD_PRELOAD_ENABLE
);
308 void bbTIM_DMACmd(TIM_TypeDef
* TIMx
, uint16_t TIM_DMASource
, FunctionalState NewState
)
310 //TIM_DMACmd(TIMx, TIM_DMASource, NewState);
311 if (NewState
== ENABLE
) {
312 SET_BIT(TIMx
->DIER
, TIM_DMASource
);
314 CLEAR_BIT(TIMx
->DIER
, TIM_DMASource
);
318 void bbDMA_ITConfig(bbPort_t
*bbPort
)
320 //xDMA_ITConfig(bbPort->dmaResource, DMA_IT_TC, ENABLE);
322 xLL_EX_DMA_EnableIT_TC(bbPort
->dmaResource
);
325 SET_BIT(((DMA_Channel_TypeDef
*)(bbPort
->dmaResource
))->CCR
, DMA_CCR_TCIE
|DMA_CCR_TEIE
);
327 SET_BIT(((DMA_Stream_TypeDef
*)(bbPort
->dmaResource
))->CR
, DMA_SxCR_TCIE
|DMA_SxCR_TEIE
);
331 void bbDMA_Cmd(bbPort_t
*bbPort
, FunctionalState NewState
)
333 //xDMA_Cmd(bbPort->dmaResource, NewState);
335 if (NewState
== ENABLE
) {
336 xLL_EX_DMA_EnableResource(bbPort
->dmaResource
);
338 xLL_EX_DMA_DisableResource(bbPort
->dmaResource
);
342 int bbDMA_Count(bbPort_t
*bbPort
)
344 return xLL_EX_DMA_GetDataLength(bbPort
->dmaResource
);
346 #endif // USE_DSHOT_BITBANG