mirror of
https://github.com/eddyem/stm32samples.git
synced 2026-02-28 03:44:30 +03:00
Seems like all U[S]ARTs works well. Tested on speeds up to 2Mbaud (as CH340 can).
This commit is contained in:
@@ -46,7 +46,7 @@ typedef struct {
|
||||
// maybe DMA1ch2 would be for SPI1Rx (or SSI should be @ SPI3), in this case USART3 would be interrupt-driven
|
||||
// IF1[0]: USART3 APB2 72 MHz DMA1ch2 DMA1ch3 PB14
|
||||
// IF2[1]: USART1 APB1 36 MHz DMA1ch4 DMA1ch5 PB0
|
||||
// IF3[2]: USART2 APB2 72 MHz DMA1ch6 DMA1ch7 PA1
|
||||
// IF3[2]: USART2 APB2 72 MHz DMA1ch7 DMA1ch6 PA1
|
||||
// IF4[3]: UART4 APB2 72 MHz DMA2ch5 DMA2ch3
|
||||
// IF5[4]: UART5 APB2 72 MHz - - - interrupt-driven
|
||||
// IF6[5]: (CAN)
|
||||
@@ -54,19 +54,20 @@ typedef struct {
|
||||
static const USART_Config UC[USARTSNO] = {
|
||||
[0] = {.instance = USART3, .pclk_freq = 36000000, .UIRQn = USART3_IRQn, .DIRQn = DMA1_Channel2_IRQn, .dma_controller = DMA1, .dma_rx_channel = DMA1_Channel3, .dma_tx_channel = DMA1_Channel2, .TTCflag = DMA_ISR_TCIF2, .DEport = GPIOB, .DEpin = 1<<14 },
|
||||
[1] = {.instance = USART1, .pclk_freq = 72000000, .UIRQn = USART1_IRQn, .DIRQn = DMA1_Channel4_IRQn, .dma_controller = DMA1, .dma_rx_channel = DMA1_Channel5, .dma_tx_channel = DMA1_Channel4, .TTCflag = DMA_ISR_TCIF4, .DEport = GPIOB, .DEpin = 1<<0 },
|
||||
[2] = {.instance = USART2, .pclk_freq = 36000000, .UIRQn = USART2_IRQn, .DIRQn = DMA1_Channel7_IRQn, .dma_controller = DMA1, .dma_rx_channel = DMA1_Channel6, .dma_tx_channel = DMA1_Channel7, .TTCflag = DMA_ISR_TCIF7, .DEport = GPIOB, .DEpin = 1<<1 },
|
||||
[2] = {.instance = USART2, .pclk_freq = 36000000, .UIRQn = USART2_IRQn, .DIRQn = DMA1_Channel7_IRQn, .dma_controller = DMA1, .dma_rx_channel = DMA1_Channel6, .dma_tx_channel = DMA1_Channel7, .TTCflag = DMA_ISR_TCIF7, .DEport = GPIOA, .DEpin = 1<<1 },
|
||||
[3] = {.instance = UART4, .pclk_freq = 36000000, .UIRQn = UART4_IRQn, .DIRQn = DMA2_Channel5_IRQn, .dma_controller = DMA2, .dma_rx_channel = DMA2_Channel3, .dma_tx_channel = DMA2_Channel5, .TTCflag = DMA_ISR_TCIF5 },
|
||||
[4] = {.instance = UART5, .pclk_freq = 36000000, .UIRQn = UART5_IRQn }, // no DMA
|
||||
};
|
||||
|
||||
// buffers for DMA or interrupt-driven data management
|
||||
static uint8_t inbuffers[USARTSNO][DMARXBUFSZ];
|
||||
static uint16_t inbufidx[USARTSNO] = {0}; // for interrupt-driven - index of next character (also amount of received bytes)
|
||||
static uint16_t inbufidx[USARTSNO] = {0}; // for interrupt-driven - index of next character (also amount of received bytes)
|
||||
static uint8_t outbuffers[USARTSNO][DMATXBUFSZ];
|
||||
static uint16_t outbufidx[USARTSNO] = {0}; // index of next char to transmit over interrupt
|
||||
static uint16_t outbuflen[USARTSNO] = {0}; // length of data to transmit over interrupt [equal 0 if nothing to send]
|
||||
static uint8_t need2send[USARTSNO] = {0}; // flags from IDLE interrupt to send data portion
|
||||
static uint8_t TXrdy[USARTSNO] = {1,1,1,1,1}; // TX DMA ready
|
||||
static uint16_t outbufidx[USARTSNO] = {0}; // index of next char to transmit over interrupt
|
||||
static uint16_t outbuflen[USARTSNO] = {0}; // length of data to transmit over interrupt [equal 0 if nothing to send]
|
||||
static uint8_t need2send[USARTSNO] = {0}; // flags from IDLE interrupt to send data portion
|
||||
static uint8_t TXrdy[USARTSNO] = {1,1,1,1,1}; // TX DMA ready
|
||||
static int dma_read_idx[USARTSNO] = {0}; // start of data in DMA inbuffers
|
||||
// there's no way to tell recipient about overfull, so we will just "eat" spare data!
|
||||
|
||||
|
||||
@@ -172,7 +173,7 @@ void usart_config(uint8_t ifNo, usb_LineCoding *lc){
|
||||
R->CPAR = (uint32_t) &U->RDR;
|
||||
R->CMAR = (uint32_t) inbuffers[ifNo];
|
||||
R->CNDTR = DMARXBUFSZ;
|
||||
R->CCR = DMA_CCR_MINC | DMA_CCR_EN; // | DMA_CCR_TCIE
|
||||
R->CCR = DMA_CCR_MINC | DMA_CCR_CIRC | DMA_CCR_EN; // manage circular buffer in polling
|
||||
// enable U[S]ART DMA
|
||||
U->CR3 = USART_CR3_DMAT | USART_CR3_DMAR;
|
||||
}else{
|
||||
@@ -200,7 +201,16 @@ void usart_start(uint8_t ifNo){
|
||||
const USART_Config *cfg = &UC[ifNo];
|
||||
cfg->instance->CR1 |= USART_CR1_UE;
|
||||
NVIC_EnableIRQ(cfg->UIRQn);
|
||||
if(cfg->dma_controller) NVIC_EnableIRQ(cfg->DIRQn);
|
||||
if(cfg->dma_controller){ // reset Rx DMA
|
||||
volatile DMA_Channel_TypeDef *R = cfg->dma_rx_channel;
|
||||
dma_read_idx[ifNo] = 0;
|
||||
R->CCR = 0;
|
||||
R->CPAR = (uint32_t) &cfg->instance->RDR;
|
||||
R->CMAR = (uint32_t) inbuffers[ifNo];
|
||||
R->CNDTR = DMARXBUFSZ;
|
||||
R->CCR = DMA_CCR_MINC | DMA_CCR_CIRC | DMA_CCR_EN;
|
||||
NVIC_EnableIRQ(cfg->DIRQn);
|
||||
}
|
||||
DBGch('0' + ifNo);
|
||||
DBG("U[S]ART started");
|
||||
}
|
||||
@@ -213,13 +223,11 @@ void usart_stop(uint8_t ifNo){
|
||||
if(ifNo >= USARTSNO || UC[ifNo].instance == NULL) return;
|
||||
const USART_Config *cfg = &UC[ifNo];
|
||||
cfg->instance->CR1 &= ~USART_CR1_UE;
|
||||
if(cfg->DEport) RX485(cfg->DEport, cfg->DEpin);
|
||||
/*
|
||||
if(cfg->dma_controller){
|
||||
NVIC_DisableIRQ(cfg->DIRQn);
|
||||
}
|
||||
NVIC_DisableIRQ(cfg->UIRQn);
|
||||
*/
|
||||
if(cfg->DEport) RX485(cfg->DEport, cfg->DEpin);
|
||||
DBGch('0' + ifNo);
|
||||
DBG("U[S]ART stopped");
|
||||
}
|
||||
@@ -234,11 +242,28 @@ void usarts_process(){
|
||||
if(!(cfg->instance->CR1 & USART_CR1_UE)) continue; // USART disabled
|
||||
if(cfg->dma_controller){ // DMA-driven
|
||||
// Input data
|
||||
int write_idx = DMARXBUFSZ - cfg->dma_rx_channel->CNDTR; // next symbol to be written
|
||||
int available = (write_idx - dma_read_idx[i] + DMARXBUFSZ) % DMARXBUFSZ; // length of data available
|
||||
if(need2send[i] && available == 0) need2send[i] = 0;
|
||||
if(available >= USB_TXBUFSZ || need2send[i]){ // enough data or lonely couple of bytes
|
||||
// copy data in one or two chunks (wrap handling)
|
||||
if(dma_read_idx[i] + available <= DMARXBUFSZ){ // head before tail
|
||||
USB_send(i, &inbuffers[i][dma_read_idx[i]], available);
|
||||
}else{ // head after tail - two chunks
|
||||
uint32_t first = DMARXBUFSZ - dma_read_idx[i];
|
||||
USB_send(i, &inbuffers[i][dma_read_idx[i]], first);
|
||||
USB_send(i, inbuffers[i], available - first);
|
||||
}
|
||||
DBG("USART -> USB over DMA");
|
||||
dma_read_idx[i] = write_idx; // update read pointer
|
||||
}
|
||||
#if 0
|
||||
if(DMARXBUFSZ - cfg->dma_rx_channel->CNDTR > DMARXBUFSZ/2 || need2send[i]){
|
||||
volatile DMA_Channel_TypeDef *R = cfg->dma_rx_channel;
|
||||
R->CCR &= ~DMA_CCR_EN; // pause DMA input transactions
|
||||
register int l = DMARXBUFSZ - R->CNDTR;
|
||||
if(l){ // have some input data -> send and restart DMA
|
||||
DBG("need");
|
||||
if(USB_send(i, inbuffers[i], l)){
|
||||
DBG("USART -> USB over DMA");
|
||||
// restart DMA only in case of succesfull sent or if failed, but have ability of buffer overfull
|
||||
@@ -249,6 +274,7 @@ void usarts_process(){
|
||||
}
|
||||
R->CCR |= DMA_CCR_EN; // re-enable DMA
|
||||
}
|
||||
#endif
|
||||
// Output data
|
||||
if(TXrdy[i]){ // ready to send new data
|
||||
int got = USB_receive(i, outbuffers[i], DMATXBUFSZ);
|
||||
@@ -308,7 +334,7 @@ void usarts_process(){
|
||||
int usart_send(uint8_t ifNo, const uint8_t *data, int len){
|
||||
if(ifNo >= USARTSNO || !data || len < 1) return 0;
|
||||
const USART_Config *cfg = &UC[ifNo];
|
||||
if(TXrdy[ifNo] == 0 || (!cfg->instance->CR1 & USART_CR1_UE)) return -1; // busy or not active
|
||||
if(TXrdy[ifNo] == 0 || !(cfg->instance->CR1 & USART_CR1_UE)) return -1; // busy or not active
|
||||
if(len > DMATXBUFSZ) len = DMATXBUFSZ;
|
||||
memcpy(outbuffers[ifNo], data, len);
|
||||
if(cfg->dma_controller){
|
||||
@@ -350,7 +376,6 @@ static void usart_isr(uint8_t ifno){
|
||||
volatile USART_TypeDef *U = cfg->instance;
|
||||
// for every flag we should also check if it's IRQ active
|
||||
if((U->ISR & USART_ISR_RXNE) && (U->CR1 & USART_CR1_RXNEIE)){ // got new byte
|
||||
DBG("RXNE");
|
||||
if(inbufidx[ifno] == DMARXBUFSZ) (void) U->RDR; // throw away data: buffer overfull
|
||||
else inbuffers[ifno][ inbufidx[ifno]++ ] = U->RDR; // put new byte into buffer
|
||||
}
|
||||
@@ -358,22 +383,17 @@ static void usart_isr(uint8_t ifno){
|
||||
if(U->ISR & USART_ISR_IDLE){ // try to send collected data (DMA-driven)
|
||||
need2send[ifno] = 1; // seems like data portion is over - try to send it
|
||||
U->ICR = USART_ICR_IDLECF;
|
||||
DBG("IDLE");
|
||||
}
|
||||
if((U->ISR & USART_ISR_TXE) && (U->CR1 & USART_CR1_TXEIE)){ // send next byte if need (interrupt-driven)
|
||||
DBG("TXE");
|
||||
if(outbuflen[ifno] > outbufidx[ifno]){
|
||||
U->TDR = outbuffers[ifno][ outbufidx[ifno]++ ];
|
||||
}else if(U->CR1 & USART_CR1_TXEIE){
|
||||
U->CR1 &= ~USART_CR1_TXEIE; // disable interrupt: no data to send
|
||||
TXrdy[ifno] = 1;
|
||||
DBG("TXRDY");
|
||||
}
|
||||
}
|
||||
if(U->ISR & USART_ISR_TC){ // switch RS-485 to Rx after transmission complete
|
||||
DBG("TC");
|
||||
if(cfg->DEport){
|
||||
DBG("485 -> RX");
|
||||
RX485(cfg->DEport, cfg->DEpin);
|
||||
U->CR1 &= ~USART_CR1_TE;
|
||||
U->CR1 |= USART_CR1_RE;
|
||||
@@ -390,7 +410,7 @@ void uart4_exti34_isr(){ usart_isr(3); }
|
||||
void uart5_exti35_isr(){ usart_isr(4); }
|
||||
|
||||
// DMA Tx interrupts (to arm ready flag)
|
||||
void dma1_channel2_isr(){ DBG("DMA1 done"); TXrdy[0] = 1; DMA1->IFCR = DMA_IFCR_CTCIF2; }
|
||||
void dma1_channel4_isr(){ DBG("DMA2 done"); TXrdy[1] = 1; DMA1->IFCR = DMA_IFCR_CTCIF4; }
|
||||
void dma1_channel6_isr(){ DBG("DMA3 done"); TXrdy[2] = 1; DMA1->IFCR = DMA_IFCR_CTCIF6; }
|
||||
void dma2_channel5_isr(){ DBG("DMA4 done"); TXrdy[3] = 1; DMA2->IFCR = DMA_IFCR_CTCIF5; }
|
||||
void dma1_channel2_isr(){ TXrdy[0] = 1; DMA1->IFCR = DMA_IFCR_CTCIF2; }
|
||||
void dma1_channel4_isr(){ TXrdy[1] = 1; DMA1->IFCR = DMA_IFCR_CTCIF4; }
|
||||
void dma1_channel7_isr(){ TXrdy[2] = 1; DMA1->IFCR = DMA_IFCR_CTCIF7; }
|
||||
void dma2_channel5_isr(){ TXrdy[3] = 1; DMA2->IFCR = DMA_IFCR_CTCIF5; }
|
||||
|
||||
Reference in New Issue
Block a user