#include "stm32f030x6.h" #include #include #include #include #include static GPIO_TypeDef *dmx_tx_port; static uint8_t dmx_tx_pin; static uint32_t dmx_universe_length; static uint8_t *dmx_data_ptr; static uint16_t dmx_tx_break_len; static uint16_t dmx_tx_break_pause; static volatile enum dmx_tx_state tx_state; /* USART1 TX is mapped on DMA Channel2 */ static void dmx_break(bool break_enable) { uint32_t tmp; /* Force pin to low */ dmx_tx_port->BSRR |= (1 << (dmx_tx_pin + 16)); /* Change pin mode from alternate function (UART) to output and vice versa */ tmp = dmx_tx_port->MODER; if (break_enable) { tmp &= MODER_DELETE(dmx_tx_pin); tmp |= OUTPUT(dmx_tx_pin); } else { tmp &= MODER_DELETE(dmx_tx_pin); tmp |= ALTFUNC(dmx_tx_pin); } dmx_tx_port->MODER = tmp; } void dmx_init(uint8_t *data, uint32_t universe_length, GPIO_TypeDef *tx_port, uint8_t tx_pin, uint16_t dmx_delay, uint16_t dmx_break_len, uint16_t break_pause) { dmx_tx_pin = tx_pin; dmx_tx_port = tx_port; dmx_data_ptr = data; dmx_universe_length = universe_length; dmx_tx_break_len = dmx_break_len; dmx_tx_break_pause = break_pause; /* Enable UART1 and TIM14 clock */ RCC->APB2ENR |= RCC_APB2ENR_USART1EN; RCC->APB1ENR |= RCC_APB1ENR_TIM14EN; /* Enable DMA clock */ RCC->AHBENR |= RCC_AHBENR_DMAEN; /* Set baudrate: 48MHz / 250k = 129 */ USART1->BRR = 192u; /* Two stop bits */ USART1->CR2 = USART_CR2_STOP_1; /* Transmitter enable, USART enable, trawsnmission complete interrupt */ USART1->CR1 = USART_CR1_TE | USART_CR1_UE | USART_CR1_TCIE; /* Configure TIM14 to count in 10 us steps */ TIM14->PSC = 480u - 1u; /* Configure the reload value. Must be higher or equal to 200 */ if (dmx_delay < 2000) dmx_delay = 2000; TIM14->ARR = dmx_delay - 1; /* Enable TIM14 interrupt on update */ TIM14->DIER = TIM_DIER_UIE | TIM_DIER_CC1IE; NVIC_EnableIRQ(TIM14_IRQn); /* Enable Interrupts for USART and DMA */ NVIC_EnableIRQ(USART1_IRQn); NVIC_EnableIRQ(DMA1_Channel2_3_IRQn); } void dmx_stream_start(void) { tx_state = DMX_TX_IDLE; TIM14->CR1 |= TIM_CR1_CEN; } void dmx_stream_stop(void) { tx_state = DMX_TX_OFF; TIM14->CR1 &= ~TIM_CR1_CEN; DMA1_Channel2->CCR = 0ul; __DSB(); USART1->CR3 = 0ul; } void TIM14_IRQHandler(void) { uint32_t sr; sr = TIM14->SR; /* Clear interrupt sources */ TIM14->SR = 0; /* Start the break sequence if idle */ switch (tx_state) { case DMX_TX_IDLE: if (!(sr & TIM_SR_UIF)) break; tx_state = DMX_TX_BREAK; /* Disable the DMA transfer */ USART1->CR3 = 0ul; TIM14->CCR1 = dmx_tx_break_len; /* Send break */ dmx_break(true); break; case DMX_TX_BREAK: /* Stop break pulse */ dmx_break(false); TIM14->CCR1 = dmx_tx_break_len + dmx_tx_break_pause; tx_state = DMX_TX_BREAK_PAUSE; break; case DMX_TX_BREAK_PAUSE: /* Send null byte */ tx_state = DMX_TX_NULLBYTE; USART1->TDR = 0x00; break; default: break; } } enum dmx_tx_state dmx_tx_get_current_state(void) { return tx_state; } void USART1_IRQHandler(void) { uint32_t isr; isr = USART1->ISR; /* Clear interrupt flag */ USART1->ICR = USART_ICR_TCCF; if (isr & USART_ISR_TC) { if (tx_state == DMX_TX_NULLBYTE) { /* Null byte transferred. Go to data state and setup DMA */ tx_state = DMX_TX_DATA; USART1->CR3 |= USART_CR3_DMAT; DMA1_Channel2->CPAR = (uint32_t)&USART1->TDR; DMA1_Channel2->CMAR = (uint32_t)dmx_data_ptr; DMA1_Channel2->CNDTR = dmx_universe_length & 0xFFFF; DMA1_Channel2->CCR = DMA_CCR_PL_1 | DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_TCIE | DMA_CCR_EN; } } } void DMA_CH2_3_DMA2_CH1_2_IRQHandler(void) { uint32_t isr; isr = DMA1->ISR; DMA1->IFCR = isr & DMA_ISR_TCIF2; if (isr & DMA_ISR_TCIF2) { /* Channnel 2 trasnfer complete. Universe trasnferred */ tx_state = DMX_TX_IDLE; DMA1_Channel2->CCR = 0u; USART1->CR3 &= ~USART_CR3_DMAT; } }