diff --git a/firmware/dmx.c b/firmware/dmx.c index 5986a57..16636cc 100644 --- a/firmware/dmx.c +++ b/firmware/dmx.c @@ -8,15 +8,14 @@ enum dmx_rx_state_enum { DMX_RX_DATA, }; +static volatile bool break_received; + /** * @brief DMX data received. Contains the whole DMX universe including the first 0 byte. * The controller does check the first byte to be zero. */ static volatile uint8_t dmx_channel_data[DMX_UNIVERSE_SIZE + 1]; -static volatile enum dmx_rx_state_enum dmx_rx_state; -static volatile uint32_t dmx_write_pointer; - void dmx_init(uint32_t base_channel) { int i; @@ -27,11 +26,10 @@ void dmx_init(uint32_t base_channel) } dmx_base_channel = base_channel; - dmx_write_pointer = 0u; - dmx_rx_state = DMX_RX_WAIT_FOR_BREAK; + break_received = false; /* Enable GPIOA and USART1 clock */ - RCC->AHBENR |= RCC_AHBENR_GPIOAEN; + RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_DMAEN; RCC->APB2ENR |= RCC_APB2ENR_USART1EN; /* Switch RXTX pin low, activating permanent READ mode */ @@ -51,49 +49,78 @@ void dmx_init(uint32_t base_channel) /* Map USART1 RX to DMA Channel 3 */ SYSCFG->CFGR1 &= ~SYSCFG_CFGR1_USART1RX_DMA_RMP; + DMA1_Channel3->CCR = DMA_CCR_PL_1 | DMA_CCR_MINC | DMA_CCR_TCIE; + + NVIC_EnableIRQ(DMA1_Channel2_3_IRQn); NVIC_EnableIRQ(USART1_IRQn); } const uint8_t *dmx_get_data() { - return (const uint8_t *)&dmx_channel_data[1]; + return (const uint8_t *)dmx_channel_data; } void USART1_IRQHandler(void) { uint32_t isr; - uint8_t dreg; isr = USART1->ISR; USART1->ICR = USART_ICR_ORECF | USART_ICR_NCF | USART_ICR_FECF; if (isr & USART_ISR_FE) { /* Frame error received. Start of DMX frame */ - dmx_write_pointer = 0u; - dmx_rx_state = DMX_RX_DATA; /* Flush RX data */ USART1->RQR = USART_RQR_RXFRQ; + USART1->CR3 |= USART_CR3_DMAR; + DMA1_Channel3->CCR &= ~DMA_CCR_EN; + DMA1_Channel3->CMAR = (uint32_t)dmx_channel_data; + DMA1_Channel3->CPAR = (uint32_t)&USART1->RDR; + DMA1_Channel3->CNDTR = DMX_UNIVERSE_SIZE + 1; + DMA1_Channel3->CCR |= DMA_CCR_EN; + USART1->CR3 |= USART_CR3_DMAR; + break_received = true; } else if (isr & USART_ISR_RXNE) { - /* Received valid symbol */ - dreg = (uint8_t)USART1->RDR; - - if (dmx_rx_state == DMX_RX_DATA) { - /* Ready to recieve data */ - if (dmx_write_pointer < (DMX_UNIVERSE_SIZE + 1)) { - dmx_channel_data[dmx_write_pointer] = dreg; - dmx_write_pointer++; - } else { - dmx_rx_state = DMX_RX_WAIT_FOR_BREAK; - } - } } __DSB(); } -void DMA_CH2_3_DMA2_CH1_2_IRQHandler() +void DMA_CH2_3_DMA2_CH1_2_IRQHandler(void) { + uint32_t isr; + isr = DMA1->ISR; + /* Only clear the interupts of channel 2 (bits 11:9) */ + DMA1->IFCR = isr & 0xF00; + + if (isr & DMA_ISR_TCIF3) { + DMA1->ISR; + } + __DSB(); +} + +bool dmx_poll_break_received(void) +{ + bool ret; + + /* Atomically reset the flag */ + __disable_irq(); + ret = break_received; + break_received = false; + __enable_irq(); + + return ret; +} + +bool dmx_enough_data_received() +{ + uint32_t received_count = (DMX_UNIVERSE_SIZE + 1) - DMA1_Channel3->CNDTR; + + if (received_count > (dmx_base_channel + DMX_USED_CHANNEL_COUNT)) { + return true; + } + + return false; } diff --git a/firmware/include/ring-light/dmx.h b/firmware/include/ring-light/dmx.h index 748f11b..68970db 100644 --- a/firmware/include/ring-light/dmx.h +++ b/firmware/include/ring-light/dmx.h @@ -34,4 +34,16 @@ void dmx_init(uint32_t base_channel); */ const uint8_t *dmx_get_data(void); +/** + * @brief Check if a break was received. This resets the flag + * @return true if a break was received since the last time calling this function + */ +bool dmx_poll_break_received(void); + +/** + * @brief The DMX receiver has received all data for the ring light. It can be read + * @return + */ +bool dmx_enough_data_received(void); + #endif /* _DMX_H_ */ diff --git a/firmware/main.c b/firmware/main.c index 1bba292..add76ee 100644 --- a/firmware/main.c +++ b/firmware/main.c @@ -16,7 +16,9 @@ enum ring_modes { RING_MODE_ARC, /*!< SK6812 closing ring */ RING_MODE_QUARTER, /*!< SK6812 walking quarter */ RING_MODE_IN_FARBE_UND_BUNT, /*!< SK6812 color mix */ - RING_MODE_MAX /*!< end of list */ + RING_MODE_MAX, /*!< end of list */ + RING_MODE_WAIT_DMX, + RING_MODE_WAIT_DMX_BREAK }; volatile int32_t temperature; @@ -34,10 +36,13 @@ static void wait_for_ticks(uint32_t ticks) int main(void) { uint32_t led_val = 0x00UL; + uint32_t last_led_val = 0x00UL; uint32_t led_calc_val[RING_MAX_LED] = {0x00UL}; uint8_t led_pwm_val = 0u; + const uint8_t *dmx_data; bool button_pressed = false; + bool force_led_update; enum ring_modes mode; RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN; @@ -88,14 +93,23 @@ int main(void) SysTick_Config(800000); while(1) { + force_led_update = false; temperature = temperature_adc_get_temp(); + if (led_val != last_led_val || button_pressed) { + force_led_update = true; + } + /*! -# Gradually dim down the LED brightness in case the temperature is too high */ if (temperature > ((MAX_TEMP_CELSIUS) * 10)) { if (led_val > 20) led_val--; } + if (dmx_poll_break_received()) { + mode = RING_MODE_WAIT_DMX; + } + led_pwm_val = 0u; switch (mode) { @@ -165,6 +179,26 @@ int main(void) } } break; + case RING_MODE_WAIT_DMX: + force_led_update = false; + if (dmx_enough_data_received()) { + dmx_data = dmx_get_data(); + mode = RING_MODE_WAIT_DMX_BREAK; + if (dmx_data[0] != 0) + break; + for (int i = 0; i < RING_MAX_LED; i++) { + led_calc_val[i] = (dmx_data[1 + i*4 + 3]) | + (dmx_data[1 + i*4 + 2] << 8) | + (dmx_data[1 + i*4 + 0] << 16) | + (dmx_data[1 + i*4 + 1] << 24); + } + led_pwm_val = dmx_data[129]; + force_led_update = true; + } + break; + case RING_MODE_WAIT_DMX_BREAK: + force_led_update = false; + break; default: for(int i = 0; i < RING_MAX_LED; i ++) { led_calc_val[i] = 0x00000000UL; @@ -172,30 +206,35 @@ int main(void) break; } - TIM14->CCR1 = led_pwm_val; + if (force_led_update) { + TIM14->CCR1 = led_pwm_val; - for(int i = 0; i < RING_MAX_LED; i ++) { - /* Allow interrupts in between LEDs. - * They must not exceed the reset length of 80us of SK6812. - */ - __disable_irq(); - sk6812_send_led(led_calc_val[i]); - __enable_irq(); + for(int i = 0; i < RING_MAX_LED; i ++) { + /* Allow interrupts in between LEDs. + * They must not exceed the reset length of 80us of SK6812. + */ + __disable_irq(); + sk6812_send_led(led_calc_val[i]); + __enable_irq(); + } + last_led_val = led_val; } + + wait_for_ticks(5); if((int16_t)TIM3->CNT > (int16_t)led_val) { led_val = 0u; - } - else if(((int16_t)led_val - (int16_t)TIM3->CNT) > UINT8_MAX) { + } else if(((int16_t)led_val - (int16_t)TIM3->CNT) > UINT8_MAX) { led_val = 255u; - } - else { + } else { led_val = (int16_t)led_val - (int16_t)TIM3->CNT; } + TIM3->CNT = 0u; + if(button_pressed) { if(GPIOA->IDR & GPIO_IDR_0) { button_pressed = false;