]> mj.ucw.cz Git - home-hw.git/blob - test-neopixel-square/main.c
More experiments with neopixel square
[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 /*** Main ***/
158
159 int main(void)
160 {
161         clock_init();
162         gpio_init();
163         usart_init();
164         tick_init();
165         neopixel_init();
166
167         debug_printf("Hello, world!\n");
168
169         uint i=63;
170
171         for (;;) {
172                 // wait_for_interrupt();
173                 debug_led(1);
174                 // neopixel_set(NPIX_NUM_LEDS-1, 0, 0x7f, 0);
175                 neopixel_set(i, 0, 0, 7);
176                 i = (i+1) % 64;
177                 neopixel_set(i, 0, 63, 0);
178                 delay_ms(100);
179                 debug_led(0);
180                 // neopixel_set(NPIX_NUM_LEDS-1, 0, 0, 0x7f);
181                 delay_ms(500);
182         }
183
184         return 0;
185 }