add (not checked) pre-pre-pre-pre...-alpha version of Multistepper

This commit is contained in:
Edward Emelianov 2023-02-03 23:05:43 +03:00
parent fecadc71f7
commit 756ed4be08
33 changed files with 18491 additions and 25 deletions

View File

@ -0,0 +1,8 @@
BINARY := canusb
# MCU code
MCU := F303xd
# change this linking script depending on particular MCU model,
LDSCRIPT := stm32f303xD.ld
include ../makefile.f3
include ../../makefile.stm32

View File

@ -1,4 +1,221 @@
Management of 8 independend steppers
====================================
Eighth stepper could be changed to 8 dependent multiplexed. Based on STM32F303VDT6.
# Pinout
(all GPIO outs are push-pull if not mentioned another)
### Sorted by pin number
|**Pin #**|**Pin name **| **function**| **settings** |**comment ** |
|---------|-------------|-------------|---------------|---------------------|
| 1 | PE2 | DIAG | in | `diag` output of TMC|
| 2 | PE3 | MUL0 | slow out | multiplexer address |
| 3 | PE4 | MUL1 | slow out | for DIAG input |
| 4 | PE5 | MUL2 | slow out | |
| 5 | PE6 | MUL EN | slow out | enable mul. |
| 6 | (vbat) | - | - | |
| 7 | PC13 | M0 L1 | slow in PU/out| enable motor |
| 8 | PC14 | M0 L0 | slow in PU | direction of motor |
| 9 | PC15 | M0 DIR | slow out | limit-switch 0 |
| 10 | PF9 | M0 STEP | AF | clock of motor |
| 11 | PF10 | M0 EN | slow out | l-s 0 or CS of SPI |
| 12 | (OSC IN) | | | |
| 13 | (OSC OUT) | | | |
| 14 | (nrst) | | | reset |
| 15 | PC0 | MOTMUL0 | slow out | external motors |
| 16 | PC1 | MOTMUL1 | slow out | multiplexer |
| 17 | PC2 | MOTMUL2 | slow out | |
| 18 | PC3 | MOTMUL EN | slow out | |
| 19 | PF2 | ADC2 in10 | ADC | 5V in / 2 |
| 20 | (VSSA) | | | |
| 21 | (VREF+) | | | |
| 22 | (VDDA) | | | |
| 23 | PA0 | ADC1 in1 | ADC | External ADC inputs |
| 24 | PA1 | ADC1 in2 | ADC | |
| 25 | PA2 | ADC1 in3 | ADC | |
| 26 | PA3 | ADC1 in4 | ADC | |
| 27 | (VSS) | | | |
| 28 | (VDD) | | | |
| 29 | PA4 | ADC2 in1 | ADC | Vdrive / 10 |
| 30 | PA5 | SPI1 SCK | AF | In case of SPI TMC |
| 31 | PA6 | SPI1 MISO | AF | |
| 32 | PA7 | SPI1 MOSI | AF | |
| 33 | PC4 | USART1 Tx | AF | External USART |
| 34 | PC5 | USART1 Rx | AF | |
| 35 | PB0 | M7 STEP | AF | |
| 36 | PB1 | M7 EN | slow out | |
| 37 | PB2 | M7 DIR | slow out | |
| 38 | PE7 | M7 L0 | slow in PU | |
| 39 | PE8 | M7 L1 | slow in PU/out| |
| 40 | PE9 | M6 STEP | AF | |
| 41 | PE10 | M6 L1 | slow in PU/out| |
| 42 | PE11 | M6 L0 | slow in PU | |
| 43 | PE12 | M6 DIR | slow out | |
| 44 | PE13 | M6 EN | slow out | |
| 45 | PE14 | OUT2 | slow in PU/out| external GPIO |
| 46 | PE15 | OUT1 | slow in PU/out| |
| 47 | PB10 | USART3 Tx | AF | USART for 4 of TMC |
| 48 | PB11 | OUT0 | slow in PU/out| |
| 49 | (VSS) | | | |
| 50 | (VDD) | | | |
| 51 | PB12 | SCRN DCRS | slow out | screen commands |
| 52 | PB13 | SCRN SCK | AF | SPI for screen |
| 53 | PB14 | SCRN MISO | AF | |
| 54 | PB15 | SCRN MOSI | AF | |
| 55 | PD8 | SCRN RST | slow out | reset screen |
| 56 | PD9 | SCRN CS | slow out | activate screen |
| 57 | PD10 | M5 L1 | slow in PU/out| |
| 58 | PD11 | M5 L0 | slow in PU | |
| 59 | PD12 | M5 STEP | AF | |
| 60 | PD13 | M5 DIR | slow out | |
| 61 | PD14 | M5 EN | slow out | |
| 62 | PD15 | M4 L1 | slow in PU/out| |
| 63 | PC6 | M4 STEP | AF | |
| 64 | PC7 | M4 L0 | slow in PU | |
| 65 | PC8 | M4 DIR | slow out | |
| 66 | PC9 | M4 EN | slow out | |
| 67 | PA8 | BTN0 | slow in PU | Buttons/joystick |
| 68 | PA9 | BTN1 | slow in PU | to operate with |
| 69 | PA10 | BTN2 (SDA) | slow in PU/AF | screen |
| 70 | PA11 | USB DM | AF | USB |
| 71 | PA12 | USB DP | AF | |
| 72 | PA13 | SWDIO/USBpu | dflt/slow out | USB pullup or dbg |
| 73 | PF6 | BTN3 (SCL) | slow in PU/AF | (possible I2C2) |
| 74 | (VSS) | | | |
| 75 | (VDD) | | | |
| 76 | PA14 | SWCLK | dflt | debug/sew |
| 77 | PA15 | M3 STEP | AF | |
| 78 | PC10 | M3 L0 | slow in PU | |
| 79 | PC11 | M3 L1 | slow in PU/out| |
| 80 | PC12 | M3 DIR | slow out | |
| 81 | PD0 | CAN Rx | AF | CAN bus |
| 82 | PD1 | CAN Tx | AF | |
| 83 | PD2 | M3 EN | slow out | |
| 84 | PD3 | BTN4 | slow in PU | |
| 85 | PD4 | BTN5 | slow in PU | |
| 86 | PD5 | BTN6 | slow in PU | |
| 87 | PD6 | M2 L1 | slow in PU | |
| 88 | PD7 | M2 L0 | slow in PU/out| |
| 89 | PB3 | USART2 Tx | AF | USART for 4 of TMC |
| 90 | PB4 | M2 DIR | slow out | |
| 91 | PB5 | M2 STEP | AF | |
| 92 | PB6 | M2 EN | slow out | |
| 93 | PB7 | M1 L1 | slow in PU/out| |
| 94 | (BOOT0) | | | boot mode |
| 95 | PB8 | M1 STEP | AF | |
| 96 | PB9 | M1 L0 | slow in PU | |
| 97 | PE0 | M1 DIR | slow out | |
| 98 | PE1 | M1 EN | slow out | |
| 99 | (VSS) | | | |
| 100 | (VDD) | | | |
### Some comments.
_DIAG_ input used to detect problems with TMC drivers (multiplexed by _MUL_ outputs).
Each motor have next control signals: _EN_ - enable driver, _DIR_ - rotation direction, _STEP_ - microstepping clock signal, _L0_ - zero end-switch, _L1_ - max end-switch (or Cable Select for SPI-based TMC).
_MOTMUL_ - external multiplexer or other GPIO signals (e.g. to manage of 64 stepper motors).
_ADC1 in_ - four external ADC signals (0..3.3V).
_SPI1_ used to manage TMC drivers in case of SPI-based.
_USART1_ - to connect external something (master or slave).
_OUT_ - external GPIO.
_USART2 Tx_ used to manage USART-based TMC (numbers 0-3).
_USART3 Tx_ used to manage USART-based TMC (numbers 4-7).
_SCRN_ - control signals for SPI TFT screen.
_BTN_ - external button, keypad, joystick etc. Two of them could be connected to I2C devices.
_USB_ and _CAN_ used as main device control buses.
_SW_ used as debugging/sewing; also (I remember about USB pullup only after end of PCB design) _SWDIO_ used as USB pullup (so the device have no USB in debug mode - when BTN0 and BTN1 are pressed at start).
### Sorted by ports (with AF numbers).
|**Pin #**|**Pin name **| **function**| **settings** |**comment ** |
|---------|-------------|-------------|---------------|---------------------|
| 23 | PA0 | ADC1 in1 | ADC | External ADC inputs |
| 24 | PA1 | ADC1 in2 | ADC | |
| 25 | PA2 | ADC1 in3 | ADC | |
| 26 | PA3 | ADC1 in4 | ADC | |
| 29 | PA4 | ADC2 in1 | ADC | Vdrive / 10 |
| 30 | PA5 | SPI1 SCK | AF5 | In case of SPI TMC |
| 31 | PA6 | SPI1 MISO | AF5 | |
| 32 | PA7 | SPI1 MOSI | AF5 | |
| 67 | PA8 | BTN0 | slow in PU | Buttons/joystick |
| 68 | PA9 | BTN1 | slow in PU | to operate with |
| 69 | PA10 | BTN2 (SDA) | slow in PU/AF4| screen |
| 70 | PA11 | USB DM | USB | USB |
| 71 | PA12 | USB DP | USB | |
| 72 | PA13 | SWDIO/USBpu | dflt/slow out | USB pullup or dbg |
| 76 | PA14 | SWCLK | dflt | debug/sew |
| 77 | PA15 | M3 STEP | AF1 (T2ch1) | |
| 35 | PB0 | M7 STEP | AF2 (T3ch3) | |
| 36 | PB1 | M7 EN | slow out | |
| 37 | PB2 | M7 DIR | slow out | |
| 89 | PB3 | USART2 Tx | AF7 | USART for 4 of TMC |
| 90 | PB4 | M2 DIR | slow out | |
| 91 | PB5 | M2 STEP | AF10 (T17ch1) | |
| 92 | PB6 | M2 EN | slow out | |
| 93 | PB7 | M1 L1 | slow in PU/out| |
| 95 | PB8 | M1 STEP | AF1 (T16ch1) | |
| 96 | PB9 | M1 L0 | slow in PU | |
| 47 | PB10 | USART3 Tx | AF7 | USART for 4 of TMC |
| 48 | PB11 | OUT0 | slow in PU/out| |
| 51 | PB12 | SCRN DCRS | slow out | screen commands |
| 52 | PB13 | SCRN SCK | AF5 | SPI for screen |
| 53 | PB14 | SCRN MISO | AF5 | |
| 54 | PB15 | SCRN MOSI | AF5 | |
| 15 | PC0 | MOTMUL0 | slow out | external motors |
| 16 | PC1 | MOTMUL1 | slow out | multiplexer |
| 17 | PC2 | MOTMUL2 | slow out | |
| 18 | PC3 | MOTMUL EN | slow out | |
| 33 | PC4 | USART1 Tx | AF7 | External USART |
| 34 | PC5 | USART1 Rx | AF7 | |
| 63 | PC6 | M4 STEP | AF4 (T8ch1) | |
| 64 | PC7 | M4 L0 | slow in PU | |
| 65 | PC8 | M4 DIR | slow out | |
| 66 | PC9 | M4 EN | slow out | |
| 78 | PC10 | M3 L0 | slow in PU | |
| 79 | PC11 | M3 L1 | slow in PU/out| |
| 80 | PC12 | M3 DIR | slow out | |
| 7 | PC13 | M0 L1 | slow in PU/out| enable motor |
| 8 | PC14 | M0 L0 | slow in PU | direction of motor |
| 9 | PC15 | M0 DIR | slow out | limit-switch 0 |
| 81 | PD0 | CAN Rx | AF7 | CAN bus |
| 82 | PD1 | CAN Tx | AF7 | |
| 83 | PD2 | M3 EN | slow out | |
| 84 | PD3 | BTN4 | slow in PU | |
| 85 | PD4 | BTN5 | slow in PU | |
| 86 | PD5 | BTN6 | slow in PU | |
| 87 | PD6 | M2 L1 | slow in PU | |
| 88 | PD7 | M2 L0 | slow in PU/out| |
| 55 | PD8 | SCRN RST | slow out | reset screen |
| 56 | PD9 | SCRN CS | slow out | activate screen |
| 57 | PD10 | M5 L1 | slow in PU/out| |
| 58 | PD11 | M5 L0 | slow in PU | |
| 59 | PD12 | M5 STEP | AF2 (T4ch1) | |
| 60 | PD13 | M5 DIR | slow out | |
| 61 | PD14 | M5 EN | slow out | |
| 62 | PD15 | M4 L1 | slow in PU/out| |
| 97 | PE0 | M1 DIR | slow out | |
| 98 | PE1 | M1 EN | slow out | |
| 1 | PE2 | DIAG | slow in PU | `diag` output of TMC|
| 2 | PE3 | MUL0 | slow out | multiplexer address |
| 3 | PE4 | MUL1 | slow out | for DIAG input |
| 4 | PE5 | MUL2 | slow out | |
| 5 | PE6 | MUL EN | slow out | enable mul. |
| 38 | PE7 | M7 L0 | slow in PU | |
| 39 | PE8 | M7 L1 | slow in PU/out| |
| 40 | PE9 | M6 STEP | AF2 (T1ch1) | |
| 41 | PE10 | M6 L1 | slow in PU/out| |
| 42 | PE11 | M6 L0 | slow in PU | |
| 43 | PE12 | M6 DIR | slow out | |
| 44 | PE13 | M6 EN | slow out | |
| 45 | PE14 | OUT2 | slow in PU/out| external GPIO |
| 46 | PE15 | OUT1 | slow in PU/out| |
| 19 | PF2 | ADC2 in10 | ADC | 5V in / 2 |
| 73 | PF6 | BTN3 (SCL) | slow in PU/AF4| (possible I2C2) |
| 10 | PF9 | M0 STEP | AF3 (T15ch1) | clock of motor |
| 11 | PF10 | M0 EN | slow out | l-s 0 or CS of SPI |
# Debug with SWD
Press both _BTN0_ and _BTN1_ and turn on device. In this case the USB won't be activated and you can use SWD. If both these buttons not pressed on poweron, device will use _SWDIO_ as USB pullup, so SWD connection in this mode won't be available.

