diff options
Diffstat (limited to 'controller/fw/src/serial.c')
-rw-r--r-- | controller/fw/src/serial.c | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/controller/fw/src/serial.c b/controller/fw/src/serial.c new file mode 100644 index 0000000..71e1868 --- /dev/null +++ b/controller/fw/src/serial.c @@ -0,0 +1,153 @@ +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> + +#include "dma_util.h" +#include "sr_global.h" +#include "serial.h" + +#include <tinyprintf.h> + +static void usart_schedule_dma(volatile struct usart_desc *us); +static void usart_dma_reset(volatile struct usart_desc *us); +static void usart_putc_nonblocking_tpf(void *us, char c); + +void usart_dma_reset(volatile struct usart_desc *us) { + us->tx_buf.xfr_start = -1; + us->tx_buf.xfr_end = 0; + us->tx_buf.wr_pos = 0; + us->tx_buf.wr_idx = 0; + us->tx_buf.xfr_next = 0; + us->tx_buf.wraparound = false; + + for (size_t i=0; i<ARRAY_LENGTH(us->tx_buf.chunk_end); i++) + us->tx_buf.chunk_end[i] = -1; +} + +void usart_dma_init(volatile struct usart_desc *us, unsigned int baudrate) { + usart_dma_reset(us); + + /* Configure DMA 1 Channel 2 to handle uart transmission */ + us->tx_dmas->PAR = (uint32_t)&(us->le_usart->DR); + us->tx_dmas->CR = + (us->tx_dma_ch<<DMA_SxCR_CHSEL_Pos) + | (0<<DMA_SxCR_PL_Pos) + | (1<<DMA_SxCR_DIR_Pos) + | (0<<DMA_SxCR_MSIZE_Pos) /* 8 bit */ + | (0<<DMA_SxCR_PSIZE_Pos) /* 8 bit */ + | DMA_SxCR_MINC + | DMA_SxCR_TCIE; /* Enable transfer complete interrupt. */ + + /* triggered on transfer completion. We use this to process the ADC data */ + NVIC_EnableIRQ(us->tx_dma_irqn); + NVIC_SetPriority(us->tx_dma_irqn, 30); + + us->le_usart->CR1 = USART_CR1_TE; + /* Set divider for 115.2kBd @48MHz system clock. */ + us->le_usart->BRR = apb2_speed * 16 / baudrate / 16; /* 250kBd */ + us->le_usart->CR3 |= USART_CR3_DMAT; /* TX DMA enable */ + + /* And... go! */ + us->le_usart->CR1 |= USART_CR1_UE; +} + +void usart_schedule_dma(volatile struct usart_desc *us) { + volatile struct dma_tx_buf *buf = &us->tx_buf; + + ssize_t xfr_start, xfr_end, xfr_len; + if (buf->wraparound) { + buf->wraparound = false; + xfr_start = 0; + xfr_len = buf->xfr_end; + xfr_end = buf->xfr_end; + + } else { + if (buf->chunk_end[buf->xfr_next] == -1) + return; /* Nothing to trasnmit */ + + xfr_start = buf->xfr_end; + xfr_end = buf->chunk_end[buf->xfr_next]; + buf->chunk_end[buf->xfr_next] = -1; + buf->xfr_next = (buf->xfr_next + 1) % ARRAY_LENGTH(buf->chunk_end); + + if (xfr_end > xfr_start) { /* no wraparound */ + xfr_len = xfr_end - xfr_start; + + } else { /* wraparound */ + if (xfr_end != 0) + buf->wraparound = true; + xfr_len = sizeof(us->data) - xfr_start; + } + } + + buf->xfr_start = xfr_start; + buf->xfr_end = xfr_end; + + us->comm_led = 100; + + /* initiate transmission of new buffer */ + us->tx_dmas->M0AR = (uint32_t)(us->data + xfr_start); + us->tx_dmas->NDTR = xfr_len; + us->tx_dmas->CR |= DMA_SxCR_EN; +} + +void usart_dma_stream_irq(volatile struct usart_desc *us) { + uint8_t iflags = dma_get_isr_and_clear(us->tx_dma, us->tx_dma_sn); + + if (iflags & DMA_LISR_TCIF0) { /* Transfer complete */ + us->tx_dmas->CR &= ~DMA_SxCR_EN; + //if (us->tx_buf.wraparound) + usart_schedule_dma(us); + } + + if (iflags & DMA_LISR_FEIF0) + us->tx_errors++; +} + +int usart_putc_nonblocking(volatile struct usart_desc *us, char c) { + volatile struct dma_tx_buf *buf = &us->tx_buf; + + if (buf->wr_pos == buf->xfr_start) { + us->tx_byte_overruns++; + return -EBUSY; + } + + buf->data[buf->wr_pos] = c; + buf->wr_pos = (buf->wr_pos + 1) % sizeof(us->data); + return 0; +} + +void usart_putc_nonblocking_tpf(void *us, char c) { + usart_putc_nonblocking((struct usart_desc *)us, c); +} + + +int usart_send_chunk_nonblocking(volatile struct usart_desc *us, const char *chunk, size_t chunk_len) { + for (size_t i=0; i<chunk_len; i++) + usart_putc_nonblocking(us, chunk[i]); + + return usart_flush(us); +} + +int usart_flush(volatile struct usart_desc *us) { + /* Find a free slot for this chunk */ + if (us->tx_buf.chunk_end[us->tx_buf.wr_idx] != -1) { + us->tx_chunk_overruns++; + return -EBUSY; + } + + us->tx_buf.chunk_end[us->tx_buf.wr_idx] = us->tx_buf.wr_pos; + us->tx_buf.wr_idx = (us->tx_buf.wr_idx + 1) % ARRAY_LENGTH(us->tx_buf.chunk_end); + + if (!(us->tx_dmas->CR & DMA_SxCR_EN)) + usart_schedule_dma(us); + return 0; +} + +int usart_printf(volatile struct usart_desc *us, char *fmt, ...) { + va_list va; + va_start(va, fmt); + tfp_format((void *)us, usart_putc_nonblocking_tpf, fmt, va); + return usart_flush(us); +} + |