Seems like all U[S]ARTs works well. Tested on speeds up to 2Mbaud (as CH340 can).

This commit is contained in:
Edward Emelianov
2026-02-17 23:37:17 +03:00
parent fb570367fa
commit 68028f42a3
13 changed files with 104 additions and 64 deletions

View File

@@ -50,13 +50,18 @@ void debug_newline_only(){
RB_write(&dbgrb, (const uint8_t*)&nl, 1);
}
// print by lines until there's place in USB ringbuffer
void print_debug_messages(){
if(!Config_mode) return;
uint8_t rcvbuf[256];
do{
int n = RB_readto(&dbgrb, '\n', rcvbuf, 256);
if(n == 0) break;
else if(n < 0) n = -n; // partial string: longer than 256 bytes
int l = RB_datalento(&dbgrb, '\n');
if(l < 1) break;
int freesize = USB_sendbufspace(ICFG);
if(freesize < l) break;
if(l > 256) l = 256;
int n = RB_read(&dbgrb, rcvbuf, l);
if(n < 1) break; // empty or busy
USB_send(ICFG, rcvbuf, n);
}while(1);
}

View File

@@ -32,6 +32,7 @@
#else
#define DBG(str)
#define DBGs(str)
#define DBGch(ch)
#define DBGn()
#define DBGpri()
#endif

View File