378
F3:F303/Multistepper/can.c Normal file
View File

@ -0,0 +1,378 @@
/*
* This file is part of the multistepper project.
* Copyright 2023 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 "can.h"
#include "hardware.h"
#include "strfunc.h"
#include "usb.h"
// PD1 - Tx, PD0 - Rx !!!
#include <string.h> // memcpy
// circular buffer for received messages
static CAN_message messages[CAN_INMESSAGE_SIZE];
static uint8_t first_free_idx = 0; // index of first empty cell
static int8_t first_nonfree_idx = -1; // index of first data cell
static uint16_t oldspeed = 100; // speed of last init
uint32_t floodT = FLOOD_PERIOD_MS; // flood period in ms
static uint8_t incrflood = 0; // ==1 for incremental flooding
static uint32_t last_err_code = 0;
static CAN_status can_status = CAN_STOP;
static void can_process_fifo(uint8_t fifo_num);
static CAN_message loc_flood_msg;
static CAN_message *flood_msg = NULL; // == loc_flood_msg - to flood
CAN_status CAN_get_status(){
int st = can_status;
can_status = CAN_OK;
return st;
}
// push next message into buffer; return 1 if buffer overfull
static int CAN_messagebuf_push(CAN_message *msg){
//MSG("Try to push\n");
#ifdef EBUG
USB_sendstr("push\n");
#endif
if(first_free_idx == first_nonfree_idx){
#ifdef EBUG
USB_sendstr("INBUF OVERFULL\n");
#endif
return 1; // no free space
}
if(first_nonfree_idx < 0) first_nonfree_idx = 0; // first message in empty buffer
memcpy(&messages[first_free_idx++], msg, sizeof(CAN_message));
// need to roll?
if(first_free_idx == CAN_INMESSAGE_SIZE) first_free_idx = 0;
return 0;
}
// pop message from buffer
CAN_message *CAN_messagebuf_pop(){
if(first_nonfree_idx < 0) return NULL;
#ifdef EBUG
//MSG("read from idx "); printu(first_nonfree_idx); NL();
#endif
CAN_message *msg = &messages[first_nonfree_idx++];
if(first_nonfree_idx == CAN_INMESSAGE_SIZE) first_nonfree_idx = 0;
if(first_nonfree_idx == first_free_idx){ // buffer is empty - refresh it
first_nonfree_idx = -1;
first_free_idx = 0;
}
return msg;
}
void CAN_reinit(uint16_t speed){
CAN->TSR |= CAN_TSR_ABRQ0 | CAN_TSR_ABRQ1 | CAN_TSR_ABRQ2;
RCC->APB1RSTR |= RCC_APB1RSTR_CANRST;
RCC->APB1RSTR &= ~RCC_APB1RSTR_CANRST;
CAN_setup(speed);
}
/*
Can filtering: FSCx=0 (CAN->FS1R) -> 16-bit identifiers
MASK: FBMx=0 (CAN->FM1R), two filters (n in FR1 and n+1 in FR2)
ID: CAN->sFilterRegister[x].FRn[0..15]
MASK: CAN->sFilterRegister[x].FRn[16..31]
FR bits: STID[10:0] RTR IDE EXID[17:15]
LIST: FBMx=1, four filters (n&n+1 in FR1, n+2&n+3 in FR2)
IDn: CAN->sFilterRegister[x].FRn[0..15]
IDn+1: CAN->sFilterRegister[x].FRn[16..31]
*/
/*
Can timing: main freq - APB (PLL=48MHz)
segment = 1sync + TBS1 + TBS2, sample point is between TBS1 and TBS2,
so if TBS1=4 and TBS2=3, sum=8, bit sampling freq is 48/8 = 6MHz
-> to get 100kbps we need prescaler=60
250kbps - 24
500kbps - 12
1MBps - 6
*/
// speed - in kbps
// GPIO configured in hw_setup
void CAN_setup(uint16_t speed){
if(speed == 0) speed = oldspeed;
else if(speed < 50) speed = 50;
else if(speed > 3000) speed = 3000;
oldspeed = speed;
uint32_t tmout = 16000000;
/* Enable the peripheral clock CAN */
RCC->APB1ENR |= RCC_APB1ENR_CANEN;
/* Configure CAN */
/* (1) Enter CAN init mode to write the configuration */
/* (2) Wait the init mode entering */
/* (3) Exit sleep mode */
/* (4) Normal mode, set timing to 100kb/s: TBS1 = 4, TBS2 = 3, prescaler = 60 */
/* (5) Leave init mode */
/* (6) Wait the init mode leaving */
/* (7) Enter filter init mode, (16-bit + mask, bank 0 for FIFO 0) */
/* (8) Acivate filter 0 for two IDs */
/* (9) Identifier list mode */
/* (10) Set the Id list */
/* (12) Leave filter init */
/* (13) Set error interrupts enable (& bus off) */
CAN->MCR |= CAN_MCR_INRQ; /* (1) */
while((CAN->MSR & CAN_MSR_INAK) != CAN_MSR_INAK) /* (2) */
if(--tmout == 0) break;
CAN->MCR &=~ CAN_MCR_SLEEP; /* (3) */
CAN->MCR |= CAN_MCR_ABOM; /* allow automatically bus-off */
CAN->BTR = 2 << 20 | 3 << 16 | (4500/speed - 1); //| CAN_BTR_SILM | CAN_BTR_LBKM; /* (4) */
CAN->MCR &= ~CAN_MCR_INRQ; /* (5) */
tmout = 16000000;
while((CAN->MSR & CAN_MSR_INAK) == CAN_MSR_INAK) /* (6) */
if(--tmout == 0) break;
// accept ALL
CAN->FMR = CAN_FMR_FINIT; /* (7) */
CAN->FA1R = CAN_FA1R_FACT0 | CAN_FA1R_FACT1; /* (8) */
// set to 1 all needed bits of CAN->FFA1R to switch given filters to FIFO1
CAN->sFilterRegister[0].FR1 = (1<<21)|(1<<5); // all odd IDs
CAN->FFA1R = 2; // filter 1 for FIFO1, filter 0 - for FIFO0
CAN->sFilterRegister[1].FR1 = (1<<21); // all even IDs
CAN->FMR &= ~CAN_FMR_FINIT; /* (12) */
CAN->IER |= CAN_IER_ERRIE | CAN_IER_FOVIE0 | CAN_IER_FOVIE1 | CAN_IER_BOFIE; /* (13) */
/* Configure IT */
NVIC_SetPriority(USB_LP_CAN_RX0_IRQn, 0); // RX FIFO0 IRQ
NVIC_SetPriority(CAN_RX1_IRQn, 0); // RX FIFO1 IRQ
NVIC_SetPriority(CAN_SCE_IRQn, 0); // RX status changed IRQ
NVIC_EnableIRQ(USB_LP_CAN_RX0_IRQn);
NVIC_EnableIRQ(CAN_RX1_IRQn);
NVIC_EnableIRQ(CAN_SCE_IRQn);
CAN->MSR = 0; // clear SLAKI, WKUI, ERRI
can_status = CAN_READY;
}
void printCANerr(){
if(!last_err_code) last_err_code = CAN->ESR;
if(!last_err_code){
USB_sendstr("No errors\n");
return;
}
USB_sendstr("Receive error counter: ");
USB_sendstr(u2str((last_err_code & CAN_ESR_REC)>>24));
USB_sendstr("\nTransmit error counter: ");
USB_sendstr(u2str((last_err_code & CAN_ESR_TEC)>>16));
USB_sendstr("\nLast error code: ");
int lec = (last_err_code & CAN_ESR_LEC) >> 4;
const char *errmsg = "No";
switch(lec){
case 1: errmsg = "Stuff"; break;
case 2: errmsg = "Form"; break;
case 3: errmsg = "Ack"; break;
case 4: errmsg = "Bit recessive"; break;
case 5: errmsg = "Bit dominant"; break;
case 6: errmsg = "CRC"; break;
case 7: errmsg = "(set by software)"; break;
}
USB_sendstr(errmsg); USB_sendstr(" error\n");
if(last_err_code & CAN_ESR_BOFF) USB_sendstr("Bus off ");
if(last_err_code & CAN_ESR_EPVF) USB_sendstr("Passive error limit ");
if(last_err_code & CAN_ESR_EWGF) USB_sendstr("Error counter limit");
last_err_code = 0;
USB_putbyte('\n');
}
void can_proc(){
#ifdef EBUG
if(last_err_code){
USB_sendstr("Error, ESR=");
USB_sendstr(u2str(last_err_code));
USB_putbyte('\n');
last_err_code = 0;
}
#endif
// check for messages in FIFO0 & FIFO1
if(CAN->RF0R & CAN_RF0R_FMP0){
can_process_fifo(0);
}
if(CAN->RF1R & CAN_RF1R_FMP1){
can_process_fifo(1);
}
IWDG->KR = IWDG_REFRESH;
if(CAN->ESR & (CAN_ESR_BOFF | CAN_ESR_EPVF | CAN_ESR_EWGF)){ // much errors - restart CAN BUS
USB_sendstr("\nToo much errors, restarting CAN!\n");
printCANerr();
// request abort for all mailboxes
CAN->TSR |= CAN_TSR_ABRQ0 | CAN_TSR_ABRQ1 | CAN_TSR_ABRQ2;
// reset CAN bus
RCC->APB1RSTR |= RCC_APB1RSTR_CANRST;
RCC->APB1RSTR &= ~RCC_APB1RSTR_CANRST;
CAN_setup(0);
}
static uint32_t lastFloodTime = 0;
static uint32_t incrmessagectr = 0;
if(flood_msg && (Tms - lastFloodTime) >= floodT){ // flood every ~5ms
lastFloodTime = Tms;
can_send(flood_msg->data, flood_msg->length, flood_msg->ID);
}else if(incrflood && (Tms - lastFloodTime) >= floodT){
lastFloodTime = Tms;
if(CAN_OK == can_send((uint8_t*)&incrmessagectr, 4, flood_msg->ID)) ++incrmessagectr;
}
}
CAN_status can_send(uint8_t *msg, uint8_t len, uint16_t target_id){
uint8_t mailbox = 0;
// check first free mailbox
if(CAN->TSR & (CAN_TSR_TME)){
mailbox = (CAN->TSR & CAN_TSR_CODE) >> 24;
}else{ // no free mailboxes
#ifdef EBUG
USB_sendstr("No free mailboxes\n");
#endif
return CAN_BUSY;
}
#ifdef EBUG
USB_sendstr("Send data. Len="); USB_sendstr(u2str(len));
USB_sendstr(", tagid="); USB_sendstr(u2str(target_id));
USB_sendstr(", data=");
for(int i = 0; i < len; ++i){
USB_sendstr(" "); USB_sendstr(uhex2str(msg[i]));
}
USB_putbyte('\n');
#endif
CAN_TxMailBox_TypeDef *box = &CAN->sTxMailBox[mailbox];
uint32_t lb = 0, hb = 0;
switch(len){
case 8:
hb |= (uint32_t)msg[7] << 24;
__attribute__((fallthrough));
case 7:
hb |= (uint32_t)msg[6] << 16;
__attribute__((fallthrough));
case 6:
hb |= (uint32_t)msg[5] << 8;
__attribute__((fallthrough));
case 5:
hb |= (uint32_t)msg[4];
__attribute__((fallthrough));
case 4:
lb |= (uint32_t)msg[3] << 24;
__attribute__((fallthrough));
case 3:
lb |= (uint32_t)msg[2] << 16;
__attribute__((fallthrough));
case 2:
lb |= (uint32_t)msg[1] << 8;
__attribute__((fallthrough));
default:
lb |= (uint32_t)msg[0];
}
box->TDLR = lb;
box->TDHR = hb;
box->TDTR = len;
box->TIR = (target_id & 0x7FF) << 21 | CAN_TI0R_TXRQ;
return CAN_OK;
}
void set_flood(CAN_message *msg, int incr){
if(incr){ incrflood = 1; return; }
incrflood = 0;
if(!msg) flood_msg = NULL;
else{
memcpy(&loc_flood_msg, msg, sizeof(CAN_message));
flood_msg = &loc_flood_msg;
}
}
static void can_process_fifo(uint8_t fifo_num){
if(fifo_num > 1) return;
CAN_FIFOMailBox_TypeDef *box = &CAN->sFIFOMailBox[fifo_num];
volatile uint32_t *RFxR = (fifo_num) ? &CAN->RF1R : &CAN->RF0R;
#ifdef EBUG
USB_sendstr(u2str(*RFxR & CAN_RF0R_FMP0)); USB_sendstr(" messages in FIFO\n");
#endif
// read all
while(*RFxR & CAN_RF0R_FMP0){ // amount of messages pending
// CAN_RDTxR: (16-31) - timestamp, (8-15) - filter match index, (0-3) - data length
/* TODO: check filter match index if more than one ID can receive */
CAN_message msg;
uint8_t *dat = msg.data;
uint8_t len = box->RDTR & 0x0f;
msg.length = len;
msg.ID = box->RIR >> 21;
//msg.filterNo = (box->RDTR >> 8) & 0xff;
//msg.fifoNum = fifo_num;
if(len){ // message can be without data
uint32_t hb = box->RDHR, lb = box->RDLR;
switch(len){
case 8:
dat[7] = hb>>24;
__attribute__((fallthrough));
case 7:
dat[6] = (hb>>16) & 0xff;
__attribute__((fallthrough));
case 6:
dat[5] = (hb>>8) & 0xff;
__attribute__((fallthrough));
case 5:
dat[4] = hb & 0xff;
__attribute__((fallthrough));
case 4:
dat[3] = lb>>24;
__attribute__((fallthrough));
case 3:
dat[2] = (lb>>16) & 0xff;
__attribute__((fallthrough));
case 2:
dat[1] = (lb>>8) & 0xff;
__attribute__((fallthrough));
case 1:
dat[0] = lb & 0xff;
}
}
if(CAN_messagebuf_push(&msg)) return; // error: buffer is full, try later
*RFxR |= CAN_RF0R_RFOM0; // release fifo for access to next message
}
//if(*RFxR & CAN_RF0R_FULL0) *RFxR &= ~CAN_RF0R_FULL0;
*RFxR = 0; // clear FOVR & FULL
}
void usb_lp_can1_rx0_isr(){ // Rx FIFO0 (overrun)
if(CAN->RF0R & CAN_RF0R_FOVR0){ // FIFO overrun
CAN->RF0R &= ~CAN_RF0R_FOVR0;
can_status = CAN_FIFO_OVERRUN;
}
}
void can1_rx1_isr(){ // Rx FIFO1 (overrun)
if(CAN->RF1R & CAN_RF1R_FOVR1){
CAN->RF1R &= ~CAN_RF1R_FOVR1;
can_status = CAN_FIFO_OVERRUN;
}
}
void can1_sce_isr(){ // status changed
if(CAN->MSR & CAN_MSR_ERRI){ // Error
#ifdef EBUG
last_err_code = CAN->ESR;
#endif
CAN->MSR &= ~CAN_MSR_ERRI;
// request abort for problem mailbox
if(CAN->TSR & CAN_TSR_TERR0) CAN->TSR |= CAN_TSR_ABRQ0;
if(CAN->TSR & CAN_TSR_TERR1) CAN->TSR |= CAN_TSR_ABRQ1;
if(CAN->TSR & CAN_TSR_TERR2) CAN->TSR |= CAN_TSR_ABRQ2;
can_status = CAN_ERR;
}
}

