From b44fd9e91ffee9dbfe8701755a7a74a932d575d5 Mon Sep 17 00:00:00 2001 From: Edward Emelianov Date: Wed, 11 Mar 2026 23:40:39 +0300 Subject: [PATCH] start with USART (now DEBUG-compile is unavialable) --- F0:F030,F042,F072/usbcan_gpio/Readme.md | 7 +- F0:F030,F042,F072/usbcan_gpio/gpio.c | 5 + F0:F030,F042,F072/usbcan_gpio/gpioproto.cpp | 2 + F0:F030,F042,F072/usbcan_gpio/hardware.c | 2 + F0:F030,F042,F072/usbcan_gpio/main.c | 2 +- F0:F030,F042,F072/usbcan_gpio/usart.c | 263 ++++++++++++++++++ F0:F030,F042,F072/usbcan_gpio/usart.h | 48 ++++ F0:F030,F042,F072/usbcan_gpio/usbcangpio.bin | Bin 20564 -> 21048 bytes .../usbcan_gpio/usbcangpio.creator.user | 2 +- .../usbcan_gpio/usbcangpio.files | 2 + F0:F030,F042,F072/usbcan_gpio/version.inc | 4 +- 11 files changed, 332 insertions(+), 5 deletions(-) create mode 100644 F0:F030,F042,F072/usbcan_gpio/usart.c create mode 100644 F0:F030,F042,F072/usbcan_gpio/usart.h diff --git a/F0:F030,F042,F072/usbcan_gpio/Readme.md b/F0:F030,F042,F072/usbcan_gpio/Readme.md index f0291a7..5a88ded 100644 --- a/F0:F030,F042,F072/usbcan_gpio/Readme.md +++ b/F0:F030,F042,F072/usbcan_gpio/Readme.md @@ -12,4 +12,9 @@ New interface allows you to configure GPIO and use it's base functions: in/out/A ## DMA channels -DMA1 channel1: ADC. \ No newline at end of file +DMA1 channel 1: ADC + +DMA1 channel 4: USART[1,2]_TX + +DMA1 channel 5: USART[1,2]_RX + diff --git a/F0:F030,F042,F072/usbcan_gpio/gpio.c b/F0:F030,F042,F072/usbcan_gpio/gpio.c index 8df5540..ec5cfee 100644 --- a/F0:F030,F042,F072/usbcan_gpio/gpio.c +++ b/F0:F030,F042,F072/usbcan_gpio/gpio.c @@ -22,6 +22,7 @@ #include "adc.h" #include "flash.h" #include "gpio.h" +#include "usart.h" 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) @@ -155,6 +156,7 @@ TRUE_INLINE int8_t get_adc_channel(uint8_t port, uint8_t pin){ int gpio_reinit(){ bzero(monitor_mask, sizeof(monitor_mask)); bzero(oldstates, sizeof(oldstates)); + usartconf_t UC = {0}; // fill next for(int port = 0; port < 2; port++){ GPIO_TypeDef *gpio = (port == 0) ? GPIOA : GPIOB; for(int pin = 0; pin < 16; pin++){ @@ -200,6 +202,9 @@ int gpio_reinit(){ } } // TODO: configure USART, SPI etc + if(UC.No && usart_config(&UC)){ + if(!usart_start()) return FALSE; + } // also chech cfg->monitor! return TRUE; } diff --git a/F0:F030,F042,F072/usbcan_gpio/gpioproto.cpp b/F0:F030,F042,F072/usbcan_gpio/gpioproto.cpp index 26763bf..d13c4e7 100644 --- a/F0:F030,F042,F072/usbcan_gpio/gpioproto.cpp +++ b/F0:F030,F042,F072/usbcan_gpio/gpioproto.cpp @@ -28,6 +28,7 @@ extern "C"{ #include "gpioproto.h" #include "gpio.h" #include "gpioproto.h" +#include "usart.h" #undef USBIF #define USBIF IGPIO #include "strfunc.h" @@ -571,6 +572,7 @@ void GPIO_process(){ if(alert & pinmask) pin_getter(port, i); } } + usart_process(NULL, 0); if(l == 0) return; if(l < 0) SEND("ERROR: USB buffer overflow or string was too long\n"); else{ diff --git a/F0:F030,F042,F072/usbcan_gpio/hardware.c b/F0:F030,F042,F072/usbcan_gpio/hardware.c index eae568c..4b9e796 100644 --- a/F0:F030,F042,F072/usbcan_gpio/hardware.c +++ b/F0:F030,F042,F072/usbcan_gpio/hardware.c @@ -35,6 +35,8 @@ 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; 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 e9ce141..0d2e39e 100644 --- a/F0:F030,F042,F072/usbcan_gpio/main.c +++ b/F0:F030,F042,F072/usbcan_gpio/main.c @@ -17,10 +17,10 @@ */ #include "can.h" +#include "canproto.h" #include "flash.h" #include "gpioproto.h" #include "hardware.h" -#include "canproto.h" volatile uint32_t Tms = 0; diff --git a/F0:F030,F042,F072/usbcan_gpio/usart.c b/F0:F030,F042,F072/usbcan_gpio/usart.c new file mode 100644 index 0000000..9bab9fb --- /dev/null +++ b/F0:F030,F042,F072/usbcan_gpio/usart.c @@ -0,0 +1,263 @@ +/* + * This file is part of the usbcangpio project. + * Copyright 2026 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "ringbuffer.h" +#include "usart.h" + +// We share DMAch4/5 for both USARTs, so only one can work in a time!!! + +#define USARTSNO 2 + +// buffers for DMA or interrupt-driven data management +// here we use INDEX (usart number minus 1) +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 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 +static int curUSARTidx = -1; // working USART index (0/1), -1 if unconfigured + +static ringbuffer RBin = {.data = rbbuffer, .length = RXRBSZ}; + +static volatile USART_TypeDef* const Usarts[USARTSNO] = {USART1, USART2}; +static uint8_t const UIRQs[USARTSNO] = {USART1_IRQn, USART2_IRQn}; + +/** + * @brief usart_config - configure US[A]RT based on usb_LineCoding + * @param usartconf (io) - (modified to real speeds) + * @return TRUE if all OK + */ +int usart_config(usartconf_t *usartconf){ + SYSCFG->CFGR1 |= SYSCFG_CFGR1_USART1RX_DMA_RMP | SYSCFG_CFGR1_USART1TX_DMA_RMP; // both USARTs on DMA1ch4(tx)/5(rx) + // all clocking and GPIO config should be done in hardware_setup() & gpio_reinit()! + if(!usartconf) return FALSE; + if(curUSARTidx != -1) usart_stop(); // disable previous USART if enabled + uint8_t No = usartconf->No; + if(No >= USARTSNO || No < 1 || usartconf->speed < USART_MIN_SPEED) return FALSE; + --No; // now this is an INDEX! + volatile USART_TypeDef *U = Usarts[No]; + uint32_t peripheral_clock = 48000000; + // Disable USART while configuring + U->CR1 = 0; + U->ICR = 0xFFFFFFFF; // Clear all interrupt flags + // Assuming oversampling by 16 (default after reset). For higher baud rates you might use by 8. + U->BRR = peripheral_clock / (usartconf->speed); + usartconf->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(usartconf->monitor){ + if(usartconf->textproto){ + U->CR2 = USART_CR2_ADD_VAL('\n'); + cr1 |= USART_CR1_CMIE; + }else cr1 |= USART_CR1_IDLEIE; // monitor binary data by IDLE flag + } + textformat = usartconf->textproto; + monitor = usartconf->monitor; + // Enable transmitter, receiver, and interrupts (optional) + if(usartconf->Rxen) cr1 |= USART_CR1_RE; + if(usartconf->Txen){ + cr1 |= USART_CR1_TE; + // DMA Tx + volatile DMA_Channel_TypeDef *T = DMA1_Channel4; + T->CCR = 0; + T->CPAR = (uint32_t) &U->TDR; + T->CCR = DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_TCIE; + } + // Main config + U->CR1 = cr1; + curUSARTidx = No; + return TRUE; +} + +// start NUMBER +int usart_start(){ + if(curUSARTidx == -1) return FALSE; + volatile USART_TypeDef *U = Usarts[curUSARTidx]; + if(monitor) NVIC_EnableIRQ(UIRQs[curUSARTidx]); + 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; + 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; +} + +/** + * @brief usart_stop - turn off U[S]ART for given interface + * @param ifNo - interface number + */ +void usart_stop(){ + if(curUSARTidx == -1) return; + Usarts[curUSARTidx]->CR1 &= ~USART_CR1_UE; + NVIC_DisableIRQ(DMA1_Channel4_5_IRQn); + NVIC_DisableIRQ(UIRQs[curUSARTidx]); + curUSARTidx = -1; +} + +/** + * @brief usart_receive - receive data from USART by user request (return none if monitor == 1) + * @param buf (io) - user buffer + * @param len - `buf` length + * @return -1 if USART not configured or amount of data from ringbuffer + */ +int usart_receive(uint8_t *buf, int len){ + if(curUSARTidx == -1) return -1; + if(textformat){ + int toN = RB_datalento(&RBin, '\n'); + if(toN == 0) return 0; + if(toN < len) len = toN; // read only until '\n' in text format + } + int got = RB_read(&RBin, buf, len); + if(got < 0) got = 0; + return got; +} + +/** + * @brief usart_process - send/receive processing + * Try to send data from output ringbuffer, check input DMA buffer and full input ringbuffer + */ +/** + * @brief usart_process - USART data processing + * @param buf - buffer for "async" messages (when monitor==1) + * @param len - length of `buf` + * @return amount of bytes read or -1 if USART isn't active + */ +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 available = (write_idx - dma_read_idx); // length of data available + if(available < 0) available += DMARXBUFSZ; // write to the left of read + if(available){ + if(RXrdy){ + if(buf && len > 0){ + if(len < available) available = len; + }else RXrdy = 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; + 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){ + memcpy(buf, &inbuffer[dma_read_idx], first); + memcpy(buf, inbuffer, available - first); + ret = available; + wrOK = TRUE; + }else{ + if((first == RB_write(&RBin, &inbuffer[dma_read_idx], first)) && + (available - first) == RB_write(&RBin, inbuffer, available - first)) wrOK = TRUE; + } + } + if(wrOK){ + RXrdy = 0; + dma_read_idx = write_idx; // update read pointer + } + } + } +#if 0 + // Output data + if(TXrdy){ // ready to send new data - here we can process RBout, if have + int got = RB_read... + if(got > 0){ // send next data portion + volatile DMA_Channel_TypeDef *T = cfg->dma_tx_channel; + T->CCR &= ~DMA_CCR_EN; + T->CMAR = (uint32_t) outbuffers[i]; + T->CNDTR = got; + TXrdy = 0; + T->CCR |= DMA_CCR_EN; // start new transmission + } + } +#endif + return ret; +} + +// send data buffer +int usart_send(const uint8_t *data, int len){ + if(curUSARTidx == -1 || !data || len < 1) return 0; + if(TXrdy == 0) return -1; + if(len > DMATXBUFSZ) len = DMATXBUFSZ; + memcpy(outbuffer, data, len); + volatile DMA_Channel_TypeDef *T = DMA1_Channel4; + T->CCR &= ~DMA_CCR_EN; + T->CMAR = (uint32_t) outbuffer; + T->CNDTR = len; + TXrdy = 0; + T->CCR |= DMA_CCR_EN; // start new transmission + return len; +} + +/** + * @brief usart_isr - U[S]ART interrupt: IDLE (for DMA-driven) or + * @param ifno - interface index + */ +static void usart_isr(){ + if(curUSARTidx == -1) return; // WTF??? + volatile USART_TypeDef *U = Usarts[curUSARTidx]; + // IDLE active when we monitor binary data + if((U->ISR & USART_ISR_IDLE) && (U->CR1 & USART_CR1_IDLEIE)){ // try to send collected data (DMA-driven) + RXrdy = 1; // seems like data portion is over - try to send it + } + if((U->ISR & USART_ISR_CMF) && (U->CR1 & USART_CR1_CMIE)){ // character match -> the same for text data + RXrdy = 1; + } + U->ICR = 0xffffffff; // clear all flags +} + +void dma1_channel4_5_isr(){ // TX ready, channel5 + if(DMA1->ISR & DMA_ISR_TCIF5){ + TXrdy = 1; + DMA1->IFCR = DMA_IFCR_CTCIF5; + DMA1_Channel4->CCR &= ~DMA_CCR_EN; // disable DMA channel until next send + } +} + +// U[S]ART interrupts +void usart1_isr() {usart_isr();} +void usart2_isr() {usart_isr();} diff --git a/F0:F030,F042,F072/usbcan_gpio/usart.h b/F0:F030,F042,F072/usbcan_gpio/usart.h new file mode 100644 index 0000000..5f3b10b --- /dev/null +++ b/F0:F030,F042,F072/usbcan_gpio/usart.h @@ -0,0 +1,48 @@ +/* + * This file is part of the usbcangpio project. + * Copyright 2026 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +// DMA linear buffers for Rx/Tx +#define DMARXBUFSZ 128 +#define DMATXBUFSZ 128 +// incoming ring buffer - only if there's a lot of data in DMA RX buffer +#define RXRBSZ 256 + +#define USART_MIN_SPEED 1024 +#define USART_MAX_SPEED 1000000 + +typedef struct{ + uint32_t speed; // baudrate + uint8_t No : 2; // Usart number (1/2) + uint8_t Rxen : 1; // enable rx + uint8_t Txen : 1; // enable tx + uint8_t textproto : 1; // match '\n' and force output by lines (if there's enough place in buffers and monitor == 1) + uint8_t monitor : 1; // async output by incoming (over '\n', IDLE or buffer full) +} usartconf_t; + +int usart_config(usartconf_t *config); +int usart_start(); +void usart_stop(); + +int usart_process(uint8_t *buf, int len); + +int usart_receive(uint8_t *buf, int len); +int usart_send(const uint8_t *data, int len); diff --git a/F0:F030,F042,F072/usbcan_gpio/usbcangpio.bin b/F0:F030,F042,F072/usbcan_gpio/usbcangpio.bin index 3e42d3714016d5d70aec5bbbac1c85a9f1671a01..2741aab1efeb18cd5f09e08b0c83eef8b7f7f637 100755 GIT binary patch delta 9094 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 delta 8573 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 diff --git a/F0:F030,F042,F072/usbcan_gpio/usbcangpio.creator.user b/F0:F030,F042,F072/usbcan_gpio/usbcangpio.creator.user index 407bc5f..da59294 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/usbcangpio.files b/F0:F030,F042,F072/usbcan_gpio/usbcangpio.files index a18589b..1df8100 100644 --- a/F0:F030,F042,F072/usbcan_gpio/usbcangpio.files +++ b/F0:F030,F042,F072/usbcan_gpio/usbcangpio.files @@ -20,6 +20,8 @@ ringbuffer.c ringbuffer.h strfunc.c strfunc.h +usart.c +usart.h usb.c usb.h usb_defs.h diff --git a/F0:F030,F042,F072/usbcan_gpio/version.inc b/F0:F030,F042,F072/usbcan_gpio/version.inc index e4c8082..68c5f07 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 "132" -#define BUILD_DATE "2026-03-10" +#define BUILD_NUMBER "146" +#define BUILD_DATE "2026-03-11"