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);
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);
/*** 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);
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 ***/