From b10fb37c58d5fb12511aa516830d191612c625c5 Mon Sep 17 00:00:00 2001 From: Edward Emelianov Date: Fri, 16 Aug 2024 12:37:11 +0300 Subject: [PATCH] Continue to write USB protocol documentation and fix bugs in code. --- F3:F303/Multistepper/Makefile | 1 + F3:F303/Multistepper/Readme.md | 274 +++++++++++++++++++++-- F3:F303/Multistepper/can.c | 11 +- F3:F303/Multistepper/commonproto.c | 5 + F3:F303/Multistepper/commonproto.h | 1 + F3:F303/Multistepper/flash.c | 10 +- F3:F303/Multistepper/flash.h | 3 +- F3:F303/Multistepper/hardware.c | 2 +- F3:F303/Multistepper/hashgen/hashgen.c | 19 +- F3:F303/Multistepper/hashgen/hdr.c | 9 +- F3:F303/Multistepper/hashgen/hdr.h | 6 +- F3:F303/Multistepper/hashgen/helpcmds.in | 1 + F3:F303/Multistepper/hashgen/run | 2 + F3:F303/Multistepper/hashgen/test | Bin 0 -> 22536 bytes F3:F303/Multistepper/hashgen/test.c | 6 +- F3:F303/Multistepper/hashgen/testdic | 1 + F3:F303/Multistepper/multistepper.bin | Bin 30072 -> 30164 bytes F3:F303/Multistepper/proto.c | 31 +-- F3:F303/Multistepper/version.inc | 4 +- 19 files changed, 321 insertions(+), 65 deletions(-) create mode 100755 F3:F303/Multistepper/hashgen/test diff --git a/F3:F303/Multistepper/Makefile b/F3:F303/Multistepper/Makefile index e17542c..33e7110 100644 --- a/F3:F303/Multistepper/Makefile +++ b/F3:F303/Multistepper/Makefile @@ -4,6 +4,7 @@ MCU := F303xd # change this linking script depending on particular MCU model, LDSCRIPT := stm32f303xD.ld DEFINES := -DUSB2_16 +#DEFINES := -DUSB1_16 include ../makefile.f3 include ../../makefile.stm32 diff --git a/F3:F303/Multistepper/Readme.md b/F3:F303/Multistepper/Readme.md index d3f5790..d65409c 100644 --- a/F3:F303/Multistepper/Readme.md +++ b/F3:F303/Multistepper/Readme.md @@ -9,7 +9,7 @@ Eighth stepper could be changed to 8 dependent multiplexed. Based on STM32F303VD ### Sorted by pin number -|**Pin #**|**Pin name **| **function**| **settings** |**comment ** | +| Pin # | Pin name | function | settings | comment | |---------|-------------|-------------|---------------|---------------------| | 1 | PE2 | DIAG | in | `diag` output of TMC| | 2 | PE3 | MUL0 | slow out | multiplexer address | @@ -113,23 +113,36 @@ Eighth stepper could be changed to 8 dependent multiplexed. Based on STM32F303VD | 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). +**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/flashing. ### Sorted by ports (with AF numbers). -|**Pin #**|**Pin name **| **function**| **settings** |**comment ** | +| Pin # | Pin name | function | settings | comment | |---------|-------------|-------------|---------------|---------------------| | 23 | PA0 | ADC1 in1 | ADC | External ADC inputs | | 24 | PA1 | ADC1 in2 | ADC | | @@ -216,18 +229,233 @@ _SW_ used as debugging/sewing; also (I remember about USB pullup only after end | 10 | PF9 | M0 STEP | AF3 (T15ch1) | clock of motor | | 11 | PF10 | M0 EN | slow out | l-s 0 or CS of SPI | - ## DMA usage * ADC1 - DMA1_ch1 * ADC2 - DMA2_ch1 -* USART2 (PDN-UART for drivers 0..3) - DMA1_ch6 (Rx), DMA1_ch7 (Tx) -* USART3 (PDN-UART for drivers 4..7) - DMA1_ch3 (Rx), DMA1_ch2 (Tx) -* SPI2 (screen) - DMA1_ch4 (Rx), DMA1_ch5 (Tx) [or may be dedicated to USART1] -# Other stepper drivers connection -## DRV8825 -Solder jumpers E2, B and A to connect ~RST and ~SLP to 3.3V. +# Stepper drivers connection -Microstepping selection produced by soldering H2 (bit0), G2 (bit1) and/or C (bit2). \ No newline at end of file +After changing stepper type by soldering/desoldering appropriate jumpers, don't forget to change their types in settings +(and save settings in MSU flash memory after checking it with `dumpconf`). + +Connection diagram for soldering jumpers shown on reverse side of PCB. + +Jumpers **A**, **B** and **C** allow to short the contacts of driver's pins 6 (CLK / DCO / ~SLEEP), 5 (PDN_UART / SDO / ~RESET) and 4 +(SPREAD / CS / M2), respectively. + +Jumper **D1** connects MCU UART output with jumper **E1**, **D2** connects MCU MISO with same contact of **E1**. +Jumpers **E1** and **E2** allows to pullup pin 5 of driver to be connected with 3.3V or UART/MISO. + +Jumper **F1** allows to connect pin 4 to CS line. **F2** connects pin 4 to 3.3V. + +Jumper **G1** connects pin 3 (MS2 / SCK / M1) to SCK line. **G2** connects this pin to 3.3V. + +Jumper **H1** connects pin pin 2 (MS1 / SDI / M0) to MOSI line **H2** connects it to 3.3V. + +So, depending on driver type and its mode you can solder these jumpers so, than driver can work over PDN-UART, SPI or configuration +pins can select microstepping directly. + + +## DRV8825 and other simplest drivers +Solder jumpers **E2**, **B** and **A** to connect **~RST** and **~SLP** to 3.3V. + +Microstepping selection produced by soldering **H2** (bit0), **G2** (bit1) and/or **C** (bit2). + +## TMC2209 and other PDN-UART based +Solder jumpers **D1** and **E1** to connect MCU UART2/UART3 to PDN-UART pin of driver. Solder jumpers **G2**/**H2** according +address table (X - soldered, O - not soldered): + +| Address | G2 | H2 | +|:-------:|:-----:|:-----:| +| 0 | **O** | **O** | +| 1 | **O** | **X** | +| 2 | **X** | **O** | +| 3 | **X** | **X** | +| 4 | **O** | **O** | +| 5 | **O** | **X** | +| 6 | **X** | **O** | +| 7 | **X** | **X** | + +## TMC2130 and other SPI based +(not negotiated yet) + +Solder jumpers **D2** and **E1** to connect MISO, **F1** to connect CS, **G1** for SCK and **H1** for MOSI. + +# Protocol + +## Errors +Error codes are in brackets: + +- **OK** (0) - all OK; +- **BADPAR** (1) - bad parameter (`N`) value (e.g. greater than max available or absent); +- **BADVAL** (2) - bad setter value (absent, text or over range); +- **WRONGLEN** (3) - (only for CAN bus, you can't meet in text protocol) wrong CAN message length; +- **BADCMD** (4) - unknown command, in case of USB proto user will see full command list instead of this error; +- **CANTRUN** (5) - can't run required function. + +## USB + +Used simple text protocol each string should contain one command and ends with `'\n'`. +Uppercase N at the end of command means parameter number (No of motor, button, ADC channel etc). +Getters and action commands have simplest format `command[N]`: some commands have no parameter, +other have two variants - with and without parameter; wariant without parameter will act on all +parameters (e.g. getter for all buttons) - in this case `N` enclosed in square brackets. +Setters have format `command[N] = value`. The `value` is integer number, so in case floating values +you need to multiply it by 1000 and write result. So, voltage and temperature are measured in millivolts +and thousandths of degC respectively. + +Some commands (especially that have no CAN analogue) have non-standard format described later. +They are not intended to be used in automatic control systems, only in manual mode. + +Answer for every (excluding special) setter and getter in case of success is `command[N]=value` +showing current value for getter or new value for setter. +In case of error you will see one of error mesages (message `OK` with errcode=0 usually not shown). +Also you can get such answers: + +- `BADCMD` - wrong command (not in dictionary); +- `BADARGS` - bad arguments format (e.g. for command `cansend` etc); +- `FAIL` - if something wrong and parser got impermissible error code. + +In following list letter `G` means "getter", `S` - "setter", no of them - custom USB-only command. +The number in brackets is CAN command code. The asterisk following command means that it isn't implemented yet (and maybe +will be never implemented). + +### absposN (35) GS +Absolute stepper position in steps, setter just changes current value. E.g. you want to set current position +as zero (be carefull: `gotoz` will zero position again on a zero-point limit switch). Maximum absolute value is `maxstepsN`. +### accelN (17) GS +Stepper acceleration/deceleration on ramp (steps/s^2), only positive. Maximum value is `ACCELMAXSTEPS` from `flash.h`. +### adcN (4) G +ADC value (N=0..3). +### buttonN (5) G +Given (N=0..6) buttons' state. If number is right, returns two strings: `buttonN=state` (where `state` is +`none`, `press`, `hold` or `release`) and `buttontmN=time` (where `time` is time from start of last event). + +!!!NOTE: Button numbering starts from 0, not 1 as shown on PCB!!! +### canerrcodes +Print last CAN errcodes. +Print "No errors" or last error code. +### canfilter DATA +CAN bus hardware filters, format: bank# FIFO# mode(M/I) num0 [num1 [num2 [num3]]] . +By default have two filters allowing to listen any CAN ID: + + Filter 0, FIFO0 in MASK mode: ID=0x01, MASK=0x01 + Filter 1, FIFO1 in MASK mode: ID=0x00, MASK=0x01 + +### canflood ID DATA +Send or clear (if empty) flood message: ID byte0 ... byteN. +On empy message return `NO ID given, send nothing!` (and stops flooding), or `Message parsed OK`. +### canfloodT N +Flood period, N in milliseconds (N >= 0ms). +### canid [ID] +Get or set CAN ID of device. Default CAN ID is "1". +### canignore [ID] +Software ignore list (max 10 IDs), negative to delete all, non-negative to add next. +### canincrflood ID +Send incremental flood message with given ID. Message have uint32_t little endian type and increments +for each packet starting from 0. Empty command stops flooding (`canflood` too). +### canpause +Pause filtered IN packets displaying. Returns `Pause CAN messages`. +### canreinit +Reinit CAN with last settings. Returns `OK`. +### canresume +Resume filtered IN packets displaying, returns `RESUME CAN messages`. +### cansend ID [data] +Send data over CAN with given ID. If `data` is omitted, send empty message. +In case of absence of `ACK` you can get message `CAN bus is off, try to restart it`. +### canspeed N +CAN bus speed (in kbps, 50 <= N <= 1500) +In case of setter, store new speed value in global parameters (and if you call `saveconf` later, it will be saved in flash memory). +### canstat +Get CAN bus status: values of registers `CAN->MSR`, `CAN->TSR`, `CAN->RF0R` and `CAN->RF1F`. +### diagn[N] (37) G * +DIAG state of motor N (or all)\n" +### drvtypeN (45) GS +Nth driver type (0 - only step/dir, 1 - UART, 2 - SPI, 3 - reserved). This parameter is taken from `.drvtype` bits of `motflags` settings parameter. +### dumperr +Dump error codes. Returns: + + Error codes: + 0 - OK + 1 - BADPAR + 2 - BADVAL + 3 - WRONGLEN + 4 - BADCMD + 5 - CANTRUN + +### dumpcmd +Dump command codes. Returns all list of CAN bus command codes. +### dumpconf +Dump current configuration. Returns a lot of information both common (like CAN bus speed, ID and so on) and +for each motor driver. You can call independeng getters for each of this parameter. +### dumpmotflags +Dump motor flags' bits (for `motflagsN`) and reaction to limit switches (`eswreact`) values: + + Motor flags: + bit0 - 0: reverse - invert motor's rotation + bit1 - 1: [reserved] + bit2 - 2: [reserved] + bit3 - 3: donthold - clear motor's power after stop + bit4 - 4: eswinv - inverse end-switches (1->0 instead of 0->1) + bit5 - 5: [reserved] + bit6 - 6,7: drvtype - driver type (0 - only step/dir, 1 - UART, 2 - SPI, 3 - reserved) + End-switches reaction: + 0 - ignore end-switches + 1 - stop @ esw in any moving direction + 2 - stop only when moving in given direction (e.g. to minus @ESW0) + +### dumpstates +Dump motors' state codes (for getter `stateN`): + + Motor's state codes: + 0 - relax + 1 - acceleration + 2 - moving + 3 - moving at lowest speed + 4 - deceleration + 5 - stalled (not used here!) + 6 - error + +### emstop[N] (29 with `N` and 31 without) +Emergency stop Nth motor or all (if `N` absent). Returns `OK` or error text. + + "eraseflash [=N] (38) erase flash data storage (full or only N'th page of it)\n" + "esw[N] (6) G end-switches state\n" + "eswreactN (24) GS end-switches reaction (0 - ignore, 1 - stop@any, 2 - stop@zero)\n" + "gotoN (26) GS move motor to given absolute position\n" + "gotozN (32) find zero position & refresh counters\n" + "gpioconfN* - GS GPIO configuration (0 - PUin, 1 - PPout, 2 - ODout), N=0..2\n" + "gpioN* (12) GS GPIO values, N=0..2\n" +help + "maxspeedN (18) GS max speed (steps per sec)\n" + "maxstepsN (21) GS max steps (from zero ESW)\n" + "mcut (7) G MCU T\n" + "mcuvdd (8) G MCU Vdd\n" + "microstepsN (16) GS microsteps settings (2^0..2^9)\n" + "minspeedN (19) min speed (steps per sec)\n" + "motcurrentN (46) GS motor current (1..32 for 1/32..32/32 of max current)\n" + "motflagsN (23) motorN flags\n" + "motmul* (36) GS external multiplexer status (<0 - disable, 0..7 - enable and set address)\n" + "motno (44) GS motor number for next `pdn` commands\n" + "motreinit (25) re-init motors after configuration changed\n" + "pdnN (43) GS read/write TMC2209 registers over uart @ motor0\n" + "ping (1)- echo given command back\n" + "relposN (27) GS relative move (get remaining)\n" + "relslowN (28) GS like 'relpos' but with slowest speed\n" + "reset (9) software reset\n" + "saveconf (13) save current configuration\n" + "screen* - GS screen enable (1) or disable (0)\n" + "speedlimit (20) G limiting speed for current microsteps setting\n" + "stateN (33) G motor state (0-relax, 1-accel, 2-move, 3-mvslow, 4-decel, 5-stall, 6-err)\n" + "stopN (30) stop motor with deceleration\n" + "time (10) G time from start (ms)\n" + "tmcbus* - GS TMC control bus (0 - USART, 1 - SPI)\n" + "udata* (39) GS data by usart in slave mode (text strings, '\\n'-terminated)\n" + "usartstatus* (40) GS status of USART1 (0 - off, 1 - master, 2 - slave)\n" + "vdrive (41) G approx voltage on Vdrive\n" + "vfive (42) G approx voltage on 5V bus\n" + +## CAN bus +Default CAN ID is 1. \ No newline at end of file diff --git a/F3:F303/Multistepper/can.c b/F3:F303/Multistepper/can.c index 9dc5069..f282ef8 100644 --- a/F3:F303/Multistepper/can.c +++ b/F3:F303/Multistepper/can.c @@ -37,6 +37,7 @@ 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 incrmessagectr = 0; // counter for incremental flooding static uint32_t last_err_code = 0; static CAN_status can_status = CAN_STOP; @@ -130,7 +131,7 @@ so if TBS1=4 and TBS2=3, sum=8, bit sampling freq is 48/8 = 6MHz void CAN_setup(uint16_t speed){ if(speed == 0) speed = oldspeed; else if(speed < 50) speed = 50; - else if(speed > 3000) speed = 3000; + else if(speed > 1500) speed = 1500; oldspeed = speed; uint32_t tmout = 10000; /* Enable the peripheral clock CAN */ @@ -240,7 +241,6 @@ void CAN_proc(){ 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); @@ -305,8 +305,11 @@ CAN_status CAN_send(uint8_t *msg, uint8_t len, uint16_t target_id){ } void CAN_flood(CAN_message *msg, int incr){ - if(incr){ incrflood = 1; return; } - incrflood = 0; + if(incr){ + incrmessagectr = 0; + incrflood = 1; + return; + }else incrflood = 0; if(!msg) flood_msg = NULL; else{ memcpy(&loc_flood_msg, msg, sizeof(CAN_message)); diff --git a/F3:F303/Multistepper/commonproto.c b/F3:F303/Multistepper/commonproto.c index 872bf3d..9b10d57 100644 --- a/F3:F303/Multistepper/commonproto.c +++ b/F3:F303/Multistepper/commonproto.c @@ -108,6 +108,11 @@ errcodes cu_emstop(uint8_t _U_ par, int32_t _U_ *val){ return ERR_OK; } +errcodes cu_emstopall(uint8_t _U_ par, int32_t _U_ *val){ + for(int i = 0; i < MOTORSNO; ++i) emstopmotor(i); + return ERR_OK; +} + errcodes cu_eraseflash(uint8_t _U_ par, int32_t _U_ *val){ NOPARCHK(par); if(ISSETTER(par)){ diff --git a/F3:F303/Multistepper/commonproto.h b/F3:F303/Multistepper/commonproto.h index 40c6672..f21d245 100644 --- a/F3:F303/Multistepper/commonproto.h +++ b/F3:F303/Multistepper/commonproto.h @@ -113,6 +113,7 @@ errcodes cu_canid(uint8_t par, int32_t *val); errcodes cu_diagn(uint8_t par, int32_t *val); errcodes cu_drvtype(uint8_t par, int32_t *val); errcodes cu_emstop(uint8_t par, int32_t *val); +errcodes cu_emstopall(uint8_t par, int32_t *val); errcodes cu_eraseflash(uint8_t par, int32_t *val); errcodes cu_esw(uint8_t par, int32_t *val); errcodes cu_eswreact(uint8_t par, int32_t *val); diff --git a/F3:F303/Multistepper/flash.c b/F3:F303/Multistepper/flash.c index c120c35..0553832 100644 --- a/F3:F303/Multistepper/flash.c +++ b/F3:F303/Multistepper/flash.c @@ -32,12 +32,12 @@ static const uint32_t FLASH_blocksize = (uint32_t)&_BLOCKSIZE; // max amount of Config records stored (will be recalculate in flashstorage_init() static uint32_t maxCnum = 1024 / sizeof(user_conf); // can't use blocksize here -#define DEFMF {.donthold = 1, .drvtype = DRVTYPE_UART} +#define DEFMF {.donthold = 1, .drvtype = DRVTYPE_SIMPLE} #define USERCONF_INITIALIZER { \ .userconf_sz = sizeof(user_conf) \ ,.CANspeed = 100 \ - ,.CANID = 0xaa \ + ,.CANID = 1 \ ,.microsteps = {32,32,32,32,32,32,32,32}\ ,.accel = {500,500,500,500,500,500,500,500} \ ,.maxspd = {2000,2000,2000,2000,2000,2000,2000,2000} \ @@ -203,8 +203,8 @@ int erase_storage(int npage){ int fn_dumpconf(uint32_t _U_ hash, char _U_ *args){ // "dumpconf" (3271513185) #ifdef EBUG - USB_sendstr("flashsize="); printu(FLASH_SIZE); USB_putbyte('*'); - printu(FLASH_blocksize); USB_putbyte('='); printu(FLASH_SIZE*FLASH_blocksize); + USB_sendstr("flashsize="); printu(FLASH_SIZE); USB_sendstr("kB\nblocksize="); + printu(FLASH_blocksize); newline(); #endif USB_sendstr("userconf_addr="); printuhex((uint32_t)Flash_Data); @@ -230,7 +230,7 @@ int fn_dumpconf(uint32_t _U_ hash, char _U_ *args){ // "dumpconf" (3271513185) printu(the_conf.motcurrent[i]); PROPNAME("motflags"); printuhex(*((uint8_t*)&the_conf.motflags[i])); - PROPNAME("eswreaction"); + PROPNAME("eswreact"); printu(the_conf.ESW_reaction[i]); #undef PROPNAME } diff --git a/F3:F303/Multistepper/flash.h b/F3:F303/Multistepper/flash.h index c59322b..26e5426 100644 --- a/F3:F303/Multistepper/flash.h +++ b/F3:F303/Multistepper/flash.h @@ -63,6 +63,7 @@ typedef struct{ * struct to save user configurations */ typedef struct __attribute__((packed, aligned(4))){ + uint32_t maxsteps[MOTORSNO]; // maximal amount of steps uint16_t userconf_sz; // "magick number" uint16_t CANspeed; // default CAN speed uint16_t CANID; // identifier @@ -70,11 +71,9 @@ typedef struct __attribute__((packed, aligned(4))){ uint16_t accel[MOTORSNO]; // acceleration/deceleration (steps/s^2) uint16_t maxspd[MOTORSNO]; // max motor speed (steps per second) uint16_t minspd[MOTORSNO]; // min motor speed (steps per second) - uint32_t maxsteps[MOTORSNO]; // maximal amount of steps motflags_t motflags[MOTORSNO]; // motor's flags uint8_t ESW_reaction[MOTORSNO]; // end-switches reaction (esw_react) uint8_t motcurrent[MOTORSNO]; // IRUN as fraction of max current (1..32) - uint8_t isSPI; // ==1 if there's SPI drivers instead of UART } user_conf; extern user_conf the_conf; // global user config (read from FLASH to RAM) diff --git a/F3:F303/Multistepper/hardware.c b/F3:F303/Multistepper/hardware.c index 940d6e0..3fe8aee 100644 --- a/F3:F303/Multistepper/hardware.c +++ b/F3:F303/Multistepper/hardware.c @@ -76,7 +76,7 @@ static IRQn_Type motirqs[MOTORSNO] = { // return two bits: 0 - ESW0, 1 - ESW1 (if inactive -> 1; if active -> 0) uint8_t ESW_state(uint8_t MOTno){ uint8_t val = 0; - if(the_conf.isSPI){ // only ESW0 used + if(the_conf.motflags[MOTno].drvtype == DRVTYPE_SPI){ // only ESW0 used val = ((ESWports[MOTno][0]->IDR & ESWpins[MOTno][0]) ? 1 : 0); if(the_conf.motflags[MOTno].eswinv) val ^= 1; return val; diff --git a/F3:F303/Multistepper/hashgen/hashgen.c b/F3:F303/Multistepper/hashgen/hashgen.c index a708d39..ac30ac6 100644 --- a/F3:F303/Multistepper/hashgen/hashgen.c +++ b/F3:F303/Multistepper/hashgen/hashgen.c @@ -170,14 +170,13 @@ static char *fnname(const char *cmd){ static const char *fhdr = "int parsecmd(const char *str){\n\ - char cmd[CMD_MAXLEN + 1];\n\ if(!str || !*str) return RET_CMDNOTFOUND;\n\ int i = 0;\n\ - while(*str > '@' && i < CMD_MAXLEN){ cmd[i++] = *str++; }\n\ - cmd[i] = 0;\n\ + while(*str > '@' && i < CMD_MAXLEN){ lastcmd[i++] = *str++; }\n\ + lastcmd[i] = 0;\n\ while(*str && *str <= ' ') ++str;\n\ char *args = (char*) str;\n\ - uint32_t h = hashf(cmd);\n\ + uint32_t h = hashf(lastcmd);\n\ switch(h){\n\n" ; static const char *ffooter = @@ -195,12 +194,15 @@ static const char *headercontent = #endif\n\n\ #define CMD_MAXLEN (32)\n\n\ enum{\n\ + RET_HELP = -3,\n\ RET_CMDNOTFOUND = -2,\n\ RET_WRONGCMD = -1,\n\ - RET_BAD = 0,\n\ - RET_GOOD = 1\n\ + RET_GOOD = 0,\n\ + RET_BAD = 1\n\ };\n\n\ -int parsecmd(const char *cmdwargs);\n\n"; +int parsecmd(const char *cmdwargs);\n\n\ +extern char lastcmd[];\n\n"; + static const char *sw = " case CMD_%s:\n\ return fn_%s(h, args);\n\ @@ -213,7 +215,8 @@ static const char *srchdr = #include \"%s\"\n\n\ #ifndef WAL\n\ #define WAL __attribute__ ((weak, alias (\"__f1\")))\n\ -#endif\n\nstatic int __f1(uint32_t _U_ h, char _U_ *a){return 1;}\n\n" +#endif\n\nstatic int __f1(uint32_t _U_ h, char _U_ *a){return 1;}\n\n\ +char lastcmd[CMD_MAXLEN + 1];\n\n" ; static void build(strhash *H, int hno, int hlen){ diff --git a/F3:F303/Multistepper/hashgen/hdr.c b/F3:F303/Multistepper/hashgen/hdr.c index 2d59d0f..b3a39ba 100644 --- a/F3:F303/Multistepper/hashgen/hdr.c +++ b/F3:F303/Multistepper/hashgen/hdr.c @@ -8,10 +8,10 @@ #define WAL __attribute__ ((weak, alias ("__f1"))) #endif -char lastcmd[CMD_MAXLEN + 1]; - static int __f1(uint32_t _U_ h, char _U_ *a){return 1;} +char lastcmd[CMD_MAXLEN + 1]; + int fn_abspos(uint32_t _U_ hash, char _U_ *args) WAL; // "abspos" (3056382221) int fn_accel(uint32_t _U_ hash, char _U_ *args) WAL; // "accel" (1490521981) @@ -76,6 +76,8 @@ int fn_gpio(uint32_t _U_ hash, char _U_ *args) WAL; // "gpio" (4286324660) int fn_gpioconf(uint32_t _U_ hash, char _U_ *args) WAL; // "gpioconf" (1309721562) +int fn_help(uint32_t _U_ hash, char _U_ *args) WAL; // "help" (4288288686) + int fn_maxspeed(uint32_t _U_ hash, char _U_ *args) WAL; // "maxspeed" (1498078812) int fn_maxsteps(uint32_t _U_ hash, char _U_ *args) WAL; // "maxsteps" (1506667002) @@ -244,6 +246,9 @@ int parsecmd(const char *str){ case CMD_GPIOCONF: return fn_gpioconf(h, args); break; + case CMD_HELP: + return fn_help(h, args); + break; case CMD_MAXSPEED: return fn_maxspeed(h, args); break; diff --git a/F3:F303/Multistepper/hashgen/hdr.h b/F3:F303/Multistepper/hashgen/hdr.h index 202d42f..1bd8555 100644 --- a/F3:F303/Multistepper/hashgen/hdr.h +++ b/F3:F303/Multistepper/hashgen/hdr.h @@ -5,10 +5,11 @@ #define CMD_MAXLEN (32) enum{ + RET_HELP = -3, RET_CMDNOTFOUND = -2, RET_WRONGCMD = -1, - RET_BAD = 0, - RET_GOOD = 1 + RET_GOOD = 0, + RET_BAD = 1 }; int parsecmd(const char *cmdwargs); @@ -47,6 +48,7 @@ extern char lastcmd[]; #define CMD_GOTOZ (3178103736) #define CMD_GPIO (4286324660) #define CMD_GPIOCONF (1309721562) +#define CMD_HELP (4288288686) #define CMD_MAXSPEED (1498078812) #define CMD_MAXSTEPS (1506667002) #define CMD_MCUT (4022718) diff --git a/F3:F303/Multistepper/hashgen/helpcmds.in b/F3:F303/Multistepper/hashgen/helpcmds.in index d03b2aa..23e2d78 100644 --- a/F3:F303/Multistepper/hashgen/helpcmds.in +++ b/F3:F303/Multistepper/hashgen/helpcmds.in @@ -30,6 +30,7 @@ "gotozN - find zero position & refresh counters\n" "gpioconfN* - GS GPIO configuration (0 - PUin, 1 - PPout, 2 - ODout), N=0..2\n" "gpioN* - GS GPIO values, N=0..2\n" + "help - print this help\n" "maxspeedN - GS max speed (steps per sec)\n" "maxstepsN - GS max steps (from zero ESW)\n" "mcut - G MCU T\n" diff --git a/F3:F303/Multistepper/hashgen/run b/F3:F303/Multistepper/hashgen/run index e79297c..a041db4 100755 --- a/F3:F303/Multistepper/hashgen/run +++ b/F3:F303/Multistepper/hashgen/run @@ -8,3 +8,5 @@ gcc hdr.c test.c strfunc.c -o test ./test "esw45 = some text" ./test "goto 55 = " ./test "stop 3256" +./test "mcut" + diff --git a/F3:F303/Multistepper/hashgen/test b/F3:F303/Multistepper/hashgen/test new file mode 100755 index 0000000000000000000000000000000000000000..8fae8fc4fde72574fb426ee093032d1d8e118d4b GIT binary patch literal 22536 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 literal 0 HcmV?d00001 diff --git a/F3:F303/Multistepper/hashgen/test.c b/F3:F303/Multistepper/hashgen/test.c index 3dc0dda..fce0292 100644 --- a/F3:F303/Multistepper/hashgen/test.c +++ b/F3:F303/Multistepper/hashgen/test.c @@ -7,9 +7,9 @@ static int noargs(uint32_t hash){ switch(hash){ - case CMD_REBOOT: printf("REBOOT\n"); break; + case CMD_RESET: printf("REBOOT\n"); break; case CMD_TIME: printf("TIME!\n"); break; - case CMD_TEMP: printf("Temp\n"); break; + case CMD_MCUT: printf("Temp\n"); break; default: printf("Unknown hash 0x%x\n", hash); return 0; } return 1; @@ -30,7 +30,7 @@ static int withparno(uint32_t hash, char *args){ switch(hash){ case CMD_ESW: fname = "ESW"; break; case CMD_GOTO: fname = "GOTO"; break; - case CMD_POS: fname = "POS"; break; + case CMD_ABSPOS: fname = "ABSPOS"; break; case CMD_STOP: fname = "STOP"; break; default: fname = "unknown"; } diff --git a/F3:F303/Multistepper/hashgen/testdic b/F3:F303/Multistepper/hashgen/testdic index daaab7e..0fe640b 100644 --- a/F3:F303/Multistepper/hashgen/testdic +++ b/F3:F303/Multistepper/hashgen/testdic @@ -30,6 +30,7 @@ goto gotoz gpioconf gpio +help maxspeed maxsteps mcut diff --git a/F3:F303/Multistepper/multistepper.bin b/F3:F303/Multistepper/multistepper.bin index f06205dfee23369d1a07d797ce67e5295782be55..97ee936a5428b664c8e02e09bd5a58809a119e0d 100755 GIT binary patch delta 10830 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 3000){ - USB_sendstr("Highest speed is 3000kbps"); + }else if(N > 1500){ + USND("Highest speed is 1500kbps"); return RET_GOOD; } CAN_reinit((uint16_t)N); - // theconf.canspeed = N; + uint32_t regval = 4500 / N; + the_conf.CANspeed = 4500 * regval; USB_sendstr("Reinit CAN bus with speed "); - printu(N); USB_sendstr("kbps"); newline(); + printu(the_conf.CANspeed); USB_sendstr("kbps"); newline(); return RET_GOOD; } @@ -529,7 +529,8 @@ static int canusb_function(uint32_t hash, char *args){ } USB_sendstr("button"); USB_putbyte('0'+PARBASE(par)); USB_putbyte('='); USB_sendstr(kstate); - USB_sendstr("\nbuttontm="); USB_sendstr(u2str(val)); + USB_sendstr("\nbuttontm"); USB_putbyte('0'+PARBASE(par)); + USB_putbyte('='); USB_sendstr(u2str(val)); newline(); return RET_GOOD; break; @@ -552,7 +553,9 @@ static int canusb_function(uint32_t hash, char *args){ e = cu_drvtype(par, &val); break; case CMD_EMSTOP: - e = cu_emstop(par, &val); + if(par == CANMESG_NOPAR) e = cu_emstopall(par, &val); + else e = cu_emstop(par, &val); + if(e == ERR_OK){ USND("OK"); return RET_GOOD;} break; case CMD_ESWREACT: e = cu_eswreact(par, &val); @@ -682,6 +685,7 @@ int fn_goto(uint32_t _U_ hash, char _U_ *args) AL; //* "goto" (4286309438) int fn_gotoz(uint32_t _U_ hash, char _U_ *args) AL; //* "gotoz" (3178103736) int fn_gpio(uint32_t _U_ hash, char _U_ *args) AL; //* "gpio" (4286324660) int fn_gpioconf(uint32_t _U_ hash, char _U_ *args) AL; //* "gpioconf" (1309721562) +int fn_help(uint32_t _U_ hash, char _U_ *args){return RET_HELP;} // "help" (4288288686) int fn_maxspeed(uint32_t _U_ hash, char _U_ *args) AL; //* "maxspeed" (1498078812) int fn_maxsteps(uint32_t _U_ hash, char _U_ *args) AL; //* "maxsteps" (1506667002) int fn_mcut(uint32_t _U_ hash, char _U_ *args) AL; // "mcut" (4022718) @@ -717,8 +721,9 @@ int fn_vfive(uint32_t _U_ hash, char _U_ *args) AL; // "vfive" (3017477285) const char *cmd_parser(const char *txt){ int ret = parsecmd(txt); switch(ret){ - case RET_CMDNOTFOUND: return helpstring; break; - case RET_WRONGCMD: return "Wrong command parameters\n"; break; + case RET_HELP: return helpstring; break; + case RET_CMDNOTFOUND: return "BADCMD\n"; break; + case RET_WRONGCMD: return "BADARGS\n"; break; case RET_GOOD: return NULL; break; default: return "FAIL\n"; break; } diff --git a/F3:F303/Multistepper/version.inc b/F3:F303/Multistepper/version.inc index a6f8163..d05e2b0 100644 --- a/F3:F303/Multistepper/version.inc +++ b/F3:F303/Multistepper/version.inc @@ -1,2 +1,2 @@ -#define BUILD_NUMBER "114" -#define BUILD_DATE "2024-08-15" +#define BUILD_NUMBER "119" +#define BUILD_DATE "2024-08-16"