@@ -45,8 +45,8 @@ int main(void){
USBPU_OFF();
USB_setup();
//uint32_t ctr = Tms;
usb_LineCoding lc = {9600, 0, 0, 8};
for(int i = 0; i < 5; ++i) usart_config(i, &lc); // configure all U[S]ARTs for default data
//usb_LineCoding lc = {9600, 0, 0, 8};
//for(int i = 0; i < 5; ++i) usart_config(i, &lc); // configure all U[S]ARTs for default data
USBPU_ON();
while(1){
// Put here code working WITOUT USB connected

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 18.0.2, 2026-02-16T23:57:09. -->
<!-- Written by QtCreator 18.0.2, 2026-02-17T23:35:31. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>

View File

@@ -95,8 +95,7 @@ static const char* erpg(const char *str){
static void sendoverU(uint8_t ifno, char *str){
int len = strlen(str);
CFGWR("try to send "); CFGWRn(str);
str[len] = '\n';
len = usart_send(ifno, (const uint8_t*)str, len+1);
len = usart_send(ifno, (const uint8_t*)str, len);
CFGWR("sent "); CFGWR(i2str(len)); CFGWR("bytes\n");
}

View File

@@ -15,6 +15,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include "ringbuffer.h"
static int datalen(ringbuffer *b){
@@ -24,7 +26,7 @@ static int datalen(ringbuffer *b){
// stored data length
int RB_datalen(ringbuffer *b){
if(b->busy) return -1;
if(!b || b->busy) return -1;
b->busy = 1;
int l = datalen(b);
b->busy = 0;
@@ -32,7 +34,7 @@ int RB_datalen(ringbuffer *b){
}
static int hasbyte(ringbuffer *b, uint8_t byte){
if(b->head == b->tail) return -1; // no data in buffer
if(!b || b->head == b->tail) return -1; // no data in buffer
int startidx = b->head;
if(b->head > b->tail){ //
for(int found = b->head; found < b->length; ++found)
@@ -51,18 +53,13 @@ static int hasbyte(ringbuffer *b, uint8_t byte){
* @return index if found, -1 if none or busy
*/
int RB_hasbyte(ringbuffer *b, uint8_t byte){
if(b->busy) return -1;
if(!b || b->busy) return -1;
b->busy = 1;
int ret = hasbyte(b, byte);
b->busy = 0;
return ret;
}
// poor memcpy
static void mcpy(uint8_t *targ, const uint8_t *src, int l){
while(l--) *targ++ = *src++;
}
// increment head or tail
TRUE_INLINE void incr(ringbuffer *b, volatile int *what, int n){
*what += n;
@@ -76,9 +73,9 @@ static int read(ringbuffer *b, uint8_t *s, int len){
int _1st = b->length - b->head;
if(_1st > l) _1st = l;
if(_1st > len) _1st = len;
mcpy(s, b->data + b->head, _1st);
memcpy(s, b->data + b->head, _1st);
if(_1st < len && l > _1st){
mcpy(s+_1st, b->data, l - _1st);
memcpy(s+_1st, b->data, l - _1st);
incr(b, &b->head, l);
return l;
}
@@ -94,20 +91,27 @@ static int read(ringbuffer *b, uint8_t *s, int len){
* @return bytes read or -1 if busy
*/
int RB_read(ringbuffer *b, uint8_t *s, int len){
if(b->busy) return -1;
if(!b || b->busy || !s || len < 1) return -1;
b->busy = 1;
int r = read(b, s, len);
b->busy = 0;
return r;
}
static int readto(ringbuffer *b, uint8_t byte, uint8_t *s, int len){
// length of data from current position to `byte` (including byte)
static int lento(ringbuffer *b, uint8_t byte){
int idx = hasbyte(b, byte);
if(idx < 0) return 0;
int partlen = idx + 1 - b->head;
// now calculate length of new data portion
if(idx < b->head) partlen += b->length;
if(partlen > len) return -read(b, s, len);
return partlen;
}
static int readto(ringbuffer *b, uint8_t byte, uint8_t *s, int len){
int partlen = lento(b, byte);
if(!partlen) return 0;
if(partlen > len) return -1;
return read(b, s, partlen);
}
@@ -120,21 +124,29 @@ static int readto(ringbuffer *b, uint8_t byte, uint8_t *s, int len){
* @return amount of bytes written (negative, if len<data in buffer or buffer is busy)
*/
int RB_readto(ringbuffer *b, uint8_t byte, uint8_t *s, int len){
if(b->busy) return -1;
if(!b || b->busy || !s || len < 1) return -1;
b->busy = 1;
int n = readto(b, byte, s, len);
b->busy = 0;
return n;
}
int RB_datalento(ringbuffer *b, uint8_t byte){
if(!b || b->busy) return -1;
b->busy = 1;
int n = lento(b, byte);
b->busy = 0;
return n;
}
static int write(ringbuffer *b, const uint8_t *str, int l){
int r = b->length - 1 - datalen(b); // rest length
if(l > r || !l) return 0;
int _1st = b->length - b->tail;
if(_1st > l) _1st = l;
mcpy(b->data + b->tail, str, _1st);
memcpy(b->data + b->tail, str, _1st);
if(_1st < l){ // add another piece from start
mcpy(b->data, str+_1st, l-_1st);
memcpy(b->data, str+_1st, l-_1st);
}
incr(b, &b->tail, l);
return l;
@@ -148,7 +160,7 @@ static int write(ringbuffer *b, const uint8_t *str, int l){
* @return amount of bytes written or -1 if busy
*/
int RB_write(ringbuffer *b, const uint8_t *str, int l){
if(b->busy) return -1;
if(!b || b->busy || !str || l < 1) return -1;
b->busy = 1;
int w = write(b, str, l);
b->busy = 0;
@@ -157,7 +169,7 @@ int RB_write(ringbuffer *b, const uint8_t *str, int l){
// just delete all information in buffer `b`
int RB_clearbuf(ringbuffer *b){
if(b->busy) return -1;
if(!b || b->busy) return -1;
b->busy = 1;
b->head = 0;
b->tail = 0;

View File

@@ -38,4 +38,5 @@ int RB_readto(ringbuffer *b, uint8_t byte, uint8_t *s, int len);
int RB_hasbyte(ringbuffer *b, uint8_t byte);
int RB_write(ringbuffer *b, const uint8_t *str, int l);
int RB_datalen(ringbuffer *b);
int RB_datalento(ringbuffer *b, uint8_t byte);
int RB_clearbuf(ringbuffer *b);

View File

@@ -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,7 +54,7 @@ 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
};
@@ -67,6 +67,7 @@ static uint16_t outbufidx[USARTSNO] = {0}; // index of next char to transmit ove
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; }

View File

@@ -22,11 +22,8 @@
#include "usb_dev.h"
// DMA linear buffers for Rx/Tx
#define DMARXBUFSZ 128
#define DMATXBUFSZ 128
// ringbuffers for collected data
#define USARTRXRBSZ 256
#define USARTTXRBSZ 256
#define DMARXBUFSZ 512
#define DMATXBUFSZ 512
void usart_config(uint8_t ifNo, usb_LineCoding *lc);
void usart_start(uint8_t ifNo);

View File

@@ -184,14 +184,11 @@ void usb_class_request(config_pack_t *req, uint8_t *data, uint16_t datalen){
case REQ_RECIPIENT_INTERFACE:
switch(req->bRequest){
case SET_LINE_CODING:
//DBG("SLC");
if(!data || !datalen) break; // wait for data
//DBG("test");
if(datalen == sizeof(usb_LineCoding))
linecoding_handler(ifno, (usb_LineCoding*)data);
break;
case GET_LINE_CODING:
DBG("GLC");
EP_WriteIRQ(0, (uint8_t*)&lineCoding[ifno], sizeof(lineCoding));
break;
case SET_CONTROL_LINE_STATE:
@@ -226,17 +223,26 @@ int USB_sendall(uint8_t ifno){
return TRUE;
}
// return amount of free space in buffer
int USB_sendbufspace(uint8_t ifno){
if(!CDCready[ifno]) return 0;
return rbout[ifno].length - RB_datalen((ringbuffer*)&rbout[ifno]);
}
// put `buf` into queue to send
int USB_send(uint8_t ifno, const uint8_t *buf, int len){
if(!buf || !CDCready[ifno] || !len) return FALSE;
if(ifno != ICFG) DBG("USB_send");
if(!buf || !CDCready[ifno] || !len){
return FALSE;
}
uint32_t T0 = Tms;
while(len){
if(Tms - T0 > DISCONN_TMOUT){
//break_handler(ifno);
return FALSE;
}
if(!CDCready[ifno]) return FALSE;
if(!CDCready[ifno]){
return FALSE;
}
IWDG->KR = IWDG_REFRESH;
int l = RB_datalen((ringbuffer*)&rbout[ifno]);
if(l < 0) continue;
@@ -255,7 +261,6 @@ int USB_send(uint8_t ifno, const uint8_t *buf, int len){
}
}
if(buf[len-1] == '\n' && lastdsz[ifno] < 0){
if(ifno != ICFG) DBG("send_next");
send_next(ifno);
}
return TRUE;
@@ -279,7 +284,6 @@ int USB_putbyte(uint8_t ifno, uint8_t byte){
}
// send line if got EOL
if(byte == '\n' && lastdsz[ifno] < 0){
if(ifno != ICFG) DBG("send_next");
send_next(ifno);
}
return TRUE;

View File

@@ -55,6 +55,7 @@ void linecoding_handler(uint8_t ifno, usb_LineCoding *lc);
#define CFGWRn(s) do{USB_sendstr(ICFG, s); USB_putbyte(ICFG, '\n');}while(0)
#define CFGn() USB_putbyte(ICFG, '\n')
int USB_sendbufspace(uint8_t ifno);
int USB_sendall(uint8_t ifno);
int USB_send(uint8_t ifno, const uint8_t *buf, int len);
int USB_putbyte(uint8_t ifno, uint8_t byte);

View File

@@ -1,2 +1,2 @@
#define BUILD_NUMBER "110"
#define BUILD_DATE "2026-02-16"
#define BUILD_NUMBER "140"
#define BUILD_DATE "2026-02-17"