X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;f=bsb%2Ffirmware%2Fmain.c;h=4e10dc5b563dab8d32eba3ead6e497155df6376c;hb=1a225be603a00f82c68272074d92776c10a6f6f7;hp=778dc4516b15bcebe7841e65a5a31ce89c1e1814;hpb=6f25b29b5e45d6cdae78491f203e27b1d5192323;p=home-hw.git diff --git a/bsb/firmware/main.c b/bsb/firmware/main.c index 778dc45..4e10dc5 100644 --- a/bsb/firmware/main.c +++ b/bsb/firmware/main.c @@ -6,17 +6,22 @@ #include "util.h" +#include #include #include +#include #include #include #include #include #include +#include #include #include +/*** 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;