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
|
- 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).
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
*/
|
*/
|
||||||
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;
|
||||||
|
|||||||
@@ -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(){
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
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){
|
else if(l){
|
||||||
const char *ans = parse_cmd(inbuff);
|
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"?>
|
<?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>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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(RX_FLAG(epstatus)){ // receive data
|
if(ifno > InterfacesAmount-1){
|
||||||
if(rcvbuflen){
|
return;
|
||||||
bufovrfl = 1; // lost last data
|
|
||||||
rcvbuflen = 0;
|
|
||||||
}
|
}
|
||||||
rcvbuflen = EP_Read(1, (uint8_t*)rcvbuf);
|
uint16_t epstatus = KEEP_DTOG(USB->EPnR[1+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
|
if(RX_FLAG(epstatus)){ // receive data
|
||||||
chkin(); // try to write current data into RXbuf if it's not busy
|
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
|
}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:
|
default:
|
||||||
|
// WTF?
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
// WTF?
|
||||||
if(dev2host) EP_WriteIRQ(0, NULL, 0);
|
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(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;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
#define BUILD_NUMBER "8"
|
#define BUILD_NUMBER "20"
|
||||||
#define BUILD_DATE "2025-12-28"
|
#define BUILD_DATE "2026-02-11"
|
||||||
|
|||||||
Reference in New Issue
Block a user