From 1179ee48d9d2d23d43df0d074fdd56609484810f Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Sat, 11 Apr 2020 15:06:59 +0200 Subject: [PATCH] DMX: Interrupt-driven transfer --- dmx/README | 3 +- dmx/firmware/main.c | 127 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 116 insertions(+), 14 deletions(-) diff --git a/dmx/README b/dmx/README index 43f8f09..8d351f9 100644 --- a/dmx/README +++ b/dmx/README @@ -2,7 +2,8 @@ Assignment of peripherals and pins ================================== USART1 debugging -USART2 DMX512 +USART2 DMX512 data +TIM3 DMX512 timing Blue Pill pinout diff --git a/dmx/firmware/main.c b/dmx/firmware/main.c index c8d35b5..cc0b826 100644 --- a/dmx/firmware/main.c +++ b/dmx/firmware/main.c @@ -30,6 +30,7 @@ static void clock_init(void) rcc_periph_clock_enable(RCC_GPIOA); rcc_periph_clock_enable(RCC_GPIOB); rcc_periph_clock_enable(RCC_GPIOC); + rcc_periph_clock_enable(RCC_TIM3); rcc_periph_clock_enable(RCC_USART1); rcc_periph_clock_enable(RCC_USART3); rcc_periph_clock_enable(RCC_USB); @@ -37,6 +38,7 @@ static void clock_init(void) rcc_periph_reset_pulse(RST_GPIOA); rcc_periph_reset_pulse(RST_GPIOB); rcc_periph_reset_pulse(RST_GPIOC); + rcc_periph_reset_pulse(RST_TIM3); rcc_periph_reset_pulse(RST_USART1); rcc_periph_reset_pulse(RST_USART3); rcc_periph_reset_pulse(RST_USB); @@ -96,6 +98,26 @@ static void delay_ms(uint ms) /*** DMX ***/ +static volatile byte dmx_state; +static byte dmx_packet[64]; +static byte dmx_pos, dmx_len; + +#define DEBUG_DMX +#ifdef DEBUG_DMX +#define DMX_DEBUG(ch) debug_putc(ch) +#else +#define DMX_DEBUG(ch) do { } while (0) +#endif + +enum dmx_state { + DMX_STATE_IDLE = 0, + DMX_STATE_BREAK, + DMX_STATE_MARK, + DMX_STATE_DATA, + DMX_STATE_LAST_BYTE, + DMX_STATE_GAP, +}; + static void dmx_init(void) { usart_set_baudrate(USART3, 250000); @@ -104,28 +126,107 @@ static void dmx_init(void) usart_set_mode(USART3, USART_MODE_TX); usart_set_parity(USART3, USART_PARITY_NONE); usart_set_flow_control(USART3, USART_FLOWCONTROL_NONE); + nvic_enable_irq(NVIC_USART3_IRQ); usart_enable(USART3); + + timer_set_prescaler(TIM3, CPU_CLOCK_MHZ - 1); // 1 MHz + timer_set_mode(TIM3, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); + timer_update_on_overflow(TIM3); + timer_disable_preload(TIM3); + timer_one_shot_mode(TIM3); + timer_enable_irq(TIM3, TIM_DIER_UIE); + nvic_enable_irq(NVIC_TIM3_IRQ); } -static void dmx_send(void) +void tim3_isr(void) { - delay_ms(1); + if (TIM_SR(TIM3) & TIM_SR_UIF) { + TIM_SR(TIM3) &= ~TIM_SR_UIF; + switch (dmx_state) { + case DMX_STATE_BREAK: + // Send mark for 50 μs + DMX_DEBUG('m'); + dmx_state = DMX_STATE_MARK; + gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, GPIO10); + timer_set_period(TIM3, 199); + timer_generate_event(TIM3, TIM_EGR_UG); + timer_enable_counter(TIM3); + break; + case DMX_STATE_MARK: + // Start sending data bytes + DMX_DEBUG('d'); + dmx_state = DMX_STATE_DATA; + usart_enable_tx_interrupt(USART3); + break; + case DMX_STATE_GAP: + DMX_DEBUG('>'); + dmx_state = DMX_STATE_IDLE; + break; + } + } +} - // Send break +void usart3_isr(void) +{ + u32 status = USART_SR(USART3); + + if (dmx_state == DMX_STATE_DATA) { + if (status & USART_SR_TXE) { + if (dmx_pos < dmx_len) { + DMX_DEBUG('.'); + usart_send(USART3, dmx_packet[dmx_pos++]); + } else { + // The transmitter is double-buffered, so at this moment, it is transmitting + // the last byte of the frame. Wait until transfer is completed. + DMX_DEBUG('l'); + usart_disable_tx_interrupt(USART3); + USART_CR1(USART3) |= USART_CR1_TCIE; + dmx_state = DMX_STATE_LAST_BYTE; + } + } + } else if (dmx_state == DMX_STATE_LAST_BYTE) { + // Transfer of the last byte is complete, but we have to wait + // before the start of the next packet: minimum time between two + // breaks must be at least 1204 μs. We already sent a 200μs break, + // 200μs mark, and dmx_len 40μs data slots. + USART_CR1(USART3) &= ~USART_CR1_TCIE; + uint sent = 200 + 200 + 40*dmx_len; + if (sent < 1300) { + DMX_DEBUG('g'); + dmx_state = DMX_STATE_GAP; + timer_set_period(TIM3, 1300 - sent - 1); + timer_generate_event(TIM3, TIM_EGR_UG); + timer_enable_counter(TIM3); + } else { + DMX_DEBUG('>'); + dmx_state = DMX_STATE_IDLE; + } + } +} + +static void dmx_send(void) +{ + if (dmx_state != DMX_STATE_IDLE) + return; + + dmx_packet[0] = 0; + dmx_packet[1] = 0xff; // warm + dmx_packet[2] = 0xff; // cold + dmx_packet[3] = 0; + dmx_packet[4] = 0; + dmx_pos = 0; + dmx_len = 5; + + // Send break for 200 μs + DMX_DEBUG('<'); + dmx_state = DMX_STATE_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 + timer_set_period(TIM3, 199); + timer_generate_event(TIM3, TIM_EGR_UG); + timer_enable_counter(TIM3); } /*** USB ***/ -- 2.39.2