all descriptors read - OK, interfaces work - bad (only first two works - why? need to debug)

This commit is contained in:
Edward Emelianov
2026-02-11 23:34:14 +03:00
parent 3a903d7d8c
commit e17058b2de
16 changed files with 565 additions and 259 deletions

View File

@@ -1,15 +1,95 @@
7 interfaces over USB Multiport board
====================== ====================================
Seven isolated interfaces:
- 1 CAN - 1 CAN
- 3 RS-485 - 3 RS-485
- 2 or 1 RS-232 - 2 or 1 RS-232
- 1 SSI or 1 RS-422 (in this case 1 RS-232) - 1 SSI or 1 RS-422 (in this case only one RS-232)
Inner USB interfaces (IFx): Inner USB interfaces (IFx):
1..3 - RS-485 (1..3)
4 - RS-232 (1) 1. RS-485 (1)
5 - RS-232 (2) or RS-485 2. RS-485 (2)
6 - CAN 3. RS-485 (3)
7 - SSI (over SPI) or configuration interface (if "Config mode" jumper shortened) 4. RS-232 (1)
5. RS-232 (2) or RS-422 (by jumpers)
6. CAN
7. SSI (over SPI, by jumpers) or configuration interface (if "Config mode" jumper opened)
# Pinout
### Sorted by pin number
| Pin # | Pin name | function | settings | comment |
|---------|-------------|-------------|---------------|---------------------|
| 1 | (VBAT) | | | |
| 2 | PC13 | NC | | |
| 3 | PC14 | NC | | |
| 4 | PC15 | NC | | |
| 5 | (OSC IN) | | | |
| 6 | (OSC OUT) | | | |
| 7 | (NRST) | | | |
| 8 | PC0 | NC | | |
| 9 | PC1 | NC | | |
| 10 | PC2 | NC | | |
| 11 | PC3 | NC | | |
| 12 | (VREF-) | | | |
| 13 | (VREF+) | | | |
| 14 | PA0 | NC | | |
| 15 | PA1 | USART2 DE | AF7 | 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 | | |
| 19 | (VDD) | | | |
| 20 | PA4 | NC | | |
| 21 | PA5 | SPI1 SCK | AF5 | SSI CLK |
| 22 | PA6 | SPI1 MISO | AF5 | SSI DAT |
| 23 | PA7 | NC | | |
| 24 | PC4 | USART1 TX | AF7 | RS-485 (2) Tx |
| 25 | PC5 | USART1 RX | AF7 | RS-485 (2) Rx |
| 26 | PB0 | (USART1 DE) | PP OUT | RS-485 (2) DE |
| 27 | PB1 | NC | | |
| 28 | PB2 | NC | | |
| 29 | PB10 | USART3 TX | AF7 | RS-485 (1) Tx |
| 30 | PB11 | USART3 RX | AF7 | RS-485 (1) Rx |
| 31 | (VSS) | | | |
| 32 | (VDD) | | | |
| 33 | PB12 | NC | | |
| 34 | PB13 | NC | | |
| 35 | PB14 | USART3 DE | AF7 | RS-485 (1) DE |
| 36 | PB15 | | | |
| 37 | PC6 | NC | | |
| 38 | PC7 | NC | | |
| 39 | PC8 | NC | | |
| 40 | PC9 | 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 | |
| 47 | (VSS) | | | |
| 48 | (VDD) | | | |
| 49 | PA14 | SWCLK | AF0 | |
| 50 | PA15 | 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 |
| 54 | PD2 | UART5 RX | AF5 | RS-232(2) / 485 Rx |
| 55 | PB3 | NC | | |
| 56 | PB4 | NC | | |
| 57 | PB5 | NC | | |
| 58 | PB6 | NC | | |
| 59 | PB7 | NC | | |
| 60 | (BOOT0) | | | |
| 61 | PB8 | CAN RX | AF9 | |
| 62 | PB9 | CAN TX | AF9 | |
| 63 | (VSS) | | | |
| 64 | (VDD) | | | |
### Some comments.
### Sorted by ports (with AF numbers).

View File

