]> mj.ucw.cz Git - home-hw.git/blob - rainbow/firmware/main.c
Protab: Use USB boot loader in all examples
[home-hw.git] / rainbow / firmware / main.c
1 /*
2  *      Neopixel (WS2812B) Rainbow
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 #include <libopencm3/usb/dfu.h>
19 #include <libopencm3/usb/usbd.h>
20
21 #include <string.h>
22
23 #include "interface.h"
24
25 /*** Hardware init ***/
26
27 static void clock_init(void)
28 {
29         rcc_clock_setup_pll(&rcc_hse_configs[RCC_CLOCK_HSE8_72MHZ]);
30
31         rcc_periph_clock_enable(RCC_GPIOA);
32         rcc_periph_clock_enable(RCC_GPIOB);
33         rcc_periph_clock_enable(RCC_GPIOC);
34         rcc_periph_clock_enable(RCC_USART1);
35         rcc_periph_clock_enable(RCC_TIM4);
36         rcc_periph_clock_enable(RCC_DMA1);
37
38         rcc_periph_reset_pulse(RST_GPIOA);
39         rcc_periph_reset_pulse(RST_GPIOB);
40         rcc_periph_reset_pulse(RST_GPIOC);
41         rcc_periph_reset_pulse(RST_USART1);
42         rcc_periph_reset_pulse(RST_TIM4);
43 }
44
45 static void gpio_init(void)
46 {
47         // PA9 = TXD1 for debugging console
48         // PA10 = RXD1 for debugging console
49         gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO9);
50         gpio_set_mode(GPIOA, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO10);
51
52         // PC13 = BluePill LED
53         gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO13);
54         gpio_clear(GPIOC, GPIO13);
55
56         // PB8 = data for Neopixel
57         gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, GPIO8);
58 }
59
60 static void usart_init(void)
61 {
62         usart_set_baudrate(USART1, 115200);
63         usart_set_databits(USART1, 8);
64         usart_set_stopbits(USART1, USART_STOPBITS_1);
65         usart_set_mode(USART1, USART_MODE_TX);
66         usart_set_parity(USART1, USART_PARITY_NONE);
67         usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE);
68
69         usart_enable(USART1);
70 }
71
72 /*** System ticks ***/
73
74 static volatile u32 ms_ticks;
75
76 void sys_tick_handler(void)
77 {
78         ms_ticks++;
79 }
80
81 static void tick_init(void)
82 {
83         systick_set_frequency(1000, CPU_CLOCK_MHZ * 1000000);
84         systick_counter_enable();
85         systick_interrupt_enable();
86 }
87
88 static void delay_ms(uint ms)
89 {
90         u32 start_ticks = ms_ticks;
91         while (ms_ticks - start_ticks < ms)
92                 ;
93 }
94
95 /*** Neopixels ***/
96
97 #define NPIX_PERIOD 90          // timer runs on 72 MHz, so 90 periods = 1250 ns
98 #define NPIX_RESET 64           // the chip needs longer reset pulse than documented
99 #define NPIX_B0 30
100 #define NPIX_B1 60
101
102 static byte led_rgb[NPIX_NUM_LEDS][3];
103
104 static byte neopixel_buf[NPIX_RESET + NPIX_NUM_LEDS*24 + 1];
105 static bool neopixel_dma_running;
106 static bool neopixel_want_send;
107
108 static void neopixel_run_dma(void)
109 {
110         // When STM32 is programmed using ST-Link, the DMA sometimes keeps running.
111         dma_channel_reset(DMA1, 7);
112
113         // This order of register writes is recommended in the manual.
114         dma_set_peripheral_address(DMA1, 7, (u32) &TIM_CCR3(TIM4));
115         dma_set_memory_address(DMA1, 7, (u32) neopixel_buf);
116         dma_set_number_of_data(DMA1, 7, ARRAY_SIZE(neopixel_buf));
117         dma_set_priority(DMA1, 7, DMA_CCR_PL_VERY_HIGH);
118
119         dma_set_read_from_memory(DMA1, 7);
120
121         dma_set_memory_size(DMA1, 7, DMA_CCR_MSIZE_8BIT);
122         dma_enable_memory_increment_mode(DMA1, 7);
123
124         dma_set_peripheral_size(DMA1, 7, DMA_CCR_PSIZE_16BIT);
125         dma_disable_peripheral_increment_mode(DMA1, 7);
126
127         dma_clear_interrupt_flags(DMA1, 7, DMA_TCIF);
128         dma_enable_channel(DMA1, 7);
129         neopixel_dma_running = 1;
130 }
131
132 static void neopixel_recalc(void)
133 {
134         byte *buf = neopixel_buf;
135         for (uint i = 0; i < NPIX_RESET; i++)
136                 *buf++ = 0;
137         for (uint i = 0; i < NPIX_NUM_LEDS; i++) {
138                 // The order is GRB, MSB first
139                 for (uint m = 0x80; m; m >>= 1)
140                         *buf++ = ((led_rgb[i][1] & m) ? NPIX_B1 : NPIX_B0);
141                 for (uint m = 0x80; m; m >>= 1)
142                         *buf++ = ((led_rgb[i][0] & m) ? NPIX_B1 : NPIX_B0);
143                 for (uint m = 0x80; m; m >>= 1)
144                         *buf++ = ((led_rgb[i][2] & m) ? NPIX_B1 : NPIX_B0);
145         }
146         *buf++ = NPIX_PERIOD;
147
148         neopixel_run_dma();
149         neopixel_want_send = 0;
150 }
151
152 static void neopixel_init(void)
153 {
154         // TIM4 is always running and producing DMA requests on each update
155         // (connected to DMA1 channel 7). When we have something to send,
156         // the DMA is enabled.
157
158         timer_set_prescaler(TIM4, 0);
159         timer_set_mode(TIM4, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP);
160         timer_disable_preload(TIM4);
161         timer_set_period(TIM4, NPIX_PERIOD - 1);
162
163         timer_set_oc_mode(TIM4, TIM_OC3, TIM_OCM_PWM1);
164         timer_set_oc_value(TIM4, TIM_OC3, 0);
165         timer_set_oc_polarity_high(TIM4, TIM_OC3);
166         timer_enable_oc_output(TIM4, TIM_OC3);
167
168         timer_set_dma_on_update_event(TIM4);
169         TIM_DIER(TIM4) |= TIM_DIER_UDE;
170
171         led_rgb[NPIX_NUM_LEDS-1][1] = 0xaa;
172
173         timer_enable_counter(TIM4);
174         neopixel_recalc();
175 }
176
177 static bool neopixel_ready(void)
178 {
179         if (!neopixel_dma_running)
180                 return 1;
181
182         if (!dma_get_interrupt_flag(DMA1, 7, DMA_TCIF))
183                 return 0;
184
185         dma_disable_channel(DMA1, 7);
186         neopixel_dma_running = 0;
187         return 1;
188 }
189
190 /*** USB ***/
191
192 static usbd_device *usbd_dev;
193
194 enum usb_string {
195         STR_MANUFACTURER = 1,
196         STR_PRODUCT,
197         STR_SERIAL,
198 };
199
200 static char usb_serial_number[13];
201
202 static const char *usb_strings[] = {
203         "United Computer Wizards",
204         "Neopixel Rainbow",
205         usb_serial_number,
206 };
207
208 static const struct usb_device_descriptor device = {
209         .bLength = USB_DT_DEVICE_SIZE,
210         .bDescriptorType = USB_DT_DEVICE,
211         .bcdUSB = 0x0200,
212         .bDeviceClass = 0xFF,
213         .bDeviceSubClass = 0,
214         .bDeviceProtocol = 0,
215         .bMaxPacketSize0 = 64,
216         .idVendor = NPIX_USB_VENDOR,
217         .idProduct = NPIX_USB_PRODUCT,
218         .bcdDevice = NPIX_USB_VERSION,
219         .iManufacturer = STR_MANUFACTURER,
220         .iProduct = STR_PRODUCT,
221         .iSerialNumber = STR_SERIAL,
222         .bNumConfigurations = 1,
223 };
224
225 static const struct usb_endpoint_descriptor endpoints[] = {{
226         // Bulk end-point for sending LED values
227         .bLength = USB_DT_ENDPOINT_SIZE,
228         .bDescriptorType = USB_DT_ENDPOINT,
229         .bEndpointAddress = 0x01,
230         .bmAttributes = USB_ENDPOINT_ATTR_BULK,
231         .wMaxPacketSize = 64,
232         .bInterval = 1,
233 }};
234
235 static const struct usb_interface_descriptor iface = {
236         .bLength = USB_DT_INTERFACE_SIZE,
237         .bDescriptorType = USB_DT_INTERFACE,
238         .bInterfaceNumber = 0,
239         .bAlternateSetting = 0,
240         .bNumEndpoints = 1,
241         .bInterfaceClass = 0xFF,
242         .bInterfaceSubClass = 0,
243         .bInterfaceProtocol = 0,
244         .iInterface = 0,
245         .endpoint = endpoints,
246 };
247
248 static const struct usb_dfu_descriptor dfu_function = {
249         .bLength = sizeof(struct usb_dfu_descriptor),
250         .bDescriptorType = DFU_FUNCTIONAL,
251         .bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_WILL_DETACH,
252         .wDetachTimeout = 255,
253         .wTransferSize = 1024,
254         .bcdDFUVersion = 0x0100,
255 };
256
257 static const struct usb_interface_descriptor dfu_iface = {
258         .bLength = USB_DT_INTERFACE_SIZE,
259         .bDescriptorType = USB_DT_INTERFACE,
260         .bInterfaceNumber = 1,
261         .bAlternateSetting = 0,
262         .bNumEndpoints = 0,
263         .bInterfaceClass = 0xFE,
264         .bInterfaceSubClass = 1,
265         .bInterfaceProtocol = 1,
266         .iInterface = 0,
267
268         .extra = &dfu_function,
269         .extralen = sizeof(dfu_function),
270 };
271
272 static const struct usb_interface ifaces[] = {{
273         .num_altsetting = 1,
274         .altsetting = &iface,
275 }, {
276         .num_altsetting = 1,
277         .altsetting = &dfu_iface,
278 }};
279
280 static const struct usb_config_descriptor config = {
281         .bLength = USB_DT_CONFIGURATION_SIZE,
282         .bDescriptorType = USB_DT_CONFIGURATION,
283         .wTotalLength = 0,
284         .bNumInterfaces = 2,
285         .bConfigurationValue = 1,
286         .iConfiguration = 0,
287         .bmAttributes = 0x80,
288         .bMaxPower = 100,       // multiplied by 2 mA
289         .interface = ifaces,
290 };
291
292 static byte usb_configured;
293 static uint8_t usbd_control_buffer[64];
294
295 static void dfu_detach_complete(usbd_device *dev UNUSED, struct usb_setup_data *req UNUSED)
296 {
297         // Reset to bootloader, which implements the rest of DFU
298         debug_printf("Switching to DFU\n");
299         debug_flush();
300         scb_reset_core();
301 }
302
303 static enum usbd_request_return_codes dfu_control_cb(usbd_device *dev UNUSED,
304         struct usb_setup_data *req,
305         uint8_t **buf UNUSED,
306         uint16_t *len UNUSED,
307         void (**complete)(usbd_device *dev, struct usb_setup_data *req))
308 {
309         if (req->bmRequestType != 0x21 || req->bRequest != DFU_DETACH)
310                 return USBD_REQ_NOTSUPP;
311
312         *complete = dfu_detach_complete;
313         return USBD_REQ_HANDLED;
314 }
315
316 static byte usb_rx_buf[3*NPIX_NUM_LEDS];
317
318 static void ep01_cb(usbd_device *dev, uint8_t ep UNUSED)
319 {
320         // We received a frame from the USB host
321         uint len = usbd_ep_read_packet(dev, 0x01, usb_rx_buf, sizeof(usb_rx_buf));
322         debug_printf("USB: Host sent %u bytes\n", len);
323         memcpy(led_rgb, usb_rx_buf, len);
324         neopixel_want_send = 1;
325 }
326
327 static void set_config_cb(usbd_device *dev, uint16_t wValue UNUSED)
328 {
329         usbd_register_control_callback(
330                 dev,
331                 USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE,
332                 USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT,
333                 dfu_control_cb);
334         usbd_ep_setup(dev, 0x01, USB_ENDPOINT_ATTR_BULK, 64, ep01_cb);
335         usb_configured = 1;
336 }
337
338 static void reset_cb(void)
339 {
340         debug_printf("USB: Reset\n");
341         usb_configured = 0;
342 }
343
344 static volatile bool usb_event_pending;
345
346 void usb_lp_can_rx0_isr(void)
347 {
348         /*
349          *  We handle USB in the main loop to avoid race conditions between
350          *  USB interrupts and other code. However, we need an interrupt to
351          *  up the main loop from sleep.
352          *
353          *  We set up only the low-priority ISR, because high-priority ISR handles
354          *  only double-buffered bulk transfers and isochronous transfers.
355          */
356         nvic_disable_irq(NVIC_USB_LP_CAN_RX0_IRQ);
357         usb_event_pending = 1;
358 }
359
360 static void usb_init(void)
361 {
362         // Simulate USB disconnect
363         gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO11 | GPIO12);
364         gpio_clear(GPIOA, GPIO11 | GPIO12);
365         delay_ms(100);
366
367         usbd_dev = usbd_init(
368                 &st_usbfs_v1_usb_driver,
369                 &device,
370                 &config,
371                 usb_strings,
372                 ARRAY_SIZE(usb_strings),
373                 usbd_control_buffer,
374                 sizeof(usbd_control_buffer)
375         );
376         usbd_register_reset_callback(usbd_dev, reset_cb);
377         usbd_register_set_config_callback(usbd_dev, set_config_cb);
378         usb_event_pending = 1;
379 }
380
381 /*** Main ***/
382
383 int main(void)
384 {
385         clock_init();
386         gpio_init();
387         usart_init();
388         tick_init();
389         neopixel_init();
390         usb_init();
391
392         debug_printf("The Bifrőst bridge spans its colors between Midgard and Asgard...\n");
393
394         u32 last_blink = 0;
395         u32 last_send = 0;
396
397         for (;;) {
398                 if (ms_ticks - last_blink >= 300) {
399                         debug_led_toggle();
400                         last_blink = ms_ticks;
401                         // led_rgb[NPIX_NUM_LEDS - 1][1] ^= 0x33;
402                         neopixel_want_send = 1;
403                 }
404
405                 if (usb_event_pending) {
406                         usbd_poll(usbd_dev);
407                         usb_event_pending = 0;
408                         nvic_clear_pending_irq(NVIC_USB_LP_CAN_RX0_IRQ);
409                         nvic_enable_irq(NVIC_USB_LP_CAN_RX0_IRQ);
410                 }
411
412                 if (neopixel_ready()) {
413                         if (neopixel_want_send || ms_ticks - last_send >= 100) {
414                                 // Re-send every 100 ms
415                                 neopixel_recalc();
416                                 last_send = ms_ticks;
417                         }
418                 }
419
420                 wait_for_interrupt();
421         }
422
423         return 0;
424 }