From 410e38651052038e34843b17269d61e75720f0ba Mon Sep 17 00:00:00 2001 From: jaseg Date: Thu, 23 Jan 2020 14:38:36 +0100 Subject: board bringup: adc, usart working --- gm_platform/fw/serial.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 gm_platform/fw/serial.c (limited to 'gm_platform/fw/serial.c') diff --git a/gm_platform/fw/serial.c b/gm_platform/fw/serial.c new file mode 100644 index 0000000..ae639c0 --- /dev/null +++ b/gm_platform/fw/serial.c @@ -0,0 +1,139 @@ +/* + * This file is part of the libusbhost library + * hosted at http://github.com/libusbhost/libusbhost + * + * Copyright (C) 2015 Amir Hammad + * + * + * libusbhost is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + + +#include "global.h" +#include "serial.h" +#include "cobs.h" + +#include +#include +#include + +volatile struct dma_tx_buf usart_tx_buf; + +static void usart_schedule_dma(); + +void usart_dma_init() { + usart_tx_buf.xfr_start = -1, + usart_tx_buf.xfr_end = 0, + usart_tx_buf.wr_pos = 0, + + /* Configure DMA 1 Channel 2 to handle uart transmission */ + DMA1_Channel2->CPAR = (unsigned int)&(USART1->TDR); + DMA1_Channel2->CCR = (0<CR1 = /* 8-bit -> M1, M0 clear */ + /* OVER8 clear. Use default 16x oversampling */ + /* CMIF clear */ + USART_CR1_MME + /* WAKE clear */ + /* PCE, PS clear */ + | USART_CR1_RXNEIE /* Enable receive interrupt */ + /* other interrupts clear */ + | USART_CR1_TE + | USART_CR1_RE; + /* Set divider for 1MBd @48MHz system clock. */ + USART1->BRR = 48; + + USART1->CR2 = USART_CR2_TXINV | USART_CR2_RXINV; + + USART1->CR3 |= USART_CR3_DMAT; /* TX DMA enable */ + + /* Enable receive interrupt */ + //NVIC_EnableIRQ(USART1_IRQn); + //NVIC_SetPriority(USART1_IRQn, 1); + + /* And... go! */ + USART1->CR1 |= USART_CR1_UE; +} + +void usart_schedule_dma() { + /* This function is only called when the DMA channel is disabled. This means we don't have to guard it in IRQ + * disables. */ + volatile struct dma_tx_buf *buf = &usart_tx_buf; + + size_t xfr_len, xfr_start = buf->xfr_end; + if (buf->wr_pos > xfr_start) /* no wraparound */ + xfr_len = buf->wr_pos - xfr_start; + else /* wraparound */ + xfr_len = sizeof(buf->data) - xfr_start; /* schedule transfer until end of buffer */ + + buf->xfr_start = xfr_start; + buf->xfr_end = (xfr_start + xfr_len) % sizeof(buf->data); /* handle wraparound */ + + /* initiate transmission of new buffer */ + DMA1_Channel2->CMAR = (uint32_t)(buf->data + xfr_start); + DMA1_Channel2->CNDTR = xfr_len; + DMA1_Channel2->CCR |= DMA_CCR_EN; +} + +int usart_dma_fifo_push(volatile struct dma_tx_buf *buf, char c) { + /* This function must be guarded by IRQ disable since the IRQ may schedule a new transfer and charge pos/start. */ + NVIC_DisableIRQ(DMA1_Channel2_3_IRQn); + + if (buf->wr_pos == buf->xfr_start) { + NVIC_EnableIRQ(DMA1_Channel2_3_IRQn); + return -EBUSY; + } + + buf->data[buf->wr_pos] = c; + buf->wr_pos = (buf->wr_pos + 1) % sizeof(buf->data); + + NVIC_EnableIRQ(DMA1_Channel2_3_IRQn); + return 0; +} + +void usart_putc(char c) { + /* push char to fifo, busy-loop if stalled to wait for USART to empty fifo via DMA */ + while (usart_dma_fifo_push(&usart_tx_buf, c) == -EBUSY) { + /* idle */ + } +} + +void DMA1_Channel2_3_IRQHandler(void) { + /* Transfer complete */ + DMA1->IFCR |= DMA_IFCR_CTCIF2; + + DMA1_Channel2->CCR &= ~DMA_CCR_EN; + if (usart_tx_buf.wr_pos != usart_tx_buf.xfr_end) /* buffer not empty */ + usart_schedule_dma(); +} + +void usart_send_packet(const uint8_t *data, size_t len) { + /* ignore return value as putf is blocking and always succeeds */ + (void)cobs_encode_usart((char *)data, len); + + /* If the DMA stream is idle right now, schedule a transfer */ + if (!(DMA1_Channel2->CCR & DMA_CCR_EN)) + usart_schedule_dma(); +} + -- cgit