@@ -32,15 +32,15 @@ const user_conf *Flash_Data = (const user_conf *)(&__varsstart);
user_conf the_conf = { user_conf the_conf = {
.userconf_sz = sizeof(user_conf), .userconf_sz = sizeof(user_conf),
.iInterface = { .iInterface = {
[ISerial0] = u"usbserial0", [ISerial0] = u"usbserial0.",
[ISerial1] = u"usbserial1", [ISerial1] = u"usbserial1.",
[ISerial2] = u"usbserial2", [ISerial2] = u"usbserial2.",
[ISerial3] = u"usbserial3", [ISerial3] = u"usbserial3.",
[ISerial4] = u"usbserial4", [ISerial4] = u"usbserial4.",
[ISPI] = u"usbSPI", [ISPI] = u"usbSPI",
[ICAN] = u"usbCAN" [ICAN] = u"usbCAN"
}, },
.iIlengths = {20,20,20,20,20,12,12}, .iIlengths = {22,22,22,22,22,12,12},
}; };
int currentconfidx = -1; // index of current configuration int currentconfidx = -1; // index of current configuration
@@ -117,12 +117,12 @@ static int write2flash(const void *start, const void *wrdata, uint32_t stor_size
*(volatile uint16_t*)(address + i) = data[i]; *(volatile uint16_t*)(address + i) = data[i];
while(FLASH->SR & FLASH_SR_BSY) IWDG->KR = IWDG_REFRESH; while(FLASH->SR & FLASH_SR_BSY) IWDG->KR = IWDG_REFRESH;
if(*(volatile uint16_t*)(address + i) != data[i]){ if(*(volatile uint16_t*)(address + i) != data[i]){
USB_sendstr("DON'T MATCH!\n"); CFGWR("DON'T MATCH!\n");
ret = 1; ret = 1;
break; break;
} }
if(FLASH->SR & FLASH_SR_PGERR){ if(FLASH->SR & FLASH_SR_PGERR){
USB_sendstr("Prog err\n"); CFGWR("Prog err\n");
ret = 1; // program error - meet not 0xffff ret = 1; // program error - meet not 0xffff
break; break;
} }
@@ -136,7 +136,7 @@ static int write2flash(const void *start, const void *wrdata, uint32_t stor_size
static int erase_pageN(int N){ static int erase_pageN(int N){
int ret = 0; int ret = 0;
#ifdef EBUG #ifdef EBUG
//CMDWR("Erase page #"); CMDWR(u2str(N)); CMDn(); CFGWR("Erase page #"); CFGWRn(u2str(N));
#endif #endif
FLASH->AR = (uint32_t)Flash_Data + N*FLASH_blocksize; FLASH->AR = (uint32_t)Flash_Data + N*FLASH_blocksize;
FLASH->CR |= FLASH_CR_STRT; FLASH->CR |= FLASH_CR_STRT;
@@ -158,16 +158,13 @@ int erase_storage(int npage){
flsz -= (uint32_t)Flash_Data - FLASH_BASE; flsz -= (uint32_t)Flash_Data - FLASH_BASE;
} }
end = flsz / FLASH_blocksize; end = flsz / FLASH_blocksize;
/*
#ifdef EBUG #ifdef EBUG
CMDWR("FLASH_SIZE="); CMDWR(u2str(FLASH_SIZE)); CFGWR("FLASH_SIZE="); CFGWR(u2str(FLASH_SIZE));
CMDWR("\nflsz="); CMDWR(u2str(flsz)); CFGWR("\nflsz="); CFGWR(u2str(flsz));
CMDWR("\nend="); CMDWR(u2str(end)); CFGWR("\nend="); CFGWR(u2str(end));
CMDWR("\ncurrentconfidx="); CMDWR(u2str(currentconfidx)); CFGWR("\ncurrentconfidx="); CFGWR(u2str(currentconfidx));
CMDWR("\nmaxCnum="); CMDWR(u2str(maxCnum)); CFGWR("\nmaxCnum="); CFGWRn(u2str(maxCnum));
CMDn();
#endif #endif
*/
if(end == 0 || end >= FLASH_SIZE) return 1; if(end == 0 || end >= FLASH_SIZE) return 1;
if(npage > -1){ // erase only one page if(npage > -1){ // erase only one page
if((uint32_t)npage >= end) return 1; if((uint32_t)npage >= end) return 1;

View File

@@ -34,8 +34,9 @@
*/ */
typedef struct __attribute__((packed, aligned(4))){ typedef struct __attribute__((packed, aligned(4))){
uint16_t userconf_sz; // "magick number" uint16_t userconf_sz; // "magick number"
// we store iInterface "as is"
uint16_t iInterface[InterfacesAmount][MAX_IINTERFACE_SZ]; // hryunikod! uint16_t iInterface[InterfacesAmount][MAX_IINTERFACE_SZ]; // hryunikod!
uint8_t iIlengths[InterfacesAmount]; uint8_t iIlengths[InterfacesAmount];
} user_conf; } user_conf;
extern user_conf the_conf; extern user_conf the_conf;

View File

@@ -1,11 +1,36 @@
/*
* This file is part of the multiiface project.
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "hardware.h" #include "hardware.h"
uint8_t Config_mode = 0;
static inline void gpio_setup(){ 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; // for USART and LEDs
for(int i = 0; i < 10000; ++i) nop(); for(int i = 0; i < 10000; ++i) nop();
// USB - alternate function 14 @ pins PA11/PA12; SWD - AF0 @PA13/14 // USB - alternate function 14 @ pins PA11/PA12; SWD - AF0 @PA13/14
GPIOA->AFR[1] = AFRf(14, 11) | AFRf(14, 12); GPIOA->AFR[1] = AFRf(14, 11) | AFRf(14, 12);
GPIOA->MODER = MODER_O(10) | MODER_AF(11) | MODER_AF(12) | MODER_AF(13) | MODER_AF(14); // 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->PUPDR = PUPD_PU(9);
for(int i = 0; i < 10000; ++i) nop();
if(CFG_ON()) Config_mode = 1;
} }
void hw_setup(){ void hw_setup(){

View File

@@ -1,3 +1,21 @@
/*
* This file is part of the multiiface project.
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once #pragma once
#include <stm32f3.h> #include <stm32f3.h>
@@ -7,7 +25,12 @@
#define USBPU_ON() pin_clear(USBPU_port, USBPU_pin) #define USBPU_ON() pin_clear(USBPU_port, USBPU_pin)
#define USBPU_OFF() pin_set(USBPU_port, USBPU_pin) #define USBPU_OFF() pin_set(USBPU_port, USBPU_pin)
#define CFG_port GPIOA
#define CFG_pin (1<<9)
#define CFG_ON() (CFG_port->IDR & CFG_pin)
extern volatile uint32_t Tms; extern volatile uint32_t Tms;
extern uint8_t Config_mode;
void hw_setup(); void hw_setup();

View File

@@ -43,15 +43,20 @@ int main(void){
//uint32_t ctr = Tms; //uint32_t ctr = Tms;
USBPU_ON(); USBPU_ON();
while(1){ while(1){
/*if(Tms - ctr > 499){ // Put here code working WITOUT USB connected
ctr = Tms;
}*/
if(!usbON) continue; if(!usbON) continue;
int l = USB_receivestr(inbuff, MAXSTRLEN); for(int i = 0; i < 6; ++i){ // just echo for first time
if(l < 0) USB_sendstr("ERROR: USB buffer overflow or string was too long\n"); int l = USB_receive(i, (uint8_t*)inbuff, MAXSTRLEN);
else if(l){ if(l) USB_send(i, (uint8_t*)inbuff, l);
const char *ans = parse_cmd(inbuff); }
if(ans) USB_sendstr(ans); // and here is code what should run when USB connected
if(Config_mode){
int l = USB_receivestr(ICFG, inbuff, MAXSTRLEN);
if(l < 0) CFGWR("ERROR: USB buffer overflow or string was too long\n");
else if(l){
const char *ans = parse_cmd(inbuff);
if(ans) CFGWR(ans);
}
} }
} }
} }

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject> <!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 18.0.1, 2025-12-28T20:25:46. --> <!-- Written by QtCreator 18.0.2, 2026-02-11T23:33:00. -->
<qtcreator> <qtcreator>
<data> <data>
<variable>EnvironmentId</variable> <variable>EnvironmentId</variable>

View File

