mirror of
https://github.com/eddyem/stm32samples.git
synced 2026-02-28 03:44:30 +03:00
all descriptors read - OK, interfaces work - bad (only first two works - why? need to debug)
This commit is contained in:
@@ -1,15 +1,95 @@
|
||||
7 interfaces over USB
|
||||
======================
|
||||
Multiport board
|
||||
====================================
|
||||
|
||||
Seven isolated interfaces:
|
||||
|
||||
- 1 CAN
|
||||
- 3 RS-485
|
||||
- 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):
|
||||
1..3 - RS-485 (1..3)
|
||||
4 - RS-232 (1)
|
||||
5 - RS-232 (2) or RS-485
|
||||
6 - CAN
|
||||
7 - SSI (over SPI) or configuration interface (if "Config mode" jumper shortened)
|
||||
|
||||
1. RS-485 (1)
|
||||
2. RS-485 (2)
|
||||
3. RS-485 (3)
|
||||
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).
|
||||
|
||||
|
||||
@@ -32,15 +32,15 @@ const user_conf *Flash_Data = (const user_conf *)(&__varsstart);
|
||||
user_conf the_conf = {
|
||||
.userconf_sz = sizeof(user_conf),
|
||||
.iInterface = {
|
||||
[ISerial0] = u"usbserial0",
|
||||
[ISerial1] = u"usbserial1",
|
||||
[ISerial2] = u"usbserial2",
|
||||
[ISerial3] = u"usbserial3",
|
||||
[ISerial4] = u"usbserial4",
|
||||
[ISerial0] = u"usbserial0.",
|
||||
[ISerial1] = u"usbserial1.",
|
||||
[ISerial2] = u"usbserial2.",
|
||||
[ISerial3] = u"usbserial3.",
|
||||
[ISerial4] = u"usbserial4.",
|
||||
[ISPI] = u"usbSPI",
|
||||
[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
|
||||
@@ -117,12 +117,12 @@ static int write2flash(const void *start, const void *wrdata, uint32_t stor_size
|
||||
*(volatile uint16_t*)(address + i) = data[i];
|
||||
while(FLASH->SR & FLASH_SR_BSY) IWDG->KR = IWDG_REFRESH;
|
||||
if(*(volatile uint16_t*)(address + i) != data[i]){
|
||||
USB_sendstr("DON'T MATCH!\n");
|
||||
CFGWR("DON'T MATCH!\n");
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
if(FLASH->SR & FLASH_SR_PGERR){
|
||||
USB_sendstr("Prog err\n");
|
||||
CFGWR("Prog err\n");
|
||||
ret = 1; // program error - meet not 0xffff
|
||||
break;
|
||||
}
|
||||
@@ -136,7 +136,7 @@ static int write2flash(const void *start, const void *wrdata, uint32_t stor_size
|
||||
static int erase_pageN(int N){
|
||||
int ret = 0;
|
||||
#ifdef EBUG
|
||||
//CMDWR("Erase page #"); CMDWR(u2str(N)); CMDn();
|
||||
CFGWR("Erase page #"); CFGWRn(u2str(N));
|
||||
#endif
|
||||
FLASH->AR = (uint32_t)Flash_Data + N*FLASH_blocksize;
|
||||
FLASH->CR |= FLASH_CR_STRT;
|
||||
@@ -158,16 +158,13 @@ int erase_storage(int npage){
|
||||
flsz -= (uint32_t)Flash_Data - FLASH_BASE;
|
||||
}
|
||||
end = flsz / FLASH_blocksize;
|
||||
/*
|
||||
#ifdef EBUG
|
||||
CMDWR("FLASH_SIZE="); CMDWR(u2str(FLASH_SIZE));
|
||||
CMDWR("\nflsz="); CMDWR(u2str(flsz));
|
||||
CMDWR("\nend="); CMDWR(u2str(end));
|
||||
CMDWR("\ncurrentconfidx="); CMDWR(u2str(currentconfidx));
|
||||
CMDWR("\nmaxCnum="); CMDWR(u2str(maxCnum));
|
||||
CMDn();
|
||||
CFGWR("FLASH_SIZE="); CFGWR(u2str(FLASH_SIZE));
|
||||
CFGWR("\nflsz="); CFGWR(u2str(flsz));
|
||||
CFGWR("\nend="); CFGWR(u2str(end));
|
||||
CFGWR("\ncurrentconfidx="); CFGWR(u2str(currentconfidx));
|
||||
CFGWR("\nmaxCnum="); CFGWRn(u2str(maxCnum));
|
||||
#endif
|
||||
*/
|
||||
if(end == 0 || end >= FLASH_SIZE) return 1;
|
||||
if(npage > -1){ // erase only one page
|
||||
if((uint32_t)npage >= end) return 1;
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
*/
|
||||
typedef struct __attribute__((packed, aligned(4))){
|
||||
uint16_t userconf_sz; // "magick number"
|
||||
// we store iInterface "as is"
|
||||
uint16_t iInterface[InterfacesAmount][MAX_IINTERFACE_SZ]; // hryunikod!
|
||||
uint8_t iIlengths[InterfacesAmount];
|
||||
} user_conf;
|
||||
|
||||
@@ -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"
|
||||
|
||||
uint8_t Config_mode = 0;
|
||||
|
||||
static inline void gpio_setup(){
|
||||
RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN; // for USART and LEDs
|
||||
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);
|
||||
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(){
|
||||
|
||||
@@ -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
|
||||
|
||||
#include <stm32f3.h>
|
||||
@@ -7,7 +25,12 @@
|
||||
#define USBPU_ON() pin_clear(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 uint8_t Config_mode;
|
||||
|
||||
void hw_setup();
|
||||
|
||||
|
||||
@@ -43,15 +43,20 @@ int main(void){
|
||||
//uint32_t ctr = Tms;
|
||||
USBPU_ON();
|
||||
while(1){
|
||||
/*if(Tms - ctr > 499){
|
||||
ctr = Tms;
|
||||
}*/
|
||||
// Put here code working WITOUT USB connected
|
||||
if(!usbON) continue;
|
||||
int l = USB_receivestr(inbuff, MAXSTRLEN);
|
||||
if(l < 0) USB_sendstr("ERROR: USB buffer overflow or string was too long\n");
|
||||
for(int i = 0; i < 6; ++i){ // just echo for first time
|
||||
int l = USB_receive(i, (uint8_t*)inbuff, MAXSTRLEN);
|
||||
if(l) USB_send(i, (uint8_t*)inbuff, l);
|
||||
}
|
||||
// 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) USB_sendstr(ans);
|
||||
if(ans) CFGWR(ans);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!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>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
|
||||
@@ -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 <string.h>
|
||||
|
||||
@@ -6,6 +24,8 @@
|
||||
#include "usb_dev.h"
|
||||
#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";
|
||||
|
||||
//#define LOCBUFFSZ (32)
|
||||
@@ -25,19 +45,18 @@ const char *helpstring =
|
||||
;
|
||||
|
||||
static void dumpflash(){
|
||||
USB_sendstr("userconf_sz="); USB_sendstr(u2str(the_conf.userconf_sz));
|
||||
USB_sendstr("\ncurrentconfidx="); USB_sendstr(i2str(currentconfidx));
|
||||
USB_putbyte('\n');
|
||||
CFGWR("userconf_sz="); CFGWR(u2str(the_conf.userconf_sz));
|
||||
CFGWR("\ncurrentconfidx="); CFGWRn(i2str(currentconfidx));
|
||||
for(int i = 0; i < InterfacesAmount; ++i){
|
||||
USB_sendstr("interface"); USB_putbyte('0' + i);
|
||||
USB_putbyte('=');
|
||||
CFGWR("interface"); USB_putbyte(ICFG, '0' + i);
|
||||
USB_putbyte(ICFG, '=');
|
||||
int l = the_conf.iIlengths[i] / 2;
|
||||
char *ptr = (char*) the_conf.iInterface[i];
|
||||
for(int j = 0; j < l; ++j){
|
||||
USB_putbyte(*ptr);
|
||||
USB_putbyte(ICFG, *ptr);
|
||||
ptr += 2;
|
||||
}
|
||||
USB_putbyte('\n');
|
||||
CFGn();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,10 +77,10 @@ static void setiface(const char *str){
|
||||
*ptr++ = *nxt++;
|
||||
*ptr++ = 0;
|
||||
}
|
||||
USB_sendstr(sOKn);
|
||||
CFGWR(sOKn);
|
||||
return;
|
||||
err:
|
||||
USB_sendstr(sERRn);
|
||||
CFGWR(sERRn);
|
||||
}
|
||||
|
||||
static const char* erpg(const char *str){
|
||||
@@ -106,12 +125,11 @@ const char *parse_cmd(const char *buf){
|
||||
if(store_userconf()) return sERRn;
|
||||
return sOKn;
|
||||
case 'T':
|
||||
USB_sendstr("T=");
|
||||
USB_sendstr(u2str(Tms));
|
||||
newline();
|
||||
CFGWR("T=");
|
||||
CFGWRn(u2str(Tms));
|
||||
break;
|
||||
default: // help
|
||||
USB_sendstr(helpstring);
|
||||
CFGWR(helpstring);
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
|
||||
@@ -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
|
||||
|
||||
char *parse_cmd(char *buf);
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
* 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"
|
||||
|
||||
// low/high for uint16_t
|
||||
@@ -55,7 +58,88 @@ static const uint8_t USB_DeviceQualifierDescriptor[] = {
|
||||
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[] = {
|
||||
// Configuration Descriptor
|
||||
@@ -68,89 +152,52 @@ static const uint8_t USB_ConfigDescriptor[] = {
|
||||
0, // iConfiguration: Index of string descriptor describing the configuration or 0
|
||||
BusPowered, // bmAttributes - Bus powered
|
||||
50, // MaxPower in 2mA units
|
||||
//---------------------------------------------------------------------------
|
||||
// Virtual command Interface Descriptor
|
||||
USB_DT_INTERFACE_SIZE, // bLength: Interface Descriptor size
|
||||
USB_DT_INTERFACE, // bDescriptorType: Interface
|
||||
0, // bInterfaceNumber: Number of Interface
|
||||
0, // bAlternateSetting: Alternate setting
|
||||
1, // bNumEndpoints: one for this
|
||||
USB_CLASS_COMM, // bInterfaceClass
|
||||
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
|
||||
|
||||
//--IADs-------------------------------------------------------------------------
|
||||
USB_IAD(0, iINTERFACE_DESCR1, 8, 1),
|
||||
USB_IAD(2, iINTERFACE_DESCR2, 9, 2),
|
||||
USB_IAD(4, iINTERFACE_DESCR3, 10, 3),
|
||||
USB_IAD(6, iINTERFACE_DESCR4, 11, 4),
|
||||
USB_IAD(8, iINTERFACE_DESCR5, 12, 5),
|
||||
USB_IAD(10, iINTERFACE_DESCR6, 13, 6),
|
||||
USB_IAD(12, iINTERFACE_DESCR7, 14, 7),
|
||||
};
|
||||
|
||||
//const uint8_t HID_ReportDescriptor[];
|
||||
|
||||
_USB_LANG_ID_(LD, LANG_US);
|
||||
_USB_STRING_(SD, u"0.0.1");
|
||||
_USB_STRING_(MD, u"eddy@sao.ru");
|
||||
_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] = {
|
||||
[iLANGUAGE_DESCR] = &LD,
|
||||
[iMANUFACTURER_DESCR] = &MD,
|
||||
[iPRODUCT_DESCR] = &PD,
|
||||
[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){
|
||||
@@ -208,3 +255,18 @@ void get_descriptor(config_pack_t *pack){
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,21 +31,24 @@
|
||||
#define bcdDevice_Ver 0x0200
|
||||
#define bNumConfigurations 1
|
||||
|
||||
// amount of interfaces and endpoints (except 0) used
|
||||
#define bNumInterfaces 2
|
||||
#define bTotNumEndpoints 3
|
||||
#define bNumCsInterfaces 4
|
||||
|
||||
// amount of interfaces
|
||||
#define InterfacesAmount 7
|
||||
// index of interface (ep number minus one)
|
||||
#define ISerial0 0
|
||||
#define ICFG 0
|
||||
#define ISerial1 1
|
||||
#define ISerial2 2
|
||||
#define ISerial3 3
|
||||
#define ISerial4 4
|
||||
#define ISPI 5
|
||||
#define ICAN 6
|
||||
#define ICAN 5
|
||||
#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
|
||||
#define BusPowered (1<<7)
|
||||
@@ -55,10 +58,14 @@
|
||||
// buffer sizes
|
||||
// for USB FS EP0 buffers are from 8 to 64 bytes long
|
||||
#define USB_EP0BUFSZ 64
|
||||
// virtual
|
||||
#define USB_EP1BUFSZ 10
|
||||
// Rx/Tx EPs
|
||||
#define USB_RXBUFSZ 64
|
||||
#define USB_TXBUFSZ 64
|
||||
// Rx/Tx EPs (USB_BTABLE_SIZE-64-2*USB_EP0BUFSZ)/(2*InterfacesAmount) rounded to 8
|
||||
// 534 / 112 -> 4
|
||||
#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
|
||||
enum{
|
||||
@@ -67,7 +74,14 @@ enum{
|
||||
iPRODUCT_DESCR,
|
||||
iSERIAL_DESCR,
|
||||
iINTERFACE_DESCR1,
|
||||
iINTERFACE_DESCR2,
|
||||
iINTERFACE_DESCR3,
|
||||
iINTERFACE_DESCR4,
|
||||
iINTERFACE_DESCR5,
|
||||
iINTERFACE_DESCR6,
|
||||
iINTERFACE_DESCR7,
|
||||
iDESCR_AMOUNT
|
||||
};
|
||||
|
||||
void get_descriptor(config_pack_t *pack);
|
||||
void setup_interfaces();
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "hardware.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "usb_descr.h"
|
||||
#include "usb_dev.h"
|
||||
@@ -37,170 +38,226 @@
|
||||
#define CONTROL_RTS 0x02
|
||||
|
||||
// inbuf overflow when receiving
|
||||
static volatile uint8_t bufovrfl = 0;
|
||||
static volatile uint8_t bufovrfl[InterfacesAmount] = {0};
|
||||
|
||||
// receive buffer: hold data until chkin() call
|
||||
static uint8_t volatile rcvbuf[USB_RXBUFSZ];
|
||||
static uint8_t volatile rcvbuflen = 0;
|
||||
static uint8_t volatile rcvbuf[InterfacesAmount][USB_RXBUFSZ];
|
||||
static uint8_t volatile rcvbuflen[InterfacesAmount] = {0};
|
||||
// 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
|
||||
volatile uint8_t CDCready = 0;
|
||||
volatile uint8_t CDCready[InterfacesAmount] = {0};
|
||||
|
||||
// ring buffers for incoming and outgoing data
|
||||
static uint8_t obuf[RBOUTSZ], ibuf[RBINSZ];
|
||||
static volatile ringbuffer rbout = {.data = obuf, .length = RBOUTSZ, .head = 0, .tail = 0};
|
||||
static volatile ringbuffer rbin = {.data = ibuf, .length = RBINSZ, .head = 0, .tail = 0};
|
||||
// last send data size
|
||||
static volatile int lastdsz = 0;
|
||||
static uint8_t obuf[InterfacesAmount][RBOUTSZ], ibuf[InterfacesAmount][RBINSZ];
|
||||
#define OBUF(N) {.data = obuf[N], .length = RBOUTSZ, .head = 0, .tail = 0}
|
||||
static volatile ringbuffer rbout[InterfacesAmount] = {OBUF(0), OBUF(1), OBUF(2), OBUF(3), OBUF(4), OBUF(5), OBUF(6)};
|
||||
#define IBUF(N) {.data = ibuf[N], .length = RBINSZ, .head = 0, .tail = 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(){
|
||||
if(bufovrfl) return; // allow user to know that previous buffer was overflowed and cleared
|
||||
if(!rcvbuflen) return;
|
||||
int w = RB_write((ringbuffer*)&rbin, (uint8_t*)rcvbuf, rcvbuflen);
|
||||
static void chkin(uint8_t ifno){
|
||||
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){
|
||||
return;
|
||||
}
|
||||
if(w != rcvbuflen) bufovrfl = 1;
|
||||
rcvbuflen = 0;
|
||||
uint16_t status = KEEP_DTOG(USB->EPnR[1]); // 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
|
||||
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
|
||||
}
|
||||
|
||||
// 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];
|
||||
int buflen = RB_read((ringbuffer*)&rbout, (uint8_t*)usbbuff, USB_TXBUFSZ);
|
||||
if(buflen == 0){
|
||||
if(lastdsz == 64) EP_Write(1, NULL, 0); // send ZLP after 64 bits packet when nothing more to send
|
||||
lastdsz = 0;
|
||||
return;
|
||||
}else if(buflen < 0){
|
||||
lastdsz = 0;
|
||||
int buflen = RB_read((ringbuffer*)&rbout[ifno], (uint8_t*)usbbuff, USB_TXBUFSZ);
|
||||
if(!CDCready[ifno]){
|
||||
lastdsz[ifno] = -1;
|
||||
return;
|
||||
}
|
||||
EP_Write(1, (uint8_t*)usbbuff, buflen);
|
||||
lastdsz = buflen;
|
||||
if(buflen == 0){
|
||||
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
|
||||
static void rxtx_handler(){
|
||||
uint16_t epstatus = KEEP_DTOG(USB->EPnR[1]);
|
||||
if(RX_FLAG(epstatus)){ // receive data
|
||||
if(rcvbuflen){
|
||||
bufovrfl = 1; // lost last data
|
||||
rcvbuflen = 0;
|
||||
uint8_t ifno = (USB->ISTR & USB_ISTR_EPID) - 1;
|
||||
if(ifno > InterfacesAmount-1){
|
||||
return;
|
||||
}
|
||||
rcvbuflen = EP_Read(1, (uint8_t*)rcvbuf);
|
||||
USB->EPnR[1] = 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
|
||||
uint16_t epstatus = KEEP_DTOG(USB->EPnR[1+ifno]);
|
||||
if(RX_FLAG(epstatus)){ // receive data
|
||||
if(rcvbuflen[ifno]){
|
||||
bufovrfl[ifno] = 1; // lost last data
|
||||
rcvbuflen[ifno] = 0;
|
||||
}
|
||||
rcvbuflen[ifno] = EP_Read(1+ifno, (uint8_t*)rcvbuf[ifno]);
|
||||
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(ifno); // try to write current data into RXbuf if it's not busy
|
||||
}else{ // tx successfull
|
||||
USB->EPnR[1] = (epstatus & ~(USB_EPnR_CTR_TX | USB_EPnR_STAT_TX)) ^ USB_EPnR_STAT_RX;
|
||||
send_next();
|
||||
USB->EPnR[1+ifno] = (epstatus & ~(USB_EPnR_CTR_TX | USB_EPnR_STAT_TX)) ^ USB_EPnR_STAT_RX;
|
||||
send_next(ifno);
|
||||
}
|
||||
}
|
||||
|
||||
// weak handlers: change them somewhere else if you want to setup USART
|
||||
// SET_LINE_CODING
|
||||
void WEAK linecoding_handler(usb_LineCoding *lc){
|
||||
lineCoding = *lc;
|
||||
void linecoding_handler(uint8_t ifno, usb_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
|
||||
void WEAK clstate_handler(uint16_t val){
|
||||
CDCready = val; // CONTROL_DTR | CONTROL_RTS -> interface connected; 0 -> disconnected
|
||||
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);
|
||||
}
|
||||
|
||||
// SEND_BREAK
|
||||
void WEAK break_handler(){
|
||||
CDCready = 0;
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
// USB is configured: setup endpoints
|
||||
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){
|
||||
uint8_t recipient = REQUEST_RECIPIENT(req->bmRequestType);
|
||||
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){
|
||||
case REQ_RECIPIENT_INTERFACE:
|
||||
switch(req->bRequest){
|
||||
case SET_LINE_CODING:
|
||||
if(!data || !datalen) break; // wait for data
|
||||
if(datalen == sizeof(usb_LineCoding))
|
||||
linecoding_handler((usb_LineCoding*)data);
|
||||
linecoding_handler(ifno, (usb_LineCoding*)data);
|
||||
break;
|
||||
case GET_LINE_CODING:
|
||||
EP_WriteIRQ(0, (uint8_t*)&lineCoding, sizeof(lineCoding));
|
||||
EP_WriteIRQ(0, (uint8_t*)&lineCoding[ifno], sizeof(lineCoding));
|
||||
break;
|
||||
case SET_CONTROL_LINE_STATE:
|
||||
clstate_handler(req->wValue);
|
||||
clstate_handler(ifno, req->wValue);
|
||||
break;
|
||||
case SEND_BREAK:
|
||||
break_handler();
|
||||
break_handler(ifno);
|
||||
break;
|
||||
default:
|
||||
// WTF?
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// WTF?
|
||||
if(dev2host) EP_WriteIRQ(0, NULL, 0);
|
||||
}
|
||||
if(!dev2host) EP_WriteIRQ(0, NULL, 0);
|
||||
}
|
||||
|
||||
// blocking send full content of ring buffer
|
||||
int USB_sendall(){
|
||||
while(lastdsz > 0){
|
||||
if(!CDCready) return FALSE;
|
||||
int USB_sendall(uint8_t ifno){
|
||||
uint32_t T0 = Tms;
|
||||
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;
|
||||
}
|
||||
|
||||
// put `buf` into queue to send
|
||||
int USB_send(const uint8_t *buf, int len){
|
||||
if(!buf || !CDCready || !len) return FALSE;
|
||||
int USB_send(uint8_t ifno, const uint8_t *buf, int len){
|
||||
if(!buf || !CDCready[ifno] || !len) return FALSE;
|
||||
uint32_t T0 = Tms;
|
||||
while(len){
|
||||
IWDG->KR = IWDG_REFRESH;
|
||||
int l = RB_datalen((ringbuffer*)&rbout);
|
||||
if(l < 0) continue;
|
||||
int portion = rbout.length - 1 - l;
|
||||
if(portion < 1){
|
||||
if(lastdsz == 0) send_next();
|
||||
continue;
|
||||
if(Tms - T0 > DISCONN_TMOUT){
|
||||
break_handler(ifno);
|
||||
return FALSE;
|
||||
}
|
||||
if(portion > len) portion = len;
|
||||
int a = RB_write((ringbuffer*)&rbout, buf, portion);
|
||||
if(!CDCready[ifno]) return FALSE;
|
||||
IWDG->KR = IWDG_REFRESH;
|
||||
int a = RB_write((ringbuffer*)&rbout[ifno], buf, len);
|
||||
if(a > 0){
|
||||
len -= a;
|
||||
buf += a;
|
||||
} else if (a < 0) continue; // do nothing if buffer is in reading state
|
||||
if(lastdsz == 0) send_next(); // need to run manually - all data sent, so no IRQ on IN
|
||||
}else if(a == 0){ // overfull
|
||||
if(lastdsz[ifno] < 0) send_next(ifno);
|
||||
}
|
||||
}
|
||||
if(buf[len-1] == '\n' && lastdsz[ifno] < 0) send_next(ifno);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int USB_putbyte(uint8_t byte){
|
||||
if(!CDCready) return FALSE;
|
||||
int USB_putbyte(uint8_t ifno, uint8_t byte){
|
||||
if(!CDCready[ifno]) return FALSE;
|
||||
int l = 0;
|
||||
while((l = RB_write((ringbuffer*)&rbout, &byte, 1)) != 1){
|
||||
if(l < 0) continue;
|
||||
uint32_t T0 = Tms;
|
||||
while((l = RB_write((ringbuffer*)&rbout[ifno], &byte, 1)) != 1){
|
||||
if(Tms - T0 > DISCONN_TMOUT){
|
||||
break_handler(ifno);
|
||||
return FALSE;
|
||||
}
|
||||
if(lastdsz == 0) send_next(); // need to run manually - all data sent, so no IRQ on IN
|
||||
if(!CDCready[ifno]) return FALSE;
|
||||
IWDG->KR = IWDG_REFRESH;
|
||||
if(l == 0){ // overfull
|
||||
if(lastdsz[ifno] < 0) send_next(ifno);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// send line if got EOL
|
||||
if(byte == '\n' && lastdsz[ifno] < 0) send_next(ifno);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int USB_sendstr(const char *string){
|
||||
if(!string || !CDCready) return FALSE;
|
||||
int len = 0;
|
||||
const char *b = string;
|
||||
while(*b++) ++len;
|
||||
int USB_sendstr(uint8_t ifno, const char *string){
|
||||
if(!string || !CDCready[ifno]) return FALSE;
|
||||
int len = strlen(string);
|
||||
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`
|
||||
* @return amount of received bytes (negative, if overfull happened)
|
||||
*/
|
||||
int USB_receive(uint8_t *buf, int len){
|
||||
chkin();
|
||||
if(bufovrfl){
|
||||
while(1 != RB_clearbuf((ringbuffer*)&rbin));
|
||||
bufovrfl = 0;
|
||||
int USB_receive(uint8_t ifno, uint8_t *buf, int len){
|
||||
chkin(ifno); // rxtx_handler could leave last message unwritten if buffer was busy
|
||||
if(bufovrfl[ifno]){
|
||||
while(1 != RB_clearbuf((ringbuffer*)&rbin[ifno])); // run watchdog in case of problems
|
||||
bufovrfl[ifno] = 0;
|
||||
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
|
||||
return sz;
|
||||
}
|
||||
@@ -227,17 +284,17 @@ int USB_receive(uint8_t *buf, int len){
|
||||
* @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)
|
||||
*/
|
||||
int USB_receivestr(char *buf, int len){
|
||||
chkin();
|
||||
if(bufovrfl){
|
||||
while(1 != RB_clearbuf((ringbuffer*)&rbin));
|
||||
bufovrfl = 0;
|
||||
int USB_receivestr(uint8_t ifno, char *buf, int len){
|
||||
chkin(ifno); // rxtx_handler could leave last message unwritten if buffer was busy
|
||||
if(bufovrfl[ifno]){
|
||||
while(1 != RB_clearbuf((ringbuffer*)&rbin[ifno]));
|
||||
bufovrfl[ifno] = 0;
|
||||
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(rbin.length == RB_datalen((ringbuffer*)&rbin)){ // buffer is full but no '\n' found
|
||||
while(1 != RB_clearbuf((ringbuffer*)&rbin));
|
||||
if(rbin[ifno].length == RB_datalen((ringbuffer*)&rbin[ifno])){ // buffer is full but no '\n' found
|
||||
while(1 != RB_clearbuf((ringbuffer*)&rbin[ifno]));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
@@ -246,4 +303,3 @@ int USB_receivestr(char *buf, int len){
|
||||
buf[l-1] = 0; // replace '\n' with strend
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include <stm32f3.h>
|
||||
#include "usb_lib.h"
|
||||
#include "usb_descr.h"
|
||||
|
||||
typedef struct {
|
||||
uint32_t dwDTERate;
|
||||
@@ -34,24 +35,29 @@ typedef struct {
|
||||
uint8_t bDataBits;
|
||||
} __attribute__ ((packed)) usb_LineCoding;
|
||||
|
||||
extern usb_LineCoding lineCoding;
|
||||
extern volatile uint8_t CDCready;
|
||||
extern volatile uint8_t CDCready[InterfacesAmount];
|
||||
|
||||
void break_handler();
|
||||
void clstate_handler(uint16_t val);
|
||||
void linecoding_handler(usb_LineCoding *lc);
|
||||
void break_handler(uint8_t ifno);
|
||||
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 - more than 2ms
|
||||
#define DISCONN_TMOUT (2)
|
||||
|
||||
// sizes of ringbuffers for outgoing and incoming data
|
||||
#define RBOUTSZ (1024)
|
||||
#define RBINSZ (1024)
|
||||
#define RBOUTSZ (256)
|
||||
#define RBINSZ (256)
|
||||
|
||||
#define newline() USB_putbyte('\n')
|
||||
#define USND(s) do{USB_sendstr(s); USB_putbyte('\n');}while(0)
|
||||
#define newline(ifno) USB_putbyte(ifno, '\n')
|
||||
#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_send(const uint8_t *buf, int len);
|
||||
int USB_putbyte(uint8_t byte);
|
||||
int USB_sendstr(const char *string);
|
||||
int USB_receive(uint8_t *buf, int len);
|
||||
int USB_receivestr(char *buf, int len);
|
||||
int USB_sendall(uint8_t ifno);
|
||||
int USB_send(uint8_t ifno, const uint8_t *buf, int len);
|
||||
int USB_putbyte(uint8_t ifno, uint8_t byte);
|
||||
int USB_sendstr(uint8_t ifno, const char *string);
|
||||
int USB_receive(uint8_t ifno, uint8_t *buf, int len);
|
||||
int USB_receivestr(uint8_t ifno, char *buf, int len);
|
||||
|
||||
@@ -356,6 +356,7 @@ void USB_setup(){
|
||||
USB->BCDR |= USB_BCDR_DPPU;
|
||||
NVIC_EnableIRQ(USB_IRQn);
|
||||
#endif
|
||||
setup_interfaces(); // refresh interfaces names
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
#define BUILD_NUMBER "8"
|
||||
#define BUILD_DATE "2025-12-28"
|
||||
#define BUILD_NUMBER "20"
|
||||
#define BUILD_DATE "2026-02-11"
|
||||
|
||||
Reference in New Issue
Block a user