View File

@ -0,0 +1,59 @@
/*
* This file is part of the multistepper project.
* Copyright 2023 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 <stdint.h>
// amount of filter banks in STM32F0
#define STM32F0FBANKNO 28
// flood period in milliseconds
#define FLOOD_PERIOD_MS 5
// incoming message buffer size
#define CAN_INMESSAGE_SIZE (8)
extern uint32_t floodT;
// CAN message
typedef struct{
uint8_t data[8]; // up to 8 bytes of data
uint8_t length; // data length
uint16_t ID; // ID of receiver
} CAN_message;
typedef enum{
CAN_STOP,
CAN_READY,
CAN_BUSY,
CAN_OK,
CAN_ERR,
CAN_FIFO_OVERRUN
} CAN_status;
CAN_status CAN_get_status();
void CAN_reinit(uint16_t speed);
void CAN_setup(uint16_t speed);
CAN_status can_send(uint8_t *msg, uint8_t len, uint16_t target_id);
void can_proc();
void printCANerr();
CAN_message *CAN_messagebuf_pop();
void set_flood(CAN_message *msg, int incr);

BIN
F3:F303/Multistepper/canusb.bin Executable file

Binary file not shown.

View File

@ -0,0 +1,122 @@
/*
* This file is part of the multistepper project.
* Copyright 2023 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"
// Buttons: PA8, PA9, PA10, PF6, PD3, PD4, PD5, pullup (active - 0)
volatile GPIO_TypeDef* const BTNports[BTNSNO] = {GPIOA, GPIOA, GPIOA, GPIOF, GPIOD, GPIOD, GPIOD};
const uint32_t BTNpins[BTNSNO] = {1<<8, 1<<9, 1<<10, 1<<6, 1<<3, 1<<4, 1<<5};
// setup here ALL GPIO pins (due to table in Readme.md)
// leave SWD as default AF; high speed for CLK and some other AF; med speed for some another AF
TRUE_INLINE void gpio_setup(){
RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | RCC_AHBENR_GPIOCEN | RCC_AHBENR_GPIODEN
| RCC_AHBENR_GPIOEEN | RCC_AHBENR_GPIOFEN;
for(int i = 0; i < 10000; ++i) nop();
GPIOA->ODR = 0;
GPIOA->AFR[0] = AFRf(5, 5) | AFRf(5, 6) | AFRf(5, 7);
GPIOA->AFR[1] = /*AFRf(4, 10) |*/ AFRf(14, 11) | AFRf(14,12) | AFRf(1, 15);
GPIOA->MODER = MODER_AF(5) | MODER_AF(6) | MODER_AF(7) | MODER_I(8) | MODER_I(9) | MODER_I(10) /*MODER_AF(10)*/
| MODER_AF(11) | MODER_AF(12) | MODER_O(13) | MODER_AF(14) | MODER_AF(15);
GPIOA->OSPEEDR = OSPEED_MED(5) | OSPEED_MED(6) | OSPEED_MED(7) | OSPEED_HI(11) | OSPEED_HI(12)
| OSPEED_HI(13) | OSPEED_HI(15);
GPIOA->OTYPER = 0;
GPIOA->PUPDR = PUPD_PU(8) | PUPD_PU(9) | PUPD_PU(10);
GPIOB->ODR = 0;
GPIOB->AFR[0] = AFRf(2, 0) | AFRf(7, 3) | AFRf(10, 5);
GPIOB->AFR[1] = AFRf(1, 8) | AFRf(7, 10) | AFRf(5, 13) | AFRf(5, 14) | AFRf(5, 15);
GPIOB->MODER = MODER_AF(0) | MODER_O(1) | MODER_O(2) | MODER_AF(3) | MODER_O(4) | MODER_AF(5) | MODER_O(6)
| MODER_I(7) | MODER_AF(8) | MODER_I(9) | MODER_AF(10) | MODER_I(11) | MODER_O(12) | MODER_AF(13)
| MODER_AF(14) | MODER_AF(15);
GPIOB->OSPEEDR = OSPEED_HI(0) | OSPEED_HI(5) | OSPEED_HI(8) | OSPEED_MED(13) | OSPEED_MED(14) | OSPEED_MED(15);
GPIOB->OTYPER = 0;
GPIOB->PUPDR = PUPD_PU(7) | PUPD_PU(9) | PUPD_PU(11);
GPIOC->ODR = 0;
GPIOC->AFR[0] = AFRf(7, 4) | AFRf(7, 5) | AFRf(4, 6);
GPIOC->MODER = MODER_O(0) | MODER_O(1) | MODER_O(2) | MODER_O(3) | MODER_AF(4) | MODER_AF(5) | MODER_AF(6)
| MODER_I(7) | MODER_O(8) | MODER_O(9) | MODER_I(10) | MODER_I(11) | MODER_O(12) | MODER_I(13)
| MODER_I(14) | MODER_O(15);
GPIOC->OSPEEDR = OSPEED_HI(6);
GPIOC->OTYPER = 0;
GPIOC->PUPDR = PUPD_PU(7) | PUPD_PU(10) | PUPD_PU(11) | PUPD_PU(13) | PUPD_PU(14);
GPIOD->ODR = 0;
GPIOD->AFR[0] = AFRf(7, 0) | AFRf(7, 1);
GPIOD->AFR[1] = AFRf(2, 12);
GPIOD->MODER = MODER_AF(0) | MODER_AF(1) | MODER_O(2) | MODER_I(3) | MODER_I(4) | MODER_I(5) | MODER_I(6)
| MODER_I(7) | MODER_O(8) | MODER_O(9) | MODER_I(10) | MODER_I(11) | MODER_AF(12) | MODER_O(13)
| MODER_O(14) | MODER_I(15);
GPIOD->OSPEEDR = OSPEED_HI(0) | OSPEED_HI(1) | OSPEED_HI(12);
GPIOD->OTYPER = 0;
GPIOD->PUPDR = PUPD_PU(3) | PUPD_PU(4) | PUPD_PU(5) | PUPD_PU(6) | PUPD_PU(7) | PUPD_PU(10) | PUPD_PU(11)
| PUPD_PU(15);
GPIOE->ODR = 0;
GPIOE->AFR[1] = AFRf(2, 9);
GPIOE->MODER = MODER_O(0) | MODER_O(1) | MODER_I(2) | MODER_O(3) | MODER_O(4) | MODER_O(5) | MODER_O(6)
| MODER_I(7) | MODER_I(8) | MODER_AF(9) | MODER_I(10) | MODER_I(11) | MODER_O(12) | MODER_O(13)
| MODER_I(14) | MODER_I(15);
GPIOE->OSPEEDR = OSPEED_HI(9);
GPIOE->OTYPER = 0;
GPIOE->PUPDR = PUPD_PU(2) | PUPD_PU(7) | PUPD_PU(8) | PUPD_PU(10) | PUPD_PU(11) | PUPD_PU(14) | PUPD_PU(15);
GPIOF->ODR = 0;
/*GPIOF->AFR[0] = AFRf(4, 6);*/
GPIOF->AFR[1] = AFRf(3, 9);
GPIOF->MODER = MODER_I(6) | MODER_AF(9) | MODER_O(10);
GPIOF->OSPEEDR = OSPEED_HI(9);
GPIOF->OTYPER = 0;
GPIOF->PUPDR = PUPD_PU(6);
USBPU_OFF();
}
#ifndef EBUG
TRUE_INLINE void iwdg_setup(){
uint32_t tmout = 16000000;
/* Enable the peripheral clock RTC */
/* (1) Enable the LSI (40kHz) */
/* (2) Wait while it is not ready */
RCC->CSR |= RCC_CSR_LSION; /* (1) */
while((RCC->CSR & RCC_CSR_LSIRDY) != RCC_CSR_LSIRDY){if(--tmout == 0) break;} /* (2) */
/* Configure IWDG */
/* (1) Activate IWDG (not needed if done in option bytes) */
/* (2) Enable write access to IWDG registers */
/* (3) Set prescaler by 64 (1.6ms for each tick) */
/* (4) Set reload value to have a rollover each 2s */
/* (5) Check if flags are reset */
/* (6) Refresh counter */
IWDG->KR = IWDG_START; /* (1) */
IWDG->KR = IWDG_WRITE_ACCESS; /* (2) */
IWDG->PR = IWDG_PR_PR_1; /* (3) */
IWDG->RLR = 1250; /* (4) */
tmout = 16000000;
while(IWDG->SR){if(--tmout == 0) break;} /* (5) */
IWDG->KR = IWDG_REFRESH; /* (6) */
}
#endif
void hw_setup(){
gpio_setup();
#ifndef EBUG
iwdg_setup();
#endif
}

View File

