USART almost working (but hangs on long messages)

This commit is contained in:
Edward Emelianov
2026-03-14 01:16:00 +03:00
parent 14f544374a
commit 65c9ae34bc
12 changed files with 162 additions and 96 deletions

View File

@@ -1,10 +1,10 @@
BINARY := usbcangpio
# MCU code
#MCU := F072xB
MCU := F042x6
MCU := F072xB
#MCU := F042x6
# change this linking script depending on particular MCU model,
#LDSCRIPT := stm32f072xB.ld
LDSCRIPT := stm32f042x6.ld
LDSCRIPT := stm32f072x8.ld
#LDSCRIPT := stm32f042x6.ld
DEFINES := -DUSB2_16

View File

@@ -387,8 +387,10 @@ static void CommandParser(char *txt){
break;
#ifdef STM32F072xB
case 'D':
USB_sendstr("Go into DFU mode\n");
USB_sendall();
SEND("Go into DFU mode\n");
USB_sendall(ICAN);
uint32_t t = Tms;
while(Tms - t < 2000){IWDG->KR = IWDG_REFRESH;}
Jump2Boot();
break;
#endif

View File

@@ -27,13 +27,6 @@
static uint16_t monitor_mask[2] = {0}; // pins to monitor == 1 (ONLY GPIO and ADC)
static uint16_t oldstates[2][16] = {0}; // previous state (16 bits - as some pins could be analog)
// strings for keywords
const char *str_keywords[] = {
#define KW(x) [STR_ ## x] = #x,
KEYWORDS
#undef KW
};
// intermediate buffer to change pin's settings by user request; after checking in will be copied to the_conf
static pinconfig_t pinconfig[2][16] = {0};
static uint8_t pinconfig_notinited = 1; // ==0 after first memcpy from the_conf to pinconfig
@@ -207,7 +200,8 @@ int chkpinconf(){
}
// now check USART configuration
if(active_usart != -1){
if(chkusartconf(&UC)) ret = FALSE;
UC.idx = active_usart;
if(!chkusartconf(&UC)) ret = FALSE;
}else{
get_defusartconf(&UC); // clear global configuration
the_conf.usartconfig = UC;
@@ -318,7 +312,7 @@ int gpio_reinit(){
int shift4 = pin << 4;
gpio->AFR[0] = (gpio->AFR[0] & ~(0xf << shift4)) | (cfg->afno << shift4);
}else{
int shift4 = (pin - 8) << 4;
int shift4 = (pin - 8) << 2;
gpio->AFR[1] = (gpio->AFR[1] & ~(0xf << shift4)) | (cfg->afno << shift4);
}
if(cfg->monitor && cfg->mode != MODE_AF){

View File

@@ -99,32 +99,6 @@ typedef struct{
} spiconfig_t;
*/
// strings for keywords
extern const char *str_keywords[];
#define KEYWORDS \
KW(AIN) \
KW(IN) \
KW(OUT) \
KW(AF) \
KW(PU)\
KW(PD) \
KW(FL) \
KW(PP) \
KW(OD) \
KW(USART) \
KW(SPI) \
KW(I2C) \
KW(MONITOR) \
KW(THRESHOLD) \
KW(SPEED) \
KW(TEXT)
enum{ // indexes of string keywords
#define KW(k) STR_ ## k,
KEYWORDS
#undef KW
};
int is_disabled(uint8_t port, uint8_t pin);
int chkpinconf();

View File

@@ -25,7 +25,6 @@ extern "C"{
#include "adc.h"
#include "can.h"
#include "flash.h"
#include "gpioproto.h"
#include "gpio.h"
#include "gpioproto.h"
#include "usart.h"
@@ -36,6 +35,10 @@ extern "C"{
extern volatile uint32_t Tms;
static uint8_t curbuf[MAXSTRLEN]; // buffer for receiving data from USART etc
static uint8_t usart_text = 0; // ==1 for text USART proto
// TODO: add analog threshold!
// list of all commands and handlers
@@ -56,9 +59,8 @@ extern volatile uint32_t Tms;
COMMAND(setiface, "set/get name of interface x (0 - CAN, 1 - GPIO)") \
COMMAND(storeconf, "save config to flash") \
COMMAND(time, "show current time (ms)") \
COMMAND(vdd, "get approx Vdd value (V*100)")
// COMMAND(USART, "Read USART data or send (USART=hex)")
COMMAND(vdd, "get approx Vdd value (V*100)") \
COMMAND(USART, "Read USART data or send (USART=hex)")
// COMMAND(usartconf, "set USART params (e.g. usartconf=115200 8N1)")
// COMMAND(SPI, "Read SPI data or send (SPI=hex)")
// COMMAND(spiconf, "set SPI params")
@@ -99,7 +101,42 @@ enum MiscValues{
MISC_MONITOR = 1,
MISC_THRESHOLD,
MISC_SPEED,
MISC_TEXT
MISC_TEXT,
MISC_BIN
};
// TODO: add HEX input?
#define KEYWORDS \
KW(AIN) \
KW(IN) \
KW(OUT) \
KW(AF) \
KW(PU)\
KW(PD) \
KW(FL) \
KW(PP) \
KW(OD) \
KW(USART) \
KW(SPI) \
KW(I2C) \
KW(MONITOR) \
KW(THRESHOLD) \
KW(SPEED) \
KW(TEXT) \
KW(BIN) \
enum{ // indexes of string keywords
#define KW(k) STR_ ## k,
KEYWORDS
#undef KW
};
// strings for keywords
static const char *str_keywords[] = {
#define KW(x) [STR_ ## x] = #x,
KEYWORDS
#undef KW
};
static const Keyword keywords[] = {
@@ -120,17 +157,19 @@ static const Keyword keywords[] = {
KEY(THRESHOLD, GROUP_MISC, MISC_THRESHOLD)
KEY(SPEED, GROUP_MISC, MISC_SPEED)
KEY(TEXT, GROUP_MISC, MISC_TEXT)
KEY(BIN, GROUP_MISC, MISC_BIN)
#undef K
};
#define NUM_KEYWORDS (sizeof(keywords)/sizeof(keywords[0]))
static const char* errtxt[ERR_AMOUNT] = {
[ERR_OK] = "OK",
[ERR_BADCMD] = "BADCMD",
[ERR_BADPAR] = "BADPAR",
[ERR_BADVAL] = "BADVAL",
[ERR_WRONGLEN] = "WRONGLEN",
[ERR_CANTRUN] = "CANTRUN",
[ERR_OK] = "OK",
[ERR_BADCMD] = "BADCMD",
[ERR_BADPAR] = "BADPAR",
[ERR_BADVAL] = "BADVAL",
[ERR_WRONGLEN] = "WRONGLEN",
[ERR_CANTRUN] = "CANTRUN",
[ERR_BUSY] = "BUSY",
};
static const char *pinhelp =
@@ -141,6 +180,9 @@ static const char *pinhelp =
" FUNC: USART or SPI (enable alternate function and configure peripheal)\n"
" MISC: MONITOR - send data by USB as only state changed\n"
" THRESHOLD (ADC only) - monitoring threshold, ADU\n"
" SPEED - interface speed/frequency\n"
" TEXT - USART means data as text ('\n'-separated strings)\n"
" BIN - USART means data as binary (output: HEX)\n"
"\n"
;
@@ -197,7 +239,10 @@ static bool argsvals(char *args, int32_t *parno, int32_t *parval){
// `PAx = ` also printed there
static void pin_getter(uint8_t port, uint8_t pin){
int16_t val = pin_in(port, pin);
if(val < 0) SENDn(errtxt[ERR_CANTRUN]);
if(val < 0){
SENDn(errtxt[ERR_CANTRUN]);
return;
}
SEND(port == 0 ? "PA" : "PB"); SEND(u2str((uint32_t)pin)); SEND(EQ);
SENDn(u2str((uint32_t)val));
}
@@ -284,6 +329,9 @@ static errcodes_t pin_setter(uint8_t port, uint8_t pin, char *setter){
case MISC_TEXT: // what to do, if textproto is set, but user wants binary?
UsartConf.textproto = 1;
break;
case MISC_BIN: // clear text flag
UsartConf.textproto = 0;
break;
}
break;
}
@@ -342,7 +390,13 @@ static errcodes_t cmd_PB(const char *cmd, char *args){
}
static errcodes_t cmd_reinit(const char _U_ *cmd, char _U_ *args){
if(gpio_reinit()) return ERR_OK;
if(gpio_reinit()){
usartconf_t UC;
if(get_curusartconf(&UC)){
usart_text = UC.textproto;
}
return ERR_OK;
}
SEND("Can't reinit: check your configuration!\n");
return ERR_AMOUNT;
}
@@ -573,6 +627,44 @@ static errcodes_t cmd_help(const char _U_ *cmd, char _U_ *args){
return ERR_AMOUNT;
}
static int sendfun(const char *s){
if(!s) return 0;
return USB_sendstr(IGPIO, s);
}
static void sendusartdata(const uint8_t *buf, int len){
if(!buf || len < 1) return;
SEND(str_keywords[STR_USART]); SEND(EQ);
if(usart_text){
USB_send(IGPIO, curbuf, len);
if(curbuf[len-1] != '\n') NL();
}else{
NL();
hexdump(sendfun, (uint8_t*)curbuf, len);
}
}
static errcodes_t cmd_USART(const char _U_ *cmd, char *args){
if(!args) return ERR_BADVAL;
char *setter = splitargs(args, NULL);
if(setter){
DBG("Try to send over USART\n");
int l = strlen(setter);
if(usart_text){ // add '\n' as we removed it @ parser
if(setter[l-1] != '\n') setter[l++] = '\n';
}
l = usart_send((uint8_t*)setter, l);
if(l < 0) return ERR_BUSY;
else if(l == 0) return ERR_CANTRUN;
return ERR_OK;
} // getter: try to read
int l = usart_receive(curbuf, MAXSTRLEN);
if(l < 0) return ERR_CANTRUN;
if(l > 0) sendusartdata(curbuf, l);
// or silence: nothing to read
return ERR_AMOUNT;
}
constexpr uint32_t hash(const char* str, uint32_t h = 0){
return *str ? hash(str + 1, h + ((h << 7) ^ *str)) : h;
}
@@ -599,9 +691,8 @@ static const char *CommandParser(char *str){
}
void GPIO_process(){
char inbuff[MAXSTRLEN];
int l = RECV(inbuff, MAXSTRLEN);
// TODO: check SPI/USART/I2C
int l;
// TODO: check SPI/I2C etc
for(uint8_t port = 0; port < 2; ++port){
uint16_t alert = gpio_alert(port);
if(alert == 0) continue;
@@ -610,11 +701,13 @@ void GPIO_process(){
if(alert & pinmask) pin_getter(port, i);
}
}
usart_process(NULL, 0);
l = usart_process(curbuf, MAXSTRLEN);
if(l > 0) sendusartdata(curbuf, l);
l = RECV((char*)curbuf, MAXSTRLEN);
if(l == 0) return;
if(l < 0) SEND("ERROR: USB buffer overflow or string was too long\n");
else{
const char *ans = CommandParser(inbuff);
const char *ans = CommandParser((char*)curbuf);
if(ans) SENDn(ans);
}
}

View File

@@ -26,6 +26,7 @@ typedef enum{
ERR_BADVAL, // wrong value (for setter)
ERR_WRONGLEN, // wrong message length
ERR_CANTRUN, // can't run given command due to bad parameters or other
ERR_BUSY, // target interface busy, try later
ERR_AMOUNT // amount of error codes or "send nothing"
} errcodes_t;

View File

@@ -34,9 +34,9 @@ TRUE_INLINE void gpio_setup(){ // setup some common GPIO
void hardware_setup(){
// enable all active GPIO clocking
RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN;
RCC->APB1ENR |= RCC_APB2ENR_USART1EN;
RCC->APB2ENR |= RCC_APB1ENR_USART2EN;
RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | RCC_AHBENR_DMA1EN;
RCC->APB2ENR |= RCC_APB2ENR_USART1EN | RCC_APB2ENR_SYSCFGEN;
RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
gpio_setup();
//gpio_reinit();
adc_setup();

View File

@@ -32,6 +32,7 @@ void sys_tick_handler(void){
int main(void){
sysreset();
SysTick_Config(6000, 1);
StartHSE();
flashstorage_init();
hardware_setup();
USB_setup();

View File

@@ -32,8 +32,8 @@
static uint8_t inbuffer[DMARXBUFSZ]; // DMA in buffer
static uint8_t rbbuffer[RXRBSZ]; // for in ringbuffer
static uint8_t outbuffer[DMATXBUFSZ]; // DMA out buffer
static uint8_t TXrdy = 1; // TX DMA ready
static uint8_t RXrdy = 0; // == 1 when got IDLE or '\n' (only when `monitoring` is on
static volatile uint8_t TXrdy = 1; // TX DMA ready
static volatile uint8_t RXrdy = 0; // == 1 when got IDLE or '\n' (only when `monitoring` is on
static uint8_t textformat = 0; // out by '\n'-terminated lines
static uint8_t monitor = 0; // monitor USART rx
static int dma_read_idx = 0; // start of data in DMA inbuffers
@@ -108,18 +108,19 @@ int usart_config(usartconf_t *uc){
// Assuming oversampling by 16 (default after reset). For higher baud rates you might use by 8.
U->BRR = peripheral_clock / (usartconfig.speed);
usartconfig.speed= peripheral_clock / U->BRR; // fix for real speed
uint32_t cr1 = 0;
// format: 8N1, so CR2 used only for character match (if need)
if(usartconfig.monitor){
if(usartconfig.textproto){
U->CR2 = USART_CR2_ADD_VAL('\n');
cr1 |= USART_CR1_CMIE;
}else cr1 |= USART_CR1_IDLEIE; // monitor binary data by IDLE flag
}
uint32_t cr1 = 0, cr3 = 0;
textformat = usartconfig.textproto;
monitor = usartconfig.monitor;
// Enable transmitter, receiver, and interrupts (optional)
if(usartconfig.RXen) cr1 |= USART_CR1_RE;
if(usartconfig.RXen){
cr1 |= USART_CR1_RE;
cr3 |= USART_CR3_DMAR;
// format: 8N1, so CR2 used only for character match (if need)
if(usartconfig.textproto){
U->CR2 = USART_CR2_ADD_VAL('\n'); // buffer text data by EOL
cr1 |= USART_CR1_CMIE;
}else cr1 |= USART_CR1_IDLEIE; // buffer binary data by IDLE flag
}
if(usartconfig.TXen){
cr1 |= USART_CR1_TE;
// DMA Tx
@@ -127,9 +128,11 @@ int usart_config(usartconf_t *uc){
T->CCR = 0;
T->CPAR = (uint32_t) &U->TDR;
T->CCR = DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_TCIE;
cr3 |= USART_CR3_DMAT;
}
// Main config
U->CR1 = cr1;
U->CR3 = cr3;
curUSARTidx = No;
// all OK -> copy to global config
the_conf.usartconfig = usartconfig;
@@ -140,26 +143,21 @@ int usart_config(usartconf_t *uc){
int usart_start(){
if(curUSARTidx == -1) return FALSE;
volatile USART_TypeDef *U = Usarts[curUSARTidx];
if(monitor) NVIC_EnableIRQ(UIRQs[curUSARTidx]);
NVIC_EnableIRQ(UIRQs[curUSARTidx]); // copy to ring buffer after each '\n' in text mode or IDLE in binary
NVIC_EnableIRQ(DMA1_Channel4_5_IRQn);
// reset Rx DMA
if(U->CR1 & USART_CR1_RE){
volatile DMA_Channel_TypeDef *R = DMA1_Channel5;
dma_read_idx = 0;
R->CCR = 0;
R->CPAR = (uint32_t) U->RDR;
RB_clearbuf(&RBin);
R->CPAR = (uint32_t) &U->RDR;
R->CMAR = (uint32_t) inbuffer;
R->CNDTR = DMARXBUFSZ;
R->CCR = DMA_CCR_MINC | DMA_CCR_CIRC | DMA_CCR_EN;
RB_clearbuf(&RBin);
}
U->CR1 |= USART_CR1_UE; // enable USARTx
U->ICR = 0xFFFFFFFF; // Clear flags
// Wait for the idle frame to complete (optional)
uint32_t tmout = 16000000;
while(!(U->ISR & USART_ISR_TC)){
if (--tmout == 0) break;
}
TXrdy = 1;
return TRUE;
}
@@ -208,33 +206,36 @@ int usart_process(uint8_t *buf, int len){
if(curUSARTidx == -1 || !(Usarts[curUSARTidx]->CR1 & USART_CR1_UE)) return -1; // none activated or started
int ret = 0; // returned value
// Input data
int write_idx = DMARXBUFSZ - DMA1_Channel5->CNDTR; // next symbol to be written
int remained = DMA1_Channel5->CNDTR;
int write_idx = DMARXBUFSZ - remained; // next symbol to be written
int available = (write_idx - dma_read_idx); // length of data available
int monitored_len = available;
uint8_t locmonitor = monitor; // if `buf` not pointed, set this flag to zero
if(available < 0) available += DMARXBUFSZ; // write to the left of read
if(available){
if(RXrdy){
if(locmonitor){
if(buf && len > 0){
if(len < available) available = len;
}else RXrdy = 0;
if(len < monitored_len) monitored_len = len;
}else locmonitor = 0;
}
// TODO: force copying data to "async" buffer in case of overflow danger
if(available >= (DMARXBUFSZ/2) || RXrdy){ // enough data or lonely couple of bytes but need to show
// copy data in one or two chunks (wrap handling)
int wrOK = FALSE;
if(dma_read_idx + available <= DMARXBUFSZ){ // head before tail
if(RXrdy){
memcpy(buf, &inbuffer[dma_read_idx], available);
ret = available;
if(locmonitor){
memcpy(buf, &inbuffer[dma_read_idx], monitored_len);
ret = monitored_len;
wrOK = TRUE;
}else{
if(available == RB_write(&RBin, &inbuffer[dma_read_idx], available)) wrOK = TRUE;
}
}else{ // head after tail - two chunks
int first = DMARXBUFSZ - dma_read_idx;
if(RXrdy){
if(locmonitor){
memcpy(buf, &inbuffer[dma_read_idx], first);
memcpy(buf, inbuffer, available - first);
ret = available;
memcpy(buf, inbuffer, monitored_len - first);
ret = monitored_len;
wrOK = TRUE;
}else{
if((first == RB_write(&RBin, &inbuffer[dma_read_idx], first)) &&
@@ -297,9 +298,9 @@ static void usart_isr(){
}
void dma1_channel4_5_isr(){ // TX ready, channel5
if(DMA1->ISR & DMA_ISR_TCIF5){
if(DMA1->ISR & DMA_ISR_TCIF4){
TXrdy = 1;
DMA1->IFCR = DMA_IFCR_CTCIF5;
DMA1->IFCR = DMA_IFCR_CTCIF4;
DMA1_Channel4->CCR &= ~DMA_CCR_EN; // disable DMA channel until next send
}
}

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 18.0.2, 2026-03-13T00:12:36. -->
<!-- Written by QtCreator 18.0.2, 2026-03-14T01:13:52. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>

View File

@@ -1,2 +1,2 @@
#define BUILD_NUMBER "156"
#define BUILD_DATE "2026-03-12"
#define BUILD_NUMBER "173"
#define BUILD_DATE "2026-03-14"