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
;
54 bbPort
->gpioModeMask
|= (GPIO_MODER_MODER0
<< (pinIndex
* 2));
55 bbPort
->gpioModeInput
|= (LL_GPIO_MODE_INPUT
<< (pinIndex
* 2));
56 bbPort
->gpioModeOutput
|= (LL_GPIO_MODE_OUTPUT
<< (pinIndex
* 2));
58 #ifdef USE_DSHOT_TELEMETRY
59 if (useDshotTelemetry
) {
60 bbPort
->gpioIdleBSRR
|= (1 << pinIndex
); // BS (lower half)
64 bbPort
->gpioIdleBSRR
|= (1 << (pinIndex
+ 16)); // BR (higher half)
67 #ifdef USE_DSHOT_TELEMETRY
68 if (useDshotTelemetry
) {
69 IOWrite(bbMotor
->io
, 1);
73 IOWrite(bbMotor
->io
, 0);
77 IOConfigGPIO(bbMotor
->io
, IO_CONFIG(GPIO_MODE_OUTPUT_PP
, GPIO_SPEED_FREQ_HIGH
, bbPuPdMode
));
79 #error MCU dependent code required
83 void bbTimerChannelInit(bbPort_t
*bbPort
)
85 const timerHardware_t
*timhw
= bbPort
->timhw
;
87 switch (bbPort
->timhw
->channel
) {
88 case TIM_CHANNEL_1
: bbPort
->llChannel
= LL_TIM_CHANNEL_CH1
; break;
89 case TIM_CHANNEL_2
: bbPort
->llChannel
= LL_TIM_CHANNEL_CH2
; break;
90 case TIM_CHANNEL_3
: bbPort
->llChannel
= LL_TIM_CHANNEL_CH3
; break;
91 case TIM_CHANNEL_4
: bbPort
->llChannel
= LL_TIM_CHANNEL_CH4
; break;
94 LL_TIM_OC_InitTypeDef ocInit
;
95 LL_TIM_OC_StructInit(&ocInit
);
96 ocInit
.OCMode
= LL_TIM_OCMODE_PWM1
;
97 ocInit
.OCIdleState
= LL_TIM_OCIDLESTATE_HIGH
;
98 ocInit
.OCState
= LL_TIM_OCSTATE_ENABLE
;
99 ocInit
.OCPolarity
= LL_TIM_OCPOLARITY_LOW
;
100 ocInit
.CompareValue
= 10; // Duty doesn't matter, but too value small would make monitor output invalid
102 //TIM_Cmd(bbPort->timhw->tim, DISABLE);
103 LL_TIM_DisableCounter(bbPort
->timhw
->tim
);
105 //timerOCInit(timhw->tim, timhw->channel, &TIM_OCStruct);
106 LL_TIM_OC_Init(timhw
->tim
, bbPort
->llChannel
, &ocInit
);
108 //timerOCPreloadConfig(timhw->tim, timhw->channel, TIM_OCPreload_Enable);
109 LL_TIM_OC_EnablePreload(timhw
->tim
, bbPort
->llChannel
);
111 #ifdef DEBUG_MONITOR_PACER
113 IO_t io
= IOGetByTag(timhw
->tag
);
114 IOConfigGPIOAF(io
, IOCFG_AF_PP
, timhw
->alternateFunction
);
115 IOInit(io
, OWNER_DSHOT_BITBANG
, 0);
116 //TIM_CtrlPWMOutputs(timhw->tim, ENABLE);
117 LL_TIM_EnableAllOutputs(timhw
->tim
);
121 // Enable and keep it running
123 //TIM_Cmd(bbPort->timhw->tim, ENABLE);
124 LL_TIM_EnableCounter(bbPort
->timhw
->tim
);
127 #ifdef USE_DMA_REGISTER_CACHE
128 void bbLoadDMARegs(dmaResource_t
*dmaResource
, dmaRegCache_t
*dmaRegCache
)
130 ((DMA_ARCH_TYPE
*)dmaResource
)->CR
= dmaRegCache
->CR
;
131 ((DMA_ARCH_TYPE
*)dmaResource
)->FCR
= dmaRegCache
->FCR
;
132 ((DMA_ARCH_TYPE
*)dmaResource
)->NDTR
= dmaRegCache
->NDTR
;
133 ((DMA_ARCH_TYPE
*)dmaResource
)->PAR
= dmaRegCache
->PAR
;
134 ((DMA_ARCH_TYPE
*)dmaResource
)->M0AR
= dmaRegCache
->M0AR
;
137 static void bbSaveDMARegs(dmaResource_t
*dmaResource
, dmaRegCache_t
*dmaRegCache
)
139 dmaRegCache
->CR
= ((DMA_ARCH_TYPE
*)dmaResource
)->CR
;
140 dmaRegCache
->FCR
= ((DMA_ARCH_TYPE
*)dmaResource
)->FCR
;
141 dmaRegCache
->NDTR
= ((DMA_ARCH_TYPE
*)dmaResource
)->NDTR
;
142 dmaRegCache
->PAR
= ((DMA_ARCH_TYPE
*)dmaResource
)->PAR
;
143 dmaRegCache
->M0AR
= ((DMA_ARCH_TYPE
*)dmaResource
)->M0AR
;
147 void bbSwitchToOutput(bbPort_t
* bbPort
)
149 // Output idle level before switching to output
150 // Use BSRR register for this
151 // Normal: Use BR (higher half)
152 // Inverted: Use BS (lower half)
154 WRITE_REG(bbPort
->gpio
->BSRR
, bbPort
->gpioIdleBSRR
);
156 // Set GPIO to output
158 ATOMIC_BLOCK(NVIC_PRIO_TIMER
) {
159 MODIFY_REG(bbPort
->gpio
->MODER
, bbPort
->gpioModeMask
, bbPort
->gpioModeOutput
);
162 // Reinitialize port group DMA for output
164 dmaResource_t
*dmaResource
= bbPort
->dmaResource
;
165 #ifdef USE_DMA_REGISTER_CACHE
166 bbDMA_Cmd(bbPort
, DISABLE
);
167 bbLoadDMARegs(dmaResource
, &bbPort
->dmaRegOutput
);
169 //xDMA_DeInit(dmaResource);
170 xLL_EX_DMA_Deinit(dmaResource
);
171 //xDMA_Init(dmaResource, &bbPort->outputDmaInit);
172 xLL_EX_DMA_Init(dmaResource
, &bbPort
->outputDmaInit
);
173 // Needs this, as it is DeInit'ed above...
174 //xDMA_ITConfig(dmaResource, DMA_IT_TC, ENABLE);
175 xLL_EX_DMA_EnableIT_TC(dmaResource
);
178 // Reinitialize pacer timer for output
180 bbPort
->timhw
->tim
->ARR
= bbPort
->outputARR
;
182 bbPort
->direction
= DSHOT_BITBANG_DIRECTION_OUTPUT
;
185 #ifdef USE_DSHOT_TELEMETRY
186 void bbSwitchToInput(bbPort_t
*bbPort
)
190 ATOMIC_BLOCK(NVIC_PRIO_TIMER
) {
191 MODIFY_REG(bbPort
->gpio
->MODER
, bbPort
->gpioModeMask
, bbPort
->gpioModeInput
);
194 // Reinitialize port group DMA for input
196 dmaResource_t
*dmaResource
= bbPort
->dmaResource
;
197 #ifdef USE_DMA_REGISTER_CACHE
198 bbLoadDMARegs(dmaResource
, &bbPort
->dmaRegInput
);
200 //xDMA_DeInit(dmaResource);
201 xLL_EX_DMA_Deinit(dmaResource
);
202 //xDMA_Init(dmaResource, &bbPort->inputDmaInit);
203 xLL_EX_DMA_Init(dmaResource
, &bbPort
->inputDmaInit
);
205 // Needs this, as it is DeInit'ed above...
206 //xDMA_ITConfig(dmaResource, DMA_IT_TC, ENABLE);
207 xLL_EX_DMA_EnableIT_TC(dmaResource
);
210 // Reinitialize pacer timer for input
212 bbPort
->timhw
->tim
->ARR
= bbPort
->inputARR
;
214 bbDMA_Cmd(bbPort
, ENABLE
);
216 bbPort
->direction
= DSHOT_BITBANG_DIRECTION_INPUT
;
220 void bbDMAPreconfigure(bbPort_t
*bbPort
, uint8_t direction
)
222 LL_DMA_InitTypeDef
*dmainit
= (direction
== DSHOT_BITBANG_DIRECTION_OUTPUT
) ? &bbPort
->outputDmaInit
: &bbPort
->inputDmaInit
;
224 LL_DMA_StructInit(dmainit
);
226 dmainit
->Mode
= LL_DMA_MODE_NORMAL
;
227 dmainit
->Channel
= bbPort
->dmaChannel
;
228 dmainit
->PeriphOrM2MSrcIncMode
= LL_DMA_PERIPH_NOINCREMENT
;
229 dmainit
->MemoryOrM2MDstIncMode
= LL_DMA_MEMORY_INCREMENT
;
230 dmainit
->FIFOMode
= LL_DMA_FIFOMODE_ENABLE
;
232 dmainit
->DMA_FIFOThreshold
= DMA_FIFOThreshold_1QuarterFull
;
233 dmainit
->DMA_MemoryBurst
= DMA_MemoryBurst_Single
;
234 dmainit
->DMA_PeripheralBurst
= DMA_PeripheralBurst_Single
;
237 if (direction
== DSHOT_BITBANG_DIRECTION_OUTPUT
) {
238 dmainit
->Priority
= LL_DMA_PRIORITY_VERYHIGH
;
239 dmainit
->Direction
= LL_DMA_DIRECTION_MEMORY_TO_PERIPH
;
240 dmainit
->NbData
= bbPort
->portOutputCount
;
241 dmainit
->PeriphOrM2MSrcAddress
= (uint32_t)&bbPort
->gpio
->BSRR
;
242 dmainit
->PeriphOrM2MSrcDataSize
= LL_DMA_PDATAALIGN_WORD
;
243 dmainit
->MemoryOrM2MDstAddress
= (uint32_t)bbPort
->portOutputBuffer
;
244 dmainit
->MemoryOrM2MDstDataSize
= LL_DMA_MDATAALIGN_WORD
;
246 #ifdef USE_DMA_REGISTER_CACHE
247 xLL_EX_DMA_Init(bbPort
->dmaResource
, dmainit
);
248 bbSaveDMARegs(bbPort
->dmaResource
, &bbPort
->dmaRegOutput
);
251 dmainit
->Priority
= LL_DMA_PRIORITY_HIGH
;
252 dmainit
->Direction
= LL_DMA_DIRECTION_PERIPH_TO_MEMORY
;
253 dmainit
->NbData
= bbPort
->portInputCount
;
255 dmainit
->PeriphOrM2MSrcAddress
= (uint32_t)&bbPort
->gpio
->IDR
;
256 dmainit
->PeriphOrM2MSrcDataSize
= LL_DMA_PDATAALIGN_HALFWORD
;
257 dmainit
->MemoryOrM2MDstAddress
= (uint32_t)bbPort
->portInputBuffer
;
258 dmainit
->MemoryOrM2MDstDataSize
= LL_DMA_MDATAALIGN_WORD
;
260 #ifdef USE_DMA_REGISTER_CACHE
261 xLL_EX_DMA_Init(bbPort
->dmaResource
, dmainit
);
262 bbSaveDMARegs(bbPort
->dmaResource
, &bbPort
->dmaRegInput
);
267 void bbTIM_TimeBaseInit(bbPort_t
*bbPort
, uint16_t period
)
269 LL_TIM_InitTypeDef
*init
= &bbPort
->timeBaseInit
;
271 init
->Prescaler
= 0; // Feed raw timerClock
272 init
->ClockDivision
= LL_TIM_CLOCKDIVISION_DIV1
;
273 init
->CounterMode
= LL_TIM_COUNTERMODE_UP
;
274 init
->Autoreload
= period
;
275 //TIM_TimeBaseInit(bbPort->timhw->tim, &bbPort->timeBaseInit);
276 LL_TIM_Init(bbPort
->timhw
->tim
, init
);
277 MODIFY_REG(bbPort
->timhw
->tim
->CR1
, TIM_CR1_ARPE
, TIM_AUTORELOAD_PRELOAD_ENABLE
);
280 void bbTIM_DMACmd(TIM_TypeDef
* TIMx
, uint16_t TIM_DMASource
, FunctionalState NewState
)
282 //TIM_DMACmd(TIMx, TIM_DMASource, NewState);
283 if (NewState
== ENABLE
) {
284 SET_BIT(TIMx
->DIER
, TIM_DMASource
);
286 CLEAR_BIT(TIMx
->DIER
, TIM_DMASource
);
290 void bbDMA_ITConfig(bbPort_t
*bbPort
)
292 //xDMA_ITConfig(bbPort->dmaResource, DMA_IT_TC, ENABLE);
294 xLL_EX_DMA_EnableIT_TC(bbPort
->dmaResource
);
296 SET_BIT(((DMA_Stream_TypeDef
*)(bbPort
->dmaResource
))->CR
, DMA_SxCR_TCIE
|DMA_SxCR_TEIE
);
299 void bbDMA_Cmd(bbPort_t
*bbPort
, FunctionalState NewState
)
301 //xDMA_Cmd(bbPort->dmaResource, NewState);
303 if (NewState
== ENABLE
) {
304 xLL_EX_DMA_EnableResource(bbPort
->dmaResource
);
306 xLL_EX_DMA_DisableResource(bbPort
->dmaResource
);
310 int bbDMA_Count(bbPort_t
*bbPort
)
312 return xLL_EX_DMA_GetDataLength(bbPort
->dmaResource
);
314 #endif // USE_DSHOT_BITBANG