// ***************************** Interrupt handlers *****************************
void uart_tx_ring ( uart_ring * q ) {
ENTER_CRITICAL ( ) ;
// Send out next byte of TX buffer
if ( q - > w_ptr_tx ! = q - > r_ptr_tx ) {
// Only send if transmit register is empty (aka last byte has been sent)
if ( ( q - > uart - > SR & USART_SR_TXE ) ! = 0 ) {
q - > uart - > DR = q - > elems_tx [ q - > r_ptr_tx ] ; // This clears TXE
q - > r_ptr_tx = ( q - > r_ptr_tx + 1U ) % q - > tx_fifo_size ;
}
// Enable TXE interrupt if there is still data to be sent
if ( q - > r_ptr_tx ! = q - > w_ptr_tx ) {
q - > uart - > CR1 | = USART_CR1_TXEIE ;
} else {
q - > uart - > CR1 & = ~ USART_CR1_TXEIE ;
}
}
EXIT_CRITICAL ( ) ;
}
void uart_rx_ring ( uart_ring * q ) {
ENTER_CRITICAL ( ) ;
// Read out RX buffer
uint8_t c = q - > uart - > DR ; // This read after reading SR clears a bunch of interrupts
uint16_t next_w_ptr = ( q - > w_ptr_rx + 1U ) % q - > rx_fifo_size ;
if ( ( next_w_ptr = = q - > r_ptr_rx ) & & q - > overwrite ) {
// overwrite mode: drop oldest byte
q - > r_ptr_rx = ( q - > r_ptr_rx + 1U ) % q - > rx_fifo_size ;
}
// Do not overwrite buffer data
if ( next_w_ptr ! = q - > r_ptr_rx ) {
q - > elems_rx [ q - > w_ptr_rx ] = c ;
q - > w_ptr_rx = next_w_ptr ;
if ( q - > callback ! = NULL ) {
q - > callback ( q ) ;
}
}
EXIT_CRITICAL ( ) ;
}
void uart_send_break ( uart_ring * u ) {
while ( ( u - > uart - > CR1 & USART_CR1_SBK ) ! = 0 ) ;
u - > uart - > CR1 | = USART_CR1_SBK ;
}
// This read after reading SR clears all error interrupts. We don't want compiler warnings, nor optimizations
# define UART_READ_DR(uart) volatile uint8_t t = (uart)->DR; UNUSED(t);
void uart_interrupt_handler ( uart_ring * q ) {
ENTER_CRITICAL ( ) ;
// Read UART status. This is also the first step necessary in clearing most interrupts
uint32_t status = q - > uart - > SR ;
// If RXNE is set, perform a read. This clears RXNE, ORE, IDLE, NF and FE
if ( ( status & USART_SR_RXNE ) ! = 0U ) {
uart_rx_ring ( q ) ;
}
// Detect errors and clear them
uint32_t err = ( status & USART_SR_ORE ) | ( status & USART_SR_NE ) | ( status & USART_SR_FE ) | ( status & USART_SR_PE ) ;
if ( err ! = 0U ) {
# ifdef DEBUG_UART
print ( " Encountered UART error: " ) ; puth ( err ) ; print ( " \n " ) ;
# endif
UART_READ_DR ( q - > uart )
}
// Send if necessary
uart_tx_ring ( q ) ;
EXIT_CRITICAL ( ) ;
}
void USART2_IRQ_Handler ( void ) { uart_interrupt_handler ( & uart_ring_debug ) ; }
void USART3_IRQ_Handler ( void ) { uart_interrupt_handler ( & uart_ring_lin2 ) ; }
void UART5_IRQ_Handler ( void ) { uart_interrupt_handler ( & uart_ring_lin1 ) ; }
// ***************************** Hardware setup *****************************
# define __DIV(_PCLK_, _BAUD_) (((_PCLK_) * 25U) / (4U * (_BAUD_)))
# define __DIVMANT(_PCLK_, _BAUD_) (__DIV((_PCLK_), (_BAUD_)) / 100U)
# define __DIVFRAQ(_PCLK_, _BAUD_) ((((__DIV((_PCLK_), (_BAUD_)) - (__DIVMANT((_PCLK_), (_BAUD_)) * 100U)) * 16U) + 50U) / 100U)
# define __USART_BRR(_PCLK_, _BAUD_) ((__DIVMANT((_PCLK_), (_BAUD_)) << 4) | (__DIVFRAQ((_PCLK_), (_BAUD_)) & 0x0FU))
void uart_set_baud ( USART_TypeDef * u , unsigned int baud ) {
u - > BRR = __USART_BRR ( APB1_FREQ * 1000000U , baud ) ;
}
void uart_init ( uart_ring * q , int baud ) {
if ( q - > uart ! = NULL ) {
// Register interrupts (max data rate: 115200 baud)
if ( q - > uart = = USART2 ) {
REGISTER_INTERRUPT ( USART2_IRQn , USART2_IRQ_Handler , 150000U , FAULT_INTERRUPT_RATE_UART_2 )
} else if ( q - > uart = = USART3 ) {
REGISTER_INTERRUPT ( USART3_IRQn , USART3_IRQ_Handler , 150000U , FAULT_INTERRUPT_RATE_UART_3 )
} else if ( q - > uart = = UART5 ) {
REGISTER_INTERRUPT ( UART5_IRQn , UART5_IRQ_Handler , 150000U , FAULT_INTERRUPT_RATE_UART_5 )
} else {
// UART not used. Skip registering interrupts
}
// Set baud and enable peripheral with TX and RX mode
uart_set_baud ( q - > uart , baud ) ;
q - > uart - > CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE ;
if ( ( q - > uart = = USART2 ) | | ( q - > uart = = USART3 ) | | ( q - > uart = = UART5 ) ) {
q - > uart - > CR1 | = USART_CR1_RXNEIE ;
}
// Enable UART interrupts
if ( q - > uart = = USART2 ) {
NVIC_EnableIRQ ( USART2_IRQn ) ;
} else if ( q - > uart = = USART3 ) {
NVIC_EnableIRQ ( USART3_IRQn ) ;
} else if ( q - > uart = = UART5 ) {
NVIC_EnableIRQ ( UART5_IRQn ) ;
} else {
// UART not used. Skip enabling interrupts
}
}
}