@@ -1,3 +1,21 @@
/*
* This file is part of the multiiface project.
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stm32f3.h> #include <stm32f3.h>
#include <string.h> #include <string.h>
@@ -6,6 +24,8 @@
#include "usb_dev.h" #include "usb_dev.h"
#include "version.inc" #include "version.inc"
// all functions of this file could be run only in configuration mode
static const char *const sOKn = "OK\n", *const sERRn = "ERR\n"; static const char *const sOKn = "OK\n", *const sERRn = "ERR\n";
//#define LOCBUFFSZ (32) //#define LOCBUFFSZ (32)
@@ -25,19 +45,18 @@ const char *helpstring =
; ;
static void dumpflash(){ static void dumpflash(){
USB_sendstr("userconf_sz="); USB_sendstr(u2str(the_conf.userconf_sz)); CFGWR("userconf_sz="); CFGWR(u2str(the_conf.userconf_sz));
USB_sendstr("\ncurrentconfidx="); USB_sendstr(i2str(currentconfidx)); CFGWR("\ncurrentconfidx="); CFGWRn(i2str(currentconfidx));
USB_putbyte('\n');
for(int i = 0; i < InterfacesAmount; ++i){ for(int i = 0; i < InterfacesAmount; ++i){
USB_sendstr("interface"); USB_putbyte('0' + i); CFGWR("interface"); USB_putbyte(ICFG, '0' + i);
USB_putbyte('='); USB_putbyte(ICFG, '=');
int l = the_conf.iIlengths[i] / 2; int l = the_conf.iIlengths[i] / 2;
char *ptr = (char*) the_conf.iInterface[i]; char *ptr = (char*) the_conf.iInterface[i];
for(int j = 0; j < l; ++j){ for(int j = 0; j < l; ++j){
USB_putbyte(*ptr); USB_putbyte(ICFG, *ptr);
ptr += 2; ptr += 2;
} }
USB_putbyte('\n'); CFGn();
} }
} }
@@ -58,10 +77,10 @@ static void setiface(const char *str){
*ptr++ = *nxt++; *ptr++ = *nxt++;
*ptr++ = 0; *ptr++ = 0;
} }
USB_sendstr(sOKn); CFGWR(sOKn);
return; return;
err: err:
USB_sendstr(sERRn); CFGWR(sERRn);
} }
static const char* erpg(const char *str){ static const char* erpg(const char *str){
@@ -106,12 +125,11 @@ const char *parse_cmd(const char *buf){
if(store_userconf()) return sERRn; if(store_userconf()) return sERRn;
return sOKn; return sOKn;
case 'T': case 'T':
USB_sendstr("T="); CFGWR("T=");
USB_sendstr(u2str(Tms)); CFGWRn(u2str(Tms));
newline();
break; break;
default: // help default: // help
USB_sendstr(helpstring); CFGWR(helpstring);
break; break;
} }
return NULL; return NULL;

View File

@@ -1,3 +1,21 @@
/*
* This file is part of the multiiface project.
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once #pragma once
char *parse_cmd(char *buf); char *parse_cmd(char *buf);

View File

@@ -15,6 +15,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <string.h> // memcpy
#include "flash.h" // descriptors
#include "hardware.h" // `config` flag
#include "usb_descr.h" #include "usb_descr.h"
// low/high for uint16_t // low/high for uint16_t
@@ -55,7 +58,88 @@ static const uint8_t USB_DeviceQualifierDescriptor[] = {
0 // Reserved 0 // Reserved
}; };
#define wTotalLength (USB_DT_CONFIG_SIZE + (bNumInterfaces * USB_DT_INTERFACE_SIZE) + (bTotNumEndpoints * USB_DT_ENDPOINT_SIZE) + (bNumCsInterfaces * USB_DT_CS_INTERFACE_SIZE) - 1) #define wTotalLength (USB_DT_CONFIG_SIZE + (bTotNumEndpoints * 66))
/*
* _1stI - number of first interface
* IDidx - 0 or index of string descriptor for given interface
* EPx - number of virtual interrupt EP
* EP - number of real EP in/out
*/
#define USB_IAD(_1stI, IDidx, EPx, EP) \
USB_DT_IAD_SIZE, /* bLength: IAD size */ \
USB_DT_IAD, /* bDescriptorType: IAD */ \
_1stI, /* bFirstInterface */ \
2, /* bInterfaceCount */ \
2, /* bFunctionClass: CDC */ \
2, /* bFunctionSubClass */ \
0, /* bFunctionProtocol */ \
0, /* iFunction */ \
/* Interface Descriptor */ \
USB_DT_INTERFACE_SIZE, /* bLength: Interface Descriptor size */ \
USB_DT_INTERFACE, /* bDescriptorType: Interface */ \
_1stI, /* bInterfaceNumber: Number of Interface */ \
0, /* bAlternateSetting: Alternate setting */ \
1, /* bNumEndpoints: one for this */ \
USB_CLASS_COMM, /* bInterfaceClass */ \
2, /* bInterfaceSubClass: ACM */ \
0, /* bInterfaceProtocol */ \
IDidx, /* iInterface */ \
/* CDC descriptor */ \
USB_DT_CS_INTERFACE_SIZE, /* bLength */ \
USB_DT_CS_INTERFACE, /* bDescriptorType: CS_INTERFACE */ \
0, /* bDescriptorSubtype: Header Func Desc */ \
0x10, /* bcdCDC: spec release number */ \
1, /* bDataInterface */ \
USB_DT_CS_INTERFACE_SIZE, /* bLength */ \
USB_DT_CS_INTERFACE, /* bDescriptorType: CS_INTERFACE */ \
1, /* bDescriptorSubtype: Call Management Func Desc */ \
0, /* bmCapabilities: D0+D1 */ \
(_1stI+1), /* bDataInterface */ \
USB_DT_CS_INTERFACE_SIZE-1, /* bLength */ \
USB_DT_CS_INTERFACE, /* bDescriptorType: CS_INTERFACE */ \
2, /* bDescriptorSubtype: Abstract Control Management desc */ \
2, /* bmCapabilities */ \
USB_DT_CS_INTERFACE_SIZE, /* bLength */ \
USB_DT_CS_INTERFACE, /* bDescriptorType: CS_INTERFACE */ \
6, /* bDescriptorSubtype: Union func desc */ \
_1stI, /* bMasterInterface: Communication class interface */ \
(_1stI+1), /* bSlaveInterface0: Data Class Interface */ \
/* Virtual endpoint 1 Descriptor */ \
USB_DT_ENDPOINT_SIZE, /* bLength: Endpoint Descriptor size */ \
USB_DT_ENDPOINT, /* bDescriptorType: Endpoint */ \
(0x80+EPx), /* bEndpointAddress IN8 - non-existant */ \
USB_BM_ATTR_INTERRUPT, /* bmAttributes: Interrupt */ \
L16(USB_EP1BUFSZ), /* wMaxPacketSize LO */ \
H16(USB_EP1BUFSZ), /* wMaxPacketSize HI */ \
0x10, /* bInterval: 16ms */ \
/* DATA endpoint */ \
USB_DT_INTERFACE_SIZE, /* bLength: Interface Descriptor size */ \
USB_DT_INTERFACE, /* bDescriptorType: Interface */ \
(_1stI+1), /* bInterfaceNumber: Number of Interface */ \
0, /* bAlternateSetting: Alternate setting */ \
2, /* bNumEndpoints: in and out */ \
USB_CLASS_DATA, /* bInterfaceClass */ \
2, /* bInterfaceSubClass: ACM */ \
0, /* bInterfaceProtocol */ \
0, /* iInterface */ \
/*Endpoint IN1 Descriptor */ \
USB_DT_ENDPOINT_SIZE, /* bLength: Endpoint Descriptor size */ \
USB_DT_ENDPOINT, /* bDescriptorType: Endpoint */ \
(0x80+EP), /* bEndpointAddress: IN1 */ \
USB_BM_ATTR_BULK, /* bmAttributes: Bulk */ \
L16(USB_TXBUFSZ), /* wMaxPacketSize LO */ \
H16(USB_TXBUFSZ), /* wMaxPacketSize HI */ \
0, /* bInterval: ignore for Bulk transfer */ \
/* Endpoint OUT1 Descriptor */ \
USB_DT_ENDPOINT_SIZE, /* bLength: Endpoint Descriptor size */ \
USB_DT_ENDPOINT, /* bDescriptorType: Endpoint */ \
EP, /* bEndpointAddress: OUT1 */ \
USB_BM_ATTR_BULK, /* bmAttributes: Bulk */ \
L16(USB_RXBUFSZ), /* wMaxPacketSize LO */ \
H16(USB_RXBUFSZ), /* wMaxPacketSize HI */ \
0 /* bInterval: ignore for Bulk transfer */
static const uint8_t USB_ConfigDescriptor[] = { static const uint8_t USB_ConfigDescriptor[] = {
// Configuration Descriptor // Configuration Descriptor
@@ -68,89 +152,52 @@ static const uint8_t USB_ConfigDescriptor[] = {
0, // iConfiguration: Index of string descriptor describing the configuration or 0 0, // iConfiguration: Index of string descriptor describing the configuration or 0
BusPowered, // bmAttributes - Bus powered BusPowered, // bmAttributes - Bus powered
50, // MaxPower in 2mA units 50, // MaxPower in 2mA units
//--------------------------------------------------------------------------- //--IADs-------------------------------------------------------------------------
// Virtual command Interface Descriptor USB_IAD(0, iINTERFACE_DESCR1, 8, 1),
USB_DT_INTERFACE_SIZE, // bLength: Interface Descriptor size USB_IAD(2, iINTERFACE_DESCR2, 9, 2),
USB_DT_INTERFACE, // bDescriptorType: Interface USB_IAD(4, iINTERFACE_DESCR3, 10, 3),
0, // bInterfaceNumber: Number of Interface USB_IAD(6, iINTERFACE_DESCR4, 11, 4),
0, // bAlternateSetting: Alternate setting USB_IAD(8, iINTERFACE_DESCR5, 12, 5),
1, // bNumEndpoints: one for this USB_IAD(10, iINTERFACE_DESCR6, 13, 6),
USB_CLASS_COMM, // bInterfaceClass USB_IAD(12, iINTERFACE_DESCR7, 14, 7),
2, // bInterfaceSubClass: ACM
1, // bInterfaceProtocol: Common AT commands
iINTERFACE_DESCR1, // iInterface
// ---- CS Interfaces
USB_DT_CS_INTERFACE_SIZE, // bLength
USB_DT_CS_INTERFACE, // bDescriptorType: CS_INTERFACE
0, // bDescriptorSubtype: Header Func Desc
0x10, // bcdCDC: spec release number
1, // bDataInterface
USB_DT_CS_INTERFACE_SIZE, // bLength
USB_DT_CS_INTERFACE, // bDescriptorType: CS_INTERFACE
1, // bDescriptorSubtype: Call Management Func Desc
0, // bmCapabilities: D0+D1
1, // bDataInterface
USB_DT_CS_INTERFACE_SIZE-1, // bLength
USB_DT_CS_INTERFACE, // bDescriptorType: CS_INTERFACE
2, // bDescriptorSubtype: Abstract Control Management desc
2, // bmCapabilities
USB_DT_CS_INTERFACE_SIZE, // bLength
USB_DT_CS_INTERFACE, // bDescriptorType: CS_INTERFACE
6, // bDescriptorSubtype: Union func desc
0, // bMasterInterface: Communication class interface
1, // bSlaveInterface0: Data Class Interface
// Virtual endpoint 1 Descriptor
USB_DT_ENDPOINT_SIZE, // bLength: Endpoint Descriptor size
USB_DT_ENDPOINT, // bDescriptorType: Endpoint
0x8A, // bEndpointAddress IN10
USB_BM_ATTR_INTERRUPT, // bmAttributes: Interrupt
L16(USB_EP1BUFSZ), // wMaxPacketSize LO
H16(USB_EP1BUFSZ), // wMaxPacketSize HI
0x10, // bInterval: 16ms
//---------------------------------------------------------------------------
// Data interface
USB_DT_INTERFACE_SIZE, // bLength: Interface Descriptor size
USB_DT_INTERFACE, // bDescriptorType: Interface
1, // bInterfaceNumber: Number of Interface
0, // bAlternateSetting: Alternate setting
2, // bNumEndpoints: in and out
USB_CLASS_DATA, // bInterfaceClass
2, // bInterfaceSubClass: ACM
0, // bInterfaceProtocol
0, // iInterface
//Endpoint IN1 Descriptor
USB_DT_ENDPOINT_SIZE, // bLength: Endpoint Descriptor size
USB_DT_ENDPOINT, // bDescriptorType: Endpoint
0x81, // bEndpointAddress: IN1
USB_BM_ATTR_BULK, // bmAttributes: Bulk
L16(USB_TXBUFSZ), // wMaxPacketSize LO
H16(USB_TXBUFSZ), // wMaxPacketSize HI
0, // bInterval: ignore for Bulk transfer
// Endpoint OUT1 Descriptor
USB_DT_ENDPOINT_SIZE, // bLength: Endpoint Descriptor size
USB_DT_ENDPOINT, // bDescriptorType: Endpoint
0x01, // bEndpointAddress: OUT1
USB_BM_ATTR_BULK, // bmAttributes: Bulk
L16(USB_RXBUFSZ), // wMaxPacketSize LO
H16(USB_RXBUFSZ), // wMaxPacketSize HI
0, // bInterval: ignore for Bulk transfer
}; };
//const uint8_t HID_ReportDescriptor[];
_USB_LANG_ID_(LD, LANG_US); _USB_LANG_ID_(LD, LANG_US);
_USB_STRING_(SD, u"0.0.1"); _USB_STRING_(SD, u"0.0.1");
_USB_STRING_(MD, u"eddy@sao.ru"); _USB_STRING_(MD, u"eddy@sao.ru");
_USB_STRING_(PD, u"USB-CDC"); _USB_STRING_(PD, u"USB-CDC");
_USB_STRING_(ID, u"usbcdc");
// iInterface will change on initialisation by config
#define _USB_IIDESCR_(str) {sizeof(str), 0x03, str}
typedef struct{
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t bString[MAX_IINTERFACE_SZ];
}iidescr_t;
static iidescr_t iids[InterfacesAmount] = {
_USB_IIDESCR_(u"iface0"),
_USB_IIDESCR_(u"iface1"),
_USB_IIDESCR_(u"iface2"),
_USB_IIDESCR_(u"iface3"),
_USB_IIDESCR_(u"iface4"),
_USB_IIDESCR_(u"iface5"),
_USB_IIDESCR_(u"iface6"),
};
static const void* const StringDescriptor[iDESCR_AMOUNT] = { static const void* const StringDescriptor[iDESCR_AMOUNT] = {
[iLANGUAGE_DESCR] = &LD, [iLANGUAGE_DESCR] = &LD,
[iMANUFACTURER_DESCR] = &MD, [iMANUFACTURER_DESCR] = &MD,
[iPRODUCT_DESCR] = &PD, [iPRODUCT_DESCR] = &PD,
[iSERIAL_DESCR] = &SD, [iSERIAL_DESCR] = &SD,
[iINTERFACE_DESCR1] = &ID [iINTERFACE_DESCR1] = &iids[0],
[iINTERFACE_DESCR2] = &iids[1],
[iINTERFACE_DESCR3] = &iids[2],
[iINTERFACE_DESCR4] = &iids[3],
[iINTERFACE_DESCR5] = &iids[4],
[iINTERFACE_DESCR6] = &iids[5],
[iINTERFACE_DESCR7] = &iids[6],
}; };
static void wr0(const uint8_t *buf, uint16_t size, uint16_t askedsize){ static void wr0(const uint8_t *buf, uint16_t size, uint16_t askedsize){
@@ -208,3 +255,18 @@ void get_descriptor(config_pack_t *pack){
break; break;
} }
} }
// change values of iInterface by content of global config
void setup_interfaces(){
for(int i = 0; i < InterfacesAmount; ++i){
if(the_conf.iIlengths[i]){
iids[i].bLength = the_conf.iIlengths[i] + 2; // +2 - for bLength and bDescriptorType
memcpy(iids[i].bString, the_conf.iInterface[i], the_conf.iIlengths[i]);
}
iids[i].bDescriptorType = 0x03;
}
if(Config_mode){
// configuration interface identificator is hardware fixed
memcpy(iids[ICFG].bString, u"multiportCFG", 24);
iids[ICFG].bLength = 48;
}
}

