]> mj.ucw.cz Git - home-hw.git/blobdiff - dmx/firmware/main.c
DMX: Interrupt-driven transfer
[home-hw.git] / dmx / firmware / main.c
index c8d35b5cac287732074231c9171170ad6b6d9d3b..cc0b82625b26af5152df4a9e69b206b015449fc8 100644 (file)
@@ -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 ***/