summaryrefslogtreecommitdiff
path: root/gm_platform/fw/serial.c
diff options
context:
space:
mode:
Diffstat (limited to 'gm_platform/fw/serial.c')
-rw-r--r--gm_platform/fw/serial.c139
1 files changed, 139 insertions, 0 deletions
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 <amir.hammad@hotmail.com>
+ *
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#include "global.h"
+#include "serial.h"
+#include "cobs.h"
+
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+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<<DMA_CCR_PL_Pos)
+ | DMA_CCR_DIR
+ | (0<<DMA_CCR_MSIZE_Pos) /* 8 bit */
+ | (0<<DMA_CCR_PSIZE_Pos) /* 8 bit */
+ | DMA_CCR_MINC
+ | DMA_CCR_TCIE; /* Enable transfer complete interrupt. */
+
+ /* triggered on transfer completion. We use this to process the ADC data */
+ NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);
+ NVIC_SetPriority(DMA1_Channel2_3_IRQn, 1<<5);
+
+ USART1->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();
+}
+