Merge pull request #11494 from haslinghuis/dshot_gpio
[betaflight.git] / src / main / drivers / light_ws2811strip_hal.c
blob61faedafcace0d8a91726012966eec39fe7ee0c4
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>
24 #include "platform.h"
26 #ifdef USE_LED_STRIP
28 #include "common/color.h"
30 #include "drivers/dma.h"
31 #include "drivers/dma_reqmap.h"
32 #include "drivers/io.h"
33 #include "drivers/nvic.h"
34 #include "drivers/rcc.h"
35 #include "drivers/system.h"
36 #include "drivers/timer.h"
38 #include "light_ws2811strip.h"
40 static IO_t ws2811IO = IO_NONE;
42 static TIM_HandleTypeDef TimHandle;
43 static uint16_t timerChannel = 0;
45 FAST_IRQ_HANDLER void WS2811_DMA_IRQHandler(dmaChannelDescriptor_t* descriptor)
47 HAL_DMA_IRQHandler(TimHandle.hdma[descriptor->userParam]);
48 TIM_DMACmd(&TimHandle, timerChannel, DISABLE);
49 ws2811LedDataTransferInProgress = false;
52 bool ws2811LedStripHardwareInit(ioTag_t ioTag)
54 if (!ioTag) {
55 return false;
58 const timerHardware_t *timerHardware = timerAllocate(ioTag, OWNER_LED_STRIP, 0);
60 if (timerHardware == NULL) {
61 return false;
64 TIM_TypeDef *timer = timerHardware->tim;
65 timerChannel = timerHardware->channel;
67 dmaResource_t *dmaRef;
69 #if defined(USE_DMA_SPEC)
70 const dmaChannelSpec_t *dmaSpec = dmaGetChannelSpecByTimer(timerHardware);
72 if (dmaSpec == NULL) {
73 return false;
76 dmaRef = dmaSpec->ref;
77 uint32_t dmaChannel = dmaSpec->channel;
78 #else
79 dmaRef = timerHardware->dmaRef;
80 uint32_t dmaChannel = timerHardware->dmaChannel;
81 #endif
83 if (dmaRef == NULL || !dmaAllocate(dmaGetIdentifier(dmaRef), OWNER_LED_STRIP, 0)) {
84 return false;
86 TimHandle.Instance = timer;
88 /* Compute the prescaler value */
89 uint16_t prescaler = timerGetPrescalerByDesiredMhz(timer, WS2811_TIMER_MHZ);
90 uint16_t period = timerGetPeriodByPrescaler(timer, prescaler, WS2811_CARRIER_HZ);
92 BIT_COMPARE_1 = period / 3 * 2;
93 BIT_COMPARE_0 = period / 3;
95 TimHandle.Init.Prescaler = prescaler;
96 TimHandle.Init.Period = period; // 800kHz
97 TimHandle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
98 TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
99 if (HAL_TIM_PWM_Init(&TimHandle) != HAL_OK) {
100 /* Initialization Error */
101 return false;
104 static DMA_HandleTypeDef hdma_tim;
106 ws2811IO = IOGetByTag(ioTag);
107 IOInit(ws2811IO, OWNER_LED_STRIP, 0);
108 IOConfigGPIOAF(ws2811IO, IO_CONFIG(GPIO_MODE_AF_PP, GPIO_SPEED_FREQ_VERY_HIGH, GPIO_PULLDOWN), timerHardware->alternateFunction);
110 __DMA1_CLK_ENABLE();
111 __DMA2_CLK_ENABLE();
113 /* Set the parameters to be configured */
114 #if defined(STM32H7) || defined(STM32G4)
115 hdma_tim.Init.Request = dmaChannel;
116 #else
117 hdma_tim.Init.Channel = dmaChannel;
118 #endif
119 hdma_tim.Init.Direction = DMA_MEMORY_TO_PERIPH;
120 hdma_tim.Init.PeriphInc = DMA_PINC_DISABLE;
121 hdma_tim.Init.MemInc = DMA_MINC_ENABLE;
122 hdma_tim.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
123 hdma_tim.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
124 hdma_tim.Init.Mode = DMA_NORMAL;
125 hdma_tim.Init.Priority = DMA_PRIORITY_HIGH;
126 #if !defined(STM32G4)
127 hdma_tim.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
128 hdma_tim.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
129 hdma_tim.Init.MemBurst = DMA_MBURST_SINGLE;
130 hdma_tim.Init.PeriphBurst = DMA_PBURST_SINGLE;
131 #endif
133 /* Set hdma_tim instance */
134 hdma_tim.Instance = (DMA_ARCH_TYPE *)dmaRef;
136 uint16_t dmaIndex = timerDmaIndex(timerChannel);
138 /* Link hdma_tim to hdma[x] (channelx) */
139 __HAL_LINKDMA(&TimHandle, hdma[dmaIndex], hdma_tim);
141 dmaEnable(dmaGetIdentifier(dmaRef));
142 dmaSetHandler(dmaGetIdentifier(dmaRef), WS2811_DMA_IRQHandler, NVIC_PRIO_WS2811_DMA, dmaIndex);
144 /* Initialize TIMx DMA handle */
145 if (HAL_DMA_Init(TimHandle.hdma[dmaIndex]) != HAL_OK) {
146 /* Initialization Error */
147 return false;
150 TIM_OC_InitTypeDef TIM_OCInitStructure;
152 /* PWM1 Mode configuration: Channel1 */
153 TIM_OCInitStructure.OCMode = TIM_OCMODE_PWM1;
154 TIM_OCInitStructure.OCIdleState = TIM_OCIDLESTATE_RESET;
155 TIM_OCInitStructure.OCPolarity = (timerHardware->output & TIMER_OUTPUT_INVERTED) ? TIM_OCPOLARITY_LOW : TIM_OCPOLARITY_HIGH;
156 TIM_OCInitStructure.OCNIdleState = TIM_OCNIDLESTATE_RESET;
157 TIM_OCInitStructure.OCNPolarity = (timerHardware->output & TIMER_OUTPUT_INVERTED) ? TIM_OCNPOLARITY_LOW : TIM_OCNPOLARITY_HIGH;
158 TIM_OCInitStructure.Pulse = 0;
159 TIM_OCInitStructure.OCFastMode = TIM_OCFAST_DISABLE;
160 if (HAL_TIM_PWM_ConfigChannel(&TimHandle, &TIM_OCInitStructure, timerChannel) != HAL_OK) {
161 /* Configuration Error */
162 return false;
164 if (timerHardware->output & TIMER_OUTPUT_N_CHANNEL) {
165 if (HAL_TIMEx_PWMN_Start(&TimHandle, timerChannel) != HAL_OK) {
166 /* Starting PWM generation Error */
167 return false;
169 } else {
170 if (HAL_TIM_PWM_Start(&TimHandle, timerChannel) != HAL_OK) {
171 /* Starting PWM generation Error */
172 return false;
176 return true;
179 void ws2811LedStripDMAEnable(void)
181 if (DMA_SetCurrDataCounter(&TimHandle, timerChannel, ledStripDMABuffer, WS2811_DMA_BUFFER_SIZE) != HAL_OK) {
182 /* DMA set error */
183 ws2811LedDataTransferInProgress = false;
184 return;
186 /* Reset timer counter */
187 __HAL_TIM_SET_COUNTER(&TimHandle,0);
188 /* Enable channel DMA requests */
189 TIM_DMACmd(&TimHandle,timerChannel,ENABLE);
191 #endif