Merge pull request #11494 from haslinghuis/dshot_gpio
[betaflight.git] / src / main / drivers / light_ws2811strip_stdperiph.c
blob6f7ead8d55c72d5df0e2760a5ecd58f37240dab8
1 /*
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)
8 * any later version.
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/>.
21 #include <stdbool.h>
22 #include <stdint.h>
23 #include <string.h>
25 #include "platform.h"
27 #ifdef USE_LED_STRIP
29 #include "build/debug.h"
31 #include "common/color.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/timer.h"
40 #include "light_ws2811strip.h"
42 static IO_t ws2811IO = IO_NONE;
43 #if defined(STM32F4) || defined(STM32F3) || defined(STM32F1)
44 static dmaResource_t *dmaRef = NULL;
45 #else
46 #error "No MCU definition in light_ws2811strip_stdperiph.c"
47 #endif
48 static TIM_TypeDef *timer = NULL;
50 static void WS2811_DMA_IRQHandler(dmaChannelDescriptor_t *descriptor)
52 #if defined(USE_WS2811_SINGLE_COLOUR)
53 static uint32_t counter = 0;
54 #endif
56 if (DMA_GET_FLAG_STATUS(descriptor, DMA_IT_TCIF)) {
57 #if defined(USE_WS2811_SINGLE_COLOUR)
58 counter++;
59 if (counter == WS2811_LED_STRIP_LENGTH) {
60 // Output low for 50us delay
61 memset(ledStripDMABuffer, 0, sizeof(ledStripDMABuffer));
62 } else if (counter == (WS2811_LED_STRIP_LENGTH + WS2811_DELAY_ITERATIONS)) {
63 counter = 0;
64 ws2811LedDataTransferInProgress = false;
65 xDMA_Cmd(descriptor->ref, DISABLE);
67 #else
68 ws2811LedDataTransferInProgress = false;
69 xDMA_Cmd(descriptor->ref, DISABLE);
70 #endif
72 DMA_CLEAR_FLAG(descriptor, DMA_IT_TCIF);
76 bool ws2811LedStripHardwareInit(ioTag_t ioTag)
78 if (!ioTag) {
79 return false;
82 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
83 TIM_OCInitTypeDef TIM_OCInitStructure;
84 DMA_InitTypeDef DMA_InitStructure;
86 const timerHardware_t *timerHardware = timerAllocate(ioTag, OWNER_LED_STRIP, 0);
88 if (timerHardware == NULL) {
89 return false;
92 timer = timerHardware->tim;
94 #if defined(USE_DMA_SPEC)
95 const dmaChannelSpec_t *dmaSpec = dmaGetChannelSpecByTimer(timerHardware);
97 if (dmaSpec == NULL) {
98 return false;
101 dmaRef = dmaSpec->ref;
102 #if defined(STM32F4)
103 uint32_t dmaChannel = dmaSpec->channel;
104 #endif
105 #else
106 dmaRef = timerHardware->dmaRef;
107 #if defined(STM32F4)
108 uint32_t dmaChannel = timerHardware->dmaChannel;
109 #endif
110 #endif
112 if (dmaRef == NULL || !dmaAllocate(dmaGetIdentifier(dmaRef), OWNER_LED_STRIP, 0)) {
113 return false;
116 ws2811IO = IOGetByTag(ioTag);
117 IOInit(ws2811IO, OWNER_LED_STRIP, 0);
118 #ifdef STM32F1
119 IOConfigGPIO(ws2811IO, IO_CONFIG(GPIO_Speed_50MHz, GPIO_Mode_AF_PP));
120 #else
121 IOConfigGPIOAF(ws2811IO, IO_CONFIG(GPIO_Mode_AF, GPIO_Speed_50MHz, GPIO_OType_PP, GPIO_PuPd_UP), timerHardware->alternateFunction);
122 #endif
124 RCC_ClockCmd(timerRCC(timer), ENABLE);
126 // Stop timer
127 TIM_Cmd(timer, DISABLE);
129 /* Compute the prescaler value */
130 uint16_t prescaler = timerGetPrescalerByDesiredMhz(timer, WS2811_TIMER_MHZ);
131 uint16_t period = timerGetPeriodByPrescaler(timer, prescaler, WS2811_CARRIER_HZ);
133 BIT_COMPARE_1 = period / 3 * 2;
134 BIT_COMPARE_0 = period / 3;
136 /* Time base configuration */
137 TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
138 TIM_TimeBaseStructure.TIM_Period = period; // 800kHz
139 TIM_TimeBaseStructure.TIM_Prescaler = prescaler;
140 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
141 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
142 TIM_TimeBaseInit(timer, &TIM_TimeBaseStructure);
144 /* PWM1 Mode configuration */
145 TIM_OCStructInit(&TIM_OCInitStructure);
146 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
148 if (timerHardware->output & TIMER_OUTPUT_N_CHANNEL) {
149 TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
150 TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
151 TIM_OCInitStructure.TIM_OCNPolarity = (timerHardware->output & TIMER_OUTPUT_INVERTED) ? TIM_OCNPolarity_Low : TIM_OCNPolarity_High;
152 } else {
153 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
154 TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
155 TIM_OCInitStructure.TIM_OCPolarity = (timerHardware->output & TIMER_OUTPUT_INVERTED) ? TIM_OCPolarity_Low : TIM_OCPolarity_High;
158 TIM_OCInitStructure.TIM_Pulse = 0;
160 timerOCInit(timer, timerHardware->channel, &TIM_OCInitStructure);
161 timerOCPreloadConfig(timer, timerHardware->channel, TIM_OCPreload_Enable);
163 TIM_CtrlPWMOutputs(timer, ENABLE);
164 TIM_ARRPreloadConfig(timer, ENABLE);
166 if (timerHardware->output & TIMER_OUTPUT_N_CHANNEL) {
167 TIM_CCxNCmd(timer, timerHardware->channel, TIM_CCxN_Enable);
168 } else {
169 TIM_CCxCmd(timer, timerHardware->channel, TIM_CCx_Enable);
172 TIM_Cmd(timer, ENABLE);
174 dmaEnable(dmaGetIdentifier(dmaRef));
175 dmaSetHandler(dmaGetIdentifier(dmaRef), WS2811_DMA_IRQHandler, NVIC_PRIO_WS2811_DMA, 0);
177 xDMA_DeInit(dmaRef);
179 /* configure DMA */
180 xDMA_Cmd(dmaRef, DISABLE);
181 xDMA_DeInit(dmaRef);
182 DMA_StructInit(&DMA_InitStructure);
183 DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)timerCCR(timer, timerHardware->channel);
184 DMA_InitStructure.DMA_BufferSize = WS2811_DMA_BUFFER_SIZE;
185 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
186 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
188 #if defined(STM32F4)
189 DMA_InitStructure.DMA_Channel = dmaChannel;
190 DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)ledStripDMABuffer;
191 DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
192 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
193 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
194 DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
195 #elif defined(STM32F3) || defined(STM32F1)
196 DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ledStripDMABuffer;
197 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
198 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
199 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
200 DMA_InitStructure.DMA_Priority = DMA_Priority_High;
201 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
202 #endif
204 #if defined(USE_WS2811_SINGLE_COLOUR)
205 DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
206 #else
207 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
208 #endif
210 xDMA_Init(dmaRef, &DMA_InitStructure);
211 TIM_DMACmd(timer, timerDmaSource(timerHardware->channel), ENABLE);
212 xDMA_ITConfig(dmaRef, DMA_IT_TC, ENABLE);
214 return true;
217 void ws2811LedStripDMAEnable(void)
219 xDMA_SetCurrDataCounter(dmaRef, WS2811_DMA_BUFFER_SIZE); // load number of bytes to be transferred
220 TIM_SetCounter(timer, 0);
221 TIM_Cmd(timer, ENABLE);
222 xDMA_Cmd(dmaRef, ENABLE);
224 #endif