From cd8151c6845d45f392ea03a2e0131345fc5ca40d Mon Sep 17 00:00:00 2001 From: Edward Emelianov Date: Sun, 15 Feb 2026 00:24:26 +0300 Subject: [PATCH] seems like U[S]ARTS OK, not tested yet --- F3:F303/InterfaceBoard/Readme.md | 59 +++++- F3:F303/InterfaceBoard/hardware.c | 55 ++++- F3:F303/InterfaceBoard/hardware.h | 4 + F3:F303/InterfaceBoard/main.c | 3 + F3:F303/InterfaceBoard/multiiface.bin | Bin 13256 -> 15908 bytes F3:F303/InterfaceBoard/usart.c | 286 ++++++++++++++++++-------- F3:F303/InterfaceBoard/usart.h | 15 +- F3:F303/InterfaceBoard/usb_dev.c | 2 + F3:F303/InterfaceBoard/usb_dev.h | 2 +- F3:F303/InterfaceBoard/version.inc | 4 +- 10 files changed, 331 insertions(+), 99 deletions(-) diff --git a/F3:F303/InterfaceBoard/Readme.md b/F3:F303/InterfaceBoard/Readme.md index 4f8df35..1a43a71 100644 --- a/F3:F303/InterfaceBoard/Readme.md +++ b/F3:F303/InterfaceBoard/Readme.md @@ -59,7 +59,7 @@ Inner USB interfaces (IFx): | 33 | PB12 | NC | | | | 34 | PB13 | NC | | | | 35 | PB14 | USART3 DE | AF7 or PP | RS-485 (1) DE | -| 36 | PB15 | | | | +| 36 | PB15 | NC | | | | 37 | PC6 | NC | | | | 38 | PC7 | NC | | | | 39 | PC8 | NC | | | @@ -118,6 +118,61 @@ DMA2 channels: UART5 have no DMA channels, so used in interrupts. +You can try to use hardware DE management on two of RS-485, but I decide that as I can't use hardware DE for all three, it would be +simpler to use software DE for all. -### Sorted by ports (with AF numbers). +### Sorted by ports + +| Pin # | Pin name | function | settings | comment | +|---------|-------------|-------------|---------------|---------------------| +| 14 | PA0 | NC | | | +| 15 | PA1 | USART2 DE | AF7 or PP | RS-485 (3) DE | +| 16 | PA2 | USART2 TX | AF7 | RS-485 (3) Tx | +| 17 | PA3 | USART2 RX | AF7 | RS-485 (3) Rx | +| 20 | PA4 | NC | | | +| 21 | PA5 | SPI1 SCK | AF5 | SSI CLK | +| 22 | PA6 | SPI1 MISO | AF5 | SSI DAT | +| 23 | PA7 | NC | | | +| 41 | PA8 | NC | | | +| 42 | PA9 | (CONF EN) | PU IN | Config jumper | +| 43 | PA10 | (USB PU) | PP OUT | USB pullup | +| 44 | PA11 | USB DM | AF14 | | +| 45 | PA12 | USB DP | AF14 | | +| 46 | PA13 | SWDIO | AF0 | | +| 49 | PA14 | SWCLK | AF0 | | +| 50 | PA15 | NC | | | +| 26 | PB0 | (USART1 DE) | PP OUT | RS-485 (2) DE | +| 27 | PB1 | NC | | | +| 28 | PB2 | NC | | | +| 55 | PB3 | NC | | | +| 56 | PB4 | NC | | | +| 57 | PB5 | NC | | | +| 58 | PB6 | NC | | | +| 59 | PB7 | NC | | | +| 61 | PB8 | CAN RX | AF9 | | +| 62 | PB9 | CAN TX | AF9 | | +| 29 | PB10 | USART3 TX | AF7 | RS-485 (1) Tx | +| 30 | PB11 | USART3 RX | AF7 | RS-485 (1) Rx | +| 33 | PB12 | NC | | | +| 34 | PB13 | NC | | | +| 35 | PB14 | USART3 DE | AF7 or PP | RS-485 (1) DE | +| 36 | PB15 | NC | | | +| 8 | PC0 | NC | | | +| 9 | PC1 | NC | | | +| 10 | PC2 | NC | | | +| 11 | PC3 | NC | | | +| 24 | PC4 | USART1 TX | AF7 | RS-485 (2) Tx | +| 25 | PC5 | USART1 RX | AF7 | RS-485 (2) Rx | +| 37 | PC6 | NC | | | +| 38 | PC7 | NC | | | +| 39 | PC8 | NC | | | +| 40 | PC9 | NC | | | +| 51 | PC10 | UART4 TX | AF5 | RS-232 (1) Tx | +| 52 | PC11 | UART4 RX | AF5 | RS-232 (1) Rx | +| 53 | PC12 | UART5 TX | AF5 | RS-232(2) / 485 Tx | +| 2 | PC13 | NC | | | +| 3 | PC14 | NC | | | +| 4 | PC15 | NC | | | +| 54 | PD2 | UART5 RX | AF5 | RS-232(2) / 485 Rx | +| 18 | PF4 | NC | | | diff --git a/F3:F303/InterfaceBoard/hardware.c b/F3:F303/InterfaceBoard/hardware.c index 602b3ea..e6c2623 100644 --- a/F3:F303/InterfaceBoard/hardware.c +++ b/F3:F303/InterfaceBoard/hardware.c @@ -21,18 +21,61 @@ uint8_t Config_mode = 0; static inline void gpio_setup(){ - RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | RCC_AHBENR_DMA1EN | RCC_AHBENR_DMA2EN; + RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | RCC_AHBENR_GPIOCEN | RCC_AHBENR_GPIODEN + | RCC_AHBENR_DMA1EN | RCC_AHBENR_DMA2EN; RCC->APB1ENR |= RCC_APB1ENR_CANEN | RCC_APB1ENR_USART2EN | RCC_APB1ENR_USART3EN | RCC_APB1ENR_UART4EN | RCC_APB1ENR_UART5EN; RCC->APB2ENR |= RCC_APB2ENR_USART1EN; for(int i = 0; i < 10000; ++i) nop(); - // USB - alternate function 14 @ pins PA11/PA12; SWD - AF0 @PA13/14 +/******************************************** +* PA1 * USART2 DE * PP OUT * +* PA2 * USART2 TX * AF7 * +* PA3 * USART2 RX * AF7 * +* PA5 * SPI1 SCK * AF5 * +* PA6 * SPI1 MISO * AF5 * +* PA9 * (CONF EN) * PU IN * +* PA10 * (USB PU) * PP OUT * +* PA11 * USB DM * AF14 * +* PA12 * USB DP * AF14 * +* PA13 * SWDIO * AF0 * +* PA14 * SWCLK * AF0 * +********************************************/ + GPIOA->AFR[0] = AFRf(7, 2) | AFRf(7, 3) | AFRf(5, 5) | AFRf(5, 6); GPIOA->AFR[1] = AFRf(14, 11) | AFRf(14, 12); - // PA9 - config jumper (PU in), PA10 - USB pullup (PP) - GPIOA->MODER = MODER_I(9) | MODER_O(10) | MODER_AF(11) | MODER_AF(12) | MODER_AF(13) | MODER_AF(14); - GPIOA->OSPEEDR = OSPEED_HI(11) | OSPEED_HI(12) | OSPEED_HI(13) | OSPEED_HI(14); + GPIOA->MODER = MODER_O(1) | MODER_AF(2) | MODER_AF(3) | MODER_AF(5) | MODER_AF(6) | MODER_I(9) | MODER_O(10) + | MODER_AF(11) | MODER_AF(12) | MODER_AF(13) | MODER_AF(14); + GPIOA->OSPEEDR = OSPEED_MED(2) | OSPEED_MED(3) | OSPEED_HI(5) | OSPEED_HI(6) + | OSPEED_HI(11) | OSPEED_HI(12) | OSPEED_HI(13) | OSPEED_HI(14); GPIOA->PUPDR = PUPD_PU(9); - for(int i = 0; i < 10000; ++i) nop(); +/******************************************** +* PB0 * USART1 DE * PP OUT * +* PB8 * CAN RX * AF9 * +* PB9 * CAN TX * AF9 * +* PB10 * USART3 TX * AF7 * +* PB11 * USART3 RX * AF7 * +* PB14 * USART3 DE * PP OUT * +********************************************/ + GPIOB->AFR[1] = AFRf(9, 8) | AFRf(9, 9) | AFRf(7, 10) | AFRf(7, 11); + GPIOB->MODER = MODER_O(0) | MODER_AF(8) | MODER_AF(9) | MODER_AF(10) | MODER_AF(11) | MODER_O(14); + GPIOB->OSPEEDR = OSPEED_HI(8) | OSPEED_HI(9) | OSPEED_MED(10) | OSPEED_MED(11); +/******************************************** +* PC4 * USART1 TX * AF7 * +* PC5 * USART1 RX * AF7 * +* PC10 * UART4 TX * AF5 * +* PC11 * UART4 RX * AF5 * +* PC12 * UART5 TX * AF5 * +********************************************/ + GPIOC->AFR[0] = AFRf(7, 4) | AFRf(7, 5); + GPIOC->AFR[1] = AFRf(5, 10) | AFRf(5, 11) | AFRf(5, 12); + GPIOC->MODER = MODER_AF(4) | MODER_AF(5) | MODER_AF(10) | MODER_AF(11) | MODER_AF(12); + GPIOC->OSPEEDR = OSPEED_MED(4) | OSPEED_MED(5) | OSPEED_MED(10) | OSPEED_MED(11) | OSPEED_MED(12); +/******************************************** +* PD2 * UART5 RX * AF5 * +********************************************/ + GPIOD->AFR[0] = AFRf(5, 2); + GPIOD->MODER = MODER_AF(2); + GPIOD->OSPEEDR = OSPEED_MED(2); + for(int i = 0; i < 10000; ++i) nop(); // wait a little before reading CFG pin if(CFG_ON()) Config_mode = 1; } diff --git a/F3:F303/InterfaceBoard/hardware.h b/F3:F303/InterfaceBoard/hardware.h index 8dd6707..4fd70e7 100644 --- a/F3:F303/InterfaceBoard/hardware.h +++ b/F3:F303/InterfaceBoard/hardware.h @@ -29,6 +29,10 @@ #define CFG_pin (1<<9) #define CFG_ON() (CFG_port->IDR & CFG_pin) +// RS-485 Rx is low level, Tx - high +#define RX485(port, pin) do{port->BRR = pin;}while(0) +#define TX485(port, pin) do{port->BSRR = pin;}while(0) + extern volatile uint32_t Tms; extern uint8_t Config_mode; diff --git a/F3:F303/InterfaceBoard/main.c b/F3:F303/InterfaceBoard/main.c index 7f1ed6b..5494e4a 100644 --- a/F3:F303/InterfaceBoard/main.c +++ b/F3:F303/InterfaceBoard/main.c @@ -15,9 +15,11 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + #include "flash.h" #include "hardware.h" #include "proto.h" +#include "usart.h" #include "usb_dev.h" #define MAXSTRLEN RBINSZ @@ -46,6 +48,7 @@ int main(void){ while(1){ // Put here code working WITOUT USB connected if(!usbON) continue; + usarts_process(); // for(int i = 0; i < maxno; ++i){ // just echo for first time if(!CDCready[i]) continue; int l = USB_receive(i, (uint8_t*)inbuff, MAXSTRLEN); diff --git a/F3:F303/InterfaceBoard/multiiface.bin b/F3:F303/InterfaceBoard/multiiface.bin index 2972aef465015697cf5d1193f84cdcee3b1b8513..d2ffc333662b3ad14bd94452f4ab07e1541269ce 100755 GIT binary patch delta 5662 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 C`&U%} delta 3032 zcmb_d4QyN06~6b`Nt{31akAn#X=3~3|JjL?c1=i{;U=wV?a^duHI2|BCj!;!I+nnw z;<}NI3T+qzwcKj!4+4sy`(avG-LNt%xZA3gO&xOv6Qe2B+XO323?3!a&wjDr&V5eO zE{%k$?Ma{Rd*40hp7ZYc?maK=ePc%uJ&(*54rvYm9$5mk zgEs~J5{Lh@eei$&gIh}I?)oM1au>Ip?8jFPjPQdw6xFm&xk6JH;<w3xA)n;R-BF zrZF2BT*2!-Gd`7|Rv|7sX4sT%#==k1LS&_)k)2fIqs(|1-5t;s23xddeU+5}sI``&(+QGWT|>V}5RIaqf?~sZ3(t z6giT0CU^-FE3hGr5nu+?0cH_0uZ0mbXhvqn$>JRwUVi-3KOqNlhE6{Q|K8-eXQc5(Rrtk$Hn2;b2#O_m)8zyGz_AFCBY?VBfLK+ib{s5eg7d zLPsEU#2Z-*O>N7DRI}(I#4)jK54M_yQM3eVzFkxYhVMjr#);6uhHa`mWDZTD2|0dP z4j;~~QQMm0+`>!D9nhNXsNvP{;5Fk}N3CYxR_k&YVKrju@#bY+za>(py-; ze4HS|h34SzVkXaVKb?L(mh+iL)iWDDeA0)_|T2sS2iG`wPbLZYeQ54lH=jyB-X+S;Z7>ra*@7sw%_cb*ZHp^tIt`T0k zWU^o9WnNE^4|U~+iG*44qdt4-|JcY1eI0YJM4bAP><}<}ZKPjc0gmU>WUqc>Fkgo> z7Qp?tfO@dC1Mav5pXJui(jmJ>j_*#d;(G+yA;B93M`s@KnRN zIW5P}fHsOsx;_>4^Lu{;%rm?CftaalK+NWIVK&2F6k#V>qbswQnZJjN#~%KD(GCrH z*uvo~a>7!E<7CS6Bz}c>tQ%HXN|B}+&<%KO9uJq2lh!_bH%VA4wX;x)l18MdazJSs zNez1+euF&0Hsc90#a1%C4sw}wGvApb4OKg}!=S4V0Y7Juc~nJSsH#!R@jE4Qp=vFD ziR4zVGd`M<{8^ilL+eD=oT;4|Yy}k=tloupl0R2}!$}vQ3ylIor}x3Fp>o~smEefy zZYp-opyQ_e_3geQU#a)d_|yKQp<^Q`T5$b{M#+6OH0(VRd8(!nUb-Z5wx+8$*FC*e z@@w3%_03%(tB;H$P8E~FH>@A+R(e`PFV@ncn)}L1@Oc4fqOqvbL7_ zdl`APwjDk`t1Z($Qh_wj0e%HIUqMWD68r_`n!_`?k!$&6o$!@R2dvP>>Z_sL!(!0+w>>=>_gE`B5F)yvhC6HhP6H zv49U$HI3MP{00z5WMC)X?J@ZxLvNYFUnIYa-$m?hUJ7)QkLoJ(+8`equ|Xt;`n7nH z^wgJX%|IrHUW4`^$72byyWYC-EABp#b-96|czSio~4QUt|!{cOa7HNSU2(I9{07M zSxwL|5SKSC`Ae?{RBCA>+Z%3MEywRlh+u=c3)2z5lA1qniKSLaEpu9w)J`_^RZpi? zP>T#n#Sog@uPE>}?;fx8Q0Cu<$V#pfl$D(zPOgDT$z&I2TuC)cg@17Jz)c>tjJ zP9*2JI&d|JMBr@9i8QIO8R7eJk+j=dm|+!pz;+7%lvwPo%w+@Vw>MUNG`G-*P&71! zqFW)(5xB@N11=fJN&7b37FIRpW86!0O-HrQ0KZ@Z^RG6&z=%bp-*F9wa9{HeF&+Qa zQiBb&9efK``$HA1*odVBdd(`o1#w3Y9IqmTZLc-l26-Zz8|U+u@b z)y-z7xy8}4#_njbH@AcXYYK6=x6Q$5KW;*rIY0?`3Qgpx_B(JJd9(d0Gj0q2wxb3I zMOX%5Sp0@ZR4>D2EyHCm!{sc)X_nz~70!A?ScF7q<~JoniaLus#e!vK3YX!u%W!0% SvzQU}^% diff --git a/F3:F303/InterfaceBoard/usart.c b/F3:F303/InterfaceBoard/usart.c index 298050c..a7260ab 100644 --- a/F3:F303/InterfaceBoard/usart.c +++ b/F3:F303/InterfaceBoard/usart.c @@ -23,59 +23,52 @@ #include "hardware.h" #include "strfunc.h" #include "usart.h" -#include "usb_descr.h" // InterfacesAmount, IFx +#include "usb_descr.h" // InterfacesAmount, IFx, bufsz +#include "usb_dev.h" // get fresh USB input data -static volatile int idatalen = 0; // received data line length (including '\n') -// USARTs registers by interface number [IF1..IF7], index=epNo-1 -static volatile USART_TypeDef *USARTx[InterfacesAmount] = {USART3, USART1, USART2, UART4, UART5, NULL, NULL}; -static int usartByIfNo[] = {0, ISerial1, ISerial2, ISerial0, ISerial3, ISerial4}; // USARTx -> index x -// APB1/APB2 bus speeds: -static const uint32_t usart_clocks[INTERFACES_AMOUNT] = { - 72000000, // USART3 on APB2 (72 MHz) - 36000000, // USART1 on APB1 (36 MHz) - 72000000, // USART2 on APB2 (72 MHz) - 72000000, // UART4 on APB2 (72 MHz) - 72000000, // UART5 on APB2 (72 MHz) - 0, // not used - 0 // not used +// !!!!! INDEXED BY INTERFACE NUMBER (just to not check IF6 and IF7) !!!!! +#define USARTSNO 5 + +typedef struct { + volatile USART_TypeDef *instance; // U[S]ARTx + uint32_t pclk_freq; // APB1/APB2 frequency + int16_t IRQn; // IRQ number for enable/disable (maybe 0 for DMA-driven channels) + volatile DMA_TypeDef *dma_controller; // DMA1/DMA2 or NULL if not used + volatile DMA_Channel_TypeDef *dma_rx_channel; // e.g., DMA_Channel_5 or NULL if not used + volatile DMA_Channel_TypeDef *dma_tx_channel; // e.g., DMA_Channel_4 or NULL if not used + uint32_t TTCflag; // Tx transfer complete flag + uint32_t RTCflag; // Rx transfer complete flag + volatile GPIO_TypeDef *DEport; // if RS485 - DE GPIO port (NULL for RS-232 or RS-422) + uint32_t DEpin; // -//- pin +} USART_Config; + +// IF U[S]ART bus freq TxDMA RxDMA DE (if 485) +// 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 +// IF4[3]: UART4 APB2 72 MHz DMA2ch5 DMA2ch3 +// IF5[4]: UART5 APB2 72 MHz - - - interrupt-driven +// IF6[5]: (CAN) +// IF7[6]: (SPI) +static const USART_Config UC[USARTSNO] = { + [0] = {.instance = USART3, .pclk_freq = 72000000, USART3_IRQn, .dma_controller = DMA1, .dma_rx_channel = DMA1_Channel3, .dma_tx_channel = DMA1_Channel2, .TTCflag = DMA_ISR_TCIF3, .RTCflag = DMA_ISR_TCIF3, .DEport = GPIOB, .DEpin = 1<<14 }, + [1] = {.instance = USART1, .pclk_freq = 36000000, USART1_IRQn, .dma_controller = DMA1, .dma_rx_channel = DMA1_Channel5, .dma_tx_channel = DMA1_Channel4, .TTCflag = DMA_ISR_TCIF5, .RTCflag = DMA_ISR_TCIF4, .DEport = GPIOB, .DEpin = 1<<0 }, + [2] = {.instance = USART2, .pclk_freq = 72000000, USART2_IRQn, .dma_controller = DMA1, .dma_rx_channel = DMA1_Channel6, .dma_tx_channel = DMA1_Channel7, .TTCflag = DMA_ISR_TCIF6, .RTCflag = DMA_ISR_TCIF7, .DEport = GPIOB, .DEpin = 1<<1 }, + [3] = {.instance = UART4, .pclk_freq = 72000000, UART4_IRQn, .dma_controller = DMA2, .dma_rx_channel = DMA2_Channel3, .dma_tx_channel = DMA2_Channel5, .TTCflag = DMA_ISR_TCIF3, .RTCflag = DMA_ISR_TCIF5 }, + [4] = {.instance = UART5, .pclk_freq = 72000000, UART5_IRQn }, // no DMA }; -#if 0 -volatile int linerdy = 0, // received data ready - dlen = 0, // length of data (including '\n') in current buffer - bufovr = 0; // input buffer overfull +// buffers for DMA or interrupt-driven data management +static uint8_t inbuffers[USARTSNO][DMARXBUFSZ]; +static uint16_t inbufidx[USARTSNO] = {0}; // for interrupt-driven - index of next character (also amount of received bytes) +static uint8_t outbuffers[USARTSNO][DMATXBUFSZ]; +static uint16_t outbufidx[USARTSNO] = {0}; // index of next char to transmit over interrupt +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 +// there's no way to tell recipient about overfull, so we will just "eat" spare data! -static void usart_putchar(int no, uint8_t ch){ - while(!(USARTx[no]->ISR & USART_ISR_TXE)); - USARTx[no]->TDR = ch; -} -void usart_sendn(uint8_t ifNo, const uint8_t *str, int L){ - if (!str || L < 0 || ifNo < 1 || ifNo > USARTSNO) - return; - for(int i = 0; i < L; ++i){ - usart_putchar(ifNo, str[i]); - } -} - -// setup all USARTs -void usarts_setup(){ - // clock - RCC->APB1ENR |= RCC_APB1ENR_USART2EN | RCC_APB1ENR_USART3EN; - RCC->APB2ENR |= RCC_APB2ENR_USART1EN; - for(int i = 0; i < USARTSNO; ++i) - usart_config(i + USART1_IDX, lineCodings); - NVIC_EnableIRQ(USART1_IRQn); - NVIC_EnableIRQ(USART2_IRQn); - NVIC_EnableIRQ(USART3_IRQn); -} -#endif - -// TODO: fixme for different settings -//lineCoding.dwDTERate = speeds[usartNo]; -//lineCoding.bCharFormat = USB_CDC_1_STOP_BITS; -//lineCoding.bParityType = USB_CDC_NO_PARITY; -//lineCoding.bDataBits = 8; /** * @brief usart_config - configure US[A]RT based on usb_LineCoding @@ -83,9 +76,11 @@ void usarts_setup(){ * @param lc (io) - linecoding (modified to real speeds) */ void usart_config(uint8_t ifNo, usb_LineCoding *lc){ - if(ifNo >= INTERFACES_AMOUNT || USARTx[ifNo] == NULL) return; - volatile USART_TypeDef *U = USARTx[ifNo]; - uint32_t peripheral_clock = usart_clocks[ifNo]; + // all clocking and GPIO config should be done in gpio_setup()! + if(ifNo >= USARTSNO || UC[ifNo].instance == NULL) return; + const USART_Config *cfg = &UC[ifNo]; + volatile USART_TypeDef *U = cfg->instance; + uint32_t peripheral_clock = cfg->pclk_freq; // Disable USART while configuring U->CR1 = 0; U->ICR = 0xFFFFFFFF; // Clear all interrupt flags @@ -118,14 +113,11 @@ void usart_config(uint8_t ifNo, usb_LineCoding *lc){ }else if(data_bits == 7){ // 7 data + 1 parity = 8 bits total -> M=00 (8-bit mode) // do nothing - }else if(data_bits == 8){ - // 8 data + 1 parity = 9 bits total -> M=01 (9-bit mode) - cr1 |= USART_CR1_M0; // M0=1, M1=0 }else{ - // Unsupported (9 data bits with parity would be 10 bits total) + // Unsupported (8 or 9 data bits with parity would be 9/10 bits total) // Fallback to 8 data + parity cr1 |= USART_CR1_M0; - lc->bDataBits = 8; + lc->bDataBits = 8; // ??? need to be tested } }else{ // Parity disabled @@ -133,10 +125,8 @@ void usart_config(uint8_t ifNo, usb_LineCoding *lc){ cr1 |= USART_CR1_M1; // M1=1, M0=0 -> 7-bit mode }else if(data_bits == 8){ // do nothing M=00 - }else if(data_bits == 9){ - cr1 |= USART_CR1_M0; // M0=1, M1=0 -> 9-bit mode }else{ - // Unsupported (5,6 bits) -> fallback to 8 bits + // Unsupported (5,6 or bits) -> fallback to 8 bits lc->bDataBits = 8; } } @@ -157,10 +147,37 @@ void usart_config(uint8_t ifNo, usb_LineCoding *lc){ // Write CR2 (stop bits) U->CR2 = cr2; - // Enable transmitter, receiver, and RX interrupt (optional) - cr1 |= USART_CR1_TE | USART_CR1_RE | USART_CR1_UE | USART_CR1_RXNEIE; - U->CR1 = cr1; + // Enable transmitter, receiver, and interrupts (optional) + cr1 |= USART_CR1_RE | USART_CR1_UE; + if(cfg->DEport){ + RX485(cfg->DEport, cfg->DEpin); + cr1 |= USART_CR1_TCIE; + }else cr1 |= USART_CR1_TE; + // ----- DMA ----- + if(cfg->dma_controller){ // DMA-driven + volatile DMA_Channel_TypeDef *T = cfg->dma_tx_channel, *R = cfg->dma_rx_channel; + // Tx DMA + T->CCR = 0; + T->CPAR = (uint32_t) &U->TDR; + T->CCR = DMA_CCR_MINC | DMA_CCR_DIR; // | DMA_CCR_TCIE; + // Rx DMA + R->CCR = 0; + 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 + // enable U[S]ART DMA + U->CR3 = USART_CR3_DMAT | USART_CR3_DMAR; + cr1 |= USART_CR1_IDLEIE; // enable idle interrupt to force small portions of data into ringbuffer + }else{ + cr1 |= USART_CR1_RXNEIE | USART_CR1_TXEIE; // interrupt-driven + inbufidx[ifNo] = 0; + outbufidx[ifNo] = 0; + NVIC_EnableIRQ(cfg->IRQn); + } + + U->CR1 = cr1; // Wait for the idle frame to complete (optional) uint32_t tmout = 16000000; while(!(U->ISR & USART_ISR_TC)){ @@ -169,46 +186,143 @@ void usart_config(uint8_t ifNo, usb_LineCoding *lc){ U->ICR = 0xFFFFFFFF; // Clear flags again } -// TODO: leave only uart5_exti35_isr, other - over DMA -// UART5 [IF5] Tx - over finite-state machine +/** + * @brief usart_stop - turn off U[S]ART for given interface + * @param ifNo - interface number + */ +void usart_stop(uint8_t ifNo){ + if(ifNo >= USARTSNO || UC[ifNo].instance == NULL) return; + const USART_Config *cfg = &UC[ifNo]; + cfg->instance->CR1 = 0; + if(cfg->DEport) RX485(cfg->DEport, cfg->DEpin); + if(cfg->dma_controller){ + cfg->dma_tx_channel->CCR = 0; + cfg->dma_rx_channel->CCR = 0; + }else{ + NVIC_DisableIRQ(cfg->IRQn); + } +} -/* -DMA1 channels: -- Ch2: USART3_Tx [IF1] -- Ch3: USART3_Rx [IF1] -- Ch4: USART1_Tx [IF2] -- Ch5: USART1_Rx [IF2] -- Ch6: USART2_Rx [IF3] -- Ch7: USART2_Tx [IF3] +/** + * @brief usarts_process - send/receive processing + * Try to send data from output ringbuffer, check input DMA buffer and full input ringbuffer + */ +void usarts_process(){ + for(int i = 0; i < USARTSNO; ++i){ // index by interfaces number!!! + const USART_Config *cfg = &UC[i]; + if(!(cfg->instance->CR1 & USART_CR1_UE)) continue; // USART disabled + if(cfg->dma_controller){ // DMA-driven + // Input data + 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 + if(USB_send(i, inbuffers[i], l)){ + // restart DMA only in case of succesfull sent or if failed, but have ability of buffer overfull + R->CMAR = (uint32_t) inbuffers[i]; + R->CNDTR = DMARXBUFSZ; + need2send[i] = 0; + if(cfg->DEport) TX485(cfg->DEport, cfg->DEpin); + } + } + R->CCR |= DMA_CCR_EN; // re-enable DMA + } + // Output data + if(cfg->dma_controller->ISR & cfg->RTCflag){ // ready to send new data + int got = USB_receive(i, outbuffers[i], DMATXBUFSZ); + if(got > 0){ // send next data portion + volatile DMA_Channel_TypeDef *T = cfg->dma_tx_channel; + cfg->dma_controller->IFCR = cfg->RTCflag; // now we can clear TC flag (TC and CTC are the same) + T->CCR &= ~DMA_CCR_EN; + T->CMAR = (uint32_t) outbuffers[i]; + T->CNDTR = got; + if(cfg->DEport){ // switch to Tx + TX485(cfg->DEport, cfg->DEpin); + cfg->instance->CR1 &= ~USART_CR1_RE; + cfg->instance->CR1 |= USART_CR1_TE; + } + T->CCR |= DMA_CCR_EN; // start new transmission + } + } + }else{ // interrupt-driven + // Input data + volatile USART_TypeDef *U = cfg->instance; + U->CR1 &= ~USART_CR1_RXNEIE; // temporarily disable interrupt + register int l = inbufidx[i]; + if(DMARXBUFSZ - l > DMARXBUFSZ/2 || need2send[i]){ + if(l && USB_send(i, inbuffers[i], l)){ + need2send[i] = 0; + inbufidx[i] = 0; + } + } + U->CR1 |= USART_CR1_RXNEIE; // restore irq reaction + // output data + U->CR1 &= ~USART_CR1_TXEIE; + if(outbuflen[i] == 0){ + int got = USB_receive(i, outbuffers[i], DMATXBUFSZ); + if(got > 0){ + if(cfg->DEport){ // switch to Tx + TX485(cfg->DEport, cfg->DEpin); + U->CR1 &= ~USART_CR1_RE; + U->CR1 |= USART_CR1_TE; + } + outbufidx[i] = 1; // continue from next symbol + outbuflen[i] = got; + U->TDR = outbuffers[i][0]; // start transmission + } + } + U->CR1 |= USART_CR1_TXEIE; + } + } +} -DMA2 channels: -- Ch3: UART4_Rx [IF4] -- Ch5: UART4_Tx [IF4] -*/ - -static void usart_isr(int usartno, volatile USART_TypeDef *U){ - int iface = usartByIfNo[usartno]; // interface - if(U->ISR & USART_ISR_RXNE){ - USB_putbyte(iface, U->RDR); +/** + * @brief usart_isr - U[S]ART interrupt: IDLE (for DMA-driven) or + * @param ifno - interface index + */ +static void usart_isr(uint8_t ifno){ + const USART_Config *cfg = &UC[ifno]; + volatile USART_TypeDef *U = cfg->instance; + if(U->ISR & USART_ISR_RXNE){ // got new byte + if(inbufidx[ifno] == DMARXBUFSZ) (void) U->RDR; // throw away data: buffer overfull + else inbuffers[ifno][ inbufidx[ifno]++ ] = U->RDR; // put new byte into buffer + } + if(U->ISR & USART_ISR_IDLE){ // try to send collected data + need2send[ifno] = 1; // seems like data portion is over - try to send it + U->ICR = USART_ICR_IDLECF; + } + if(U->ISR & USART_ISR_TXE){ // send next byte if need + if(outbuflen[ifno] < outbufidx[ifno]){ + U->TDR = outbuffers[ifno][ outbufidx[ifno]++ ]; + } + } + if(U->ISR & USART_ISR_TC){ // switch RS-485 to Rx after transmission complete + if(cfg->DEport){ + RX485(cfg->DEport, cfg->DEpin); + U->CR1 &= ~USART_CR1_TE; + U->CR1 |= USART_CR1_RE; + } + U->ICR = USART_ICR_TCCF; } } void usart1_exti25_isr(){ - usart_isr(1, USART1); + usart_isr(1); } void usart2_exti26_isr(){ - usart_isr(2, USART2); + usart_isr(2); } void usart3_exti28_isr(){ - usart_isr(3, USART3); + usart_isr(0); } void uart4_exti34_isr(){ - usart_isr(4, UART4); + usart_isr(3); } void uart5_exti35_isr(){ - usart_isr(5, UART5); + usart_isr(4); } diff --git a/F3:F303/InterfaceBoard/usart.h b/F3:F303/InterfaceBoard/usart.h index ff95911..4ba8902 100644 --- a/F3:F303/InterfaceBoard/usart.h +++ b/F3:F303/InterfaceBoard/usart.h @@ -21,6 +21,17 @@ #include "hardware.h" #include "usb_dev.h" -void usarts_setup(); +// DMA linear buffers for Rx/Tx +#define DMARXBUFSZ 128 +#define DMATXBUFSZ 128 +// ringbuffers for collected data +#define USARTRXRBSZ 256 +#define USARTTXRBSZ 256 + void usart_config(uint8_t ifNo, usb_LineCoding *lc); -void usart_sendn(uint8_t ifNo, const uint8_t *str, int L); +void usart_stop(uint8_t ifNo); + +void usarts_process(); + +int usart_send(uint8_t ifNo, const uint8_t *data, int len); +int usart_receive(uint8_t ifNo, uint8_t *data, int len); diff --git a/F3:F303/InterfaceBoard/usb_dev.c b/F3:F303/InterfaceBoard/usb_dev.c index 2828136..9ea8d29 100644 --- a/F3:F303/InterfaceBoard/usb_dev.c +++ b/F3:F303/InterfaceBoard/usb_dev.c @@ -138,12 +138,14 @@ void clstate_handler(uint8_t ifno, uint16_t val){ CDCready[ifno] = val; // CONTROL_DTR | CONTROL_RTS -> interface connected; 0 -> disconnected lastdsz[ifno] = -1; if(val) clearbufs(ifno); + else usart_stop(ifno); // turn of USART (if it is @ this interface) } // SEND_BREAK - disconnect interface and clear its buffers // this is a fake handler as classic CDC ACM never receives this void break_handler(uint8_t ifno){ CDCready[ifno] = 0; + usart_stop(ifno); // turn of USART (if it is @ this interface) } // USB is configured: setup endpoints diff --git a/F3:F303/InterfaceBoard/usb_dev.h b/F3:F303/InterfaceBoard/usb_dev.h index 0f9c458..eb9be56 100644 --- a/F3:F303/InterfaceBoard/usb_dev.h +++ b/F3:F303/InterfaceBoard/usb_dev.h @@ -42,7 +42,7 @@ void clstate_handler(uint8_t ifno, uint16_t val); void linecoding_handler(uint8_t ifno, usb_LineCoding *lc); // as ugly CDC have no BREAK after disconnected client in non-canonical mode, we should use timeout - near 2s -#define DISCONN_TMOUT (2000) +#define DISCONN_TMOUT (2) // sizes of ringbuffers for outgoing and incoming data #define RBOUTSZ (256) diff --git a/F3:F303/InterfaceBoard/version.inc b/F3:F303/InterfaceBoard/version.inc index 39367ae..24a9816 100644 --- a/F3:F303/InterfaceBoard/version.inc +++ b/F3:F303/InterfaceBoard/version.inc @@ -1,2 +1,2 @@ -#define BUILD_NUMBER "41" -#define BUILD_DATE "2026-02-12" +#define BUILD_NUMBER "65" +#define BUILD_DATE "2026-02-15"