openpilot is an open source driver assistance system. openpilot performs the functions of Automated Lane Centering and Adaptive Cruise Control for over 200 supported car makes and models.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

440 lines
12 KiB

// IRQs: USART1, USART2, USART3, UART5
// ***************************** Definitions *****************************
#define FIFO_SIZE_INT 0x400U
#define FIFO_SIZE_DMA 0x1000U
typedef struct uart_ring {
volatile uint16_t w_ptr_tx;
volatile uint16_t r_ptr_tx;
uint8_t *elems_tx;
uint32_t tx_fifo_size;
volatile uint16_t w_ptr_rx;
volatile uint16_t r_ptr_rx;
uint8_t *elems_rx;
uint32_t rx_fifo_size;
USART_TypeDef *uart;
void (*callback)(struct uart_ring*);
bool dma_rx;
} uart_ring;
#define UART_BUFFER(x, size_rx, size_tx, uart_ptr, callback_ptr, rx_dma) \
uint8_t elems_rx_##x[size_rx]; \
uint8_t elems_tx_##x[size_tx]; \
uart_ring uart_ring_##x = { \
.w_ptr_tx = 0, \
.r_ptr_tx = 0, \
.elems_tx = ((uint8_t *)&elems_tx_##x), \
.tx_fifo_size = size_tx, \
.w_ptr_rx = 0, \
.r_ptr_rx = 0, \
.elems_rx = ((uint8_t *)&elems_rx_##x), \
.rx_fifo_size = size_rx, \
.uart = uart_ptr, \
.callback = callback_ptr, \
.dma_rx = rx_dma \
};
// ***************************** Function prototypes *****************************
void uart_init(uart_ring *q, int baud);
bool getc(uart_ring *q, char *elem);
bool putc(uart_ring *q, char elem);
void puts(const char *a);
void puth(unsigned int i);
void hexdump(const void *a, int l);
void debug_ring_callback(uart_ring *ring);
// ******************************** UART buffers ********************************
// esp_gps = USART1
UART_BUFFER(esp_gps, FIFO_SIZE_DMA, FIFO_SIZE_INT, USART1, NULL, true)
// lin1, K-LINE = UART5
// lin2, L-LINE = USART3
UART_BUFFER(lin1, FIFO_SIZE_INT, FIFO_SIZE_INT, UART5, NULL, false)
UART_BUFFER(lin2, FIFO_SIZE_INT, FIFO_SIZE_INT, USART3, NULL, false)
// debug = USART2
UART_BUFFER(debug, FIFO_SIZE_INT, FIFO_SIZE_INT, USART2, debug_ring_callback, false)
uart_ring *get_ring_by_number(int a) {
uart_ring *ring = NULL;
switch(a) {
case 0:
ring = &uart_ring_debug;
break;
case 1:
ring = &uart_ring_esp_gps;
break;
case 2:
ring = &uart_ring_lin1;
break;
case 3:
ring = &uart_ring_lin2;
break;
default:
ring = NULL;
break;
}
return ring;
}
// ***************************** 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){
// Do not read out directly if DMA enabled
if (q->dma_rx == false) {
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;
// 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();
}
}
// This function should be called on:
// * Half-transfer DMA interrupt
// * Full-transfer DMA interrupt
// * UART IDLE detection
uint32_t prev_w_index = 0;
void dma_pointer_handler(uart_ring *q, uint32_t dma_ndtr) {
ENTER_CRITICAL();
uint32_t w_index = (q->rx_fifo_size - dma_ndtr);
// Check for new data
if (w_index != prev_w_index){
// Check for overflow
if (
((prev_w_index < q->r_ptr_rx) && (q->r_ptr_rx <= w_index)) || // No rollover
((w_index < prev_w_index) && ((q->r_ptr_rx <= w_index) || (prev_w_index < q->r_ptr_rx))) // Rollover
){
// We lost data. Set the new read pointer to the oldest byte still available
q->r_ptr_rx = (w_index + 1U) % q->rx_fifo_size;
}
// Set write pointer
q->w_ptr_rx = w_index;
}
prev_w_index = w_index;
EXIT_CRITICAL();
}
// 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
puts("Encountered UART error: "); puth(err); puts("\n");
#endif
UART_READ_DR(q->uart)
}
// Send if necessary
uart_tx_ring(q);
// Run DMA pointer handler if the line is idle
if(q->dma_rx && (status & USART_SR_IDLE)){
// Reset IDLE flag
UART_READ_DR(q->uart)
if(q == &uart_ring_esp_gps){
dma_pointer_handler(&uart_ring_esp_gps, DMA2_Stream5->NDTR);
} else {
#ifdef DEBUG_UART
puts("No IDLE dma_pointer_handler implemented for this UART.");
#endif
}
}
EXIT_CRITICAL();
}
void USART1_IRQ_Handler(void) { uart_interrupt_handler(&uart_ring_esp_gps); }
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); }
void DMA2_Stream5_IRQ_Handler(void) {
ENTER_CRITICAL();
// Handle errors
if((DMA2->HISR & DMA_HISR_TEIF5) || (DMA2->HISR & DMA_HISR_DMEIF5) || (DMA2->HISR & DMA_HISR_FEIF5)){
#ifdef DEBUG_UART
puts("Encountered UART DMA error. Clearing and restarting DMA...\n");
#endif
// Clear flags
DMA2->HIFCR = DMA_HIFCR_CTEIF5 | DMA_HIFCR_CDMEIF5 | DMA_HIFCR_CFEIF5;
// Re-enable the DMA if necessary
DMA2_Stream5->CR |= DMA_SxCR_EN;
}
// Re-calculate write pointer and reset flags
dma_pointer_handler(&uart_ring_esp_gps, DMA2_Stream5->NDTR);
DMA2->HIFCR = DMA_HIFCR_CTCIF5 | DMA_HIFCR_CHTIF5;
EXIT_CRITICAL();
}
// ***************************** Hardware setup *****************************
void dma_rx_init(uart_ring *q) {
// Initialization is UART-dependent
if(q == &uart_ring_esp_gps){
// DMA2, stream 5, channel 4
// Disable FIFO mode (enable direct)
DMA2_Stream5->FCR &= ~DMA_SxFCR_DMDIS;
// Setup addresses
DMA2_Stream5->PAR = (uint32_t)&(USART1->DR); // Source
DMA2_Stream5->M0AR = (uint32_t)q->elems_rx; // Destination
DMA2_Stream5->NDTR = q->rx_fifo_size; // Number of bytes to copy
// Circular, Increment memory, byte size, periph -> memory, enable
// Transfer complete, half transfer, transfer error and direct mode error interrupt enable
DMA2_Stream5->CR = DMA_SxCR_CHSEL_2 | DMA_SxCR_MINC | DMA_SxCR_CIRC | DMA_SxCR_HTIE | DMA_SxCR_TCIE | DMA_SxCR_TEIE | DMA_SxCR_DMEIE | DMA_SxCR_EN;
// Enable DMA receiver in UART
q->uart->CR3 |= USART_CR3_DMAR;
// Enable UART IDLE interrupt
q->uart->CR1 |= USART_CR1_IDLEIE;
// Enable interrupt
NVIC_EnableIRQ(DMA2_Stream5_IRQn);
} else {
puts("Tried to initialize RX DMA for an unsupported UART\n");
}
}
#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) {
if (u == USART1) {
// USART1 is on APB2
u->BRR = __USART_BRR(48000000U, baud);
} else {
u->BRR = __USART_BRR(24000000U, baud);
}
}
void uart_init(uart_ring *q, int baud) {
// Register interrupts (max data rate: 115200 baud)
REGISTER_INTERRUPT(USART1_IRQn, USART1_IRQ_Handler, 150000U, FAULT_INTERRUPT_RATE_UART_1)
REGISTER_INTERRUPT(USART2_IRQn, USART2_IRQ_Handler, 150000U, FAULT_INTERRUPT_RATE_UART_2)
REGISTER_INTERRUPT(USART3_IRQn, USART3_IRQ_Handler, 150000U, FAULT_INTERRUPT_RATE_UART_3)
REGISTER_INTERRUPT(UART5_IRQn, UART5_IRQ_Handler, 150000U, FAULT_INTERRUPT_RATE_UART_5)
REGISTER_INTERRUPT(DMA2_Stream5_IRQn, DMA2_Stream5_IRQ_Handler, 100U, FAULT_INTERRUPT_RATE_UART_DMA) // Called twice per buffer
// 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;
// Enable UART interrupts
if(q->uart == USART1){
NVIC_EnableIRQ(USART1_IRQn);
} else 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
}
// Initialise RX DMA if used
if(q->dma_rx){
dma_rx_init(q);
}
}
// ************************* Low-level buffer functions *************************
bool getc(uart_ring *q, char *elem) {
bool ret = false;
ENTER_CRITICAL();
if (q->w_ptr_rx != q->r_ptr_rx) {
if (elem != NULL) *elem = q->elems_rx[q->r_ptr_rx];
q->r_ptr_rx = (q->r_ptr_rx + 1U) % q->rx_fifo_size;
ret = true;
}
EXIT_CRITICAL();
return ret;
}
bool injectc(uart_ring *q, char elem) {
int ret = false;
uint16_t next_w_ptr;
ENTER_CRITICAL();
next_w_ptr = (q->w_ptr_rx + 1U) % q->tx_fifo_size;
if (next_w_ptr != q->r_ptr_rx) {
q->elems_rx[q->w_ptr_rx] = elem;
q->w_ptr_rx = next_w_ptr;
ret = true;
}
EXIT_CRITICAL();
return ret;
}
bool putc(uart_ring *q, char elem) {
bool ret = false;
uint16_t next_w_ptr;
ENTER_CRITICAL();
next_w_ptr = (q->w_ptr_tx + 1U) % q->tx_fifo_size;
if (next_w_ptr != q->r_ptr_tx) {
q->elems_tx[q->w_ptr_tx] = elem;
q->w_ptr_tx = next_w_ptr;
ret = true;
}
EXIT_CRITICAL();
uart_tx_ring(q);
return ret;
}
// Seems dangerous to use (might lock CPU if called with interrupts disabled f.e.)
// TODO: Remove? Not used anyways
void uart_flush(uart_ring *q) {
while (q->w_ptr_tx != q->r_ptr_tx) {
__WFI();
}
}
void uart_flush_sync(uart_ring *q) {
// empty the TX buffer
while (q->w_ptr_tx != q->r_ptr_tx) {
uart_tx_ring(q);
}
}
void uart_send_break(uart_ring *u) {
while ((u->uart->CR1 & USART_CR1_SBK) != 0);
u->uart->CR1 |= USART_CR1_SBK;
}
void clear_uart_buff(uart_ring *q) {
ENTER_CRITICAL();
q->w_ptr_tx = 0;
q->r_ptr_tx = 0;
q->w_ptr_rx = 0;
q->r_ptr_rx = 0;
EXIT_CRITICAL();
}
// ************************ High-level debug functions **********************
void putch(const char a) {
if (has_external_debug_serial) {
// assuming debugging is important if there's external serial connected
while (!putc(&uart_ring_debug, a));
} else {
// misra-c2012-17.7: serial debug function, ok to ignore output
(void)injectc(&uart_ring_debug, a);
}
}
void puts(const char *a) {
for (const char *in = a; *in; in++) {
if (*in == '\n') putch('\r');
putch(*in);
}
}
void putui(uint32_t i) {
uint32_t i_copy = i;
char str[11];
uint8_t idx = 10;
str[idx] = '\0';
idx--;
do {
str[idx] = (i_copy % 10U) + 0x30U;
idx--;
i_copy /= 10;
} while (i_copy != 0U);
puts(&str[idx + 1U]);
}
void puth(unsigned int i) {
char c[] = "0123456789abcdef";
for (int pos = 28; pos != -4; pos -= 4) {
putch(c[(i >> (unsigned int)(pos)) & 0xFU]);
}
}
void puth2(unsigned int i) {
char c[] = "0123456789abcdef";
for (int pos = 4; pos != -4; pos -= 4) {
putch(c[(i >> (unsigned int)(pos)) & 0xFU]);
}
}
void hexdump(const void *a, int l) {
if (a != NULL) {
for (int i=0; i < l; i++) {
if ((i != 0) && ((i & 0xf) == 0)) puts("\n");
puth2(((const unsigned char*)a)[i]);
puts(" ");
}
}
puts("\n");
}