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 3e42d37..2741aab 100755 Binary files a/F0:F030,F042,F072/usbcan_gpio/usbcangpio.bin and b/F0:F030,F042,F072/usbcan_gpio/usbcangpio.bin differ 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"