update credits
[librepilot.git] / flight / pios / stm32f30x / pios_ws2811.c
blob3cb23fe31e62efb4ce2c62431089887c8dce52b7
1 /**
2 ******************************************************************************
4 * @file pios_ws2811.c
5 * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017.
6 * @brief A driver for ws2811 rgb led controller.
7 * this is a port of the CleanFlight/BetaFlight implementation.
8 * @see The GNU Public License (GPL) Version 3
10 *****************************************************************************/
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 3 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * for more details.
22 * You should have received a copy of the GNU General Public License along
23 * with this program; if not, write to the Free Software Foundation, Inc.,
24 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include "pios.h"
28 #include "pios_ws2811.h"
29 #include "pios_ws2811_cfg.h"
31 #define WS2811_BITS_PER_LED 24
32 // for 50us delay
33 #define WS2811_DELAY_BUFFER_LENGTH 42
35 #define WS2811_DATA_BUFFER_SIZE (WS2811_BITS_PER_LED * PIOS_WS2811_NUMLEDS)
36 // number of bytes needed is #LEDs * 24 bytes + 42 trailing bytes)
37 #define WS2811_DMA_BUFFER_SIZE (WS2811_DATA_BUFFER_SIZE + WS2811_DELAY_BUFFER_LENGTH)
39 #define WS2811_TIMER_HZ 24000000
40 #define WS2811_TIMER_PERIOD 29
41 // timer compare value for logical 1
42 #define BIT_COMPARE_1 17
43 // timer compare value for logical 0
44 #define BIT_COMPARE_0 9
46 #define PIOS_WS2811_MAGIC 0x00281100
48 struct pios_ws2811_dev {
49 uint32_t magic;
50 const struct pios_ws2811_cfg *config;
51 uint8_t dma_buffer[WS2811_DMA_BUFFER_SIZE];
52 bool dma_active;
55 struct pios_ws2811_dev *ws2811_dev;
57 void PIOS_WS2811_Init(uint32_t *dev_id, const struct pios_ws2811_cfg *ws2811_cfg)
59 if (ws2811_dev) {
60 return;
63 ws2811_dev = (struct pios_ws2811_dev *)pios_malloc(sizeof(*ws2811_dev));
65 PIOS_Assert(ws2811_dev);
67 memset(ws2811_dev, 0, sizeof(*ws2811_dev));
69 ws2811_dev->magic = PIOS_WS2811_MAGIC;
70 ws2811_dev->config = ws2811_cfg;
73 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
74 TIM_OCInitTypeDef TIM_OCInitStructure;
75 DMA_InitTypeDef DMA_InitStructure;
77 GPIO_Init(ws2811_cfg->pin.gpio, (GPIO_InitTypeDef *)&ws2811_cfg->pin.init);
79 GPIO_PinAFConfig(ws2811_cfg->pin.gpio, ws2811_cfg->pin.pin_source, ws2811_cfg->remap);
81 /* Compute the prescaler value */
82 uint16_t prescalerValue = (uint16_t)(SystemCoreClock / WS2811_TIMER_HZ) - 1;
83 /* Time base configuration */
84 TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
85 TIM_TimeBaseStructure.TIM_Period = WS2811_TIMER_PERIOD; // 800kHz
86 TIM_TimeBaseStructure.TIM_Prescaler = prescalerValue;
87 TIM_TimeBaseStructure.TIM_ClockDivision = 0;
88 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
89 TIM_TimeBaseInit(ws2811_cfg->timer, &TIM_TimeBaseStructure);
91 /* PWM1 Mode configuration: Channel1 */
92 TIM_OCStructInit(&TIM_OCInitStructure);
93 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
94 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
95 TIM_OCInitStructure.TIM_Pulse = 0;
96 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
98 switch (ws2811_cfg->timer_chan) {
99 case TIM_Channel_1:
100 TIM_OC1Init(ws2811_cfg->timer, &TIM_OCInitStructure);
101 TIM_OC1PreloadConfig(ws2811_cfg->timer, TIM_OCPreload_Enable);
102 break;
103 case TIM_Channel_2:
104 TIM_OC2Init(ws2811_cfg->timer, &TIM_OCInitStructure);
105 TIM_OC2PreloadConfig(ws2811_cfg->timer, TIM_OCPreload_Enable);
106 break;
107 case TIM_Channel_3:
108 TIM_OC3Init(ws2811_cfg->timer, &TIM_OCInitStructure);
109 TIM_OC3PreloadConfig(ws2811_cfg->timer, TIM_OCPreload_Enable);
110 break;
111 case TIM_Channel_4:
112 TIM_OC4Init(ws2811_cfg->timer, &TIM_OCInitStructure);
113 TIM_OC4PreloadConfig(ws2811_cfg->timer, TIM_OCPreload_Enable);
114 break;
118 TIM_CtrlPWMOutputs(ws2811_cfg->timer, ENABLE);
120 /* configure DMA */
121 // NVIC setup here
122 NVIC_InitTypeDef NVIC_InitStructure;
124 NVIC_InitStructure.NVIC_IRQChannel = ws2811_cfg->dma_irqn;
125 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = PIOS_IRQ_PRIO_LOW;
126 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
127 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
128 NVIC_Init(&NVIC_InitStructure);
131 DMA_DeInit(ws2811_cfg->dma_chan);
133 DMA_StructInit(&DMA_InitStructure);
134 DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ws2811_cfg->timer->CCR1 + ws2811_cfg->timer_chan;
135 DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ws2811_dev->dma_buffer;
136 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
137 DMA_InitStructure.DMA_BufferSize = WS2811_DMA_BUFFER_SIZE;
138 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
139 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
140 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
141 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
142 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
143 DMA_InitStructure.DMA_Priority = DMA_Priority_High;
144 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
146 DMA_Init(ws2811_cfg->dma_chan, &DMA_InitStructure);
148 TIM_DMACmd(ws2811_cfg->timer, ws2811_cfg->timer_dma_source, ENABLE);
150 DMA_ITConfig(ws2811_cfg->dma_chan, DMA_IT_TC, ENABLE);
152 *dev_id = (uint32_t)ws2811_dev;
155 void PIOS_WS2811_DMA_irq_handler()
157 if (!ws2811_dev) {
158 return;
161 if (DMA_GetITStatus(ws2811_dev->config->dma_tcif)) {
162 ws2811_dev->dma_active = false;
163 DMA_Cmd(ws2811_dev->config->dma_chan, DISABLE);
165 DMA_ClearITPendingBit(ws2811_dev->config->dma_tcif);
171 * Set a led color
172 * @param c color
173 * @param led led number
174 * @param update Perform an update after changing led color
176 void PIOS_WS2811_setColorRGB(Color_t c, uint8_t led, bool update)
178 if (!ws2811_dev || (led >= PIOS_WS2811_NUMLEDS)) {
179 return;
182 int offset = led * WS2811_BITS_PER_LED;
184 uint32_t grb = (c.G << 16) | (c.R << 8) | (c.B);
186 for (int bit = (WS2811_BITS_PER_LED - 1); bit >= 0; --bit) {
187 ws2811_dev->dma_buffer[offset++] = (grb & (1 << bit)) ? BIT_COMPARE_1 : BIT_COMPARE_0;
190 if (update) {
191 PIOS_WS2811_Update();
195 void PIOS_WS2811_Update()
197 if (!ws2811_dev || ws2811_dev->dma_active) {
198 return;
201 ws2811_dev->dma_active = true;
203 DMA_SetCurrDataCounter(ws2811_dev->config->dma_chan, WS2811_DMA_BUFFER_SIZE); // load number of bytes to be transferred
204 TIM_SetCounter(ws2811_dev->config->timer, 0);
205 TIM_Cmd(ws2811_dev->config->timer, ENABLE);
207 DMA_Cmd(ws2811_dev->config->dma_chan, ENABLE);