/*
- * Clock with Barometer
+ * Workshop Clock
*
- * (c) 2018--2019 Martin Mareš <mj@ucw.cz>
+ * (c) 2020-2023 Martin Mareš <mj@ucw.cz>
*/
-FIXME: Partial code...
-
#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/stm32/i2c.h>
+#include <libopencm3/stm32/timer.h>
+#include <libopencm3/usb/dfu.h>
+#include <libopencm3/usb/usbd.h>
#include <string.h>
+static void ep82_send(u32 key_code);
+
+/*** Hardware init ***/
+
static void clock_init(void)
{
- rcc_clock_setup_in_hse_8mhz_out_72mhz();
+ rcc_clock_setup_pll(&rcc_hse_configs[RCC_CLOCK_HSE8_72MHZ]);
+ rcc_periph_clock_enable(RCC_GPIOA);
rcc_periph_clock_enable(RCC_GPIOB);
rcc_periph_clock_enable(RCC_GPIOC);
+ rcc_periph_clock_enable(RCC_I2C1);
rcc_periph_clock_enable(RCC_USART1);
rcc_periph_clock_enable(RCC_USB);
+ rcc_periph_clock_enable(RCC_TIM1);
+ rcc_periph_reset_pulse(RST_GPIOA);
rcc_periph_reset_pulse(RST_GPIOB);
rcc_periph_reset_pulse(RST_GPIOC);
+ rcc_periph_reset_pulse(RST_I2C1);
rcc_periph_reset_pulse(RST_USART1);
rcc_periph_reset_pulse(RST_USB);
+ rcc_periph_reset_pulse(RST_TIM1);
}
static void gpio_init(void)
{
- // Switch JTAG off to free up pins
- gpio_primary_remap(AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_ON, 0);
-
// 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);
+
+ // PB7 = SDA for display controller
+ // PB6 = SCL for display controller
+ gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, GPIO6 | GPIO7);
+
+ // PA8 = SFH5110 output (5V tolerant) connected to TIM1_CH1
+ gpio_set_mode(GPIOC, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO8);
}
+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);
+ 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)
static void tick_init(void)
{
- systick_set_frequency(1000, 72000000);
+ systick_set_frequency(1000, CPU_CLOCK_MHZ * 1000000);
systick_counter_enable();
systick_interrupt_enable();
}
-#if 0
static void delay_ms(uint ms)
{
u32 start_ticks = ms_ticks;
while (ms_ticks - start_ticks < ms)
;
}
-#endif
-static void usart_init(void)
+/*** Display ***/
+
+/*
+ * Display digits:
+ *
+ * ---- 40 ----
+ * | |
+ * | |
+ * 20 80
+ * | |
+ * | |
+ * ---- 10 ----
+ * | |
+ * | |
+ * 08 02
+ * | |
+ * | |
+ * ---- 04 ----
+ * (01)
+ */
+
+static byte disp[4];
+static byte ctrl = 0x56;
+
+static const byte disp_font[] = {
+ [0] = 0xee,
+ [1] = 0x82,
+ [2] = 0xdc,
+ [3] = 0xd6,
+ [4] = 0xb2,
+ [5] = 0x76,
+ [6] = 0x7e,
+ [7] = 0xc2,
+ [8] = 0xfe,
+ [9] = 0xf6,
+ [10] = 0xea,
+ [11] = 0x3e,
+ [12] = 0x6c,
+ [13] = 0x9e,
+ [14] = 0x7c,
+ [15] = 0x78,
+};
+
+static void display_update(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);
+ // debug_puts("Display update\n");
- usart_enable(USART1);
+ byte cmds[4];
+ cmds[0] = 0;
+ cmds[1] = ctrl;
+ cmds[2] = ((disp[1] & 0x88) >> 3) | ((disp[1] & 0x44) >> 1) | ((disp[1] & 0x22) << 1) | ((disp[1] & 0x11) << 3);
+ cmds[3] = disp[0];
+ i2c_transfer7(I2C1, 0x76/2, (byte *) cmds, sizeof(cmds), NULL, 0);
+
+ cmds[2] = ((disp[3] & 0x88) >> 3) | ((disp[3] & 0x44) >> 1) | ((disp[3] & 0x22) << 1) | ((disp[3] & 0x11) << 3);
+ cmds[3] = disp[2];
+ i2c_transfer7(I2C1, 0x70/2, (byte *) cmds, sizeof(cmds), NULL, 0);
+
+ // debug_puts("Update done\n");
+}
+
+static void display_init(void)
+{
+ debug_puts("I2C init\n");
+ i2c_peripheral_disable(I2C1);
+ i2c_set_speed(I2C1, i2c_speed_sm_100k, rcc_apb1_frequency / 1000000);
+ i2c_peripheral_enable(I2C1);
+
+ disp[0] = 0x10;
+ disp[1] = 0x10;
+ disp[2] = 0x10;
+ disp[3] = 0x10;
+ display_update();
+}
+
+/*** Infrared Remote Control ***/
+
+static void ir_init(void)
+{
+ debug_puts("IR init\n");
+
+ // TIM1 will measure pulses and spaces between them with 1μs resolution
+ timer_set_prescaler(TIM1, 71); // 72 MHz / 72 = 1 MHz
+ timer_set_mode(TIM1, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP);
+ timer_set_period(TIM1, 65535);
+ timer_update_on_overflow(TIM1);
+
+ // IC1 will trigger on TI1 (TIM1_CH1) falling edge
+ timer_ic_set_input(TIM1, TIM_IC1, TIM_IC_IN_TI1);
+ // timer_ic_set_filter(TIM1, TIM_IC1, TIM_IC_OFF);
+ timer_set_oc_polarity_low(TIM1, TIM_OC1); // OC functions affect IC, too
+
+ // IC2 will trigger on TI1 (TIM1_CH1) rising edge
+ timer_ic_set_input(TIM1, TIM_IC2, TIM_IC_IN_TI1);
+ timer_set_oc_polarity_high(TIM1, TIM_OC2);
+
+ // OC3 will trigger on a break longer than 50 ms
+ timer_set_oc_mode(TIM1, TIM_OC3, TIM_OCM_ACTIVE);
+ timer_set_oc_value(TIM1, TIM_OC3, 30000);
+
+ // Program slave controller to reset the timer on IC1
+ timer_slave_set_trigger(TIM1, TIM_SMCR_TS_TI1FP1);
+ timer_slave_set_mode(TIM1, TIM_SMCR_SMS_RM);
+
+ // Request interrupts
+ timer_enable_irq(TIM1, TIM_DIER_CC1IE | TIM_DIER_CC2IE | TIM_DIER_CC3IE);
+ nvic_enable_irq(NVIC_TIM1_CC_IRQ);
+
+ // Enable ICs and OCs
+ timer_enable_oc_output(TIM1, TIM_OC1);
+ timer_enable_oc_output(TIM1, TIM_OC2);
+ timer_enable_oc_output(TIM1, TIM_OC3);
+
+ timer_enable_counter(TIM1);
+}
+
+// Circular queue of pulse durations
+#define IR_MAX_PULSES 32
+static u32 ir_pulses[IR_MAX_PULSES]; // Top 16 bits = mark, bottom 16 bits = space
+static u16 ir_last_pulse;
+static uint ir_pulses_rx, ir_pulses_tx;
+
+#define IR_INF 0xffff
+
+#define IR_MARK(x) (uint)((x) >> 16)
+#define IR_SPACE(x) (uint)((x) & 0xffff)
+
+static inline bool between(uint x, uint min, uint max)
+{
+ return x >= min && x <= max;
+}
+
+static void ir_record_pulse(uint mark, uint space)
+{
+ uint i = ir_pulses_tx;
+ ir_pulses_tx = (i + 1) % IR_MAX_PULSES;
+ if (ir_pulses_tx != ir_pulses_rx) {
+ ir_pulses[i] = (mark << 16) | space;
+ } else {
+ // Overflow detected
+ ir_pulses[i] = (IR_INF << 16) | IR_INF;
+ }
+}
+
+void tim1_cc_isr(void)
+{
+ if (TIM_SR(TIM1) & TIM_SR_CC1IF) {
+ TIM_SR(TIM1) &= ~TIM_SR_CC1IF;
+ u16 now = TIM_CCR1(TIM1);
+ if (ir_last_pulse) {
+ ir_record_pulse(ir_last_pulse, now - ir_last_pulse);
+ ir_last_pulse = 0;
+ }
+ }
+ if (TIM_SR(TIM1) & TIM_SR_CC2IF) {
+ TIM_SR(TIM1) &= ~TIM_SR_CC2IF;
+ ir_last_pulse = TIM_CCR2(TIM1);
+ }
+ if (TIM_SR(TIM1) & TIM_SR_CC3IF) {
+ TIM_SR(TIM1) &= ~TIM_SR_CC3IF;
+ if (ir_last_pulse) {
+ ir_record_pulse(ir_last_pulse, IR_INF);
+ ir_last_pulse = 0;
+ }
+ }
+}
+
+static u32 ir_get_pulse(void)
+{
+ u32 out = 0;
+
+ cm_disable_interrupts();
+ if (ir_pulses_rx != ir_pulses_tx) {
+ out = ir_pulses[ir_pulses_rx];
+ ir_pulses_rx = (ir_pulses_rx + 1) % IR_MAX_PULSES;
+ }
+ cm_enable_interrupts();
+ return out;
+}
+
+// Decoder for Onkyo RC-748S
+
+static u32 ir_blink_start;
+
+static void ir_decode(void)
+{
+ u32 pulse = ir_get_pulse();
+ if (!pulse)
+ return;
+
+ uint mark = IR_MARK(pulse);
+ uint space = IR_SPACE(pulse);
+
+#ifdef IR_TEST
+ debug_printf("IR: %d %d\n", mark, space);
+ return;
+#endif
+
+ static u16 ir_bits;
+ static u32 ir_code;
+#define IR_ERR 0xff
+
+ // debug_printf("IR(%d): %d %d\n", ir_bits, mark, space);
+
+ if (space == IR_INF) {
+ ir_bits = 0;
+ } else if (ir_bits == IR_ERR) {
+ // Error state
+ } else if (ir_bits == 0) {
+ // Start?
+ if (between(mark, 8900, 9200)) {
+ if (between(space, 4200, 4600)) {
+ ir_bits = 1;
+ ir_code = 0;
+ } else if (between(space, 2000, 2300)) {
+ debug_printf("IR: => REP\n");
+ ir_bits = IR_ERR;
+ }
+ }
+ } else {
+ if (between(mark, 500, 700)) {
+ ir_bits++;
+ if (between(space, 400, 700)) {
+ // 0
+ } else if (between(space, 1500, 1800)) {
+ // 1
+ ir_code |= 1U << (33 - ir_bits);
+ } else {
+ ir_bits = IR_ERR;
+ }
+ if (ir_bits == 33) {
+ debug_printf("IR: => %08x\n", (uint)ir_code);
+ disp[3] |= 0x01;
+ ir_blink_start = ms_ticks;
+ display_update();
+ ir_bits = IR_ERR;
+ ep82_send(ir_code);
+ }
+ } else {
+ ir_bits = IR_ERR;
+ }
+ }
}
-static const struct usb_device_descriptor dev = {
+/*** USB ***/
+
+static usbd_device *usbd_dev;
+
+enum usb_string {
+ STR_MANUFACTURER = 1,
+ STR_PRODUCT,
+ STR_SERIAL,
+};
+
+static char usb_serial_number[13];
+
+static const char *usb_strings[] = {
+ "United Computer Wizards",
+ "Workshop Clock",
+ usb_serial_number,
+};
+
+static const struct usb_device_descriptor device = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = 0x0200,
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
.bMaxPacketSize0 = 64,
- .idVendor = 0xCAFE,
- .idProduct = 0xCAFE,
- .bcdDevice = 0x0200,
- .iManufacturer = 1,
- .iProduct = 2,
- .iSerialNumber = 3,
+ .idVendor = 0x4242,
+ .idProduct = 0x0007,
+ .bcdDevice = 0x0000,
+ .iManufacturer = STR_MANUFACTURER,
+ .iProduct = STR_PRODUCT,
+ .iSerialNumber = STR_SERIAL,
.bNumConfigurations = 1,
};
static const struct usb_endpoint_descriptor endpoints[] = {{
+ // Bulk end-point for sending values to the display
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = 0x81,
+ .bEndpointAddress = 0x01,
.bmAttributes = USB_ENDPOINT_ATTR_BULK,
.wMaxPacketSize = 64,
.bInterval = 1,
+}, {
+ // Bulk end-point for receiving remote control keys
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 0x82,
+ .bmAttributes = USB_ENDPOINT_ATTR_BULK,
+ .wMaxPacketSize = 4,
+ .bInterval = 1,
}};
static const struct usb_interface_descriptor iface = {
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
- .bNumEndpoints = 1,
+ .bNumEndpoints = 2,
.bInterfaceClass = 0xFF,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
.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,
.interface = ifaces,
};
-static const char *usb_strings[] = {
- "Hippo Computing Inc.",
- "Space Alert Thermometer",
- "42",
-};
+static bool usb_configured;
+static uint8_t usbd_control_buffer[64];
+
+static bool usb_tx_in_flight;
+static byte ep82_tx_buffer[4];
-uint8_t usbd_control_buffer[64];
+static byte disp_alive;
-static void ep81_cb(usbd_device *usbd_dev, uint8_t ep UNUSED)
+static void dfu_detach_complete(usbd_device *dev UNUSED, struct usb_setup_data *req UNUSED)
{
- byte buf[4];
- if (ds_sensors[0].address[0] && ds_sensors[0].current_temp != DS_TEMP_UNKNOWN) {
- put_u32_be(buf, (u32) ds_sensors[0].current_temp);
- } else {
- put_u32_be(buf, 0x80000000);
+ // 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 ep01_cb(usbd_device *dev, uint8_t ep UNUSED)
+{
+ // We received a frame from the USB host
+ byte buf[8];
+ uint len = usbd_ep_read_packet(dev, 0x01, buf, 8);
+ debug_printf("USB: Host sent %u bytes\n", len);
+ if (len >= 5) {
+ for (uint i=0; i<4; i++) {
+ disp[i] &= 0x01;
+ if (buf[i] < 16)
+ disp[i] |= disp_font[buf[i]];
+ }
+ disp[1] &= 0xfe;
+ if (buf[4])
+ disp[1] |= 0x01;
+ display_update();
+ disp_alive = 10;
+ }
+}
+
+static void ep82_send(u32 key_code)
+{
+ if (usb_tx_in_flight) {
+ debug_printf("USB: Send overrun!\n");
+ return;
}
- usbd_ep_write_packet(usbd_dev, 0x81, buf, sizeof(buf));
+
+ debug_printf("USB: Sending key to host\n");
+ put_u32_be(ep82_tx_buffer, key_code);
+ usbd_ep_write_packet(usbd_dev, 0x82, ep82_tx_buffer, 4);
+ usb_tx_in_flight = true;
+}
+
+static void ep82_cb(usbd_device *dev UNUSED, uint8_t ep UNUSED)
+{
+ // We completed sending a frame to the USB host
+ usb_tx_in_flight = false;
+ debug_printf("USB: Key sending complete\n");
+}
+
+static void set_config_cb(usbd_device *dev, uint16_t wValue UNUSED)
+{
+ 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, 0x01, USB_ENDPOINT_ATTR_BULK, 64, ep01_cb);
+ usbd_ep_setup(dev, 0x82, USB_ENDPOINT_ATTR_BULK, 4, ep82_cb);
+ usb_configured = true;
+}
+
+static void reset_cb(void)
+{
+ debug_printf("USB: Reset\n");
+ usb_configured = false;
+}
+
+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 set_config_cb(usbd_device *usbd_dev, uint16_t wValue UNUSED)
+static void usb_init(void)
{
- usbd_ep_setup(usbd_dev, 0x81, USB_ENDPOINT_ATTR_BULK, 64, ep81_cb);
- ep81_cb(usbd_dev, 0);
+ // 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(100);
+
+ 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();
gpio_init();
- tick_init();
usart_init();
- // 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);
+ tick_init();
+ desig_get_unique_id_as_dfu(usb_serial_number);
- usbd_device *usbd_dev = usbd_init(&st_usbfs_v1_usb_driver, &dev, &config, usb_strings, 4, usbd_control_buffer, sizeof(usbd_control_buffer));
- usbd_register_set_config_callback(usbd_dev, set_config_cb);
- u32 last_ds_step = 0;
+ debug_printf("Hello, world!\n");
+
+ usb_init();
+ display_init();
+ ir_init();
+
+ u32 last_blink = 0;
for (;;) {
- if (ms_ticks - last_ds_step >= 100) {
+ if (ms_ticks - last_blink >= 500) {
debug_led_toggle();
- ds_step();
- last_ds_step = ms_ticks;
+ last_blink = ms_ticks;
+ if (disp_alive) {
+ if (!--disp_alive) {
+ disp[0] = (disp[0] & 0x01) | 0x10;
+ disp[1] = (disp[1] & 0x01) | 0x10;
+ disp[2] = (disp[2] & 0x01) | 0x10;
+ disp[3] = (disp[3] & 0x01) | 0x10;
+ }
+ }
+ display_update();
+ }
+
+ if ((disp[3] & 0x01) && ms_ticks - ir_blink_start >= 100) {
+ disp[3] &= 0xfe;
+ display_update();
+ }
+
+ ir_decode();
+
+ 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);
}
- // XXX: libopencm3 usbd does not use interrupts at the moment, need to poll...
- // wait_for_interrupt();
- usbd_poll(usbd_dev);
+ wait_for_interrupt();
}
return 0;