@ -0,0 +1,40 @@
/*
* This file is part of the multistepper project.
* Copyright 2023 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>
#define USBPU_port GPIOA
#define USBPU_pin (1<<13)
#define USBPU_ON() pin_set(USBPU_port, USBPU_pin)
#define USBPU_OFF() pin_clear(USBPU_port, USBPU_pin)
// Buttons amount
#define BTNSNO (7)
// Buttons ports & pins
extern volatile GPIO_TypeDef* const BTNports[BTNSNO];
extern const uint32_t BTNpins[BTNSNO];
// state 1 - pressed, 0 - released (pin active is zero)
#define BTN_state(x) ((BTNports[x]->IDR & BTNpins[x]) ? 0 : 1)
extern volatile uint32_t Tms;
void hw_setup();

View File

@ -0,0 +1,81 @@
/*
* This file is part of the multistepper project.
* Copyright 2023 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 "can.h"
#include "hardware.h"
#include "proto.h"
#include "usb.h"
#define MAXSTRLEN RBINSZ
volatile uint32_t Tms = 0;
void sys_tick_handler(void){
++Tms;
}
int main(void){
char inbuff[MAXSTRLEN+1];
if(StartHSE()){
SysTick_Config((uint32_t)72000); // 1ms
}else{
StartHSI();
SysTick_Config((uint32_t)48000); // 1ms
}
hw_setup();
// here we should check the debug mode and turn off SWD if no
if(BTN_state(0) && BTN_state(1)){ // NO debug - turn ON SWDIO (AF0)
GPIOA->MODER = (GPIOA->MODER & ~MODER_CLR(13)) | MODER_AF(13);
}else{
USB_setup();
USBPU_ON();
}
CAN_setup(100);
uint32_t ctr = 0;
CAN_message *can_mesg;
while(1){
IWDG->KR = IWDG_REFRESH;
if(Tms - ctr > 499){
ctr = Tms;
}
can_proc();
USB_proc();
if(CAN_get_status() == CAN_FIFO_OVERRUN){
USB_sendstr("CAN bus fifo overrun occured!\n");
}
while((can_mesg = CAN_messagebuf_pop())){
if(can_mesg && isgood(can_mesg->ID)){
if(ShowMsgs){ // display message content
IWDG->KR = IWDG_REFRESH;
uint8_t len = can_mesg->length;
printu(Tms);
USB_sendstr(" #");
printuhex(can_mesg->ID);
for(uint8_t i = 0; i < len; ++i){
USB_putbyte(' ');
printuhex(can_mesg->data[i]);
}
USB_putbyte('\n');
}
}
}
int l = USB_receivestr(inbuff, MAXSTRLEN);
if(l < 0) USB_sendstr("ERROR: USB buffer overflow or string was too long\n");
else if(l) cmd_parser(inbuff);
}
}

View File

@ -0,0 +1 @@
-std=c17

View File

@ -0,0 +1,7 @@
// Add predefined macros for your project here. For example:
// #define THE_ANSWER 42
#define EBUG
#define STM32F3
#define STM32F303xd
#define __thumb2__ 1
#define __ARM_ARCH_7M__

View File

@ -0,0 +1 @@
[General]

View File

@ -0,0 +1,171 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 8.0.2, 2023-01-18T20:42:42. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{7bd84e39-ca37-46d3-be9d-99ebea85bc0d}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="int">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">KOI8-R</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="bool" key="EditorConfiguration.PreferSingleLineComments">false</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="bool" key="EditorConfiguration.UseIndenter">false</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">false</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="QString" key="EditorConfiguration.ignoreFileTypes">*.md, *.MD, Makefile</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
<value type="bool" key="EditorConfiguration.skipTrailingWhitespace">true</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap">
<valuemap type="QVariantMap" key="AutoTest.ActiveFrameworks">
<value type="bool" key="AutoTest.Framework.Boost">true</value>
<value type="bool" key="AutoTest.Framework.CTest">false</value>
<value type="bool" key="AutoTest.Framework.Catch">true</value>
<value type="bool" key="AutoTest.Framework.GTest">true</value>
<value type="bool" key="AutoTest.Framework.QtQuickTest">true</value>
<value type="bool" key="AutoTest.Framework.QtTest">true</value>
</valuemap>
<valuemap type="QVariantMap" key="AutoTest.CheckStates"/>
<value type="int" key="AutoTest.RunAfterBuild">0</value>
<value type="bool" key="AutoTest.UseGlobal">true</value>
<valuemap type="QVariantMap" key="ClangTools">
<value type="bool" key="ClangTools.AnalyzeOpenFiles">true</value>
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
<value type="int" key="ClangTools.ParallelJobs">2</value>
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
</valuemap>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="DeviceType">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{65a14f9e-e008-4c1b-89df-4eaa4774b6e3}</value>
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/Big/Data/00__Electronics/STM32/F303-nolib/blink</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">all</value>
</valuelist>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Сборка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Очистка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Очистка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Default</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericBuildConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Развёртывание</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Развёртывание</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="int">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">22</value>
</data>
<data>
<variable>Version</variable>
<value type="int">22</value>
</data>
</qtcreator>

View File

@ -0,0 +1 @@
-std=c++17

View File

@ -0,0 +1,17 @@
can.c
can.h
hardware.c
hardware.h
main.c
proto.c
proto.h
ringbuffer.c
ringbuffer.h
strfunc.c
strfunc.h
usb.c
usb.h
usb_lib.c
usb_lib.h
usbhw.c
usbhw.h

View File

@ -0,0 +1,6 @@
.
../inc
../inc/Fx
../inc/cm
../inc/ld
../inc/startup

View File

@ -0,0 +1,119 @@
# SPDX-License-Identifier: GPL-2.0-or-later
# script for stm32f3x family
#
# stm32 devices support both JTAG and SWD transports.
#
source [find interface/stlink-v2-1.cfg]
source [find target/swj-dp.tcl]
source [find mem_helper.tcl]
if { [info exists CHIPNAME] } {
set _CHIPNAME $CHIPNAME
} else {
set _CHIPNAME stm32f3x
}
set _ENDIAN little
# Work-area is a space in RAM used for flash programming
# By default use 16kB
if { [info exists WORKAREASIZE] } {
set _WORKAREASIZE $WORKAREASIZE
} else {
set _WORKAREASIZE 0x4000
}
# JTAG speed should be <= F_CPU/6. F_CPU after reset is 8MHz, so use F_JTAG = 1MHz
#
# Since we may be running of an RC oscilator, we crank down the speed a
# bit more to be on the safe side. Perhaps superstition, but if are
# running off a crystal, we can run closer to the limit. Note
# that there can be a pretty wide band where things are more or less stable.
adapter speed 1000
adapter srst delay 100
if {[using_jtag]} {
jtag_ntrst_delay 100
}
#jtag scan chain
if { [info exists CPUTAPID] } {
set _CPUTAPID $CPUTAPID
} else {
if { [using_jtag] } {
# See STM Document RM0316
# Section 29.6.3 - corresponds to Cortex-M4 r0p1
set _CPUTAPID 0x4ba00477
} {
set _CPUTAPID 0x2ba01477
}
}
swj_newdap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPUTAPID
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu
if {[using_jtag]} {
jtag newtap $_CHIPNAME bs -irlen 5
}
set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap
$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0
set _FLASHNAME $_CHIPNAME.flash
flash bank $_FLASHNAME stm32f1x 0 0 0 0 $_TARGETNAME
reset_config srst_nogate
if {![using_hla]} {
# if srst is not fitted use SYSRESETREQ to
# perform a soft reset
cortex_m reset_config sysresetreq
}
proc stm32f3x_default_reset_start {} {
# Reset clock is HSI (8 MHz)
adapter speed 1000
}
proc stm32f3x_default_examine_end {} {
# Enable debug during low power modes (uses more power)
mmw 0xe0042004 0x00000007 0 ;# DBGMCU_CR |= DBG_STANDBY | DBG_STOP | DBG_SLEEP
# Stop watchdog counters during halt
mmw 0xe0042008 0x00001800 0 ;# DBGMCU_APB1_FZ |= DBG_IWDG_STOP | DBG_WWDG_STOP
}
proc stm32f3x_default_reset_init {} {
# Configure PLL to boost clock to HSI x 8 (64 MHz)
mww 0x40021004 0x00380400 ;# RCC_CFGR = PLLMUL[3:1] | PPRE1[2]
mmw 0x40021000 0x01000000 0 ;# RCC_CR |= PLLON
mww 0x40022000 0x00000012 ;# FLASH_ACR = PRFTBE | LATENCY[1]
sleep 10 ;# Wait for PLL to lock
mmw 0x40021004 0x00000002 0 ;# RCC_CFGR |= SW[1]
# Boost JTAG frequency
adapter speed 8000
}
# Default hooks
$_TARGETNAME configure -event examine-end { stm32f3x_default_examine_end }
$_TARGETNAME configure -event reset-start { stm32f3x_default_reset_start }
$_TARGETNAME configure -event reset-init { stm32f3x_default_reset_init }
tpiu create $_CHIPNAME.tpiu -dap $_CHIPNAME.dap -ap-num 0 -baseaddr 0xE0040000
lappend _telnet_autocomplete_skip _proc_pre_enable_$_CHIPNAME.tpiu
proc _proc_pre_enable_$_CHIPNAME.tpiu {_targetname} {
targets $_targetname
# Set TRACE_IOEN; TRACE_MODE is set to async; when using sync
# change this value accordingly to configure trace pins
# assignment
mmw 0xe0042004 0x00000020 0
}
$_CHIPNAME.tpiu configure -event pre-enable "_proc_pre_enable_$_CHIPNAME.tpiu $_TARGETNAME"

View File

@ -0,0 +1,423 @@
/*
* This file is part of the multistepper project.
* Copyright 2023 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>
#include "can.h"
#include "hardware.h"
#include "proto.h"
#include "version.inc"
// software ignore buffer size
#define IGN_SIZE 10
extern volatile uint32_t Tms;
uint8_t ShowMsgs = 1;
// software ignore buffers
static uint16_t Ignore_IDs[IGN_SIZE];
static uint8_t IgnSz = 0;
// parse `txt` to CAN_message
static CAN_message *parseCANmsg(const char *txt){
static CAN_message canmsg;
uint32_t N;
int ctr = -1;
canmsg.ID = 0xffff;
do{
const char *n = getnum(txt, &N);
if(txt == n) break;
txt = n;
if(ctr == -1){
if(N > 0x7ff){
USB_sendstr("ID should be 11-bit number!\n");
return NULL;
}
canmsg.ID = (uint16_t)(N&0x7ff);
ctr = 0;
continue;
}
if(ctr > 7){
USB_sendstr("ONLY 8 data bytes allowed!\n");
return NULL;
}
if(N > 0xff){
USB_sendstr("Every data portion is a byte!\n");
return NULL;
}
canmsg.data[ctr++] = (uint8_t)(N&0xff);
}while(1);
if(canmsg.ID == 0xffff){
USB_sendstr("NO ID given, send nothing!\n");
return NULL;
}
USB_sendstr("Message parsed OK\n");
canmsg.length = (uint8_t) ctr;
return &canmsg;
}
// USB_sendstr command, format: ID (hex/bin/dec) data bytes (up to 8 bytes, space-delimeted)
TRUE_INLINE void USB_sendstrCANcommand(char *txt){
if(CAN->MSR & CAN_MSR_INAK){
USB_sendstr("CAN bus is off, try to restart it\n");
return;
}
CAN_message *msg = parseCANmsg(txt);
if(!msg) return;
uint32_t N = 5;
while(CAN_BUSY == can_send(msg->data, msg->length, msg->ID)){
if(--N == 0) break;
}
}
TRUE_INLINE void CANini(const char *txt){
uint32_t N;
const char *n = getnum(txt, &N);
if(txt == n){
USB_sendstr("No speed given");
return;
}
if(N < 50){
USB_sendstr("Lowest speed is 50kbps");
return;
}else if(N > 3000){
USB_sendstr("Highest speed is 3000kbps");
return;
}
CAN_reinit((uint16_t)N);
USB_sendstr("Reinit CAN bus with speed ");
printu(N); USB_sendstr("kbps");
}
TRUE_INLINE void addIGN(const char *txt){
if(IgnSz == IGN_SIZE){
USB_sendstr("Ignore buffer is full");
return;
}
txt = omit_spaces(txt);
uint32_t N;
const char *n = getnum(txt, &N);
if(txt == n){
USB_sendstr("No ID given");
return;
}
if(N > 0x7ff){
USB_sendstr("ID should be 11-bit number!");
return;
}
Ignore_IDs[IgnSz++] = (uint16_t)(N & 0x7ff);
USB_sendstr("Added ID "); printu(N);
USB_sendstr("\nIgn buffer size: "); printu(IgnSz);
}
TRUE_INLINE void print_ign_buf(){
if(IgnSz == 0){
USB_sendstr("Ignore buffer is empty");
return;
}
USB_sendstr("Ignored IDs:\n");
for(int i = 0; i < IgnSz; ++i){
printu(i);
USB_sendstr(": ");
printuhex(Ignore_IDs[i]);
USB_putbyte('\n');
}
}
// print ID/mask of CAN->sFilterRegister[x] half
static void printID(uint16_t FRn){
if(FRn & 0x1f) return; // trash
printuhex(FRn >> 5);
}
/*
Can filtering: FSCx=0 (CAN->FS1R) -> 16-bit identifiers
CAN->FMR = (sb)<<8 | FINIT - init filter in starting bank sb
CAN->FFA1R FFAx = 1 -> FIFO1, 0 -> FIFO0
CAN->FA1R FACTx=1 - filter active
MASK: FBMx=0 (CAN->FM1R), two filters (n in FR1 and n+1 in FR2)
ID: CAN->sFilterRegister[x].FRn[0..15]
MASK: CAN->sFilterRegister[x].FRn[16..31]
FR bits: STID[10:0] RTR IDE EXID[17:15]
LIST: FBMx=1, four filters (n&n+1 in FR1, n+2&n+3 in FR2)
IDn: CAN->sFilterRegister[x].FRn[0..15]
IDn+1: CAN->sFilterRegister[x].FRn[16..31]
*/
TRUE_INLINE void list_filters(){
uint32_t fa = CAN->FA1R, ctr = 0, mask = 1;
while(fa){
if(fa & 1){
USB_sendstr("Filter "); printu(ctr); USB_sendstr(", FIFO");
if(CAN->FFA1R & mask) USB_sendstr("1");
else USB_sendstr("0");
USB_sendstr(" in ");
if(CAN->FM1R & mask){ // up to 4 filters in LIST mode
USB_sendstr("LIST mode, IDs: ");
printID(CAN->sFilterRegister[ctr].FR1 & 0xffff);
USB_sendstr(" ");
printID(CAN->sFilterRegister[ctr].FR1 >> 16);
USB_sendstr(" ");
printID(CAN->sFilterRegister[ctr].FR2 & 0xffff);
USB_sendstr(" ");
printID(CAN->sFilterRegister[ctr].FR2 >> 16);
}else{ // up to 2 filters in MASK mode
USB_sendstr("MASK mode: ");
if(!(CAN->sFilterRegister[ctr].FR1&0x1f)){
USB_sendstr("ID="); printID(CAN->sFilterRegister[ctr].FR1 & 0xffff);
USB_sendstr(", MASK="); printID(CAN->sFilterRegister[ctr].FR1 >> 16);
USB_sendstr(" ");
}
if(!(CAN->sFilterRegister[ctr].FR2&0x1f)){
USB_sendstr("ID="); printID(CAN->sFilterRegister[ctr].FR2 & 0xffff);
USB_sendstr(", MASK="); printID(CAN->sFilterRegister[ctr].FR2 >> 16);
}
}
USB_putbyte('\n');
}
fa >>= 1;
++ctr;
mask <<= 1;
}
}
TRUE_INLINE void setfloodt(const char *s){
uint32_t N;
s = omit_spaces(s);
const char *n = getnum(s, &N);
if(s == n){
USB_sendstr("t="); printu(floodT); USB_putbyte('\n');
return;
}
floodT = N;
}
/**
* @brief add_filter - add/modify filter
* @param str - string in format "bank# FIFO# mode num0 .. num3"
* where bank# - 0..27
* if there's nothing after bank# - delete filter
* FIFO# - 0,1
* mode - 'I' for ID, 'M' for mask
* num0..num3 - IDs in ID mode, ID/MASK for mask mode
*/
static void add_filter(const char *str){
uint32_t N;
str = omit_spaces(str);
const char *n = getnum(str, &N);
if(n == str){
USB_sendstr("No bank# given");
return;
}
if(N > STM32F0FBANKNO-1){
USB_sendstr("bank# > 27");
return;
}
uint8_t bankno = (uint8_t)N;
str = omit_spaces(n);
if(!*str){ // deactivate filter
USB_sendstr("Deactivate filters in bank ");
printu(bankno);
CAN->FMR = CAN_FMR_FINIT;
CAN->FA1R &= ~(1<<bankno);
CAN->FMR &=~ CAN_FMR_FINIT;
return;
}
uint8_t fifono = 0;
if(*str == '1') fifono = 1;
else if(*str != '0'){
USB_sendstr("FIFO# is 0 or 1");
return;
}
str = omit_spaces(str + 1);
const char c = *str;
uint8_t mode = 0; // ID
if(c == 'M' || c == 'm') mode = 1;
else if(c != 'I' && c != 'i'){
USB_sendstr("mode is 'M/m' for MASK and 'I/i' for IDLIST");
return;
}
str = omit_spaces(str + 1);
uint32_t filters[4];
uint32_t nfilt;
for(nfilt = 0; nfilt < 4; ++nfilt){
n = getnum(str, &N);
if(n == str) break;
filters[nfilt] = N;
str = omit_spaces(n);
}
if(nfilt == 0){
USB_sendstr("You should add at least one filter!");
return;
}
if(mode && (nfilt&1)){
USB_sendstr("In MASK mode you should point pairs of ID/MASK");
return;
}
CAN->FMR = CAN_FMR_FINIT;
uint32_t mask = 1<<bankno;
CAN->FA1R |= mask; // activate given filter
if(fifono) CAN->FFA1R |= mask; // set FIFO number
else CAN->FFA1R &= ~mask;
if(mode) CAN->FM1R &= ~mask; // MASK
else CAN->FM1R |= mask; // LIST
uint32_t F1 = (0x8f<<16);
uint32_t F2 = (0x8f<<16);
// reset filter registers to wrong value
CAN->sFilterRegister[bankno].FR1 = (0x8f<<16) | 0x8f;
CAN->sFilterRegister[bankno].FR2 = (0x8f<<16) | 0x8f;
switch(nfilt){
case 4:
F2 = filters[3] << 21;
// fallthrough
case 3:
CAN->sFilterRegister[bankno].FR2 = (F2 & 0xffff0000) | (filters[2] << 5);
// fallthrough
case 2:
F1 = filters[1] << 21;
// fallthrough
case 1:
CAN->sFilterRegister[bankno].FR1 = (F1 & 0xffff0000) | (filters[0] << 5);
}
CAN->FMR &=~ CAN_FMR_FINIT;
USB_sendstr("Added filter with ");
printu(nfilt); USB_sendstr(" parameters");
}
const char *helpstring =
"https://github.com/eddyem/stm32samples/tree/master/F3:F303/CANusb build#" BUILD_NUMBER " @ " BUILD_DATE "\n"
"'a' - add ID to ignore list (max 10 IDs)\n"
"'b' - reinit CAN with given baudrate\n"
"'c' - get CAN status\n"
"'d' - delete ignore list\n"
"'e' - get CAN errcodes\n"
"'f' - add/delete filter, format: bank# FIFO# mode(M/I) num0 [num1 [num2 [num3]]]\n"
"'F' - send/clear flood message: F ID byte0 ... byteN\n"
"'i' - send incremental flood message (ID == ID for `F`)\n"
"'I' - reinit CAN\n"
"'l' - list all active filters\n"
"'p' - print ignore buffer\n"
"'P' - pause/resume in packets displaying\n"
"'R' - software reset\n"
"'s/S' - send data over CAN: s ID byte0 .. byteN\n"
"'t' - change flood period (>=0ms)\n"
"'T' - get time from start (ms)\n"
;
TRUE_INLINE void getcanstat(){
USB_sendstr("CAN_MSR=");
printuhex(CAN->MSR);
USB_sendstr("\nCAN_TSR=");
printuhex(CAN->TSR);
USB_sendstr("\nCAN_RF0R=");
printuhex(CAN->RF0R);
USB_sendstr("\nCAN_RF1R=");
printuhex(CAN->RF1R);
}
/**
* @brief cmd_parser - command parsing
* @param txt - buffer with commands & data
* @param isUSB - == 1 if data got from USB
*/
void cmd_parser(char *txt){
char _1st = txt[0];
++txt;
/*
* parse long commands here
*/
switch(_1st){
case 'a':
addIGN(txt);
goto eof;
break;
case 'b':
CANini(txt);
goto eof;
break;
case 'f':
add_filter(txt);
goto eof;
break;
case 'F':
set_flood(parseCANmsg(txt), 0);
goto eof;
break;
case 's':
case 'S':
USB_sendstrCANcommand(txt);
goto eof;
break;
case 't':
setfloodt(txt);
goto eof;
break;
}
if(*txt) _1st = '?'; // help for wrong message length
switch(_1st){
case 'c':
getcanstat();
break;
case 'd':
IgnSz = 0;
break;
case 'e':
printCANerr();
break;
case 'i':
set_flood(NULL, 1);
USB_sendstr("Incremental flooding is ON ('F' to off)\n");
break;
case 'I':
CAN_reinit(0);
break;
case 'l':
list_filters();
break;
case 'p':
print_ign_buf();
break;
case 'P':
ShowMsgs = !ShowMsgs;
if(ShowMsgs) USB_sendstr("Resume\n");
else USB_sendstr("Pause\n");
break;
case 'R':
USB_sendstr("Soft reset\n");
USB_sendall(); // wait until everything will gone
NVIC_SystemReset();
break;
case 'T':
USB_sendstr("Time (ms): ");
printu(Tms);
USB_putbyte('\n');
break;
default: // help
USB_sendstr(helpstring);
break;
}
eof:
USB_putbyte('\n');
}
// check Ignore_IDs & return 1 if ID isn't in list
uint8_t isgood(uint16_t ID){
for(int i = 0; i < IgnSz; ++i)
if(Ignore_IDs[i] == ID) return 0;
return 1;
}

