177 lines
5.6 KiB
C
177 lines
5.6 KiB
C
/*
|
|
* 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);
|
|
int usart_putc_nonblocking(char c);
|
|
int usart_putc(char c);
|
|
|
|
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 = (uint32_t)&(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. */
|
|
|
|
DMA1_Channel3->CMAR = (uint32_t)&(CRC->DR);
|
|
DMA1_Channel3->CCR = (1<<DMA_CCR_PL_Pos)
|
|
| (0<<DMA_CCR_MSIZE_Pos) /* 8 bit */
|
|
| (0<<DMA_CCR_PSIZE_Pos) /* 8 bit */
|
|
| DMA_CCR_PINC
|
|
| 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 115.2kBd @48MHz system clock. */
|
|
//USART1->BRR = 417;
|
|
|
|
//USART1->BRR = 48; /* 1MBd */
|
|
USART1->BRR = 96; /* 500kBd */
|
|
USART1->BRR = 192; /* 250kBd */
|
|
//USART1->BRR = 208; /* 230400 */
|
|
|
|
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;
|
|
}
|
|
|
|
int 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 */
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int usart_putc_nonblocking(char c) {
|
|
return usart_dma_fifo_push(&usart_tx_buf, c);
|
|
}
|
|
|
|
|
|
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(usart_putc, (char *)data, len);
|
|
|
|
/* If the DMA stream is idle right now, schedule a transfer */
|
|
if (!(DMA1_Channel2->CCR & DMA_CCR_EN))
|
|
usart_schedule_dma();
|
|
}
|
|
|
|
int usart_send_packet_nonblocking(const uint8_t *data, size_t len) {
|
|
/* ignore return value as putf is blocking and always succeeds */
|
|
/* FIXME DEBUG */
|
|
//int rc = cobs_encode_usart(usart_putc_nonblocking, (char *)data, len);
|
|
//if (rc)
|
|
// return rc;
|
|
/* END */
|
|
static uint8_t x = 0;
|
|
|
|
for (size_t i=0; i<351; i++)
|
|
usart_putc_nonblocking(x++);
|
|
|
|
/* If the DMA stream is idle right now, schedule a transfer */
|
|
if (!(DMA1_Channel2->CCR & DMA_CCR_EN))
|
|
usart_schedule_dma();
|
|
return 0;
|
|
}
|
|
|