]> mj.ucw.cz Git - home-hw.git/blobdiff - bsb/firmware/main.c
BSB: Receiving works
[home-hw.git] / bsb / firmware / main.c
index 778dc4516b15bcebe7841e65a5a31ce89c1e1814..4e10dc5b563dab8d32eba3ead6e497155df6376c 100644 (file)
@@ -6,17 +6,22 @@
 
 #include "util.h"
 
+#include <libopencm3/cm3/cortex.h>
 #include <libopencm3/cm3/nvic.h>
 #include <libopencm3/cm3/systick.h>
+#include <libopencm3/cm3/scb.h>
 #include <libopencm3/stm32/rcc.h>
 #include <libopencm3/stm32/desig.h>
 #include <libopencm3/stm32/gpio.h>
 #include <libopencm3/stm32/timer.h>
 #include <libopencm3/stm32/usart.h>
+#include <libopencm3/usb/dfu.h>
 #include <libopencm3/usb/usbd.h>
 
 #include <string.h>
 
+/*** Hardware init ***/
+
 static void clock_init(void)
 {
        rcc_clock_setup_in_hse_8mhz_out_72mhz();
@@ -25,28 +30,53 @@ static void clock_init(void)
        rcc_periph_clock_enable(RCC_GPIOB);
        rcc_periph_clock_enable(RCC_GPIOC);
        rcc_periph_clock_enable(RCC_USART1);
+       rcc_periph_clock_enable(RCC_USART3);
        rcc_periph_clock_enable(RCC_USB);
 
        rcc_periph_reset_pulse(RST_GPIOA);
        rcc_periph_reset_pulse(RST_GPIOB);
        rcc_periph_reset_pulse(RST_GPIOC);
        rcc_periph_reset_pulse(RST_USART1);
+       rcc_periph_reset_pulse(RST_USART3);
        rcc_periph_reset_pulse(RST_USB);
 }
 
 static void gpio_init(void)
 {
        // PA9 = TXD1 for debugging console
-       gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO9);
-
        // PA10 = RXD1 for debugging console
+       gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO9);
        gpio_set_mode(GPIOA, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO10);
 
        // PC13 = BluePill LED
        gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO13);
        gpio_clear(GPIOC, GPIO13);
+
+       // PB0 = yellow LED*, PB1 = green LED*
+       gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO0 | GPIO1);
+       gpio_set(GPIOB, GPIO0 | GPIO1);
+
+       // PB10 = TXD3 for BSB
+       // PB11 = RXD3 for BSB
+       // FIXME: TX turned off temporarily
+       // gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, GPIO10);
+       gpio_set_mode(GPIOB, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO11);
 }
 
+static void usart_init(void)
+{
+       usart_set_baudrate(USART1, 115200);
+       usart_set_databits(USART1, 8);
+       usart_set_stopbits(USART1, USART_STOPBITS_1);
+       usart_set_mode(USART1, USART_MODE_TX_RX);
+       usart_set_parity(USART1, USART_PARITY_NONE);
+       usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE);
+
+       usart_enable(USART1);
+}
+
+/*** System ticks ***/
+
 static volatile u32 ms_ticks;
 
 void sys_tick_handler(void)
@@ -56,7 +86,7 @@ void sys_tick_handler(void)
 
 static void tick_init(void)
 {
-       systick_set_frequency(1000, 72000000);
+       systick_set_frequency(1000, CPU_CLOCK_MHZ * 1000000);
        systick_counter_enable();
        systick_interrupt_enable();
 }
@@ -68,19 +98,130 @@ static void delay_ms(uint ms)
                ;
 }
 
-static void usart_init(void)
+/*** BSB ***/
+
+#define BSB_MAX_SIZE 32
+#define BSB_RX_TIMEOUT 10  // ms
+
+#define DEBUG_BSB
+#ifdef DEBUG_BSB
+#define BSB_DEBUG(x...) debug_printf(x)
+#else
+#define BSB_DEBUG(x...) do { } while (0)
+#endif
+
+static byte bsb_rx_buf[BSB_MAX_SIZE];
+static byte bsb_rx_len;
+static u16 bsb_rx_crc;
+static volatile u32 bsb_rx_timestamp;
+
+// Passing received frames to the main loop
+static byte bsb_rx_frame[BSB_MAX_SIZE];
+static volatile byte bsb_rx_frame_len;
+static byte bsb_rx_frame_led;
+
+struct bsb_stat {
+       u32 rx_noise;
+       u32 rx_errors;
+       u32 rx_invalid;
+       u32 rx_overruns;
+       u32 rx_timeouts;
+       u32 rx_bad_crc;
+       u32 rx_ok;
+};
+
+static struct bsb_stat bsb_stat;
+
+static u16 bsb_crc_update(u16 crc, byte data)
 {
-       usart_set_baudrate(USART1, 115200);
-       usart_set_databits(USART1, 8);
-       usart_set_stopbits(USART1, USART_STOPBITS_1);
-       usart_set_mode(USART1, USART_MODE_TX_RX);
-       usart_set_parity(USART1, USART_PARITY_NONE);
-       usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE);
+       crc = crc ^ (data << 8);
+       for (uint i=0; i<8; i++) {
+               if (crc & 0x8000)
+                       crc = (crc << 1) ^ 0x1021;
+               else
+                       crc <<= 1;
+       }
+       return crc;
+}
 