View File

@ -0,0 +1,32 @@
/*
* This file is part of the multistepper project.
* Copyright 2023 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 <stdint.h>
#include "strfunc.h"
#include "usb.h"
#define printu(x) do{USB_sendstr(u2str(x));}while(0)
#define printuhex(x) do{USB_sendstr(uhex2str(x));}while(0)
extern uint8_t ShowMsgs; // show CAN messages flag
void cmd_parser(char *txt);
uint8_t isgood(uint16_t ID);

View File

@ -0,0 +1,124 @@
/*
* This file is part of the multistepper project.
* Copyright 2023 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 "ringbuffer.h"
// stored data length
int RB_datalen(ringbuffer *b){
if(b->tail >= b->head) return (b->tail - b->head);
else return (b->length - b->head + b->tail);
}
/**
* @brief RB_hasbyte - check if buffer has given byte stored
* @param b - buffer
* @param byte - byte to find
* @return index if found, -1 if none
*/
int RB_hasbyte(ringbuffer *b, uint8_t byte){
if(b->head == b->tail) return -1; // no data in buffer
int startidx = b->head;
if(b->head > b->tail){ //
for(int found = b->head; found < b->length; ++found)
if(b->data[found] == byte) return found;
startidx = 0;
}
for(int found = startidx; found < b->tail; ++found)
if(b->data[found] == byte) return found;
return -1;
}
// poor memcpy
static void mcpy(uint8_t *targ, const uint8_t *src, int l){
while(l--) *targ++ = *src++;
}
// increment head or tail
TRUE_INLINE void incr(ringbuffer *b, volatile int *what, int n){
*what += n;
if(*what >= b->length) *what -= b->length;
}
/**
* @brief RB_read - read data from ringbuffer
* @param b - buffer
* @param s - array to write data
* @param len - max len of `s`
* @return bytes read
*/
int RB_read(ringbuffer *b, uint8_t *s, int len){
int l = RB_datalen(b);
if(!l) return 0;
if(l > len) l = len;
int _1st = b->length - b->head;
if(_1st > l) _1st = l;
if(_1st > len) _1st = len;
mcpy(s, b->data + b->head, _1st);
if(_1st < len && l > _1st){
mcpy(s+_1st, b->data, l - _1st);
incr(b, &b->head, l);
return l;
}
incr(b, &b->head, _1st);
return _1st;
}
/**
* @brief RB_readto fill array `s` with data until byte `byte` (with it)
* @param b - ringbuffer
* @param byte - check byte
* @param s - buffer to write data
* @param len - length of `s`
* @return amount of bytes written (negative, if len<data in buffer)
*/
int RB_readto(ringbuffer *b, uint8_t byte, uint8_t *s, int len){
int idx = RB_hasbyte(b, byte);
if(idx < 0) return 0;
int partlen = idx + 1 - b->head;
// now calculate length of new data portion
if(idx < b->head) partlen += b->length;
if(partlen > len) return -RB_read(b, s, len);
return RB_read(b, s, partlen);
}
/**
* @brief RB_write - write some data to ringbuffer
* @param b - buffer
* @param str - data
* @param l - length
* @return amount of bytes written
*/
int RB_write(ringbuffer *b, const uint8_t *str, int l){
int r = b->length - 1 - RB_datalen(b); // rest length
if(l > r) l = r;
if(!l) return 0;
int _1st = b->length - b->tail;
if(_1st > l) _1st = l;
mcpy(b->data + b->tail, str, _1st);
if(_1st < l){ // add another piece from start
mcpy(b->data, str+_1st, l-_1st);
}
incr(b, &b->tail, l);
return l;
}
// just delete all information in buffer `b`
void RB_clearbuf(ringbuffer *b){
b->head = 0;
b->tail = 0;
}

View File

@ -0,0 +1,35 @@
/*
* This file is part of the multistepper project.
* Copyright 2023 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>
typedef struct{
uint8_t *data; // data buffer
const int length; // its length
int head; // head index
int tail; // tail index
} ringbuffer;
int RB_read(ringbuffer *b, uint8_t *s, int len);
int RB_readto(ringbuffer *b, uint8_t byte, uint8_t *s, int len);
int RB_hasbyte(ringbuffer *b, uint8_t byte);
int RB_write(ringbuffer *b, const uint8_t *str, int l);
int RB_datalen(ringbuffer *b);
void RB_clearbuf(ringbuffer *b);

View File

@ -0,0 +1,256 @@
#include <stm32f3.h>
/**
* @brief hexdump - dump hex array by 16 bytes in string
* @param sendfun - function to send data
* @param arr - array to dump
* @param len - length of `arr`
*/
void hexdump(int (*sendfun)(const char *s), uint8_t *arr, uint16_t len){
char buf[52], *bptr = buf;
for(uint16_t l = 0; l < len; ++l, ++arr){
for(int16_t j = 1; j > -1; --j){
register uint8_t half = (*arr >> (4*j)) & 0x0f;
if(half < 10) *bptr++ = half + '0';
else *bptr++ = half - 10 + 'a';
}
if(l % 16 == 15){
*bptr++ = '\n';
*bptr = 0;
sendfun(buf);
bptr = buf;
}else *bptr++ = ' ';
}
if(bptr != buf){
*bptr++ = '\n';
*bptr = 0;
sendfun(buf);
}
}
/**
* @brief _2str - convert value into string buffer
* @param val - |value|
* @param minus - ==0 if value > 0
* @return buffer with number
*/
static char *_2str(uint32_t val, uint8_t minus){
static char strbuf[12];
char *bufptr = &strbuf[11];
*bufptr = 0;
if(!val){
*(--bufptr) = '0';
}else{
while(val){
uint32_t x = val / 10;
*(--bufptr) = (val - 10*x) + '0';
val = x;
//*(--bufptr) = val % 10 + '0';
//val /= 10;
}
}
if(minus) *(--bufptr) = '-';
return bufptr;
}
// return string with number `val`
char *u2str(uint32_t val){
return _2str(val, 0);
}
char *i2str(int32_t i){
uint8_t minus = 0;
uint32_t val;
if(i < 0){
minus = 1;
val = -i;
}else val = i;
return _2str(val, minus);
}
/**
* @brief uhex2str - print 32bit unsigned int as hex
* @param val - value
* @return string with number
*/
char *uhex2str(uint32_t val){
static char buf[12] = "0x";
int npos = 2;
uint8_t *ptr = (uint8_t*)&val + 3;
int8_t i, j, z=1;
for(i = 0; i < 4; ++i, --ptr){
if(*ptr == 0){ // omit leading zeros
if(i == 3) z = 0;
if(z) continue;
}
else z = 0;
for(j = 1; j > -1; --j){
uint8_t half = (*ptr >> (4*j)) & 0x0f;
if(half < 10) buf[npos++] = half + '0';
else buf[npos++] = half - 10 + 'a';
}
}
buf[npos] = 0;
return buf;
}
/**
* @brief omit_spaces - eliminate leading spaces and other trash in string
* @param buf - string
* @return - pointer to first character in `buf` > ' '
*/
const char *omit_spaces(const char *buf){
while(*buf){
if(*buf > ' ') break;
++buf;
}
return buf;
}
/**
* @brief getdec - read decimal number & return pointer to next non-number symbol
* @param buf - string
* @param N - number read
* @return Next non-number symbol. In case of overflow return `buf` and N==0xffffffff
*/
static const char *getdec(const char *buf, uint32_t *N){
char *start = (char*)buf;
uint32_t num = 0;
while(*buf){
char c = *buf;
if(c < '0' || c > '9'){
break;
}
if(num > 429496729 || (num == 429496729 && c > '5')){ // overflow
*N = 0xffffff;
return start;
}
num *= 10;
num += c - '0';
++buf;
}
*N = num;
return buf;
}
// read hexadecimal number (without 0x prefix!)
static const char *gethex(const char *buf, uint32_t *N){
const char *start = buf;
uint32_t num = 0;
while(*buf){
char c = *buf;
uint8_t M = 0;
if(c >= '0' && c <= '9'){
M = '0';
}else if(c >= 'A' && c <= 'F'){
M = 'A' - 10;
}else if(c >= 'a' && c <= 'f'){
M = 'a' - 10;
}
if(M){
if(num & 0xf0000000){ // overflow
*N = 0xffffff;
return start;
}
num <<= 4;
num += c - M;
}else{
break;
}
++buf;
}
*N = num;
return buf;
}
// read octal number (without 0 prefix!)
static const char *getoct(const char *buf, uint32_t *N){
const char *start = (char*)buf;
uint32_t num = 0;
while(*buf){
char c = *buf;
if(c < '0' || c > '7'){
break;
}
if(num & 0xe0000000){ // overflow
*N = 0xffffff;
return start;
}
num <<= 3;
num += c - '0';
++buf;
}
*N = num;
return buf;
}
// read binary number (without b prefix!)
static const char *getbin(const char *buf, uint32_t *N){
const char *start = (char*)buf;
uint32_t num = 0;
while(*buf){
char c = *buf;
if(c < '0' || c > '1'){
break;
}
if(num & 0x80000000){ // overflow
*N = 0xffffff;
return start;
}
num <<= 1;
if(c == '1') num |= 1;
++buf;
}
*N = num;
return buf;
}
/**
* @brief getnum - read uint32_t from string (dec, hex or bin: 127, 0x7f, 0b1111111)
* @param buf - buffer with number and so on
* @param N - the number read
* @return pointer to first non-number symbol in buf
* (if it is == buf, there's no number or if *N==0xffffffff there was overflow)
*/
const char *getnum(const char *txt, uint32_t *N){
const char *nxt = NULL;
const char *s = omit_spaces(txt);
if(*s == '0'){ // hex, oct or 0
if(s[1] == 'x' || s[1] == 'X'){ // hex
nxt = gethex(s+2, N);
if(nxt == s+2) nxt = (char*)txt;
}else if(s[1] > '0'-1 && s[1] < '8'){ // oct
nxt = getoct(s+1, N);
if(nxt == s+1) nxt = (char*)txt;
}else{ // 0
nxt = s+1;
*N = 0;
}
}else if(*s == 'b' || *s == 'B'){
nxt = getbin(s+1, N);
if(nxt == s+1) nxt = (char*)txt;
}else{
nxt = getdec(s, N);
if(nxt == s) nxt = (char*)txt;
}
return nxt;
}
// get signed integer
const char *getint(const char *txt, int32_t *I){
const char *s = omit_spaces(txt);
int32_t sign = 1;
uint32_t U;
if(*s == '-'){
sign = -1;
++s;
}
const char *nxt = getnum(s, &U);
if(nxt == s) return txt;
if(U & 0x80000000) return txt; // overfull
*I = sign * (int32_t)U;
return nxt;
}
/*
void mymemcpy(char *dest, const char *src, int len){
if(len < 1) return;
while(len--) *dest++ = *src++;
}
*/