View File

@@ -31,21 +31,24 @@
#define bcdDevice_Ver 0x0200 #define bcdDevice_Ver 0x0200
#define bNumConfigurations 1 #define bNumConfigurations 1
// amount of interfaces and endpoints (except 0) used
#define bNumInterfaces 2
#define bTotNumEndpoints 3
#define bNumCsInterfaces 4
// amount of interfaces // amount of interfaces
#define InterfacesAmount 7 #define InterfacesAmount 7
// index of interface (ep number minus one)
#define ISerial0 0 #define ISerial0 0
#define ICFG 0
#define ISerial1 1 #define ISerial1 1
#define ISerial2 2 #define ISerial2 2
#define ISerial3 3 #define ISerial3 3
#define ISerial4 4 #define ISerial4 4
#define ISPI 5 #define ICAN 5
#define ICAN 6 #define ISPI 6
#define ICFG 6
// EP number of interface
#define EPNO(i) (i + 1)
// amount of interfaces (including virtual) except 0
#define bNumInterfaces (2*InterfacesAmount)
// amount of endpoints used
#define bTotNumEndpoints (1+InterfacesAmount)
// powered // powered
#define BusPowered (1<<7) #define BusPowered (1<<7)
@@ -55,10 +58,14 @@
// buffer sizes // buffer sizes
// for USB FS EP0 buffers are from 8 to 64 bytes long // for USB FS EP0 buffers are from 8 to 64 bytes long
#define USB_EP0BUFSZ 64 #define USB_EP0BUFSZ 64
// virtual
#define USB_EP1BUFSZ 10 #define USB_EP1BUFSZ 10
// Rx/Tx EPs // Rx/Tx EPs (USB_BTABLE_SIZE-64-2*USB_EP0BUFSZ)/(2*InterfacesAmount) rounded to 8
#define USB_RXBUFSZ 64 // 534 / 112 -> 4
#define USB_TXBUFSZ 64 #define _RTBUFSZ8 (((USB_BTABLE_SIZE) - 64 - (2 * (USB_EP0BUFSZ)))/(16 * (InterfacesAmount)))
// 32 bytes; so we have free 86 bytes which can't be used
#define USB_RXBUFSZ (8 * (_RTBUFSZ8))
#define USB_TXBUFSZ (8 * (_RTBUFSZ8))
// string descriptors // string descriptors
enum{ enum{
@@ -67,7 +74,14 @@ enum{
iPRODUCT_DESCR, iPRODUCT_DESCR,
iSERIAL_DESCR, iSERIAL_DESCR,
iINTERFACE_DESCR1, iINTERFACE_DESCR1,
iINTERFACE_DESCR2,
iINTERFACE_DESCR3,
iINTERFACE_DESCR4,
iINTERFACE_DESCR5,
iINTERFACE_DESCR6,
iINTERFACE_DESCR7,
iDESCR_AMOUNT iDESCR_AMOUNT
}; };
void get_descriptor(config_pack_t *pack); void get_descriptor(config_pack_t *pack);
void setup_interfaces();

View File

@@ -17,6 +17,7 @@
#include <string.h> #include <string.h>
#include "hardware.h"
#include "ringbuffer.h" #include "ringbuffer.h"
#include "usb_descr.h" #include "usb_descr.h"
#include "usb_dev.h" #include "usb_dev.h"
@@ -37,170 +38,226 @@
#define CONTROL_RTS 0x02 #define CONTROL_RTS 0x02
// inbuf overflow when receiving // inbuf overflow when receiving
static volatile uint8_t bufovrfl = 0; static volatile uint8_t bufovrfl[InterfacesAmount] = {0};
// receive buffer: hold data until chkin() call // receive buffer: hold data until chkin() call
static uint8_t volatile rcvbuf[USB_RXBUFSZ]; static uint8_t volatile rcvbuf[InterfacesAmount][USB_RXBUFSZ];
static uint8_t volatile rcvbuflen = 0; static uint8_t volatile rcvbuflen[InterfacesAmount] = {0};
// line coding // line coding
usb_LineCoding WEAK lineCoding = {115200, 0, 0, 8}; #define DEFL {115200, 0, 0, 8}
usb_LineCoding lineCoding[InterfacesAmount] = {DEFL,DEFL,DEFL,DEFL,DEFL,DEFL,DEFL};
// CDC configured and ready to use // CDC configured and ready to use
volatile uint8_t CDCready = 0; volatile uint8_t CDCready[InterfacesAmount] = {0};
// ring buffers for incoming and outgoing data // ring buffers for incoming and outgoing data
static uint8_t obuf[RBOUTSZ], ibuf[RBINSZ]; static uint8_t obuf[InterfacesAmount][RBOUTSZ], ibuf[InterfacesAmount][RBINSZ];
static volatile ringbuffer rbout = {.data = obuf, .length = RBOUTSZ, .head = 0, .tail = 0}; #define OBUF(N) {.data = obuf[N], .length = RBOUTSZ, .head = 0, .tail = 0}
static volatile ringbuffer rbin = {.data = ibuf, .length = RBINSZ, .head = 0, .tail = 0}; static volatile ringbuffer rbout[InterfacesAmount] = {OBUF(0), OBUF(1), OBUF(2), OBUF(3), OBUF(4), OBUF(5), OBUF(6)};
// last send data size #define IBUF(N) {.data = ibuf[N], .length = RBINSZ, .head = 0, .tail = 0}
static volatile int lastdsz = 0; static volatile ringbuffer rbin[InterfacesAmount] = {IBUF(0), IBUF(1), IBUF(2), IBUF(3), IBUF(4), IBUF(5), IBUF(6)};
// last send data size (<0 if USB transfer ready)
static volatile int lastdsz[InterfacesAmount] = {-1, -1, -1, -1, -1, -1, -1};
static void chkin(){ static void chkin(uint8_t ifno){
if(bufovrfl) return; // allow user to know that previous buffer was overflowed and cleared if(bufovrfl[ifno]) return; // allow user to know that previous buffer was overflowed and cleared
if(!rcvbuflen) return; if(!rcvbuflen[ifno]) return;
int w = RB_write((ringbuffer*)&rbin, (uint8_t*)rcvbuf, rcvbuflen); int w = RB_write((ringbuffer*)&rbin[ifno], (uint8_t*)rcvbuf[ifno], rcvbuflen[ifno]);
if(w < 0){ if(w < 0){
return; return;
} }
if(w != rcvbuflen) bufovrfl = 1; if(w != rcvbuflen[ifno]) bufovrfl[ifno] = 1;
rcvbuflen = 0; rcvbuflen[ifno] = 0;
uint16_t status = KEEP_DTOG(USB->EPnR[1]); // don't change DTOG uint16_t status = KEEP_DTOG(USB->EPnR[1+ifno]); // don't change DTOG
USB->EPnR[1] = (status & ~(USB_EPnR_STAT_TX|USB_EPnR_CTR_RX)) ^ USB_EPnR_STAT_RX; // prepare to get next data portion USB->EPnR[1+ifno] = (status & ~(USB_EPnR_STAT_TX|USB_EPnR_CTR_RX)) ^ USB_EPnR_STAT_RX; // prepare to get next data portion
} }
// called from transmit EP to send next data portion or by user - when new transmission starts // called from transmit EP to send next data portion or by user - when new transmission starts
static void send_next(){ static void send_next(uint8_t ifno){
uint8_t usbbuff[USB_TXBUFSZ]; uint8_t usbbuff[USB_TXBUFSZ];
int buflen = RB_read((ringbuffer*)&rbout, (uint8_t*)usbbuff, USB_TXBUFSZ); int buflen = RB_read((ringbuffer*)&rbout[ifno], (uint8_t*)usbbuff, USB_TXBUFSZ);
if(buflen == 0){ if(!CDCready[ifno]){
if(lastdsz == 64) EP_Write(1, NULL, 0); // send ZLP after 64 bits packet when nothing more to send lastdsz[ifno] = -1;
lastdsz = 0;
return;
}else if(buflen < 0){
lastdsz = 0;
return; return;
} }
EP_Write(1, (uint8_t*)usbbuff, buflen); if(buflen == 0){
lastdsz = buflen; if(lastdsz[ifno] == USB_TXBUFSZ){
EP_Write(1+ifno, NULL, 0); // send ZLP after 64 bits packet when nothing more to send
lastdsz[ifno] = 0;
}else lastdsz[ifno] = -1; // OK. User can start sending data
return;
}else if(buflen < 0){
lastdsz[ifno] = -1;
return;
}
EP_Write(1+ifno, (uint8_t*)usbbuff, buflen);
lastdsz[ifno] = buflen;
} }
// data IN/OUT handler // data IN/OUT handler
static void rxtx_handler(){ static void rxtx_handler(){
uint16_t epstatus = KEEP_DTOG(USB->EPnR[1]); uint8_t ifno = (USB->ISTR & USB_ISTR_EPID) - 1;
if(ifno > InterfacesAmount-1){
return;
}
uint16_t epstatus = KEEP_DTOG(USB->EPnR[1+ifno]);
if(RX_FLAG(epstatus)){ // receive data if(RX_FLAG(epstatus)){ // receive data
if(rcvbuflen){ if(rcvbuflen[ifno]){
bufovrfl = 1; // lost last data bufovrfl[ifno] = 1; // lost last data
rcvbuflen = 0; rcvbuflen[ifno] = 0;
} }
rcvbuflen = EP_Read(1, (uint8_t*)rcvbuf); rcvbuflen[ifno] = EP_Read(1+ifno, (uint8_t*)rcvbuf[ifno]);
USB->EPnR[1] = epstatus & ~(USB_EPnR_CTR_RX | USB_EPnR_STAT_RX | USB_EPnR_STAT_TX); // keep RX in STALL state until read data USB->EPnR[1+ifno] = epstatus & ~(USB_EPnR_CTR_RX | USB_EPnR_STAT_RX | USB_EPnR_STAT_TX); // keep RX in STALL state until read data
chkin(); // try to write current data into RXbuf if it's not busy chkin(ifno); // try to write current data into RXbuf if it's not busy
}else{ // tx successfull }else{ // tx successfull
USB->EPnR[1] = (epstatus & ~(USB_EPnR_CTR_TX | USB_EPnR_STAT_TX)) ^ USB_EPnR_STAT_RX; USB->EPnR[1+ifno] = (epstatus & ~(USB_EPnR_CTR_TX | USB_EPnR_STAT_TX)) ^ USB_EPnR_STAT_RX;
send_next(); send_next(ifno);
} }
} }
// weak handlers: change them somewhere else if you want to setup USART
// SET_LINE_CODING // SET_LINE_CODING
void WEAK linecoding_handler(usb_LineCoding *lc){ void linecoding_handler(uint8_t ifno, usb_LineCoding *lc){
lineCoding = *lc; lineCoding[ifno] = *lc;
// TODO: set up interfaces
}
// clear IN/OUT buffers on connection
static void clearbufs(uint8_t ifno){
uint32_t T0 = Tms;
while(Tms - T0 < 10){ // wait no more than 10ms
if(1 == RB_clearbuf((ringbuffer*)&rbin[ifno])) break;
}
T0 = Tms;
while(Tms - T0 < 10){
if(1 == RB_clearbuf((ringbuffer*)&rbout[ifno])) break;
}
} }
// SET_CONTROL_LINE_STATE // SET_CONTROL_LINE_STATE
void WEAK clstate_handler(uint16_t val){ void clstate_handler(uint8_t ifno, uint16_t val){
CDCready = val; // CONTROL_DTR | CONTROL_RTS -> interface connected; 0 -> disconnected CDCready[ifno] = val; // CONTROL_DTR | CONTROL_RTS -> interface connected; 0 -> disconnected
lastdsz[ifno] = -1;
if(val) clearbufs(ifno);
} }
// SEND_BREAK // SEND_BREAK - disconnect interface and clear its buffers
void WEAK break_handler(){ // this is a fake handler as classic CDC ACM never receives this
CDCready = 0; void break_handler(uint8_t ifno){
CDCready[ifno] = 0;
} }
// USB is configured: setup endpoints // USB is configured: setup endpoints
void set_configuration(){ void set_configuration(){
EP_Init(1, EP_TYPE_BULK, USB_TXBUFSZ, USB_RXBUFSZ, rxtx_handler); // IN1 and OUT1 for(int i = 0; i < InterfacesAmount; ++i){
IWDG->KR = IWDG_REFRESH;
int r = EP_Init(1+i, EP_TYPE_BULK, USB_TXBUFSZ, USB_RXBUFSZ, rxtx_handler);
if(r){
// OOPS, can't init EP. What to do? Cry?
break;
}
}
} }
// PL2303 CLASS request // USB CDC CLASS request
void usb_class_request(config_pack_t *req, uint8_t *data, uint16_t datalen){ void usb_class_request(config_pack_t *req, uint8_t *data, uint16_t datalen){
uint8_t recipient = REQUEST_RECIPIENT(req->bmRequestType); uint8_t recipient = REQUEST_RECIPIENT(req->bmRequestType);
uint8_t dev2host = (req->bmRequestType & 0x80) ? 1 : 0; uint8_t dev2host = (req->bmRequestType & 0x80) ? 1 : 0;
uint8_t ifno = req->wIndex >> 1;
if(ifno > InterfacesAmount-1){ // wrong interface number
EP_WriteIRQ(0, NULL, 0);
return;
}
switch(recipient){ switch(recipient){
case REQ_RECIPIENT_INTERFACE: case REQ_RECIPIENT_INTERFACE:
switch(req->bRequest){ switch(req->bRequest){
case SET_LINE_CODING: case SET_LINE_CODING:
if(!data || !datalen) break; // wait for data if(!data || !datalen) break; // wait for data
if(datalen == sizeof(usb_LineCoding)) if(datalen == sizeof(usb_LineCoding))
linecoding_handler((usb_LineCoding*)data); linecoding_handler(ifno, (usb_LineCoding*)data);
break; break;
case GET_LINE_CODING: case GET_LINE_CODING:
EP_WriteIRQ(0, (uint8_t*)&lineCoding, sizeof(lineCoding)); EP_WriteIRQ(0, (uint8_t*)&lineCoding[ifno], sizeof(lineCoding));
break; break;
case SET_CONTROL_LINE_STATE: case SET_CONTROL_LINE_STATE:
clstate_handler(req->wValue); clstate_handler(ifno, req->wValue);
break; break;
case SEND_BREAK: case SEND_BREAK:
break_handler(); break_handler(ifno);
break; break;
default:
break;
}
break;
default: default:
if(dev2host) EP_WriteIRQ(0, NULL, 0); // WTF?
break;
}
break;
default:
// WTF?
if(dev2host) EP_WriteIRQ(0, NULL, 0);
} }
if(!dev2host) EP_WriteIRQ(0, NULL, 0); if(!dev2host) EP_WriteIRQ(0, NULL, 0);
} }
// blocking send full content of ring buffer // blocking send full content of ring buffer
int USB_sendall(){ int USB_sendall(uint8_t ifno){
while(lastdsz > 0){ uint32_t T0 = Tms;
if(!CDCready) return FALSE; while(lastdsz[ifno] > 0){
if(Tms - T0 > DISCONN_TMOUT){
break_handler(ifno);
return FALSE;
}
if(!CDCready[ifno]) return FALSE;
IWDG->KR = IWDG_REFRESH;
} }
return TRUE; return TRUE;
} }
// put `buf` into queue to send // put `buf` into queue to send
int USB_send(const uint8_t *buf, int len){ int USB_send(uint8_t ifno, const uint8_t *buf, int len){
if(!buf || !CDCready || !len) return FALSE; if(!buf || !CDCready[ifno] || !len) return FALSE;
uint32_t T0 = Tms;
while(len){ while(len){
IWDG->KR = IWDG_REFRESH; if(Tms - T0 > DISCONN_TMOUT){
int l = RB_datalen((ringbuffer*)&rbout); break_handler(ifno);
if(l < 0) continue; return FALSE;
int portion = rbout.length - 1 - l;
if(portion < 1){
if(lastdsz == 0) send_next();
continue;
} }
if(portion > len) portion = len; if(!CDCready[ifno]) return FALSE;
int a = RB_write((ringbuffer*)&rbout, buf, portion); IWDG->KR = IWDG_REFRESH;
int a = RB_write((ringbuffer*)&rbout[ifno], buf, len);
if(a > 0){ if(a > 0){
len -= a; len -= a;
buf += a; buf += a;
} else if (a < 0) continue; // do nothing if buffer is in reading state }else if(a == 0){ // overfull
if(lastdsz == 0) send_next(); // need to run manually - all data sent, so no IRQ on IN if(lastdsz[ifno] < 0) send_next(ifno);
}
} }
if(buf[len-1] == '\n' && lastdsz[ifno] < 0) send_next(ifno);
return TRUE; return TRUE;
} }
int USB_putbyte(uint8_t byte){ int USB_putbyte(uint8_t ifno, uint8_t byte){
if(!CDCready) return FALSE; if(!CDCready[ifno]) return FALSE;
int l = 0; int l = 0;
while((l = RB_write((ringbuffer*)&rbout, &byte, 1)) != 1){ uint32_t T0 = Tms;
if(l < 0) continue; while((l = RB_write((ringbuffer*)&rbout[ifno], &byte, 1)) != 1){
if(Tms - T0 > DISCONN_TMOUT){
break_handler(ifno);
return FALSE;
}
if(!CDCready[ifno]) return FALSE;
IWDG->KR = IWDG_REFRESH;
if(l == 0){ // overfull
if(lastdsz[ifno] < 0) send_next(ifno);
continue;
}
} }
if(lastdsz == 0) send_next(); // need to run manually - all data sent, so no IRQ on IN // send line if got EOL
if(byte == '\n' && lastdsz[ifno] < 0) send_next(ifno);
return TRUE; return TRUE;
} }
int USB_sendstr(const char *string){ int USB_sendstr(uint8_t ifno, const char *string){
if(!string || !CDCready) return FALSE; if(!string || !CDCready[ifno]) return FALSE;
int len = 0; int len = strlen(string);
const char *b = string;
while(*b++) ++len;
if(!len) return FALSE; if(!len) return FALSE;
return USB_send((const uint8_t*)string, len); return USB_send(ifno, (const uint8_t*)string, len);
} }
/** /**
@@ -209,14 +266,14 @@ int USB_sendstr(const char *string){
* @param len - length of `buf` * @param len - length of `buf`
* @return amount of received bytes (negative, if overfull happened) * @return amount of received bytes (negative, if overfull happened)
*/ */
int USB_receive(uint8_t *buf, int len){ int USB_receive(uint8_t ifno, uint8_t *buf, int len){
chkin(); chkin(ifno); // rxtx_handler could leave last message unwritten if buffer was busy
if(bufovrfl){ if(bufovrfl[ifno]){
while(1 != RB_clearbuf((ringbuffer*)&rbin)); while(1 != RB_clearbuf((ringbuffer*)&rbin[ifno])); // run watchdog in case of problems
bufovrfl = 0; bufovrfl[ifno] = 0;
return -1; return -1;
} }
int sz = RB_read((ringbuffer*)&rbin, buf, len); int sz = RB_read((ringbuffer*)&rbin[ifno], buf, len);
if(sz < 0) return 0; // buffer in writting state if(sz < 0) return 0; // buffer in writting state
return sz; return sz;
} }
@@ -227,17 +284,17 @@ int USB_receive(uint8_t *buf, int len){
* @param len - its length * @param len - its length
* @return strlen or negative value indicating overflow (if so, string won't be ends with 0 and buffer should be cleared) * @return strlen or negative value indicating overflow (if so, string won't be ends with 0 and buffer should be cleared)
*/ */
int USB_receivestr(char *buf, int len){ int USB_receivestr(uint8_t ifno, char *buf, int len){
chkin(); chkin(ifno); // rxtx_handler could leave last message unwritten if buffer was busy
if(bufovrfl){ if(bufovrfl[ifno]){
while(1 != RB_clearbuf((ringbuffer*)&rbin)); while(1 != RB_clearbuf((ringbuffer*)&rbin[ifno]));
bufovrfl = 0; bufovrfl[ifno] = 0;
return -1; return -1;
} }
int l = RB_readto((ringbuffer*)&rbin, '\n', (uint8_t*)buf, len); int l = RB_readto((ringbuffer*)&rbin[ifno], '\n', (uint8_t*)buf, len);
if(l < 1){ if(l < 1){
if(rbin.length == RB_datalen((ringbuffer*)&rbin)){ // buffer is full but no '\n' found if(rbin[ifno].length == RB_datalen((ringbuffer*)&rbin[ifno])){ // buffer is full but no '\n' found
while(1 != RB_clearbuf((ringbuffer*)&rbin)); while(1 != RB_clearbuf((ringbuffer*)&rbin[ifno]));
return -1; return -1;
} }
return 0; return 0;
@@ -246,4 +303,3 @@ int USB_receivestr(char *buf, int len){
buf[l-1] = 0; // replace '\n' with strend buf[l-1] = 0; // replace '\n' with strend
return l; return l;
} }

View File

@@ -18,6 +18,7 @@
#include <stm32f3.h> #include <stm32f3.h>
#include "usb_lib.h" #include "usb_lib.h"
#include "usb_descr.h"
typedef struct { typedef struct {
uint32_t dwDTERate; uint32_t dwDTERate;
@@ -34,24 +35,29 @@ typedef struct {
uint8_t bDataBits; uint8_t bDataBits;
} __attribute__ ((packed)) usb_LineCoding; } __attribute__ ((packed)) usb_LineCoding;
extern usb_LineCoding lineCoding; extern volatile uint8_t CDCready[InterfacesAmount];
extern volatile uint8_t CDCready;
void break_handler(); void break_handler(uint8_t ifno);
void clstate_handler(uint16_t val); void clstate_handler(uint8_t ifno, uint16_t val);
void linecoding_handler(usb_LineCoding *lc); 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 - more than 2ms
#define DISCONN_TMOUT (2)
// sizes of ringbuffers for outgoing and incoming data // sizes of ringbuffers for outgoing and incoming data
#define RBOUTSZ (1024) #define RBOUTSZ (256)
#define RBINSZ (1024) #define RBINSZ (256)
#define newline() USB_putbyte('\n') #define newline(ifno) USB_putbyte(ifno, '\n')
#define USND(s) do{USB_sendstr(s); USB_putbyte('\n');}while(0) #define USND(ifno, s) do{USB_sendstr(ifno, s); USB_putbyte(ifno, '\n');}while(0)
// configuratin interface macros
#define CFGWR(s) USB_sendstr(ICFG, s)
#define CFGWRn(s) do{USB_sendstr(ICFG, s); USB_putbyte(ICFG, '\n');}while(0)
#define CFGn() USB_putbyte(ICFG, '\n')
int USB_sendall(); int USB_sendall(uint8_t ifno);
int USB_send(const uint8_t *buf, int len); int USB_send(uint8_t ifno, const uint8_t *buf, int len);
int USB_putbyte(uint8_t byte); int USB_putbyte(uint8_t ifno, uint8_t byte);
int USB_sendstr(const char *string); int USB_sendstr(uint8_t ifno, const char *string);
int USB_receive(uint8_t *buf, int len); int USB_receive(uint8_t ifno, uint8_t *buf, int len);
int USB_receivestr(char *buf, int len); int USB_receivestr(uint8_t ifno, char *buf, int len);

View File

@@ -356,6 +356,7 @@ void USB_setup(){
USB->BCDR |= USB_BCDR_DPPU; USB->BCDR |= USB_BCDR_DPPU;
NVIC_EnableIRQ(USB_IRQn); NVIC_EnableIRQ(USB_IRQn);
#endif #endif
setup_interfaces(); // refresh interfaces names
} }

View File

@@ -1,2 +1,2 @@
#define BUILD_NUMBER "8" #define BUILD_NUMBER "20"
#define BUILD_DATE "2025-12-28" #define BUILD_DATE "2026-02-11"