From 65c9ae34bc0409c8ec8d083ee0a7e3b62231a48d Mon Sep 17 00:00:00 2001 From: Edward Emelianov Date: Sat, 14 Mar 2026 01:16:00 +0300 Subject: [PATCH] USART almost working (but hangs on long messages) --- F0:F030,F042,F072/usbcan_gpio/Makefile | 8 +- F0:F030,F042,F072/usbcan_gpio/canproto.c | 6 +- F0:F030,F042,F072/usbcan_gpio/gpio.c | 12 +- F0:F030,F042,F072/usbcan_gpio/gpio.h | 26 ---- F0:F030,F042,F072/usbcan_gpio/gpioproto.cpp | 129 +++++++++++++++--- F0:F030,F042,F072/usbcan_gpio/gpioproto.h | 1 + F0:F030,F042,F072/usbcan_gpio/hardware.c | 6 +- F0:F030,F042,F072/usbcan_gpio/main.c | 1 + F0:F030,F042,F072/usbcan_gpio/usart.c | 63 ++++----- F0:F030,F042,F072/usbcan_gpio/usbcangpio.bin | Bin 22652 -> 23876 bytes .../usbcan_gpio/usbcangpio.creator.user | 2 +- F0:F030,F042,F072/usbcan_gpio/version.inc | 4 +- 12 files changed, 162 insertions(+), 96 deletions(-) diff --git a/F0:F030,F042,F072/usbcan_gpio/Makefile b/F0:F030,F042,F072/usbcan_gpio/Makefile index f9d8d4b..2bb8a42 100644 --- a/F0:F030,F042,F072/usbcan_gpio/Makefile +++ b/F0:F030,F042,F072/usbcan_gpio/Makefile @@ -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 diff --git a/F0:F030,F042,F072/usbcan_gpio/canproto.c b/F0:F030,F042,F072/usbcan_gpio/canproto.c index cacfe94..012a8f1 100644 --- a/F0:F030,F042,F072/usbcan_gpio/canproto.c +++ b/F0:F030,F042,F072/usbcan_gpio/canproto.c @@ -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 diff --git a/F0:F030,F042,F072/usbcan_gpio/gpio.c b/F0:F030,F042,F072/usbcan_gpio/gpio.c index 83215e3..ff86226 100644 --- a/F0:F030,F042,F072/usbcan_gpio/gpio.c +++ b/F0:F030,F042,F072/usbcan_gpio/gpio.c @@ -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){ diff --git a/F0:F030,F042,F072/usbcan_gpio/gpio.h b/F0:F030,F042,F072/usbcan_gpio/gpio.h index d4cfc54..b66eef5 100644 --- a/F0:F030,F042,F072/usbcan_gpio/gpio.h +++ b/F0:F030,F042,F072/usbcan_gpio/gpio.h @@ -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(); diff --git a/F0:F030,F042,F072/usbcan_gpio/gpioproto.cpp b/F0:F030,F042,F072/usbcan_gpio/gpioproto.cpp index 23a3ab1..41611bb 100644 --- a/F0:F030,F042,F072/usbcan_gpio/gpioproto.cpp +++ b/F0:F030,F042,F072/usbcan_gpio/gpioproto.cpp @@ -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); } } diff --git a/F0:F030,F042,F072/usbcan_gpio/gpioproto.h b/F0:F030,F042,F072/usbcan_gpio/gpioproto.h index 6f5478e..2facea9 100644 --- a/F0:F030,F042,F072/usbcan_gpio/gpioproto.h +++ b/F0:F030,F042,F072/usbcan_gpio/gpioproto.h @@ -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; diff --git a/F0:F030,F042,F072/usbcan_gpio/hardware.c b/F0:F030,F042,F072/usbcan_gpio/hardware.c index 4b9e796..45f312d 100644 --- a/F0:F030,F042,F072/usbcan_gpio/hardware.c +++ b/F0:F030,F042,F072/usbcan_gpio/hardware.c @@ -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(); diff --git a/F0:F030,F042,F072/usbcan_gpio/main.c b/F0:F030,F042,F072/usbcan_gpio/main.c index 0d2e39e..56e6659 100644 --- a/F0:F030,F042,F072/usbcan_gpio/main.c +++ b/F0:F030,F042,F072/usbcan_gpio/main.c @@ -32,6 +32,7 @@ void sys_tick_handler(void){ int main(void){ sysreset(); SysTick_Config(6000, 1); + StartHSE(); flashstorage_init(); hardware_setup(); USB_setup(); diff --git a/F0:F030,F042,F072/usbcan_gpio/usart.c b/F0:F030,F042,F072/usbcan_gpio/usart.c index fa572e4..85be3a6 100644 --- a/F0:F030,F042,F072/usbcan_gpio/usart.c +++ b/F0:F030,F042,F072/usbcan_gpio/usart.c @@ -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 } } diff --git a/F0:F030,F042,F072/usbcan_gpio/usbcangpio.bin b/F0:F030,F042,F072/usbcan_gpio/usbcangpio.bin index e41fd58ea5a2eea3861349fd8ec01f7879db8834..ed09300fa3a5033de5b4ca23e9897138cc034b06 100755 GIT binary patch delta 14154 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 MthNE^>gz85ACb&t3jhEB diff --git a/F0:F030,F042,F072/usbcan_gpio/usbcangpio.creator.user b/F0:F030,F042,F072/usbcan_gpio/usbcangpio.creator.user index b138528..aa40037 100644 --- a/F0:F030,F042,F072/usbcan_gpio/usbcangpio.creator.user +++ b/F0:F030,F042,F072/usbcan_gpio/usbcangpio.creator.user @@ -1,6 +1,6 @@ - + EnvironmentId diff --git a/F0:F030,F042,F072/usbcan_gpio/version.inc b/F0:F030,F042,F072/usbcan_gpio/version.inc index 1100d14..2c30622 100644 --- a/F0:F030,F042,F072/usbcan_gpio/version.inc +++ b/F0:F030,F042,F072/usbcan_gpio/version.inc @@ -1,2 +1,2 @@ -#define BUILD_NUMBER "156" -#define BUILD_DATE "2026-03-12" +#define BUILD_NUMBER "173" +#define BUILD_DATE "2026-03-14"