View File

@ -0,0 +1,13 @@
#pragma once
#include <stdint.h>
#include <string.h>
void hexdump(int (*sendfun)(const char *s), uint8_t *arr, uint16_t len);
const char *u2str(uint32_t val);
const char *i2str(int32_t i);
const char *uhex2str(uint32_t val);
const char *getnum(const char *txt, uint32_t *N);
const char *omit_spaces(const char *buf);
const char *getint(const char *txt, int32_t *I);
//void mymemcpy(char *dest, const char *src, int len);

176
F3:F303/Multistepper/usb.c Normal file
View File

@ -0,0 +1,176 @@
/*
* This file is part of the multistepper project.
* Copyright 2023 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 <string.h>
#include "hardware.h"
#include "ringbuffer.h"
#include "usb.h"
#include "usb_lib.h"
static volatile uint8_t usbbuff[USB_TXBUFSZ]; // temporary buffer for sending data
// ring buffers for incoming and outgoing data
static uint8_t obuf[RBOUTSZ], ibuf[RBINSZ];
static volatile ringbuffer out = {.data = obuf, .length = RBOUTSZ, .head = 0, .tail = 0};
static volatile ringbuffer in = {.data = ibuf, .length = RBINSZ, .head = 0, .tail = 0};
// transmission is succesfull
static volatile uint8_t bufisempty = 1;
static volatile uint8_t bufovrfl = 0;
static void send_next(){
if(bufisempty) return;
static int lastdsz = 0;
int buflen = RB_read((ringbuffer*)&out, (uint8_t*)usbbuff, USB_TXBUFSZ);
if(!buflen){
if(lastdsz == 64) EP_Write(3, NULL, 0); // send ZLP after 64 bits packet when nothing more to send
lastdsz = 0;
bufisempty = 1;
return;
}
EP_Write(3, (uint8_t*)usbbuff, buflen);
lastdsz = buflen;
}
// blocking send full content of ring buffer
int USB_sendall(){
while(!bufisempty){
if(!usbON) return 0;
}
return 1;
}
// put `buf` into queue to send
int USB_send(const uint8_t *buf, int len){
if(!buf || !usbON || !len) return 0;
while(len){
int a = RB_write((ringbuffer*)&out, buf, len);
len -= a;
buf += a;
if(bufisempty){
bufisempty = 0;
send_next();
}
}
return 1;
}
int USB_putbyte(uint8_t byte){
if(!usbON) return 0;
while(0 == RB_write((ringbuffer*)&out, &byte, 1)){
if(bufisempty){
bufisempty = 0;
send_next();
}
}
return 1;
}
int USB_sendstr(const char *string){
if(!string || !usbON) return 0;
int len = 0;
const char *b = string;
while(*b++) ++len;
if(!len) return 0;
return USB_send((const uint8_t*)string, len);
}
/**
* @brief USB_receive - get binary data from receiving ring-buffer
* @param buf (i) - buffer for received data
* @param len - length of `buf`
* @return amount of received bytes (negative, if overfull happened)
*/
int USB_receive(uint8_t *buf, int len){
int sz = RB_read((ringbuffer*)&in, buf, len);
if(bufovrfl){
RB_clearbuf((ringbuffer*)&in);
if(!sz) sz = -1;
else sz = -sz;
bufovrfl = 0;
}
return sz;
}
/**
* @brief USB_receivestr - get string up to '\n' and replace '\n' with 0
* @param buf - receiving buffer
* @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){
int l = RB_readto((ringbuffer*)&in, '\n', (uint8_t*)buf, len);
if(l == 0) return 0;
if(--l < 0 || bufovrfl) RB_clearbuf((ringbuffer*)&in);
else buf[l] = 0; // replace '\n' with strend
if(bufovrfl){
if(l > 0) l = -l;
else l = -1;
bufovrfl = 0;
}
return l;
}
// interrupt IN handler (never used?)
static void EP1_Handler(){
uint16_t epstatus = KEEP_DTOG(USB->EPnR[1]);
if(RX_FLAG(epstatus)) epstatus = (epstatus & ~USB_EPnR_STAT_TX) ^ USB_EPnR_STAT_RX; // set valid RX
else epstatus = epstatus & ~(USB_EPnR_STAT_TX|USB_EPnR_STAT_RX);
// clear CTR
epstatus = (epstatus & ~(USB_EPnR_CTR_RX|USB_EPnR_CTR_TX));
USB->EPnR[1] = epstatus;
}
// data IN/OUT handlers
static void transmit_Handler(){ // EP3IN
uint16_t epstatus = KEEP_DTOG_STAT(USB->EPnR[3]);
// clear CTR keep DTOGs & STATs
USB->EPnR[3] = (epstatus & ~(USB_EPnR_CTR_TX)); // clear TX ctr
send_next();
}
static void receive_Handler(){ // EP2OUT
uint8_t buf[USB_RXBUFSZ];
uint16_t epstatus = KEEP_DTOG(USB->EPnR[2]);
uint8_t sz = EP_Read(2, (uint16_t*)buf);
if(sz){
if(RB_write((ringbuffer*)&in, buf, sz) != sz) bufovrfl = 1;
}
// keep stat_tx & set ACK rx, clear RX ctr
USB->EPnR[2] = (epstatus & ~USB_EPnR_CTR_RX) ^ USB_EPnR_STAT_RX;
}
void USB_proc(){
switch(USB_Dev.USB_Status){
case USB_STATE_CONFIGURED:
// make new BULK endpoint
// Buffer have 1024 bytes, but last 256 we use for CAN bus (30.2 of RM: USB main features)
EP_Init(1, EP_TYPE_INTERRUPT, USB_EP1BUFSZ, 0, EP1_Handler); // IN1 - transmit
EP_Init(2, EP_TYPE_BULK, 0, USB_RXBUFSZ, receive_Handler); // OUT2 - receive data
EP_Init(3, EP_TYPE_BULK, USB_TXBUFSZ, 0, transmit_Handler); // IN3 - transmit data
USB_Dev.USB_Status = USB_STATE_CONNECTED;
break;
case USB_STATE_DEFAULT:
case USB_STATE_ADDRESSED:
if(usbON){
usbON = 0;
}
break;
default: // USB_STATE_CONNECTED - send next data portion
if(!usbON) return;
}
}

View File

@ -0,0 +1,41 @@
/*
* This file is part of the multistepper project.
* Copyright 2023 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 "usbhw.h"
// sizes of ringbuffers for outgoing and incoming data
#define RBOUTSZ (512)
#define RBINSZ (512)
#define newline() USB_putbyte('\n')
#ifdef EBUG
#define DBG(str) do{USB_sendstr(__FILE__ " (L" STR(__LINE__) "): " str);}while(0)
#else
#define DBG(str)
#endif
void USB_proc();
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);

View File

@ -0,0 +1,389 @@
/*
* This file is part of the multistepper project.
* Copyright 2023 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 <stdint.h>
#include "usb_lib.h"
ep_t endpoints[STM32ENDPOINTS];
usb_dev_t USB_Dev;
static usb_LineCoding lineCoding = {115200, 0, 0, 8};
config_pack_t setup_packet;
uint8_t ep0databuf[EP0DATABUF_SIZE];
uint8_t ep0dbuflen = 0;
usb_LineCoding getLineCoding(){return lineCoding;}
volatile uint8_t usbON = 0; // device disconnected from terminal
// definition of parts common for USB_DeviceDescriptor & USB_DeviceQualifierDescriptor
#define bcdUSB_L 0x10
#define bcdUSB_H 0x01
#define bDeviceClass 0
#define bDeviceSubClass 0
#define bDeviceProtocol 0
#define bNumConfigurations 1
static const uint8_t USB_DeviceDescriptor[] = {
18, // bLength
0x01, // bDescriptorType - Device descriptor
bcdUSB_L, // bcdUSB_L - 1.10
bcdUSB_H, // bcdUSB_H
bDeviceClass, // bDeviceClass - USB_COMM
bDeviceSubClass, // bDeviceSubClass
bDeviceProtocol, // bDeviceProtocol
USB_EP0_BUFSZ, // bMaxPacketSize
0x7b, // idVendor_L PL2303: VID=0x067b, PID=0x2303
0x06, // idVendor_H
0x03, // idProduct_L
0x23, // idProduct_H
0x00, // bcdDevice_Ver_L
0x03, // bcdDevice_Ver_H
0x01, // iManufacturer
0x02, // iProduct
0x00, // iSerialNumber
bNumConfigurations // bNumConfigurations
};
static const uint8_t USB_DeviceQualifierDescriptor[] = {
10, //bLength
0x06, // bDescriptorType - Device qualifier
bcdUSB_L, // bcdUSB_L
bcdUSB_H, // bcdUSB_H
bDeviceClass, // bDeviceClass
bDeviceSubClass, // bDeviceSubClass
bDeviceProtocol, // bDeviceProtocol
USB_EP0_BUFSZ, // bMaxPacketSize0
bNumConfigurations, // bNumConfigurations
0x00 // Reserved
};
static const uint8_t USB_ConfigDescriptor[] = {
/*Configuration Descriptor*/
0x09, /* bLength: Configuration Descriptor size */
0x02, /* bDescriptorType: Configuration */
39, /* wTotalLength:no of returned bytes */
0x00,
0x01, /* bNumInterfaces: 1 interface */
0x01, /* bConfigurationValue: Configuration value */
0x00, /* iConfiguration: Index of string descriptor describing the configuration */
0xa0, /* bmAttributes - Bus powered, Remote wakeup */
0x32, /* MaxPower 100 mA */
/*---------------------------------------------------------------------------*/
/*Interface Descriptor */
0x09, /* bLength: Interface Descriptor size */
0x04, /* bDescriptorType: Interface */
0x00, /* bInterfaceNumber: Number of Interface */
0x00, /* bAlternateSetting: Alternate setting */
0x03, /* bNumEndpoints: 3 endpoints used */
0xff, /* bInterfaceClass */
0x00, /* bInterfaceSubClass */
0x00, /* bInterfaceProtocol */
0x00, /* iInterface: */
///////////////////////////////////////////////////
/*Endpoint 1 Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
0x05, /* bDescriptorType: Endpoint */
0x81, /* bEndpointAddress IN1 */
0x03, /* bmAttributes: Interrupt */
0x0a, /* wMaxPacketSize LO: */
0x00, /* wMaxPacketSize HI: */
0x01, /* bInterval: */
/*Endpoint OUT2 Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
0x05, /* bDescriptorType: Endpoint */
0x02, /* bEndpointAddress: OUT2 */
0x02, /* bmAttributes: Bulk */
(USB_RXBUFSZ & 0xff), /* wMaxPacketSize: 64 */
(USB_RXBUFSZ >> 8),
0x00, /* bInterval: ignore for Bulk transfer */
/*Endpoint IN3 Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
0x05, /* bDescriptorType: Endpoint */
0x83, /* bEndpointAddress IN3 */
0x02, /* bmAttributes: Bulk */
(USB_TXBUFSZ & 0xff), /* wMaxPacketSize: 64 */
(USB_TXBUFSZ >> 8),
0x00, /* bInterval: ignore for Bulk transfer */
};
_USB_LANG_ID_(USB_StringLangDescriptor, LANG_US);
// these descriptors are not used in PL2303 emulator!
_USB_STRING_(USB_StringSerialDescriptor, u"0");
_USB_STRING_(USB_StringManufacturingDescriptor, u"Prolific Technology Inc.");
_USB_STRING_(USB_StringProdDescriptor, u"USB-Serial Controller");
/*
* default handlers
*/
// SET_LINE_CODING
void WEAK linecoding_handler(usb_LineCoding __attribute__((unused)) *lc){
}
// SET_CONTROL_LINE_STATE
void WEAK clstate_handler(uint16_t __attribute__((unused)) val){
}
// SEND_BREAK
void WEAK break_handler(){
}
// handler of vendor requests
void WEAK vendor_handler(config_pack_t *packet){
uint16_t c;
if(packet->bmRequestType & 0x80){ // read
switch(packet->wValue){
case 0x8484:
c = 2;
break;
case 0x0080:
c = 1;
break;
case 0x8686:
c = 0xaa;
break;
default:
c = 0;
}
EP_WriteIRQ(0, (uint8_t*)&c, 1);
}else{ // write ZLP
c = 0;
EP_WriteIRQ(0, (uint8_t *)&c, 0);
}
}
static void wr0(const uint8_t *buf, uint16_t size){
if(setup_packet.wLength < size) size = setup_packet.wLength; // shortened request
if(size < endpoints[0].txbufsz){
EP_WriteIRQ(0, buf, size);
return;
}
while(size){
uint16_t l = size;
if(l > endpoints[0].txbufsz) l = endpoints[0].txbufsz;
EP_WriteIRQ(0, buf, l);
buf += l;
size -= l;
uint8_t needzlp = (l == endpoints[0].txbufsz) ? 1 : 0;
if(size || needzlp){ // send last data buffer
uint16_t status = KEEP_DTOG(USB->EPnR[0]);
// keep DTOGs, clear CTR_RX,TX, set TX VALID, leave stat_Rx
USB->EPnR[0] = (status & ~(USB_EPnR_CTR_RX|USB_EPnR_CTR_TX|USB_EPnR_STAT_RX))
^ USB_EPnR_STAT_TX;
uint32_t ctr = 1000000;
while(--ctr && (USB->ISTR & USB_ISTR_CTR) == 0){IWDG->KR = IWDG_REFRESH;};
if((USB->ISTR & USB_ISTR_CTR) == 0){
return;
}
if(needzlp) EP_WriteIRQ(0, (uint8_t*)0, 0);
}
}
}
static inline void get_descriptor(){
switch(setup_packet.wValue){
case DEVICE_DESCRIPTOR:
wr0(USB_DeviceDescriptor, sizeof(USB_DeviceDescriptor));
break;
case CONFIGURATION_DESCRIPTOR:
wr0(USB_ConfigDescriptor, sizeof(USB_ConfigDescriptor));
break;
case STRING_LANG_DESCRIPTOR:
wr0((const uint8_t *)&USB_StringLangDescriptor, STRING_LANG_DESCRIPTOR_SIZE_BYTE);
break;
case STRING_MAN_DESCRIPTOR:
wr0((const uint8_t *)&USB_StringManufacturingDescriptor, USB_StringManufacturingDescriptor.bLength);
break;
case STRING_PROD_DESCRIPTOR:
wr0((const uint8_t *)&USB_StringProdDescriptor, USB_StringProdDescriptor.bLength);
break;
case STRING_SN_DESCRIPTOR:
wr0((const uint8_t *)&USB_StringSerialDescriptor, USB_StringSerialDescriptor.bLength);
break;
case DEVICE_QUALIFIER_DESCRIPTOR:
wr0(USB_DeviceQualifierDescriptor, USB_DeviceQualifierDescriptor[0]);
break;
default:
break;
}
}
static uint16_t configuration = 0; // reply for GET_CONFIGURATION (==1 if configured)
static inline void std_d2h_req(){
uint16_t status = 0; // bus powered
switch(setup_packet.bRequest){
case GET_DESCRIPTOR:
get_descriptor();
break;
case GET_STATUS:
EP_WriteIRQ(0, (uint8_t *)&status, 2); // send status: Bus Powered
break;
case GET_CONFIGURATION:
EP_WriteIRQ(0, (uint8_t*)&configuration, 1);
break;
default:
break;
}
}
static inline void std_h2d_req(){
switch(setup_packet.bRequest){
case SET_ADDRESS:
// new address will be assigned later - after acknowlegement or request to host
USB_Dev.USB_Addr = setup_packet.wValue;
break;
case SET_CONFIGURATION:
// Now device configured
USB_Dev.USB_Status = USB_STATE_CONFIGURED;
configuration = setup_packet.wValue;
break;
default:
break;
}
}
/*
bmRequestType: 76543210
7 direction: 0 - host->device, 1 - device->host
65 type: 0 - standard, 1 - class, 2 - vendor
4..0 getter: 0 - device, 1 - interface, 2 - endpoint, 3 - other
*/
/**
* Endpoint0 (control) handler
*/
void EP0_Handler(){
uint16_t epstatus = USB->EPnR[0]; // EP0R on input -> return this value after modifications
uint8_t reqtype = setup_packet.bmRequestType & 0x7f;
uint8_t dev2host = (setup_packet.bmRequestType & 0x80) ? 1 : 0;
int rxflag = RX_FLAG(epstatus);
if(rxflag && SETUP_FLAG(epstatus)){
switch(reqtype){
case STANDARD_DEVICE_REQUEST_TYPE: // standard device request
if(dev2host){
std_d2h_req();
}else{
std_h2d_req();
EP_WriteIRQ(0, (uint8_t *)0, 0);
}
break;
case STANDARD_ENDPOINT_REQUEST_TYPE: // standard endpoint request
if(setup_packet.bRequest == CLEAR_FEATURE){
EP_WriteIRQ(0, (uint8_t *)0, 0);
}
break;
case VENDOR_REQUEST_TYPE:
vendor_handler(&setup_packet);
break;
case CONTROL_REQUEST_TYPE:
switch(setup_packet.bRequest){
case GET_LINE_CODING:
EP_WriteIRQ(0, (uint8_t*)&lineCoding, sizeof(lineCoding));
break;
case SET_LINE_CODING: // omit this for next stage, when data will come
break;
case SET_CONTROL_LINE_STATE:
usbON = 1;
clstate_handler(setup_packet.wValue);
break;
case SEND_BREAK:
usbON = 0;
break_handler();
break;
default:
break;
}
if(setup_packet.bRequest != GET_LINE_CODING) EP_WriteIRQ(0, (uint8_t *)0, 0); // write acknowledgement
break;
default:
EP_WriteIRQ(0, (uint8_t *)0, 0);
}
}else if(rxflag){ // got data over EP0 or host acknowlegement
if(endpoints[0].rx_cnt){
if(setup_packet.bRequest == SET_LINE_CODING){
linecoding_handler((usb_LineCoding*)ep0databuf);
}
}
} else if(TX_FLAG(epstatus)){ // package transmitted
// now we can change address after enumeration
if ((USB->DADDR & USB_DADDR_ADD) != USB_Dev.USB_Addr){
USB->DADDR = USB_DADDR_EF | USB_Dev.USB_Addr;
// change state to ADRESSED
USB_Dev.USB_Status = USB_STATE_ADDRESSED;
}
}
epstatus = KEEP_DTOG(USB->EPnR[0]);
if(rxflag) epstatus ^= USB_EPnR_STAT_TX; // start ZLP/data transmission
else epstatus &= ~USB_EPnR_STAT_TX; // or leave unchanged
// keep DTOGs, clear CTR_RX,TX, set RX VALID
USB->EPnR[0] = (epstatus & ~(USB_EPnR_CTR_RX|USB_EPnR_CTR_TX)) ^ USB_EPnR_STAT_RX;
}
/**
* Write data to EP buffer (called from IRQ handler)
* @param number - EP number
* @param *buf - array with data
* @param size - its size
*/
void EP_WriteIRQ(uint8_t number, const uint8_t *buf, uint16_t size){
uint8_t i;
if(size > endpoints[number].txbufsz) size = endpoints[number].txbufsz;
uint16_t N2 = (size + 1) >> 1;
// the buffer is 16-bit, so we should copy data as it would be uint16_t
uint16_t *buf16 = (uint16_t *)buf;
uint32_t *out = (uint32_t *)endpoints[number].tx_buf;
for(i = 0; i < N2; ++i, ++out){
*out = buf16[i];
}
USB_BTABLE->EP[number].USB_COUNT_TX = size;
}
/**
* Write data to EP buffer (called outside IRQ handler)
* @param number - EP number
* @param *buf - array with data
* @param size - its size
*/
void EP_Write(uint8_t number, const uint8_t *buf, uint16_t size){
EP_WriteIRQ(number, buf, size);
uint16_t status = KEEP_DTOG(USB->EPnR[number]);
// keep DTOGs, clear CTR_TX & set TX VALID to start transmission
USB->EPnR[number] = (status & ~(USB_EPnR_CTR_TX)) ^ USB_EPnR_STAT_TX;
}
/*
* Copy data from EP buffer into user buffer area
* @param *buf - user array for data
* @return amount of data read
*/
int EP_Read(uint8_t number, uint16_t *buf){
int sz = endpoints[number].rx_cnt;
if(!sz) return 0;
endpoints[number].rx_cnt = 0;
int n = (sz + 1) >> 1;
uint32_t *in = (uint32_t *)endpoints[number].rx_buf;
if(n){
for(int i = 0; i < n; ++i, ++in)
buf[i] = *(uint16_t*)in;
}
return sz;
}

