master-thesis/reset-controller/fw/src/serial.c
2021-04-09 18:38:57 +02:00

186 lines
5.4 KiB
C

#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);
static void usart_wait_chunk_free(volatile struct usart_desc *us);
static void usart_putc_blocking_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;
}
int usart_putc_blocking(volatile struct usart_desc *us, char c) {
volatile struct dma_tx_buf *buf = &us->tx_buf;
while (buf->wr_pos == buf->xfr_start)
;
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);
}
void usart_putc_blocking_tpf(void *us, char c) {
usart_putc_blocking((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);
}
void usart_wait_chunk_free(volatile struct usart_desc *us) {
while (us->tx_buf.chunk_end[us->tx_buf.wr_idx] != -1)
;
}
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, const char *fmt, ...) {
va_list va;
va_start(va, fmt);
tfp_format((void *)us, usart_putc_nonblocking_tpf, fmt, va);
return usart_flush(us);
}
int usart_printf_blocking_va(volatile struct usart_desc *us, const char *fmt, va_list va) {
tfp_format((void *)us, usart_putc_blocking_tpf, fmt, va);
usart_wait_chunk_free(us);
return usart_flush(us);
}
int usart_printf_blocking(volatile struct usart_desc *us, const char *fmt, ...) {
va_list va;
va_start(va, fmt);
return usart_printf_blocking_va(us, fmt, va);
}