diff --git a/F3:F303/InterfaceBoard/Readme.md b/F3:F303/InterfaceBoard/Readme.md index 324814a..4f8df35 100644 --- a/F3:F303/InterfaceBoard/Readme.md +++ b/F3:F303/InterfaceBoard/Readme.md @@ -38,7 +38,7 @@ Inner USB interfaces (IFx): | 12 | (VREF-) | | | | | 13 | (VREF+) | | | | | 14 | PA0 | NC | | | -| 15 | PA1 | USART2 DE | AF7 | RS-485 (3) DE | +| 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 | | 18 | PF4 | NC | | | @@ -58,7 +58,7 @@ Inner USB interfaces (IFx): | 32 | (VDD) | | | | | 33 | PB12 | NC | | | | 34 | PB13 | NC | | | -| 35 | PB14 | USART3 DE | AF7 | RS-485 (1) DE | +| 35 | PB14 | USART3 DE | AF7 or PP | RS-485 (1) DE | | 36 | PB15 | | | | | 37 | PC6 | NC | | | | 38 | PC7 | NC | | | @@ -91,5 +91,33 @@ Inner USB interfaces (IFx): ### Some comments. +Interfaces: + +- IF1: RS-485 over USART3. +- IF2: RS-485 over USART1. +- IF3: RS-485 over USART2. +- IF4: RS-232 over UART4. +- IF5: RS-232 or RS-422 (by jumpers "IF5" and "SSI") over UART5. +- IF6: CAN bus. +- IF7: SSI (inaccessible when RS-422 selected) or interface for device configuration (if jumper "Config mode" is opened when device started"). + + +DMA1 channels: + +- Ch2: USART3_Tx +- Ch3: USART3_Rx +- Ch4: USART1_Tx +- Ch5: USART1_Rx +- Ch6: USART2_Rx +- Ch7: USART2_Tx + +DMA2 channels: + +- Ch3: UART4_Rx +- Ch5: UART4_Tx + +UART5 have no DMA channels, so used in interrupts. + + ### Sorted by ports (with AF numbers). diff --git a/F3:F303/InterfaceBoard/debug.h b/F3:F303/InterfaceBoard/debug.h new file mode 100644 index 0000000..76dbace --- /dev/null +++ b/F3:F303/InterfaceBoard/debug.h @@ -0,0 +1,37 @@ +/* + * This file is part of the multiiface 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 "usb_dev.h" + +// debugging messages on config interface + +#ifdef EBUG +#define DBG(str) do{USB_sendstr(ICFG, __FILE__ " (L" STR(__LINE__) "): " str); newline(ICFG);}while(0) +#define DBGmesg(str) do{USB_sendstr(ICFG, str);}while(0) +#define DBGmesgn(str,n) do{USB_send(ICFG, str, n);}while(0) +#define DBGnl() newline(ICFG) +#else +#define DBG(str) +#define DBGmesg(str) +#define DBGmesgn(str,n) +#define DBGnl() +#endif + + diff --git a/F3:F303/InterfaceBoard/hardware.c b/F3:F303/InterfaceBoard/hardware.c index c3634a9..602b3ea 100644 --- a/F3:F303/InterfaceBoard/hardware.c +++ b/F3:F303/InterfaceBoard/hardware.c @@ -21,7 +21,10 @@ uint8_t Config_mode = 0; static inline void gpio_setup(){ - RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN; // for USART and LEDs + RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | 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 GPIOA->AFR[1] = AFRf(14, 11) | AFRf(14, 12); diff --git a/F3:F303/InterfaceBoard/multiiface.creator.user b/F3:F303/InterfaceBoard/multiiface.creator.user index f8feebc..2b96e51 100644 --- a/F3:F303/InterfaceBoard/multiiface.creator.user +++ b/F3:F303/InterfaceBoard/multiiface.creator.user @@ -1,6 +1,6 @@ - + EnvironmentId diff --git a/F3:F303/InterfaceBoard/multiiface.files b/F3:F303/InterfaceBoard/multiiface.files index 6203f9c..c99c82d 100644 --- a/F3:F303/InterfaceBoard/multiiface.files +++ b/F3:F303/InterfaceBoard/multiiface.files @@ -1,3 +1,4 @@ +debug.h flash.c flash.h hardware.c @@ -9,6 +10,8 @@ ringbuffer.c ringbuffer.h strfunc.c strfunc.h +usart.c +usart.h usb.c usb.h usb_descr.c diff --git a/F3:F303/InterfaceBoard/usart.c b/F3:F303/InterfaceBoard/usart.c new file mode 100644 index 0000000..298050c --- /dev/null +++ b/F3:F303/InterfaceBoard/usart.c @@ -0,0 +1,214 @@ +/* + * This file is part of the MultiInterface project. + * Copyright 2022 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 "debug.h" +#include "hardware.h" +#include "strfunc.h" +#include "usart.h" +#include "usb_descr.h" // InterfacesAmount, IFx + +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 +}; + +#if 0 +volatile int linerdy = 0, // received data ready + dlen = 0, // length of data (including '\n') in current buffer + bufovr = 0; // input buffer overfull + + +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 + * @param ifNo - interface index [0..6] + * @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]; + // 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 / lc->dwDTERate; + lc->dwDTERate = peripheral_clock / U->BRR; + + // ----- Data bits & Parity ----- + uint32_t cr1 = 0; + uint32_t data_bits = lc->bDataBits; + uint32_t parity_type = lc->bParityType; + // Parity enable/disable and type + if(parity_type != USB_CDC_NO_PARITY){ + cr1 |= USART_CR1_PCE; // Parity enabled + if(parity_type == USB_CDC_ODD_PARITY){ + cr1 |= USART_CR1_PS; // Odd parity + // MARK and SPACE parity are not natively supported; treat as even or ignore. + }else if(parity_type == USB_CDC_MARK_PARITY || parity_type == USB_CDC_SPACE_PARITY){ + lc->bParityType = USB_CDC_EVEN_PARITY; + } + } + + // Word length (M bits) depends on data bits and parity + // M[1:0] encoding: 00 = 8 bits, 01 = 9 bits, 10 = 7 bits + if(cr1 & USART_CR1_PCE){ + // Parity enabled -> total frame length = data bits + 1 parity bit + if(data_bits == 6){ + // 6 data + 1 parity = 7 bits total -> M=10 (7-bit mode) + cr1 |= USART_CR1_M1; + }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) + // Fallback to 8 data + parity + cr1 |= USART_CR1_M0; + lc->bDataBits = 8; + } + }else{ + // Parity disabled + if(data_bits == 7){ + 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 + lc->bDataBits = 8; + } + } + + // ----- Stop bits ----- + uint32_t cr2 = 0; + switch(lc->bCharFormat){ // usb_LineCoding have no 0.5 stop bits field + case USB_CDC_1_5_STOP_BITS: + cr2 |= USART_CR2_STOP_0 | USART_CR2_STOP_1; // 1.5 stop bits -> CR2=11 + break; + case USB_CDC_2_STOP_BITS: + cr2 |= USART_CR2_STOP_1; // 2 stop bits -> CR2=10 + break; + default: + // do nothing: default to 1 stop bit -> CR2=00 + break; + } + + // 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; + + // Wait for the idle frame to complete (optional) + uint32_t tmout = 16000000; + while(!(U->ISR & USART_ISR_TC)){ + if (--tmout == 0) break; + } + U->ICR = 0xFFFFFFFF; // Clear flags again +} + +// TODO: leave only uart5_exti35_isr, other - over DMA +// UART5 [IF5] Tx - over finite-state machine + +/* +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] + +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); + } +} + +void usart1_exti25_isr(){ + usart_isr(1, USART1); +} + +void usart2_exti26_isr(){ + usart_isr(2, USART2); +} + +void usart3_exti28_isr(){ + usart_isr(3, USART3); +} + +void uart4_exti34_isr(){ + usart_isr(4, UART4); +} + +void uart5_exti35_isr(){ + usart_isr(5, UART5); +} diff --git a/F3:F303/InterfaceBoard/usart.h b/F3:F303/InterfaceBoard/usart.h new file mode 100644 index 0000000..ff95911 --- /dev/null +++ b/F3:F303/InterfaceBoard/usart.h @@ -0,0 +1,26 @@ +/* + * This file is part of the multiiface 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 "hardware.h" +#include "usb_dev.h" + +void usarts_setup(); +void usart_config(uint8_t ifNo, usb_LineCoding *lc); +void usart_sendn(uint8_t ifNo, const uint8_t *str, int L); diff --git a/F3:F303/InterfaceBoard/usb_descr.h b/F3:F303/InterfaceBoard/usb_descr.h index 90bc928..34e7484 100644 --- a/F3:F303/InterfaceBoard/usb_descr.h +++ b/F3:F303/InterfaceBoard/usb_descr.h @@ -33,7 +33,7 @@ // amount of interfaces #define InterfacesAmount 7 -// index of interface (ep number minus one) +// index of interface (ep number minus one): IF1..IF7 #define ISerial0 0 #define ISerial1 1 #define ISerial2 2 diff --git a/F3:F303/InterfaceBoard/usb_dev.c b/F3:F303/InterfaceBoard/usb_dev.c index 7256b33..2828136 100644 --- a/F3:F303/InterfaceBoard/usb_dev.c +++ b/F3:F303/InterfaceBoard/usb_dev.c @@ -19,6 +19,7 @@ #include "hardware.h" #include "ringbuffer.h" +#include "usart.h" #include "usb_descr.h" #include "usb_dev.h" @@ -59,20 +60,13 @@ static volatile ringbuffer rbin[InterfacesAmount] = {IBUF(0), IBUF(1), IBUF(2), static volatile int lastdsz[InterfacesAmount] = {-1, -1, -1, -1, -1, -1, -1}; static void chkin(uint8_t ifno){ - static int ovrflctr = 0; // "antistall" counter if(bufovrfl[ifno]) return; // allow user to know that previous buffer was overflowed and cleared if(!rcvbuflen[ifno]) return; int w = RB_write((ringbuffer*)&rbin[ifno], (uint8_t*)rcvbuf[ifno], rcvbuflen[ifno]); - if(w < 0){ // buffer busy + if(w < 0){ return; - }else if(w == 0){ // no enough space or (WTF) incoming string larger than buffer size - if(rcvbuflen[ifno] > rbin[ifno].length || ++ovrflctr > 9999){ - bufovrfl[ifno] = 1; // real overflow in case if ringbuffer's size less than USB buffer - ovrflctr = 0; - }else{ - return; // not enough space - } } + if(w != rcvbuflen[ifno]) bufovrfl[ifno] = 1; rcvbuflen[ifno] = 0; uint16_t status = KEEP_DTOG(USB->EPnR[1+ifno]); // don't change DTOG USB->EPnR[1+ifno] = (status & ~(USB_EPnR_STAT_TX|USB_EPnR_CTR_RX)) ^ USB_EPnR_STAT_RX; // prepare to get next data portion @@ -103,7 +97,7 @@ static void send_next(uint8_t ifno){ // data IN/OUT handler static void rxtx_handler(){ uint8_t epno = (USB->ISTR & USB_ISTR_EPID), ifno = epno - 1; - if(epno > InterfacesAmount){ + if(ifno > InterfacesAmount-1){ return; } uint16_t epstatus = KEEP_DTOG(USB->EPnR[epno]); @@ -123,8 +117,8 @@ static void rxtx_handler(){ // SET_LINE_CODING void linecoding_handler(uint8_t ifno, usb_LineCoding *lc){ + usart_config(ifno, lc); // lc would be real speed! lineCoding[ifno] = *lc; - // TODO: set up interfaces } // clear IN/OUT buffers on connection @@ -137,7 +131,6 @@ static void clearbufs(uint8_t ifno){ while(Tms - T0 < 10){ if(1 == RB_clearbuf((ringbuffer*)&rbout[ifno])) break; } - rcvbuflen[ifno] = 0; } // SET_CONTROL_LINE_STATE @@ -208,7 +201,7 @@ int USB_sendall(uint8_t ifno){ uint32_t T0 = Tms; while(lastdsz[ifno] > 0){ if(Tms - T0 > DISCONN_TMOUT){ - break_handler(ifno); + //break_handler(ifno); return FALSE; } if(!CDCready[ifno]) return FALSE; @@ -223,7 +216,7 @@ int USB_send(uint8_t ifno, const uint8_t *buf, int len){ uint32_t T0 = Tms; while(len){ if(Tms - T0 > DISCONN_TMOUT){ - break_handler(ifno); + //break_handler(ifno); return FALSE; } if(!CDCready[ifno]) return FALSE; @@ -254,7 +247,7 @@ int USB_putbyte(uint8_t ifno, uint8_t byte){ uint32_t T0 = Tms; while((l = RB_write((ringbuffer*)&rbout[ifno], &byte, 1)) != 1){ if(Tms - T0 > DISCONN_TMOUT){ - break_handler(ifno); + //break_handler(ifno); return FALSE; } if(!CDCready[ifno]) return FALSE;