microscope-ring-light-remot.../dmx.c

134 lines
2.7 KiB
C
Raw Normal View History

#include "stm32f030x6.h"
#include <dmx.h>
#include <stdint.h>
#include <stm32f0xx.h>
#include <stdbool.h>
#include <stm-periph/stm32-gpio-macros.h>
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;
enum dmx_tx_state {
DMX_TX_OFF,
DMX_TX_IDLE,
DMX_TX_BREAK,
DMX_TX_BREAK_PAUSE,
DMX_TX_NULLBYTE,
DMX_TX_DATA,
};
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_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 */
USART1->CR1 = USART_CR1_TE | USART_CR1_UE;
/* Configure TIM14 to count in 10 us steps */
TIM14->PSC = 480u;
/* 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);
}
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)
{
/* Clear interrupt sources */
TIM14->SR = 0;
/* Start the break sequence if idle */
switch (tx_state) {
case DMX_TX_IDLE:
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:
tx_state = DMX_TX_IDLE;
break;
default:
break;
}
}