]> mj.ucw.cz Git - home-hw.git/blob - test-neopixel-square/main.c
Rainbow case: TODO
[home-hw.git] / test-neopixel-square / main.c
1 /*
2  *      Neopixel (WS2812B) Test
3  *
4  *      (c) 2022 Martin Mareš <mj@ucw.cz>
5  */
6
7 #include "util.h"
8
9 #include <libopencm3/cm3/cortex.h>
10 #include <libopencm3/cm3/nvic.h>
11 #include <libopencm3/cm3/systick.h>
12 #include <libopencm3/cm3/scb.h>
13 #include <libopencm3/stm32/dma.h>
14 #include <libopencm3/stm32/gpio.h>
15 #include <libopencm3/stm32/rcc.h>
16 #include <libopencm3/stm32/timer.h>
17 #include <libopencm3/stm32/usart.h>
18
19 #include <string.h>
20
21 /*** Hardware init ***/
22
23 static void clock_init(void)
24 {
25         rcc_clock_setup_pll(&rcc_hse_configs[RCC_CLOCK_HSE8_72MHZ]);
26
27         rcc_periph_clock_enable(RCC_GPIOA);
28         rcc_periph_clock_enable(RCC_GPIOB);
29         rcc_periph_clock_enable(RCC_GPIOC);
30         rcc_periph_clock_enable(RCC_USART1);
31         rcc_periph_clock_enable(RCC_TIM4);
32         rcc_periph_clock_enable(RCC_DMA1);
33
34         rcc_periph_reset_pulse(RST_GPIOA);
35         rcc_periph_reset_pulse(RST_GPIOB);
36         rcc_periph_reset_pulse(RST_GPIOC);
37         rcc_periph_reset_pulse(RST_USART1);
38         rcc_periph_reset_pulse(RST_TIM4);
39 }
40
41 static void gpio_init(void)
42 {
43         // PA9 = TXD1 for debugging console
44         // PA10 = RXD1 for debugging console
45         gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO9);
46         gpio_set_mode(GPIOA, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO10);
47
48         // PC13 = BluePill LED
49         gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO13);
50         gpio_clear(GPIOC, GPIO13);
51
52         // PB8 = data for Neopixel
53         gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, GPIO8);
54 }
55
56 static void usart_init(void)
57 {
58         usart_set_baudrate(USART1, 115200);
59         usart_set_databits(USART1, 8);
60         usart_set_stopbits(USART1, USART_STOPBITS_1);
61         usart_set_mode(USART1, USART_MODE_TX);
62         usart_set_parity(USART1, USART_PARITY_NONE);
63         usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE);
64
65         usart_enable(USART1);
66 }
67
68 /*** System ticks ***/
69
70 static volatile u32 ms_ticks;
71
72 void sys_tick_handler(void)
73 {
74         ms_ticks++;
75 }
76
77 static void tick_init(void)
78 {
79         systick_set_frequency(1000, CPU_CLOCK_MHZ * 1000000);
80         systick_counter_enable();
81         systick_interrupt_enable();
82 }
83
84 static void delay_ms(uint ms)
85 {
86         u32 start_ticks = ms_ticks;
87         while (ms_ticks - start_ticks < ms)
88                 ;
89 }
90
91 /*** Neopixels ***/
92
93 #define NPIX_PERIOD 90          // timer runs on 72 MHz, so 90 periods = 1250 ns
94 #define NPIX_RESET 128          // the chip needs longer reset pulse than documented
95 #define NPIX_NUM_LEDS 64
96 #define B0 30
97 #define B1 60
98
99 byte neopixel_buf[NPIX_RESET + 24*NPIX_NUM_LEDS];
100
101 static void neopixel_set(uint led, byte r, byte g, byte b)
102 {
103         byte *buf = &neopixel_buf[NPIX_RESET + 24*led];
104
105         for (uint m=0x80; m; m >>= 1)
106                 *buf++ = (g & m) ? B1 : B0;
107         for (uint m=0x80; m; m >>= 1)
108                 *buf++ = (r & m) ? B1 : B0;
109         for (uint m=0x80; m; m >>= 1)
110                 *buf++ = (b & m) ? B1 : B0;
111 }
112
113
114 static void neopixel_init(void)
115 {
116         for (uint i=0; i < NPIX_NUM_LEDS; i++)
117                 neopixel_set(i, 7, 7, 7);
118
119         // TIM4 update is connected to DMA1 channel 7
120
121         // FIXME: Strange programming sequence as specified in manual
122
123         dma_channel_reset(DMA1, 7);
124
125         dma_set_peripheral_address(DMA1, 7, (u32) &TIM_CCR3(TIM4));
126         dma_set_memory_address(DMA1, 7, (u32) neopixel_buf);
127         dma_set_number_of_data(DMA1, 7, ARRAY_SIZE(neopixel_buf));
128         dma_set_priority(DMA1, 7, DMA_CCR_PL_VERY_HIGH);
129
130         dma_set_read_from_memory(DMA1, 7);
131         dma_enable_circular_mode(DMA1, 7);
132
133         dma_set_memory_size(DMA1, 7, DMA_CCR_MSIZE_8BIT);
134         dma_enable_memory_increment_mode(DMA1, 7);
135
136         dma_set_peripheral_size(DMA1, 7, DMA_CCR_PSIZE_16BIT);
137         dma_disable_peripheral_increment_mode(DMA1, 7);
138
139         dma_enable_channel(DMA1, 7);
140
141         timer_set_prescaler(TIM4, 0);
142         timer_set_mode(TIM4, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP);
143         timer_disable_preload(TIM4);
144         timer_set_period(TIM4, NPIX_PERIOD - 1);
145
146         timer_set_oc_mode(TIM4, TIM_OC3, TIM_OCM_PWM1);
147         timer_set_oc_value(TIM4, TIM_OC3, 0);
148         timer_set_oc_polarity_high(TIM4, TIM_OC3);
149         timer_enable_oc_output(TIM4, TIM_OC3);
150
151         timer_set_dma_on_update_event(TIM4);
152         TIM_DIER(TIM4) |= TIM_DIER_UDE;
153
154         timer_enable_counter(TIM4);
155 }
156
157 /*** Game of Life ***/
158
159 #define LN 8
160
161 byte life[LN+2][LN+2];
162 byte lpred[LN+2][LN+2];
163
164 static void life_init(void)
165 {
166 #if 0
167         // The R pentomino
168         life[3][4] = 1;
169         life[3][5] = 1;
170         life[4][3] = 1;
171         life[4][4] = 1;
172         life[5][4] = 1;
173 #elif 0
174         // Blinker
175         life[3][2] = 1;
176         life[3][3] = 1;
177         life[3][4] = 1;
178 #elif 0
179         // Glider
180         life[1][3] = 1;
181         life[2][1] = 1;
182         life[2][3] = 1;
183         life[3][2] = 1;
184         life[3][3] = 1;
185 #elif 1
186         // Octagon (period 5 oscillator)
187         life[1][4] = 1;
188         life[1][5] = 1;
189         life[2][3] = 1;
190         life[2][6] = 1;
191         life[3][2] = 1;
192         life[3][7] = 1;
193         life[4][1] = 1;
194         life[4][8] = 1;
195         life[5][1] = 1;
196         life[5][8] = 1;
197         life[6][2] = 1;
198         life[6][7] = 1;
199         life[7][3] = 1;
200         life[7][6] = 1;
201         life[8][4] = 1;
202         life[8][5] = 1;
203 #endif
204 }
205
206 static void life_step(void)
207 {
208         memcpy(lpred, life, sizeof(life));
209
210         for (uint i=1; i<=LN; i++)
211                 for (uint j=1; j<=LN; j++) {
212                         int neigh = 0;
213                         for (uint ii=i-1; ii <= i+1; ii++)
214                                 for (uint jj=j-1; jj <= j+1; jj++)
215                                         neigh += lpred[ii][jj];
216                         neigh -= life[i][j];
217                         if (life[i][j]) {
218                                 if (neigh < 2 || neigh > 3)
219                                         life[i][j] = 0;
220                         } else {
221                                 if (neigh == 3)
222                                         life[i][j] = 1;
223                         }
224                 }
225 }
226
227 static void life_show(void)
228 {
229         uint led = 0;
230         for (uint i=1; i<=LN; i++)
231                 for (uint j=1; j<=LN; j++) {
232                         if (life[i][j])
233                                 neopixel_set(led, 0, 7, 0);
234                         else
235                                 neopixel_set(led, 0, 0, 1);
236                         led++;
237                 }
238 }
239
240 /*** Main ***/
241
242 int main(void)
243 {
244         clock_init();
245         gpio_init();
246         usart_init();
247         tick_init();
248         neopixel_init();
249
250         debug_printf("Hello, world!\n");
251
252         // uint i=63;
253         life_init();
254         life_show();
255         uint lsteps = 0;
256
257         for (;;) {
258                 // wait_for_interrupt();
259                 debug_led(1);
260                 // neopixel_set(NPIX_NUM_LEDS-1, 0, 0x7f, 0);
261                 // neopixel_set(i, 0, 0, 7);
262                 // i = (i+1) % 64;
263                 // neopixel_set(i, 0, 63, 0);
264                 delay_ms(100);
265                 debug_led(0);
266                 // neopixel_set(NPIX_NUM_LEDS-1, 0, 0, 0x7f);
267                 delay_ms(500);
268                 if (lsteps++ < 3000)
269                         life_step();
270                 else {
271                         life_init();
272                         lsteps = 0;
273                 }
274                 life_show();
275         }
276
277         return 0;
278 }