-       usart_enable(USART1);
+void usart3_isr(void)
+{
+       u32 status = USART_SR(USART3);
+
+       if (status & USART_SR_RXNE) {
+               uint ch = ~usart_recv(USART3) & 0xff;
+               bsb_rx_timestamp = ms_ticks;
+               BSB_DEBUG(" %02x", ch);
+
+               if (status & (USART_SR_FE | USART_SR_ORE | USART_SR_NE)) {
+                       bsb_stat.rx_errors++;
+                       bsb_rx_len = 0;
+               } else if (!bsb_rx_len) {
+                       // Start of frame
+                       if (ch == 0xdc) {
+                               bsb_rx_buf[bsb_rx_len++] = ch;
+                               bsb_rx_crc = bsb_crc_update(0, ch);
+                               BSB_DEBUG("<");
+                       } else {
+                               bsb_stat.rx_noise++;
+                               BSB_DEBUG("?");
+                       }
+               } else {
+                       bsb_rx_buf[bsb_rx_len++] = ch;
+                       bsb_rx_crc = bsb_crc_update(bsb_rx_crc, ch);
+                       if (bsb_rx_len < 4) {
+                               // First three bytes: SOF, source addr, destination addr
+                       } else if (bsb_rx_len == 4) {
+                               // Received length byte
+                               if (bsb_rx_buf[3] < 7 || bsb_rx_buf[3] > BSB_MAX_SIZE) {
+                                       bsb_stat.rx_invalid++;
+                                       bsb_rx_len = 0;
+                                       BSB_DEBUG("!L");
+                               }
+                       } else if (bsb_rx_len == bsb_rx_buf[3]) {
+                               // Received a complete frame: check CRC and pass it to the main loop
+                               if (bsb_rx_crc) {
+                                       bsb_stat.rx_bad_crc++;
+                                       BSB_DEBUG("!C");
+                               } else {
+                                       if (bsb_rx_frame_len) {
+                                               // The previous one was not sent yet
+                                               bsb_stat.rx_overruns++;
+                                               BSB_DEBUG("!O");
+                                       } else {
+                                               memcpy(bsb_rx_frame, bsb_rx_buf, bsb_rx_buf[3]);
+                                               bsb_rx_frame_len = bsb_rx_buf[3];
+                                               bsb_stat.rx_ok++;
+                                               BSB_DEBUG(".");
+                                       }
+                                       gpio_clear(GPIOB, GPIO1);
+                                       bsb_rx_frame_led = 100;
+                               }
+                               bsb_rx_len = 0;
+                       }
+               }
+       }
+}
+
+static void bsb_init(void)
+{
+       usart_set_baudrate(USART3, 4800);
+       usart_set_databits(USART3, 9);
+       usart_set_stopbits(USART3, USART_STOPBITS_1);
+       usart_set_mode(USART3, USART_MODE_RX);
+       usart_set_parity(USART3, USART_PARITY_ODD);
+       usart_set_flow_control(USART3, USART_FLOWCONTROL_NONE);
+
+       usart_enable(USART3);
+       nvic_enable_irq(NVIC_USART3_IRQ);
+       usart_enable_rx_interrupt(USART3);
 }
 
