summaryrefslogtreecommitdiff
path: root/controller/fw/src/serial.c
diff options
context:
space:
mode:
Diffstat (limited to 'controller/fw/src/serial.c')
-rw-r--r--controller/fw/src/serial.c153
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);
+}
+