+
+ 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;
+ }
+ }