]> mj.ucw.cz Git - home-hw.git/commitdiff
BSB: Receiving of frames and passing of statistics
authorMartin Mares <mj@ucw.cz>
Tue, 25 Feb 2020 20:04:40 +0000 (21:04 +0100)
committerMartin Mares <mj@ucw.cz>
Tue, 25 Feb 2020 20:04:40 +0000 (21:04 +0100)
bsb/daemon/burrow-bsb.c
bsb/firmware/main.c

index 8150de2d5ec1d3df63bafa3fe1766a71e969b932..2eb9ccf0359a26d7ceb447da6fc91f53036ec631 100644 (file)
@@ -16,6 +16,9 @@
 static struct libusb_context *usb_ctxt;
 static struct libusb_device_handle *devh;
 
+typedef unsigned char byte;
+typedef unsigned int uint;
+
 static void die(const char *msg, ...)
 {
        va_list args;
@@ -75,6 +78,11 @@ out:
        libusb_free_device_list(devlist, 1);
 }
 
+static inline uint get_u32_le(byte *p)
+{
+       return (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0];
+}
+
 int main(void)
 {
        int err;
@@ -94,7 +102,7 @@ int main(void)
 
 #if 0
                int received;
-               unsigned char resp[64];
+               byte resp[64];
                if (err = libusb_bulk_transfer(devh, 0x81, resp, 64, &received, 2000)) {
                        usb_error("Receive failed: error %d", err);
                        continue;
@@ -109,7 +117,7 @@ int main(void)
                printf("-> %02x%02x%02x%02x\n", resp[0], resp[1], resp[2], resp[3]);
 #elif 0
                int received;
-               unsigned char resp[64];
+               byte resp[64];
                if (err = libusb_interrupt_transfer(devh, 0x82, resp, 64, &received, 100)) {
                        usb_error("Receive failed: error %d", err);
                        continue;
@@ -123,13 +131,17 @@ int main(void)
 
                printf("-> %02x%02x%02x%02x\n", resp[0], resp[1], resp[2], resp[3]);
 #else
-               unsigned char resp[64] = { 1, 2, 3, 4 };
-               if ((err = libusb_control_transfer(devh, 0xc0, 0x00, 0, 0, resp, 4, 100)) < 0) {
-                       usb_error("Receive failed: error %d", err);
+               byte resp[64];
+               int received;
+               if ((received = libusb_control_transfer(devh, 0xc0, 0x00, 0, 0, resp, sizeof(resp), 100)) < 0) {
+                       usb_error("Receive failed: error %d", received);
                        continue;
                }
 
-               printf("-> %02x%02x%02x%02x\n", resp[0], resp[1], resp[2], resp[3]);
+               printf("Stats:");
+               for (int i=0; i+3 < received; i+=4)
+                       printf(" %u", get_u32_le(resp+i));
+               printf("\n");
 #endif
 
                sleep(1);
index c54d914fe770e183084cccb071b609f10538a5b5..06b49ffd8ec569542c63dd0cb12955713799d8a5 100644 (file)
@@ -20,6 +20,8 @@
 
 #include <string.h>
 
+/*** Hardware init ***/
+
 static void clock_init(void)
 {
        rcc_clock_setup_in_hse_8mhz_out_72mhz();
@@ -40,16 +42,39 @@ static void clock_init(void)
 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)
@@ -71,18 +96,86 @@ static void delay_ms(uint ms)
                ;
 }
 
-static void usart_init(void)
+/*** BSB ***/
+
+#define BSB_MAX_SIZE 32
+#define BSB_RX_TIMEOUT 2  // 1-2 ms => 2-4 byte periods of silence
+
+static byte bsb_rx_buf[BSB_MAX_SIZE];
+static byte bsb_rx_len;
+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;
+
+struct bsb_stat {
+       u32 rx_noise;
+       u32 rx_errors;
+       u32 rx_invalid;
+       u32 rx_overruns;
+       u32 rx_timeouts;
+};
+
+static struct bsb_stat bsb_stat;
+
+void usart3_isr(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);
+       u32 status = USART_SR(USART3);
+
+       if (status & USART_SR_RXNE) {
+               uint ch = usart_recv(USART3);
+               bsb_rx_timestamp = ms_ticks;
+
+               if (status & (USART_SR_FE | USART_SR_ORE | USART_SR_NE)) {
+                       bsb_stat.rx_errors++;
+                       bsb_rx_len = 0;
+               } else if (!bsb_rx_len && ch != 0xdc) {
+                       // Waiting for start of frame
+                       bsb_stat.rx_noise++;
+               } else {
+                       bsb_rx_buf[bsb_rx_len++] = 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;
+                               }
+                       } else if (bsb_rx_len == bsb_rx_buf[3]) {
+                               // Received a complete frame: pass it to the main loop
+                               if (bsb_rx_frame_len) {
+                                       // The previous one was not sent yet
+                                       bsb_stat.rx_overruns++;
+                               } else {
+                                       memcpy(bsb_rx_frame, bsb_rx_buf, bsb_rx_buf[3]);
+                                       barrier();
+                                       bsb_rx_frame_len = bsb_rx_buf[3];
+                               }
+                       }
+               }
+       }
+}
 
-       usart_enable(USART1);
+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_TX_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);
 }
 
+/*** USB ***/
+
+static usbd_device *usbd_dev;
+
 static const struct usb_device_descriptor device = {
        .bLength = USB_DT_DEVICE_SIZE,
        .bDescriptorType = USB_DT_DEVICE,
@@ -182,30 +275,24 @@ 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");
-
-       byte *b = *buf;
-       b[0] = 0x12;
-       b[1] = 0x34;
-       b[2] = 0x56;
-       b[3] = 0x78;
-       if (*len > 4)
-               *len = 4;
+       // We support reading of statistics via control requests
+       debug_printf("USB: Control request: Read statistics\n");
+       uint n = MIN(*len, sizeof(bsb_stat));
+       memcpy(*buf, (char *) &bsb_stat, n);
+       *len = n;
 
        return USBD_REQ_HANDLED;
 }
@@ -213,6 +300,8 @@ static enum usbd_request_return_codes control_cb(usbd_device *usbd_dev,
 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();
 }
 
@@ -229,24 +318,34 @@ static enum usbd_request_return_codes dfu_control_cb(usbd_device *dev UNUSED,
        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_register_control_callback(usbd_dev,
+       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(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_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;
 }
 
@@ -256,6 +355,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();
@@ -266,30 +404,49 @@ 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, &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);
        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 && (bsb_rx_timestamp - ms_ticks) >= BSB_RX_TIMEOUT) {
+                       bsb_rx_len = 0;
+                       bsb_stat.rx_timeouts++;
+               }
+               cm_enable_interrupts();
+
+               wait_for_interrupt();
        }
 
        return 0;