From fc7da8e5a98bac56af0b95d197d87b7e571111fc Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Sat, 11 Apr 2020 00:08:55 +0200 Subject: [PATCH] DMX: First steps --- USB-IDS | 2 + dmx/README | 30 ++++ dmx/bootloader/Makefile | 7 + dmx/bootloader/config.h | 24 +++ dmx/firmware/Makefile | 11 ++ dmx/firmware/config.h | 14 ++ dmx/firmware/interface.h | 18 ++ dmx/firmware/main.c | 354 +++++++++++++++++++++++++++++++++++++++ mk/bluepill.mk | 2 +- 9 files changed, 461 insertions(+), 1 deletion(-) create mode 100644 dmx/README create mode 100644 dmx/bootloader/Makefile create mode 100644 dmx/bootloader/config.h create mode 100644 dmx/firmware/Makefile create mode 100644 dmx/firmware/config.h create mode 100644 dmx/firmware/interface.h create mode 100644 dmx/firmware/main.c diff --git a/USB-IDS b/USB-IDS index 5beefcb..2dbf2ca 100644 --- a/USB-IDS +++ b/USB-IDS @@ -4,5 +4,7 @@ USB IDs used by our gadgets 4242:0002 SSR module 4242:0003 BSB gateway 4242:0004 BSB gateway bootloader +4242:0005 DMX512 interface +4242:0006 DMX512 interface bootloader cafe:cafe KSP Space Alert thermometer cafe:caff KSP Space Alert accelerometer diff --git a/dmx/README b/dmx/README new file mode 100644 index 0000000..43f8f09 --- /dev/null +++ b/dmx/README @@ -0,0 +1,30 @@ +Assignment of peripherals and pins +================================== + +USART1 debugging +USART2 DMX512 + + + Blue Pill pinout + +--------------------+ + | VBATT 3.3V | +BluePill LED | PC13 GND | + | PC14 5V | + | PC15 PB9 | + | PA0 PB8 | + | PA1 PB7 | + | PA2 PB6 | + | PA3 PB5 | + | PA4 PB4 | + | PA5 PB3 | + | PA6 PA15 | + | PA7 PA12 | + | PB0 PA11 | + | PB1 PA10 | RXD1 - debugging console +TXD3 - DMX TX (5V OC) | PB10 PA9 | TXD1 - debugging console +RXD3 - DMX RX (5V) | PB11 PA8 | + | RESET PB15 | + | 3.3 V PB14 | + | GND PB13 | + | GND PB12 | + +--------------------+ diff --git a/dmx/bootloader/Makefile b/dmx/bootloader/Makefile new file mode 100644 index 0000000..9ba9617 --- /dev/null +++ b/dmx/bootloader/Makefile @@ -0,0 +1,7 @@ +ROOT=../.. +BINARY=bootloader +OBJS= +LIB_OBJS=util-debug.o dfu-bootloader.o +MAX_SIZE=8192 + +include $(ROOT)/mk/bluepill.mk diff --git a/dmx/bootloader/config.h b/dmx/bootloader/config.h new file mode 100644 index 0000000..7aa9354 --- /dev/null +++ b/dmx/bootloader/config.h @@ -0,0 +1,24 @@ +/* + * DMX512 Interface Bootloader -- Configuration + * + * (c) 2020 Martin Mareš + */ + +// Processor clock + +#define CPU_CLOCK_MHZ 48 + +// Debugging port + +#define DEBUG_USART USART1 +#define DEBUG_LED_BLUEPILL + +// Bootloader settings + +#define BOOTLOADER_DEBUG +#define BOOTLOADER_APP_START 0x08002000 +#define BOOTLOADER_MFG_ID 0x4242 +#define BOOTLOADER_PROD_ID 0x0006 +#define BOOTLOADER_PROD_VERSION 0x0100 +#define BOOTLOADER_MFG_NAME "United Computer Wizards" +#define BOOTLOADER_PROD_NAME "DMX512 Interface" diff --git a/dmx/firmware/Makefile b/dmx/firmware/Makefile new file mode 100644 index 0000000..29b2d63 --- /dev/null +++ b/dmx/firmware/Makefile @@ -0,0 +1,11 @@ +ROOT=../.. +BINARY=dmx +OBJS=main.o +LIB_OBJS=util-debug.o + +WITH_BOOT_LOADER=1 +WITH_DFU_FLASH=1 +DFU_ARGS=-d 4242:0005 +# DFU_ARGS=-d 4242:0006 + +include $(ROOT)/mk/bluepill.mk diff --git a/dmx/firmware/config.h b/dmx/firmware/config.h new file mode 100644 index 0000000..9cd4bec --- /dev/null +++ b/dmx/firmware/config.h @@ -0,0 +1,14 @@ +/* + * DMX512 Interface -- Configuration + * + * (c) 2020 Martin Mareš + */ + +// Processor clock + +#define CPU_CLOCK_MHZ 72 + +// Debugging port + +#define DEBUG_USART USART1 +#define DEBUG_LED_BLUEPILL diff --git a/dmx/firmware/interface.h b/dmx/firmware/interface.h new file mode 100644 index 0000000..1954fb5 --- /dev/null +++ b/dmx/firmware/interface.h @@ -0,0 +1,18 @@ +/* + * DMX512 Gadget -- Interface Definitions + * + * (c) 2020 Martin Mareš + */ + +#define DMX_USB_VENDOR 0x4242 +#define DMX_USB_PRODUCT 0x0005 +#define DMX_USB_VERSION 0x0100 + +/* + * Endpoints: + * + * 0x01 = bulk endpoint + * FIXME + * Used for sending frames to BSB. Accepts BSB frames. Sender address and CRC + * will be recalculated. + */ diff --git a/dmx/firmware/main.c b/dmx/firmware/main.c new file mode 100644 index 0000000..c8d35b5 --- /dev/null +++ b/dmx/firmware/main.c @@ -0,0 +1,354 @@ +/* + * DMX512 Interface + * + * (c) 2020 Martin Mareš + */ + +#include "util.h" +#include "interface.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(); + + rcc_periph_clock_enable(RCC_GPIOA); + 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 + // 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); + + // PB10 = TXD3 for DMX + // PB11 = RXD3 for DMX + 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); + 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) +{ + ms_ticks++; +} + +static void tick_init(void) +{ + systick_set_frequency(1000, CPU_CLOCK_MHZ * 1000000); + systick_counter_enable(); + systick_interrupt_enable(); +} + +static void delay_ms(uint ms) +{ + u32 start_ticks = ms_ticks; + while (ms_ticks - start_ticks < ms) + ; +} + +/*** DMX ***/ + +static void dmx_init(void) +{ + usart_set_baudrate(USART3, 250000); + usart_set_databits(USART3, 8); + usart_set_stopbits(USART3, USART_STOPBITS_1); + usart_set_mode(USART3, USART_MODE_TX); + usart_set_parity(USART3, USART_PARITY_NONE); + usart_set_flow_control(USART3, USART_FLOWCONTROL_NONE); + + usart_enable(USART3); +} + +static void dmx_send(void) +{ + delay_ms(1); + + // Send break + gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO10); + gpio_clear(GPIOB, GPIO10); + delay_ms(1); + + // Send space + gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, GPIO10); + delay_ms(1); + + usart_send_blocking(USART3, 0x00); + usart_send_blocking(USART3, 0x00); + usart_send_blocking(USART3, 0xff); // warm + usart_send_blocking(USART3, 0x00); + usart_send_blocking(USART3, 0x00); // cold +} + +/*** 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", + "DMX512 Interface", + usb_serial_number, +}; + +static const struct usb_device_descriptor device = { + .bLength = USB_DT_DEVICE_SIZE, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = 0x0200, + .bDeviceClass = 0xFF, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + .bMaxPacketSize0 = 64, + .idVendor = DMX_USB_VENDOR, + .idProduct = DMX_USB_PRODUCT, + .bcdDevice = DMX_USB_VERSION, + .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 DMX + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0x01, + .bmAttributes = USB_ENDPOINT_ATTR_BULK, + .wMaxPacketSize = 64, + .bInterval = 1, +}}; + +static const struct usb_interface_descriptor iface = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = 0xFF, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = 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 = 2, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = 0x80, + .bMaxPower = 50, // multiplied by 2 mA + .interface = ifaces, +}; + +static byte usb_configured; +static uint8_t usbd_control_buffer[64]; + +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 ep01_cb(usbd_device *dev, uint8_t ep UNUSED) +{ + // We received a frame from the USB host + byte buf[64]; + uint len = usbd_ep_read_packet(dev, 0x01, buf, sizeof(buf)); + debug_printf("USB: Host sent %u bytes\n", len); +} + +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); + usb_configured = 1; +} + +static void reset_cb(void) +{ + debug_printf("USB: Reset\n"); + 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(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(); + desig_get_unique_id_as_dfu(usb_serial_number); + + debug_printf("Hail, Lord Damian! Thy DMX interface is ready.\n"); + + usb_init(); + dmx_init(); + + u32 last_blink = 0; + + for (;;) { + if (ms_ticks - last_blink >= 100) { + debug_led_toggle(); + last_blink = ms_ticks; + dmx_send(); + } + + 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); + } + + wait_for_interrupt(); + } + + return 0; +} diff --git a/mk/bluepill.mk b/mk/bluepill.mk index 829c79b..171c086 100644 --- a/mk/bluepill.mk +++ b/mk/bluepill.mk @@ -108,7 +108,7 @@ all:: $(BINARY).dfu %.flash: %.dfu @printf " FLASH $<\n" - $(Q)dfu-util -D $< + $(Q)dfu-util $(DFU_ARGS) -D $< %.dfu: %.bin $(ROOT)/tools/dfu-sign @printf " SIGN $< -> $@\n" -- 2.39.5