Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
28c442cecThis file is pinned to the selected release. Download the original to open it in your design tool.
/*
* console.c
*
* printf-style interface to USART1.
*
* Written & released by Keir Fraser <keir.xen@gmail.com>
*
* This is free and unencumbered software released into the public domain.
* See the file COPYING for more details, or visit <http://unlicense.org>.
*/
#define BAUD 115200
#define DMA1_CH4_IRQ 14
void IRQ_14(void) __attribute__((alias("IRQ_dma1_ch4_tc")));
#define USART1_IRQ 37
/* We stage serial output in a ring buffer. DMA occurs from the ring buffer;
* the consumer index being updated each time a DMA sequence completes. */
static char ring[2048];
#define MASK(x) ((x)&(sizeof(ring)-1))
static unsigned int cons, prod, dma_sz;
/* The console can be set into synchronous mode in which case DMA is disabled
* and the transmit-empty flag is polled manually for each byte. */
static bool_t sync_console;
static void kick_tx(void)
{
if (sync_console) {
while (cons != prod) {
while (!(usart1->sr & USART_SR_TXE))
cpu_relax();
usart1->dr = ring[MASK(cons++)];
}
} else if (!dma_sz && (cons != prod)) {
dma_sz = min(MASK(prod-cons), sizeof(ring)-MASK(cons));
dma1->ch4.cmar = (uint32_t)(unsigned long)&ring[MASK(cons)];
dma1->ch4.cndtr = dma_sz;
dma1->ch4.ccr = (DMA_CCR_MSIZE_8BIT |
/* The manual doesn't allow byte accesses to usart. */
DMA_CCR_PSIZE_16BIT |
DMA_CCR_MINC |
DMA_CCR_DIR_M2P |
DMA_CCR_TCIE |
DMA_CCR_EN);
}
}
static void IRQ_dma1_ch4_tc(void)
{
/* Clear the DMA controller. */
dma1->ch4.ccr = 0;
dma1->ifcr = DMA_IFCR_CGIF(4);
/* Update ring state. */
cons += dma_sz;
dma_sz = 0;
/* Kick off more transmit activity. */
kick_tx();
}
int vprintk(const char *format, va_list ap)
{
static char str[128];
char *p, c;
int n;
IRQ_global_disable();
n = vsnprintf(str, sizeof(str), format, ap);
p = str;
while (((c = *p++) != '\0') && ((prod-cons) != (sizeof(ring) - 1))) {
switch (c) {
case '\r': /* CR: ignore as we generate our own CR/LF */
break;
case '\n': /* LF: convert to CR/LF (usual terminal behaviour) */
ring[MASK(prod++)] = '\r';
/* fall through */
default:
ring[MASK(prod++)] = c;
break;
}
}
kick_tx();
IRQ_global_enable();
return n;
}
int printk(const char *format, ...)
{
va_list ap;
int n;
va_start(ap, format);
n = vprintk(format, ap);
va_end(ap);
return n;
}
void console_sync(void)
{
if (sync_console)
return;
IRQ_global_disable();
sync_console = TRUE;
/* Wait for DMA completion and then kick off synchronous mode. */
while (dma1->ch4.cndtr)
cpu_relax();
IRQ_dma1_ch4_tc();
/* Leave IRQs globally disabled. */
}
void console_barrier(void)
{
unsigned int p = prod;
while (p != cons)
cpu_relax();
}
void console_init(void)
{
/* Turn on the clocks. */
rcc->apb2enr |= RCC_APB2ENR_USART1EN;
/* Enable TX pin (PA9) for USART output, RX pin (PA10) as input. */
gpio_configure_pin(gpioa, 9, AFO_pushpull(_10MHz));
gpio_configure_pin(gpioa, 10, GPI_pull_up);
/* BAUD, 8n1. */
usart1->brr = SYSCLK / BAUD;
usart1->cr1 = (USART_CR1_UE | USART_CR1_TE | USART_CR1_RE);
usart1->cr3 = USART_CR3_DMAT;
/* Initialise DMA1 channel 4 and its completion interrupt. */
dma1->ch4.cpar = (uint32_t)(unsigned long)&usart1->dr;
dma1->ifcr = DMA_IFCR_CGIF(4);
IRQx_set_prio(DMA1_CH4_IRQ, CONSOLE_IRQ_PRI);
IRQx_enable(DMA1_CH4_IRQ);
}
/*
* Local variables:
* mode: C
* c-file-style: "Linux"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/