Auto updated submodule references [18-01-2025]
[betaflight.git] / src / platform / APM32 / light_ws2811strip_apm32.c
blob676f719470530832746cf012809f8b5498014a31
1 /*
2 * This file is part of Betaflight.
4 * Betaflight is free software. You can redistribute this software
5 * and/or modify this software under the terms of the GNU General
6 * Public License as published by the Free Software Foundation,
7 * either version 3 of the License, or (at your option) any later
8 * version.
10 * Betaflight is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public
17 * License along with this software.
19 * If not, see <http://www.gnu.org/licenses/>.
22 #include <stdbool.h>
23 #include <stdint.h>
25 #include "platform.h"
27 #ifdef USE_LED_STRIP
29 #include "common/color.h"
31 #include "drivers/dma.h"
32 #include "drivers/dma_reqmap.h"
33 #include "drivers/io.h"
34 #include "drivers/nvic.h"
35 #include "drivers/rcc.h"
36 #include "drivers/system.h"
37 #include "drivers/timer.h"
39 #include "drivers/light_ws2811strip.h"
41 static IO_t ws2811IO = IO_NONE;
43 static TMR_HandleTypeDef TmrHandle;
44 static uint16_t timerChannel = 0;
46 FAST_IRQ_HANDLER void WS2811_DMA_IRQHandler(dmaChannelDescriptor_t* descriptor)
48 DAL_DMA_IRQHandler(TmrHandle.hdma[descriptor->userParam]);
49 TIM_DMACmd(&TmrHandle, timerChannel, DISABLE);
50 ws2811LedDataTransferInProgress = false;
53 bool ws2811LedStripHardwareInit(ioTag_t ioTag)
55 if (!ioTag) {
56 return false;
59 const timerHardware_t *timerHardware = timerAllocate(ioTag, OWNER_LED_STRIP, 0);
61 if (timerHardware == NULL) {
62 return false;
65 TMR_TypeDef *timer = timerHardware->tim;
66 timerChannel = timerHardware->channel;
68 dmaResource_t *dmaRef;
70 #if defined(USE_DMA_SPEC)
71 const dmaChannelSpec_t *dmaSpec = dmaGetChannelSpecByTimer(timerHardware);
73 if (dmaSpec == NULL) {
74 return false;
77 dmaRef = dmaSpec->ref;
78 uint32_t dmaChannel = dmaSpec->channel;
79 #else
80 dmaRef = timerHardware->dmaRef;
81 uint32_t dmaChannel = timerHardware->dmaChannel;
82 #endif
84 if (dmaRef == NULL || !dmaAllocate(dmaGetIdentifier(dmaRef), OWNER_LED_STRIP, 0)) {
85 return false;
87 TmrHandle.Instance = timer;
89 /* Compute the prescaler value */
90 uint16_t prescaler = timerGetPrescalerByDesiredMhz(timer, WS2811_TIMER_MHZ);
91 uint16_t period = timerGetPeriodByPrescaler(timer, prescaler, WS2811_CARRIER_HZ);
93 BIT_COMPARE_1 = period / 3 * 2;
94 BIT_COMPARE_0 = period / 3;
96 TmrHandle.Init.Prescaler = prescaler;
97 TmrHandle.Init.Period = period; // 800kHz
98 TmrHandle.Init.ClockDivision = TMR_CLOCKDIVISION_DIV1;
99 TmrHandle.Init.CounterMode = TMR_COUNTERMODE_UP;
100 TmrHandle.Init.AutoReloadPreload = TMR_AUTORELOAD_PRELOAD_ENABLE;
101 if (DAL_TMR_PWM_Init(&TmrHandle) != DAL_OK) {
102 /* Initialization Error */
103 return false;
106 static DMA_HandleTypeDef hdma_tim;
108 ws2811IO = IOGetByTag(ioTag);
109 IOInit(ws2811IO, OWNER_LED_STRIP, 0);
110 IOConfigGPIOAF(ws2811IO, IO_CONFIG(GPIO_MODE_AF_PP, GPIO_SPEED_FREQ_VERY_HIGH, GPIO_PULLDOWN), timerHardware->alternateFunction);
112 __DAL_RCM_DMA1_CLK_ENABLE();
113 __DAL_RCM_DMA2_CLK_ENABLE();
115 /* Set the parameters to be configured */
116 hdma_tim.Init.Channel = dmaChannel;
117 hdma_tim.Init.Direction = DMA_MEMORY_TO_PERIPH;
118 hdma_tim.Init.PeriphInc = DMA_PINC_DISABLE;
119 hdma_tim.Init.MemInc = DMA_MINC_ENABLE;
120 hdma_tim.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
121 hdma_tim.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
122 hdma_tim.Init.Mode = DMA_NORMAL;
123 hdma_tim.Init.Priority = DMA_PRIORITY_HIGH;
124 hdma_tim.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
125 hdma_tim.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
126 hdma_tim.Init.MemBurst = DMA_MBURST_SINGLE;
127 hdma_tim.Init.PeriphBurst = DMA_PBURST_SINGLE;
129 /* Set hdma_tim instance */
130 hdma_tim.Instance = (DMA_ARCH_TYPE *)dmaRef;
132 uint16_t dmaIndex = timerDmaIndex(timerChannel);
134 /* Link hdma_tim to hdma[x] (channelx) */
135 __DAL_LINKDMA(&TmrHandle, hdma[dmaIndex], hdma_tim);
137 dmaEnable(dmaGetIdentifier(dmaRef));
138 dmaSetHandler(dmaGetIdentifier(dmaRef), WS2811_DMA_IRQHandler, NVIC_PRIO_WS2811_DMA, dmaIndex);
140 /* Initialize TIMx DMA handle */
141 if (DAL_DMA_Init(TmrHandle.hdma[dmaIndex]) != DAL_OK) {
142 /* Initialization Error */
143 return false;
146 TMR_OC_InitTypeDef TMR_OCInitStructure;
148 /* PWM1 Mode configuration: Channel1 */
149 TMR_OCInitStructure.OCMode = TMR_OCMODE_PWM1;
150 TMR_OCInitStructure.OCIdleState = TMR_OCIDLESTATE_SET;
151 TMR_OCInitStructure.OCPolarity = (timerHardware->output & TIMER_OUTPUT_INVERTED) ? TMR_OCPOLARITY_LOW : TMR_OCPOLARITY_HIGH;
152 TMR_OCInitStructure.OCNIdleState = TMR_OCNIDLESTATE_RESET;
153 TMR_OCInitStructure.OCNPolarity = (timerHardware->output & TIMER_OUTPUT_INVERTED) ? TMR_OCNPOLARITY_LOW : TMR_OCNPOLARITY_HIGH;
154 TMR_OCInitStructure.Pulse = 0;
155 TMR_OCInitStructure.OCFastMode = TMR_OCFAST_DISABLE;
156 if (DAL_TMR_PWM_ConfigChannel(&TmrHandle, &TMR_OCInitStructure, timerChannel) != DAL_OK) {
157 /* Configuration Error */
158 return false;
160 if (timerHardware->output & TIMER_OUTPUT_N_CHANNEL) {
161 if (DAL_TMREx_PWMN_Start(&TmrHandle, timerChannel) != DAL_OK) {
162 /* Starting PWM generation Error */
163 return false;
165 } else {
166 if (DAL_TMR_PWM_Start(&TmrHandle, timerChannel) != DAL_OK) {
167 /* Starting PWM generation Error */
168 return false;
172 return true;
175 void ws2811LedStripDMAEnable(void)
177 if (DMA_SetCurrDataCounter(&TmrHandle, timerChannel, ledStripDMABuffer, WS2811_DMA_BUFFER_SIZE) != DAL_OK) {
178 /* DMA set error */
179 ws2811LedDataTransferInProgress = false;
180 return;
182 /* Reset timer counter */
183 __DAL_TMR_SET_COUNTER(&TmrHandle,0);
184 /* Enable channel DMA requests */
185 TIM_DMACmd(&TmrHandle,timerChannel,ENABLE);
188 #endif // USE_LED_STRIP