View File

@ -0,0 +1,182 @@
/*
* This file is part of the multistepper project.
* Copyright 2023 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 <wchar.h>
#include "usbhw.h"
#define EP0DATABUF_SIZE (64)
#define LASTADDR_DEFAULT (STM32ENDPOINTS * 8)
// bmRequestType & 0x7f
#define STANDARD_DEVICE_REQUEST_TYPE 0
#define STANDARD_ENDPOINT_REQUEST_TYPE 2
#define VENDOR_REQUEST_TYPE 0x40
#define CONTROL_REQUEST_TYPE 0x21
// bRequest, standard; for bmRequestType == 0x80
#define GET_STATUS 0x00
#define GET_DESCRIPTOR 0x06
#define GET_CONFIGURATION 0x08
// for bmRequestType == 0
#define CLEAR_FEATURE 0x01
#define SET_FEATURE 0x03 // unused
#define SET_ADDRESS 0x05
#define SET_DESCRIPTOR 0x07 // unused
#define SET_CONFIGURATION 0x09
// for bmRequestType == 0x81, 1 or 0xB2
#define GET_INTERFACE 0x0A // unused
#define SET_INTERFACE 0x0B // unused
#define SYNC_FRAME 0x0C // unused
#define VENDOR_REQUEST 0x01 // unused
// Class-Specific Control Requests
#define SEND_ENCAPSULATED_COMMAND 0x00 // unused
#define GET_ENCAPSULATED_RESPONSE 0x01 // unused
#define SET_COMM_FEATURE 0x02 // unused
#define GET_COMM_FEATURE 0x03 // unused
#define CLEAR_COMM_FEATURE 0x04 // unused
#define SET_LINE_CODING 0x20
#define GET_LINE_CODING 0x21
#define SET_CONTROL_LINE_STATE 0x22
#define SEND_BREAK 0x23
// control line states
#define CONTROL_DTR 0x01
#define CONTROL_RTS 0x02
// wValue
#define DEVICE_DESCRIPTOR 0x100
#define CONFIGURATION_DESCRIPTOR 0x200
#define STRING_LANG_DESCRIPTOR 0x300
#define STRING_MAN_DESCRIPTOR 0x301
#define STRING_PROD_DESCRIPTOR 0x302
#define STRING_SN_DESCRIPTOR 0x303
#define DEVICE_QUALIFIER_DESCRIPTOR 0x600
#define RX_FLAG(epstat) (epstat & USB_EPnR_CTR_RX)
#define TX_FLAG(epstat) (epstat & USB_EPnR_CTR_TX)
#define SETUP_FLAG(epstat) (epstat & USB_EPnR_SETUP)
// EPnR bits manipulation
#define KEEP_DTOG_STAT(EPnR) (EPnR & ~(USB_EPnR_STAT_RX|USB_EPnR_STAT_TX|USB_EPnR_DTOG_RX|USB_EPnR_DTOG_TX))
#define KEEP_DTOG(EPnR) (EPnR & ~(USB_EPnR_DTOG_RX|USB_EPnR_DTOG_TX))
// USB state: uninitialized, addressed, ready for use
typedef enum{
USB_STATE_DEFAULT,
USB_STATE_ADDRESSED,
USB_STATE_CONFIGURED,
USB_STATE_CONNECTED
} USB_state;
// EP types
#define EP_TYPE_BULK 0x00
#define EP_TYPE_CONTROL 0x01
#define EP_TYPE_ISO 0x02
#define EP_TYPE_INTERRUPT 0x03
#define LANG_US (uint16_t)0x0409
#define _USB_STRING_(name, str) \
static const struct name \
{ \
uint8_t bLength; \
uint8_t bDescriptorType; \
uint16_t bString[(sizeof(str) - 2) / 2]; \
\
} \
name = {sizeof(name), 0x03, str}
#define _USB_LANG_ID_(name, lng_id) \
\
static const struct name \
{ \
uint8_t bLength; \
uint8_t bDescriptorType; \
uint16_t bString; \
\
} \
name = {0x04, 0x03, lng_id}
#define STRING_LANG_DESCRIPTOR_SIZE_BYTE (4)
// EP0 configuration packet
typedef struct {
uint8_t bmRequestType;
uint8_t bRequest;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
} config_pack_t;
// endpoints state
typedef struct{
uint16_t *tx_buf; // transmission buffer address
uint16_t txbufsz; // transmission buffer size
uint16_t *rx_buf; // reception buffer address
void (*func)(); // endpoint action function
unsigned rx_cnt : 10; // received data counter
} ep_t;
// USB status & its address
typedef struct {
uint8_t USB_Status;
uint16_t USB_Addr;
}usb_dev_t;
typedef struct {
uint32_t dwDTERate;
uint8_t bCharFormat;
#define USB_CDC_1_STOP_BITS 0
#define USB_CDC_1_5_STOP_BITS 1
#define USB_CDC_2_STOP_BITS 2
uint8_t bParityType;
#define USB_CDC_NO_PARITY 0
#define USB_CDC_ODD_PARITY 1
#define USB_CDC_EVEN_PARITY 2
#define USB_CDC_MARK_PARITY 3
#define USB_CDC_SPACE_PARITY 4
uint8_t bDataBits;
} __attribute__ ((packed)) usb_LineCoding;
typedef struct {
uint8_t bmRequestType;
uint8_t bNotificationType;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
} __attribute__ ((packed)) usb_cdc_notification;
extern ep_t endpoints[];
extern usb_dev_t USB_Dev;
extern volatile uint8_t usbON;
extern config_pack_t setup_packet;
extern uint8_t ep0databuf[];
extern uint8_t ep0dbuflen;
void EP0_Handler();
void EP_WriteIRQ(uint8_t number, const uint8_t *buf, uint16_t size);
void EP_Write(uint8_t number, const uint8_t *buf, uint16_t size);
int EP_Read(uint8_t number, uint16_t *buf);
usb_LineCoding getLineCoding();
void linecoding_handler(usb_LineCoding *lc);
void clstate_handler(uint16_t val);
void break_handler();
void vendor_handler(config_pack_t *packet);

View File

@ -0,0 +1,126 @@
/*
* This file is part of the multistepper project.
* Copyright 2023 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 "usb.h"
#include "usb_lib.h"
// here we suppose that all PIN settings done in hw_setup earlier
void USB_setup(){
NVIC_DisableIRQ(USB_LP_IRQn);
// remap USB LP & Wakeup interrupts to 75 and 76 - works only on pure F303
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; // enable tacting of SYSCFG
SYSCFG->CFGR1 |= SYSCFG_CFGR1_USB_IT_RMP;
RCC->APB1ENR |= RCC_APB1ENR_USBEN;
USB->CNTR = USB_CNTR_FRES; // Force USB Reset
for(uint32_t ctr = 0; ctr < 72000; ++ctr) nop(); // wait >1ms
//uint32_t ctr = 0;
USB->CNTR = 0;
USB->BTABLE = 0;
USB->DADDR = 0;
USB->ISTR = 0;
USB->CNTR = USB_CNTR_RESETM | USB_CNTR_WKUPM; // allow only wakeup & reset interrupts
NVIC_EnableIRQ(USB_LP_IRQn);
}
static uint16_t lastaddr = LASTADDR_DEFAULT;
/**
* Endpoint initialisation
* @param number - EP num (0...7)
* @param type - EP type (EP_TYPE_BULK, EP_TYPE_CONTROL, EP_TYPE_ISO, EP_TYPE_INTERRUPT)
* @param txsz - transmission buffer size @ USB/CAN buffer
* @param rxsz - reception buffer size @ USB/CAN buffer
* @param uint16_t (*func)(ep_t *ep) - EP handler function
* @return 0 if all OK
*/
int EP_Init(uint8_t number, uint8_t type, uint16_t txsz, uint16_t rxsz, void (*func)(ep_t ep)){
if(number >= STM32ENDPOINTS) return 4; // out of configured amount
if(txsz > USB_BTABLE_SIZE || rxsz > USB_BTABLE_SIZE) return 1; // buffer too large
if(lastaddr + txsz + rxsz >= USB_BTABLE_SIZE) return 2; // out of btable
USB->EPnR[number] = (type << 9) | (number & USB_EPnR_EA);
USB->EPnR[number] ^= USB_EPnR_STAT_RX | USB_EPnR_STAT_TX_1;
if(rxsz & 1 || rxsz > 512) return 3; // wrong rx buffer size
uint16_t countrx = 0;
if(rxsz < 64) countrx = rxsz / 2;
else{
if(rxsz & 0x1f) return 3; // should be multiple of 32
countrx = 31 + rxsz / 32;
}
USB_BTABLE->EP[number].USB_ADDR_TX = lastaddr;
endpoints[number].tx_buf = (uint16_t *)(USB_BTABLE_BASE + lastaddr*2);
endpoints[number].txbufsz = txsz;
lastaddr += txsz;
USB_BTABLE->EP[number].USB_COUNT_TX = 0;
USB_BTABLE->EP[number].USB_ADDR_RX = lastaddr;
endpoints[number].rx_buf = (uint16_t *)(USB_BTABLE_BASE + lastaddr*2);
lastaddr += rxsz;
USB_BTABLE->EP[number].USB_COUNT_RX = countrx << 10;
endpoints[number].func = func;
return 0;
}
// standard IRQ handler
void usb_lp_isr(){
if(USB->ISTR & USB_ISTR_RESET){
usbON = 0;
// Reinit registers
USB->CNTR = USB_CNTR_RESETM | USB_CNTR_CTRM | USB_CNTR_SUSPM | USB_CNTR_WKUPM;
// Endpoint 0 - CONTROL
// ON USB LS size of EP0 may be 8 bytes, but on FS it should be 64 bytes!
lastaddr = LASTADDR_DEFAULT;
// clear address, leave only enable bit
USB->DADDR = USB_DADDR_EF;
USB_Dev.USB_Status = USB_STATE_DEFAULT;
if(EP_Init(0, EP_TYPE_CONTROL, USB_EP0_BUFSZ, USB_EP0_BUFSZ, EP0_Handler)){
return;
}
USB->ISTR = ~USB_ISTR_RESET;
}
if(USB->ISTR & USB_ISTR_CTR){
// EP number
uint8_t n = USB->ISTR & USB_ISTR_EPID;
// copy status register
uint16_t epstatus = USB->EPnR[n];
// copy received bytes amount
endpoints[n].rx_cnt = USB_BTABLE->EP[n].USB_COUNT_RX & 0x3FF; // low 10 bits is counter
// check direction
if(USB->ISTR & USB_ISTR_DIR){ // OUT interrupt - receive data, CTR_RX==1 (if CTR_TX == 1 - two pending transactions: receive following by transmit)
if(n == 0){ // control endpoint
if(epstatus & USB_EPnR_SETUP){ // setup packet -> copy data to conf_pack
EP_Read(0, (uint16_t*)&setup_packet);
ep0dbuflen = 0;
// interrupt handler will be called later
}else if(epstatus & USB_EPnR_CTR_RX){ // data packet -> push received data to ep0databuf
ep0dbuflen = endpoints[0].rx_cnt;
EP_Read(0, (uint16_t*)&ep0databuf);
}
}
}
// call EP handler
if(endpoints[n].func) endpoints[n].func(endpoints[n]);
}
if(USB->ISTR & USB_ISTR_SUSP){ // suspend -> still no connection, may sleep
usbON = 0;
USB->CNTR |= USB_CNTR_FSUSP | USB_CNTR_LP_MODE;
USB->ISTR = ~USB_ISTR_SUSP;
}
if(USB->ISTR & USB_ISTR_WKUP){ // wakeup
USB->CNTR &= ~(USB_CNTR_FSUSP | USB_CNTR_LP_MODE); // clear suspend flags
USB->ISTR = ~USB_ISTR_WKUP;
}
}

