2 ******************************************************************************
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
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
28 #include "pios_ws2811.h"
29 #include "pios_ws2811_cfg.h"
31 #define WS2811_BITS_PER_LED 24
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
{
50 const struct pios_ws2811_cfg
*config
;
51 uint8_t dma_buffer
[WS2811_DMA_BUFFER_SIZE
];
55 struct pios_ws2811_dev
*ws2811_dev
;
57 void PIOS_WS2811_Init(uint32_t *dev_id
, const struct pios_ws2811_cfg
*ws2811_cfg
)
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
) {
100 TIM_OC1Init(ws2811_cfg
->timer
, &TIM_OCInitStructure
);
101 TIM_OC1PreloadConfig(ws2811_cfg
->timer
, TIM_OCPreload_Enable
);
104 TIM_OC2Init(ws2811_cfg
->timer
, &TIM_OCInitStructure
);
105 TIM_OC2PreloadConfig(ws2811_cfg
->timer
, TIM_OCPreload_Enable
);
108 TIM_OC3Init(ws2811_cfg
->timer
, &TIM_OCInitStructure
);
109 TIM_OC3PreloadConfig(ws2811_cfg
->timer
, TIM_OCPreload_Enable
);
112 TIM_OC4Init(ws2811_cfg
->timer
, &TIM_OCInitStructure
);
113 TIM_OC4PreloadConfig(ws2811_cfg
->timer
, TIM_OCPreload_Enable
);
118 TIM_CtrlPWMOutputs(ws2811_cfg
->timer
, ENABLE
);
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()
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
);
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
)) {
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
;
191 PIOS_WS2811_Update();
195 void PIOS_WS2811_Update()
197 if (!ws2811_dev
|| ws2811_dev
->dma_active
) {
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
);