Merge pull request #11939 from blckmn/flash-fix
[betaflight.git] / src / main / drivers / light_ws2811strip_stdperiph.c
blob5c0d1fa13d5e26c2f1891e241033215f7985b73c
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)
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 IOConfigGPIOAF(ws2811IO, IO_CONFIG(GPIO_Mode_AF, GPIO_Speed_50MHz, GPIO_OType_PP, GPIO_PuPd_UP), timerHardware->alternateFunction);
120 RCC_ClockCmd(timerRCC(timer), ENABLE);
122 // Stop timer
123 TIM_Cmd(timer, DISABLE);
125 /* Compute the prescaler value */
126 uint16_t prescaler = timerGetPrescalerByDesiredMhz(timer, WS2811_TIMER_MHZ);
127 uint16_t period = timerGetPeriodByPrescaler(timer, prescaler, WS2811_CARRIER_HZ);
129 BIT_COMPARE_1 = period / 3 * 2;
130 BIT_COMPARE_0 = period / 3;
132 /* Time base configuration */
133 TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
134 TIM_TimeBaseStructure.TIM_Period = period; // 800kHz
135 TIM_TimeBaseStructure.TIM_Prescaler = prescaler;
136 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
137 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
138 TIM_TimeBaseInit(timer, &TIM_TimeBaseStructure);
140 /* PWM1 Mode configuration */
141 TIM_OCStructInit(&TIM_OCInitStructure);
142 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
144 if (timerHardware->output & TIMER_OUTPUT_N_CHANNEL) {
145 TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
146 TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
147 TIM_OCInitStructure.TIM_OCNPolarity = (timerHardware->output & TIMER_OUTPUT_INVERTED) ? TIM_OCNPolarity_Low : TIM_OCNPolarity_High;
148 } else {
149 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
150 TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
151 TIM_OCInitStructure.TIM_OCPolarity = (timerHardware->output & TIMER_OUTPUT_INVERTED) ? TIM_OCPolarity_Low : TIM_OCPolarity_High;
154 TIM_OCInitStructure.TIM_Pulse = 0;
156 timerOCInit(timer, timerHardware->channel, &TIM_OCInitStructure);
157 timerOCPreloadConfig(timer, timerHardware->channel, TIM_OCPreload_Enable);
159 TIM_CtrlPWMOutputs(timer, ENABLE);
160 TIM_ARRPreloadConfig(timer, ENABLE);
162 if (timerHardware->output & TIMER_OUTPUT_N_CHANNEL) {
163 TIM_CCxNCmd(timer, timerHardware->channel, TIM_CCxN_Enable);
164 } else {
165 TIM_CCxCmd(timer, timerHardware->channel, TIM_CCx_Enable);
168 TIM_Cmd(timer, ENABLE);
170 dmaEnable(dmaGetIdentifier(dmaRef));
171 dmaSetHandler(dmaGetIdentifier(dmaRef), WS2811_DMA_IRQHandler, NVIC_PRIO_WS2811_DMA, 0);
173 xDMA_DeInit(dmaRef);
175 /* configure DMA */
176 xDMA_Cmd(dmaRef, DISABLE);
177 xDMA_DeInit(dmaRef);
178 DMA_StructInit(&DMA_InitStructure);
179 DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)timerCCR(timer, timerHardware->channel);
180 DMA_InitStructure.DMA_BufferSize = WS2811_DMA_BUFFER_SIZE;
181 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
182 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
184 #if defined(STM32F4)
185 DMA_InitStructure.DMA_Channel = dmaChannel;
186 DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)ledStripDMABuffer;
187 DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
188 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
189 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
190 DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
191 #endif
193 #if defined(USE_WS2811_SINGLE_COLOUR)
194 DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
195 #else
196 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
197 #endif
199 xDMA_Init(dmaRef, &DMA_InitStructure);
200 TIM_DMACmd(timer, timerDmaSource(timerHardware->channel), ENABLE);
201 xDMA_ITConfig(dmaRef, DMA_IT_TC, ENABLE);
203 return true;
206 void ws2811LedStripDMAEnable(void)
208 xDMA_SetCurrDataCounter(dmaRef, WS2811_DMA_BUFFER_SIZE); // load number of bytes to be transferred
209 TIM_SetCounter(timer, 0);
210 TIM_Cmd(timer, ENABLE);
211 xDMA_Cmd(dmaRef, ENABLE);
213 #endif