View File

@ -0,0 +1,98 @@
/*
* This file is part of the multistepper project.
* Copyright 2023 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>
// max endpoints number
#define STM32ENDPOINTS 8
/**
* Buffers size definition
**/
#define USB_BTABLE_SIZE 768
// first 64 bytes of USB_BTABLE are registers!
//#define USB_EP0_BASEADDR 64
// for USB FS EP0 buffers are from 8 to 64 bytes long (64 for PL2303)
#define USB_EP0_BUFSZ 64
// USB transmit buffer size (64 for PL2303)
#define USB_TXBUFSZ 64
// USB receive buffer size (64 for PL2303)
#define USB_RXBUFSZ 64
// EP1 - interrupt - buffer size
#define USB_EP1BUFSZ 8
#define USB_BTABLE_BASE 0x40006000
#define USB ((USB_TypeDef *) USB_BASE)
#ifdef USB_BTABLE
#undef USB_BTABLE
#endif
#define USB_BTABLE ((USB_BtableDef *)(USB_BTABLE_BASE))
#define USB_ISTR_EPID 0x0000000F
#define USB_FNR_LSOF_0 0x00000800
#define USB_FNR_lSOF_1 0x00001000
#define USB_LPMCSR_BESL_0 0x00000010
#define USB_LPMCSR_BESL_1 0x00000020
#define USB_LPMCSR_BESL_2 0x00000040
#define USB_LPMCSR_BESL_3 0x00000080
#define USB_EPnR_CTR_RX 0x00008000
#define USB_EPnR_DTOG_RX 0x00004000
#define USB_EPnR_STAT_RX 0x00003000
#define USB_EPnR_STAT_RX_0 0x00001000
#define USB_EPnR_STAT_RX_1 0x00002000
#define USB_EPnR_SETUP 0x00000800
#define USB_EPnR_EP_TYPE 0x00000600
#define USB_EPnR_EP_TYPE_0 0x00000200
#define USB_EPnR_EP_TYPE_1 0x00000400
#define USB_EPnR_EP_KIND 0x00000100
#define USB_EPnR_CTR_TX 0x00000080
#define USB_EPnR_DTOG_TX 0x00000040
#define USB_EPnR_STAT_TX 0x00000030
#define USB_EPnR_STAT_TX_0 0x00000010
#define USB_EPnR_STAT_TX_1 0x00000020
#define USB_EPnR_EA 0x0000000F
#define USB_COUNTn_RX_BLSIZE 0x00008000
#define USB_COUNTn_NUM_BLOCK 0x00007C00
#define USB_COUNTn_RX 0x0000003F
#define USB_TypeDef USB_TypeDef_custom
typedef struct {
__IO uint32_t EPnR[STM32ENDPOINTS];
__IO uint32_t RESERVED[STM32ENDPOINTS];
__IO uint32_t CNTR;
__IO uint32_t ISTR;
__IO uint32_t FNR;
__IO uint32_t DADDR;
__IO uint32_t BTABLE;
} USB_TypeDef;
typedef struct{
__IO uint32_t USB_ADDR_TX;
__IO uint32_t USB_COUNT_TX;
__IO uint32_t USB_ADDR_RX;
__IO uint32_t USB_COUNT_RX;
} USB_EPDATA_TypeDef;
typedef struct{
__IO USB_EPDATA_TypeDef EP[STM32ENDPOINTS];
} USB_BtableDef;
void USB_setup();
int EP_Init(uint8_t number, uint8_t type, uint16_t txsz, uint16_t rxsz, void (*func)());

View File

@ -0,0 +1,2 @@
#define BUILD_NUMBER "5"
#define BUILD_DATE "2023-02-03"

View File

@ -23,10 +23,12 @@
#define __STM32F3_H__
#include "vector.h"
#ifdef STM32F303xb
#if defined STM32F303xb || defined STM32F303xc
#include "stm32f303xc.h"
#elif defined STM32F303xd || defined STM32F303xe
#include "stm32f303xe.h"
#else
#error "Define STM32F303xb"
#error "Define STM32F303xX"
#endif
#include "common_macros.h"
@ -256,6 +258,18 @@ TRUE_INLINE int StartHSE(){ // system bus 72MHz from PLL
#define MODER_O(n) (1<<(n*2))
#define MODER_AF(n) (2<<(n*2))
#define MODER_AI(n) (3<<(n*2))
// OSPEED: low, medium, high
#define OSPEED_CLR(n) (~(3<<(n*2)))
#define OSPEED_LO(n) (0)
#define OSPEED_MED(n) (1<<(n*2))
#define OSPEED_HI(n) (3<<(n*2))
// PUPD: pull up/down
#define PUPD_CLR(n) (~(3<<(n*2)))
#define PUPD_PU(n) (1<<(n*2))
#define PUPD_PD(n) (2<<(n*2))
// OTYPER: bit==1 for OD
#define OTYPER_PP(n) 0
#define OTYPER_OD(n) (1<<n)
// AFR field: afr - AFR number, pin - pin (0..15)
TRUE_INLINE uint32_t AFRf(uint8_t afr, uint8_t pin){

15340
F3:F303/inc/Fx/stm32f303xe.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,28 +1,6 @@
/*
* This file is part of the libopencm3 project.
*
* Copyright (C) 2012 Karl Palsson <karlp@tweak.net.au>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/* Linker script for STM32F100x4, 16K flash, 4K RAM. */
/* Define memory regions. */
MEMORY
{
rom (rx) : ORIGIN = 0x08000000, LENGTH = 256K
rom (rx) : ORIGIN = 0x08000000, LENGTH = 128K
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 40K
ccmram (rwx) : ORIGIN = 0x10000000, LENGTH = 8K
}

View File

@ -0,0 +1,9 @@
MEMORY
{
rom (rx) : ORIGIN = 0x08000000, LENGTH = 384K
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 64K
ccmram (rwx) : ORIGIN = 0x10000000, LENGTH = 16K
}
/* Include the common ld script. */
INCLUDE stm32f3.ld