]> mj.ucw.cz Git - home-hw.git/blobdiff - dmx/firmware/main.c
Auto: Meditation mode turned off
[home-hw.git] / dmx / firmware / main.c
index c8d35b5cac287732074231c9171170ad6b6d9d3b..47ff5d35e3cb7d34fa70e8c46552f2692626f997 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);
@@ -55,8 +57,12 @@ static void gpio_init(void)
 
        // 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_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO10);
        gpio_set_mode(GPIOB, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO11);
+
+       // PB1 = TX enable for DMX (always on)
+       gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO1);
+       gpio_set(GPIOB, GPIO1);
 }
 
 static void usart_init(void)
@@ -96,36 +102,141 @@ static void delay_ms(uint ms)
 
 /*** DMX ***/
 
+#define DMX_MAX_PACKET_SIZE 64
+
+static volatile byte dmx_state;
+static byte dmx_packet[DMX_MAX_PACKET_SIZE];
+static byte dmx_pos, dmx_len;
+
+static byte dmx_next_packet[DMX_MAX_PACKET_SIZE];
+static byte dmx_next_len;
+
+#undef 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);
        usart_set_databits(USART3, 8);
-       usart_set_stopbits(USART3, USART_STOPBITS_1);
+       usart_set_stopbits(USART3, USART_STOPBITS_2);
        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);
+
+       // Until we get data from USB, send "turn everything off" packets
+       memset(dmx_next_packet, 0, DMX_MAX_PACKET_SIZE);
+       dmx_next_len = DMX_MAX_PACKET_SIZE;
+}
+
+void tim3_isr(void)
+{
+       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;
+               }
+       }
+}
+
+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 44μs data slots.
+               USART_CR1(USART3) &= ~USART_CR1_TCIE;
+               uint sent = 200 + 200 + 44*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)
 {
-       delay_ms(1);
+       if (dmx_state != DMX_STATE_IDLE)
+               return;
 
-       // Send break
+       if (!dmx_len)
+               return;
+
+       dmx_pos = 0;
+
+       // 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 ***/
@@ -257,9 +368,9 @@ static enum usbd_request_return_codes dfu_control_cb(usbd_device *dev UNUSED,
 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));
+       uint len = usbd_ep_read_packet(dev, 0x01, dmx_next_packet, DMX_MAX_PACKET_SIZE);
        debug_printf("USB: Host sent %u bytes\n", len);
+       dmx_next_len = len;
 }
 
 static void set_config_cb(usbd_device *dev, uint16_t wValue UNUSED)
@@ -332,6 +443,7 @@ int main(void)
        dmx_init();
 
        u32 last_blink = 0;
+       u32 last_send = 0;
 
        for (;;) {
                if (ms_ticks - last_blink >= 100) {
@@ -347,6 +459,23 @@ int main(void)
                        nvic_enable_irq(NVIC_USB_LP_CAN_RX0_IRQ);
                }
 
+               if (dmx_state == DMX_STATE_IDLE) {
+                       if (dmx_next_len) {
+                               // We have a new packet to send
+                               debug_printf("Sending new packet: %d bytes\n", dmx_next_len);
+                               memcpy(dmx_packet, dmx_next_packet, dmx_next_len);
+                               dmx_len = dmx_next_len;
+                               dmx_next_len = 0;
+                               dmx_send();
+                               last_send = ms_ticks;
+                       } else if (ms_ticks - last_send >= 100) {
+                               // Re-send every 100 ms
+                               // debug_printf("Re-send: %d bytes\n", dmx_len);
+                               dmx_send();
+                               last_send = ms_ticks;
+                       }
+               }
+
                wait_for_interrupt();
        }