-static const struct usb_device_descriptor dev = {
+/*** USB ***/
+
+static usbd_device *usbd_dev;
+
+static const struct usb_device_descriptor device = {
        .bLength = USB_DT_DEVICE_SIZE,
        .bDescriptorType = USB_DT_DEVICE,
        .bcdUSB = 0x0200,
@@ -126,16 +267,43 @@ static const struct usb_interface_descriptor iface = {
        .endpoint = endpoints,
 };
 
+static const struct usb_dfu_descriptor dfu_function = {
+       .bLength = sizeof(struct usb_dfu_descriptor),
+       .bDescriptorType = DFU_FUNCTIONAL,
+       .bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_WILL_DETACH,
+       .wDetachTimeout = 255,
+       .wTransferSize = 1024,
+       .bcdDFUVersion = 0x0100,
+};
+
+static const struct usb_interface_descriptor dfu_iface = {
+       .bLength = USB_DT_INTERFACE_SIZE,
+       .bDescriptorType = USB_DT_INTERFACE,
+       .bInterfaceNumber = 1,
+       .bAlternateSetting = 0,
+       .bNumEndpoints = 0,
+       .bInterfaceClass = 0xFE,
+       .bInterfaceSubClass = 1,
+       .bInterfaceProtocol = 1,
+       .iInterface = 0,
+
+       .extra = &dfu_function,
+       .extralen = sizeof(dfu_function),
+};
+
 static const struct usb_interface ifaces[] = {{
        .num_altsetting = 1,
        .altsetting = &iface,
+}, {
+       .num_altsetting = 1,
+       .altsetting = &dfu_iface,
 }};
 
 static const struct usb_config_descriptor config = {
        .bLength = USB_DT_CONFIGURATION_SIZE,
        .bDescriptorType = USB_DT_CONFIGURATION,
        .wTotalLength = 0,
-       .bNumInterfaces = 1,
+       .bNumInterfaces = 2,
        .bConfigurationValue = 1,
        .iConfiguration = 0,
        .bmAttributes = 0x80,
@@ -152,48 +320,77 @@ static const char *usb_strings[] = {
 };
 
 static byte usb_configured;
+static byte usb_rx_pending;
 static uint8_t usbd_control_buffer[64];
 
-static enum usbd_request_return_codes control_cb(usbd_device *usbd_dev,
+static enum usbd_request_return_codes control_cb(
+       usbd_device *dev UNUSED,
        struct usb_setup_data *req,
        uint8_t **buf,
        uint16_t *len,
-       void (**complete)(usbd_device *usbd_dev, struct usb_setup_data *req) UNUSED)
+       void (**complete)(usbd_device *dev, struct usb_setup_data *req) UNUSED)
 {
        if (req->bmRequestType != 0xc0 || req->bRequest != 0x00)
                return USBD_REQ_NOTSUPP;
 
-       (void) usbd_dev;
-       (void) buf;
-       (void) len;
-       (void) complete;
-       debug_printf("USB: Control request\n");
+       // We support reading of statistics via control requests
+       debug_printf("USB: Reading statistics\n");
+       uint n = MIN(*len, sizeof(bsb_stat));
+       memcpy(*buf, (char *) &bsb_stat, n);
+       *len = n;
 
-       byte *b = *buf;
-       b[0] = 0x12;
-       b[1] = 0x34;
-       b[2] = 0x56;
-       b[3] = 0x78;
-       if (*len > 4)
-               *len = 4;
+       return USBD_REQ_HANDLED;
+}
+
+static void dfu_detach_complete(usbd_device *dev UNUSED, struct usb_setup_data *req UNUSED)
+{
+       // Reset to bootloader, which implements the rest of DFU
+       debug_printf("Switching to DFU\n");
+       debug_flush();
+       scb_reset_core();
+}
 
+static enum usbd_request_return_codes dfu_control_cb(usbd_device *dev UNUSED,
+       struct usb_setup_data *req,
+       uint8_t **buf UNUSED,
+       uint16_t *len UNUSED,
+       void (**complete)(usbd_device *dev, struct usb_setup_data *req))
+{
+       if (req->bmRequestType != 0x21 || req->bRequest != DFU_DETACH)
+               return USBD_REQ_NOTSUPP;
+
+       *complete = dfu_detach_complete;
        return USBD_REQ_HANDLED;
 }
 
-static void ep81_cb(usbd_device *usbd_dev, uint8_t ep UNUSED)
+static void ep81_cb(usbd_device *dev, uint8_t ep UNUSED)
 {
        byte buf[4];
        put_u32_be(buf, ms_ticks);
-       usbd_ep_write_packet(usbd_dev, 0x81, buf, sizeof(buf));
+       usbd_ep_write_packet(dev, 0x81, buf, sizeof(buf));
        debug_printf("USB: Bulk write\n");
 }
 
-static void set_config_cb(usbd_device *usbd_dev, uint16_t wValue UNUSED)
+static void rx_cb(usbd_device *dev UNUSED, uint8_t ep UNUSED)
+{
+       // Received packet passed through interrupt end-point
+       debug_printf("USB: RX EP done\n");
+       usb_rx_pending = 0;
+}
+
+static void set_config_cb(usbd_device *dev, uint16_t wValue UNUSED)
 {
-       usbd_register_control_callback(usbd_dev, USB_REQ_TYPE_VENDOR, USB_REQ_TYPE_TYPE, control_cb);
-       usbd_ep_setup(usbd_dev, 0x81, USB_ENDPOINT_ATTR_BULK, 64, ep81_cb);
-       usbd_ep_setup(usbd_dev, 0x82, USB_ENDPOINT_ATTR_BULK, 64, NULL);
-       ep81_cb(usbd_dev, 0);
+       usbd_register_control_callback(dev,
+               USB_REQ_TYPE_VENDOR,
+               USB_REQ_TYPE_TYPE,
+               control_cb);
+       usbd_register_control_callback(dev,
+               USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE,
+               USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT,
+               dfu_control_cb);
+       usbd_ep_setup(dev, 0x81, USB_ENDPOINT_ATTR_BULK, 64, ep81_cb);
+       usbd_ep_setup(dev, 0x82, USB_ENDPOINT_ATTR_BULK, 64, rx_cb);
+       ep81_cb(dev, 0);
        usb_configured = 1;
 }
 
@@ -203,6 +400,45 @@ static void reset_cb(void)
        usb_configured = 0;
 }
 
+static volatile bool usb_event_pending;
+
+void usb_lp_can_rx0_isr(void)
+{
+       /*
+        *  We handle USB in the main loop to avoid race conditions between
+        *  USB interrupts and other code. However, we need an interrupt to
+        *  up the main loop from sleep.
+        *
+        *  We set up only the low-priority ISR, because high-priority ISR handles
+        *  only double-buffered bulk transfers and isochronous transfers.
+        */
+       nvic_disable_irq(NVIC_USB_LP_CAN_RX0_IRQ);
+       usb_event_pending = 1;
+}
+
+static void usb_init(void)
+{
+       // Simulate USB disconnect
+       gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO11 | GPIO12);
+       gpio_clear(GPIOA, GPIO11 | GPIO12);
+       delay_ms(1000);
+
+       usbd_dev = usbd_init(
+               &st_usbfs_v1_usb_driver,
+               &device,
+               &config,
+               usb_strings,
+               ARRAY_SIZE(usb_strings),
+               usbd_control_buffer,
+               sizeof(usbd_control_buffer)
+       );
+       usbd_register_reset_callback(usbd_dev, reset_cb);
+       usbd_register_set_config_callback(usbd_dev, set_config_cb);
+       usb_event_pending = 1;
+}
+
+/*** Main ***/
+
 int main(void)
 {
        clock_init();
@@ -213,30 +449,54 @@ int main(void)
 
        debug_printf("Hello, kitty!\n");
 
-       // Simulate USB disconnect
-       gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO11 | GPIO12);
-       gpio_clear(GPIOA, GPIO11 | GPIO12);
-       delay_ms(1000);
+       bsb_init();
+       usb_init();
 
-       usbd_device *usbd_dev = usbd_init(&st_usbfs_v1_usb_driver, &dev, &config, usb_strings, ARRAY_SIZE(usb_strings), usbd_control_buffer, sizeof(usbd_control_buffer));
-       usbd_register_reset_callback(usbd_dev, reset_cb);
-       usbd_register_set_config_callback(usbd_dev, set_config_cb);
        u32 last_ds_step = 0;
 
        for (;;) {
                if (ms_ticks - last_ds_step >= 100) {
                        debug_led_toggle();
+                       gpio_toggle(GPIOB, GPIO0);
                        last_ds_step = ms_ticks;
-                       if (usb_configured) {
-                               byte x[4];
-                               put_u32_be(x, ms_ticks);
-                               usbd_ep_write_packet(usbd_dev, 0x82, x, 4);
+               }
+
+               if (usb_event_pending) {
+                       usbd_poll(usbd_dev);
+                       usb_event_pending = 0;
+                       nvic_clear_pending_irq(NVIC_USB_LP_CAN_RX0_IRQ);
+                       nvic_enable_irq(NVIC_USB_LP_CAN_RX0_IRQ);
+               }
+
+               if (usb_configured) {
+                       // Passing of received packets to USB
+                       if (!usb_rx_pending) {
+                               if (bsb_rx_frame_len) {
+                                       barrier();
+                                       usbd_ep_write_packet(usbd_dev, 0x82, bsb_rx_frame, bsb_rx_frame_len);
+                                       usb_rx_pending = 1;
+                                       barrier();
+                                       bsb_rx_frame_len = 0;
+                                       debug_printf("USB: RX started\n");
+                               }
                        }
                }
 
-               // XXX: libopencm3 usbd does not use interrupts at the moment, need to poll...
-               // wait_for_interrupt();
-               usbd_poll(usbd_dev);
+               // Packet timeouts
+               cm_disable_interrupts();
+               barrier();
+               if (bsb_rx_len && ms_ticks - bsb_rx_timestamp >= BSB_RX_TIMEOUT) {
+                       bsb_rx_len = 0;
+                       bsb_stat.rx_timeouts++;
+                       BSB_DEBUG("!T");
+               }
+               if (bsb_rx_frame_led) {
+                       if (!--bsb_rx_frame_led)
+                               gpio_set(GPIOB, GPIO1);
+               }
+               cm_enable_interrupts();
+
+               wait_for_interrupt();
        }
 
        return 0;