dRonin  adbada4
dRonin firmware
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
pios_ws2811.c
Go to the documentation of this file.
1 
18 /*
19  * This program is free software; you can redistribute it and/or modify
20  * it under the terms of the GNU General Public License as published by
21  * the Free Software Foundation; either version 3 of the License, or
22  * (at your option) any later version.
23  *
24  * This program is distributed in the hope that it will be useful, but
25  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
26  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
27  * for more details.
28  *
29  * You should have received a copy of the GNU General Public License along
30  * with this program; if not, write to the Free Software Foundation, Inc.,
31  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32  */
33 
34 #include "pios.h"
35 #include "pios_ws2811.h"
36 
37 #include "pios_dma.h"
38 
39 #define WS2811_BITS_PER_LED 24
40 // for 50us delay
41 #define WS2811_DELAY_BUFFER_LENGTH 42
42 
43 #define WS2811_TIMER_HZ 24000000
44 #define WS2811_TIMER_PERIOD 29 // 800KHz
45 // timer compare value for logical 1
46 #define BIT_COMPARE_1 17
47 // timer compare value for logical 0
48 #define BIT_COMPARE_0 9
49 
50 #define PIOS_WS2811_MAGIC 0x00281100
51 
52 struct ws2811_dev_s {
53  uint32_t magic;
54  const struct pios_ws2811_cfg *config;
55 
56  bool dma_active;
57 
58  int max_leds;
60 
61  uint8_t dma_buffer[];
62 };
63 
65  const struct pios_ws2811_cfg *ws2811_cfg, int max_leds)
66 {
67  if (max_leds <= 0) {
68  return -2;
69  }
70 
71  int buffer_size = WS2811_BITS_PER_LED * max_leds +
73 
74  ws2811_dev_t ws2811_dev = PIOS_malloc(sizeof(*ws2811_dev) + buffer_size);
75 
76  if (!ws2811_dev) {
77  return -3;
78  }
79 
80  memset(ws2811_dev, 0, sizeof(*ws2811_dev) + buffer_size);
81 
82  ws2811_dev->magic = PIOS_WS2811_MAGIC;
83  ws2811_dev->config = ws2811_cfg;
84  ws2811_dev->buffer_size = buffer_size;
85  ws2811_dev->max_leds = max_leds;
86 
87  TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
88  TIM_OCInitTypeDef TIM_OCInitStructure;
89  DMA_InitTypeDef DMA_InitStructure;
90 
91  GPIO_InitTypeDef gpio_cfg = {
92  .GPIO_Pin = ws2811_cfg->gpio_pin,
93  .GPIO_Mode = GPIO_Mode_AF,
94  /* Drive hard-- the pin is loaded on some targets-- like F3E,
95  * and there could be a big bus on any.
96  */
97  .GPIO_Speed = GPIO_Speed_50MHz,
98  .GPIO_OType = GPIO_OType_PP,
99  .GPIO_PuPd = GPIO_PuPd_NOPULL,
100  };
101 
102  GPIO_Init(ws2811_cfg->led_gpio, &gpio_cfg);
103 
104  GPIO_PinAFConfig(ws2811_cfg->led_gpio,
105  __builtin_ctz(ws2811_cfg->gpio_pin), ws2811_cfg->remap);
106 
107  /* Compute the prescaler value */
108  uint16_t prescalerValue = (PIOS_SYSCLK / WS2811_TIMER_HZ) - 1;
109 
110  /* Time base configuration */
111  TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
112  TIM_TimeBaseStructure.TIM_Period = WS2811_TIMER_PERIOD;
113  TIM_TimeBaseStructure.TIM_Prescaler = prescalerValue;
114  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
115  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
116  TIM_TimeBaseInit(ws2811_cfg->timer, &TIM_TimeBaseStructure);
117 
118  /* PWM1 Mode configuration: Channel1 */
119  TIM_OCStructInit(&TIM_OCInitStructure);
120  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
121  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
122  TIM_OCInitStructure.TIM_Pulse = 5;
123  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
124 
125  switch (ws2811_cfg->timer_chan) {
126  case TIM_Channel_1:
127  TIM_OC1Init(ws2811_cfg->timer, &TIM_OCInitStructure);
128  TIM_OC1PreloadConfig(ws2811_cfg->timer, TIM_OCPreload_Enable);
129  break;
130  case TIM_Channel_2:
131  TIM_OC2Init(ws2811_cfg->timer, &TIM_OCInitStructure);
132  TIM_OC2PreloadConfig(ws2811_cfg->timer, TIM_OCPreload_Enable);
133  break;
134  case TIM_Channel_3:
135  TIM_OC3Init(ws2811_cfg->timer, &TIM_OCInitStructure);
136  TIM_OC3PreloadConfig(ws2811_cfg->timer, TIM_OCPreload_Enable);
137  break;
138  case TIM_Channel_4:
139  TIM_OC4Init(ws2811_cfg->timer, &TIM_OCInitStructure);
140  TIM_OC4PreloadConfig(ws2811_cfg->timer, TIM_OCPreload_Enable);
141  break;
142  }
143 
144  TIM_CtrlPWMOutputs(ws2811_cfg->timer, ENABLE);
145 
146  DMA_DeInit(ws2811_cfg->dma_chan);
147 
148  DMA_StructInit(&DMA_InitStructure);
149  DMA_InitStructure.DMA_PeripheralBaseAddr =
150  (uint32_t)&ws2811_cfg->timer->CCR1 + ws2811_cfg->timer_chan;
151  DMA_InitStructure.DMA_MemoryBaseAddr =
152  (uint32_t)ws2811_dev->dma_buffer;
153  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
154  DMA_InitStructure.DMA_BufferSize = ws2811_dev->buffer_size;
155  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
156  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
157  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
158  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
159  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
160  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
161  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
162 
163  DMA_Init(ws2811_cfg->dma_chan, &DMA_InitStructure);
164 
165  TIM_DMACmd(ws2811_cfg->timer, ws2811_cfg->timer_dma_source, ENABLE);
166 
167  TIM_Cmd(ws2811_cfg->timer, ENABLE);
168 
169  PIOS_WS2811_set_all(ws2811_dev, 0, 0, 0);
170 
171  *dev_out = ws2811_dev;
172 
173  return 0;
174 }
175 
176 void PIOS_WS2811_set(ws2811_dev_t dev, int led,
177  uint8_t r, uint8_t g, uint8_t b)
178 {
179  if (!dev) {
180  return;
181  }
182 
183  if (led >= dev->max_leds) {
184  return;
185  }
186 
187  int offset = led * WS2811_BITS_PER_LED;
188 
189  uint32_t grb = (g << 16) | (r << 8) | b;
190 
191  for (int bit = (WS2811_BITS_PER_LED - 1); bit >= 0; --bit) {
192  dev->dma_buffer[offset++] =
193  (grb & (1 << bit)) ? BIT_COMPARE_1 : BIT_COMPARE_0;
194  }
195 }
196 
198 {
199  if (!dev) {
200  return;
201  }
202 
203  if (dev->dma_active) {
204  if (!DMA_GetFlagStatus(dev->config->dma_tcif)) {
205  return;
206  }
207 
208  dev->dma_active = false; /* LOL */
209  DMA_ClearFlag(dev->config->dma_tcif);
210  DMA_Cmd(dev->config->dma_chan, DISABLE);
211  }
212 
213  dev->dma_active = true;
214 
215  DMA_SetCurrDataCounter(dev->config->dma_chan, dev->buffer_size);
216  DMA_Cmd(dev->config->dma_chan, ENABLE);
217 }
218 
219 void PIOS_WS2811_set_all(ws2811_dev_t dev, uint8_t r, uint8_t g,
220  uint8_t b)
221 {
222  for (int i = 0; i < dev->max_leds; i++) {
223  PIOS_WS2811_set(dev, i, r, g, b);
224  }
225 }
226 
228 {
229  return dev->max_leds;
230 }
231 
232 /*
233  * @}
234  * @}
235  */
const struct pios_ws2811_cfg * config
Definition: pios_ws2811.c:54
int PIOS_WS2811_get_num_leds(ws2811_dev_t dev)
Find out how many LEDs are configured on an interface.
Definition: pios_ws2811.c:227
Main PiOS header to include all the compiled in PiOS options.
GPIO_TypeDef * led_gpio
uint32_t magic
Definition: pios_ws2811.c:53
bool dma_active
Definition: pios_ws2811.c:56
void * PIOS_malloc(size_t size)
Definition: pios_heap.c:125
#define BIT_COMPARE_1
Definition: pios_ws2811.c:46
int PIOS_WS2811_init(ws2811_dev_t *dev_out, const struct pios_ws2811_cfg *cfg, int max_leds)
Allocate and initialise WS2811 device.
Definition: pios_ws2811.c:64
void PIOS_WS2811_set_all(ws2811_dev_t dev, uint8_t r, uint8_t g, uint8_t b)
Sets all LEDs to a color value.
Definition: pios_ws2811.c:219
#define PIOS_WS2811_MAGIC
Definition: pios_ws2811.c:50
#define WS2811_BITS_PER_LED
Definition: pios_ws2811.c:39
uint8_t i
Definition: msp_messages.h:97
TIM_TypeDef * timer
#define WS2811_DELAY_BUFFER_LENGTH
Definition: pios_ws2811.c:41
uint16_t timer_dma_source
uint32_t offset
Definition: uavtalk_priv.h:51
#define BIT_COMPARE_0
Definition: pios_ws2811.c:48
void PIOS_WS2811_set(ws2811_dev_t dev, int idx, uint8_t r, uint8_t g, uint8_t b)
Set a given LED to a color value.
Definition: pios_ws2811.c:176
DMA_Channel_TypeDef * dma_chan
void PIOS_WS2811_trigger_update(ws2811_dev_t dev)
Trigger an update of the LED strand.
Definition: pios_ws2811.c:197
#define PIOS_SYSCLK
Definition: pios_board.h:143
#define WS2811_TIMER_PERIOD
Definition: pios_ws2811.c:44
uint8_t dma_buffer[]
Definition: pios_ws2811.c:61
#define WS2811_TIMER_HZ
Definition: pios_ws2811.c:43