From 8e78a12f06e9f45ac990623846de6f133750d9c0 Mon Sep 17 00:00:00 2001 From: Edward Emelianov Date: Tue, 10 Mar 2026 00:20:43 +0300 Subject: [PATCH] GPIO tested; add monitoring. TODO: USB/SPI/I2C --- F0:F030,F042,F072/usbcan_gpio/Makefile | 2 +- F0:F030,F042,F072/usbcan_gpio/gpio.c | 203 +++++-- F0:F030,F042,F072/usbcan_gpio/gpio.h | 52 +- F0:F030,F042,F072/usbcan_gpio/gpioproto.c | 151 ----- F0:F030,F042,F072/usbcan_gpio/gpioproto.cpp | 530 ++++++++++++++++++ F0:F030,F042,F072/usbcan_gpio/gpioproto.h | 16 + F0:F030,F042,F072/usbcan_gpio/hardware.c | 1 + F0:F030,F042,F072/usbcan_gpio/usb_dev.h | 4 +- F0:F030,F042,F072/usbcan_gpio/usbcangpio.bin | Bin 17892 -> 19140 bytes .../usbcan_gpio/usbcangpio.creator.user | 2 +- .../usbcan_gpio/usbcangpio.files | 4 +- F0:F030,F042,F072/usbcan_gpio/version.inc | 4 +- 12 files changed, 770 insertions(+), 199 deletions(-) delete mode 100644 F0:F030,F042,F072/usbcan_gpio/gpioproto.c create mode 100644 F0:F030,F042,F072/usbcan_gpio/gpioproto.cpp diff --git a/F0:F030,F042,F072/usbcan_gpio/Makefile b/F0:F030,F042,F072/usbcan_gpio/Makefile index 12e87d8..f9d8d4b 100644 --- a/F0:F030,F042,F072/usbcan_gpio/Makefile +++ b/F0:F030,F042,F072/usbcan_gpio/Makefile @@ -11,5 +11,5 @@ DEFINES := -DUSB2_16 include ../makefile.f0 include ../../makefile.stm32 -$(OBJDIR)/gpioproto.o: gpioproto.c $(VERSION_FILE) +$(OBJDIR)/gpioproto.o: gpioproto.cpp $(VERSION_FILE) $(OBJDIR)/canproto.o: canproto.c $(VERSION_FILE) diff --git a/F0:F030,F042,F072/usbcan_gpio/gpio.c b/F0:F030,F042,F072/usbcan_gpio/gpio.c index bbeaabd..5b222c9 100644 --- a/F0:F030,F042,F072/usbcan_gpio/gpio.c +++ b/F0:F030,F042,F072/usbcan_gpio/gpio.c @@ -17,101 +17,236 @@ */ #include +#include #include "flash.h" #include "gpio.h" +static uint16_t monitor_mask[2] = {0}; // pins to monitor == 1 (ONLY GPIO!!!) +static uint16_t oldstates[2][16] = {0}; // previous state (16 bits - as some pins could be analog) + +// strings for keywords +const char *str_keywords[] = { +#define KW(x) [STR_ ## x] = #x, + KEYWORDS +#undef KW +}; + // TODO: remove AFmask, make function to get right AF number by pin's FuncValues typedef struct{ - funcvalues_t vals; - uint8_t AFmask; + uint8_t funcs; // bitmask according to enum FuncNames + uint8_t AF[FUNC_AMOUNT]; // alternate function number for corresponding `FuncNames` number } pinprops_t; +#define CANADC(x) ((x) & (1< 1 || pin > 15) return FALSE; - if(!the_conf.pinconfig[port][pin].enable) return FALSE; + if(the_conf.pinconfig[port][pin].enable) return FALSE; return TRUE; } /** - * @brief is_func_allowed - check if alternate function `afno` allowed on given pin + * @brief set_pinfunc - check if alternate function `afno` allowed on given pin * @param port - 0 for GPIOA and 1 for GPIOB * @param pin - 0..15 - * @param afno - number of alternate function + * @param pcfg (io) - pin configuration * @return TRUE if all OK */ -int is_func_allowed(uint8_t port, uint8_t pin, pinconfig_t *pcfg){ - if(is_disabled(port, pin)) return FALSE; +int set_pinfunc(uint8_t port, uint8_t pin, pinconfig_t *pcfg){ + DBG("set_pinfunc()\n"); + if(is_disabled(port, pin) || !pcfg){ + DBG("Disabled?\n"); + return FALSE; + } const pinprops_t *props = &pin_props[port][pin]; switch(pcfg->mode){ case MODE_ANALOG: - if(!props->vals.canADC) return FALSE; + DBG("Analog\n"); + if(!CANADC(props->funcs)){ + DBG("Can't ADC\n"); + return FALSE; + } pcfg->pull = PULL_NONE; // no PullUp for analog mode break; case MODE_AF: + DBG("Altfun\n"); // here af is one of enum FuncValues !!! we should change `af` later - if(!((1<af) & props->vals.flags)) return FALSE; - // TODO: set right AF number here !!! - //if(!(props->AFmask & (1 << pcfg->af))) return FALSE; // no such AF or not supported + if(pcfg->af >= FUNC_AMOUNT || !((1<af) & props->funcs)){ + DBG("Wrong AF\n"); + return FALSE; + } + pcfg->afno = props->AF[pcfg->af]; pcfg->speed = SPEED_HIGH; // many AF needs high speed pcfg->otype = OUTPUT_PP; // no OD for AF break; case MODE_INPUT: // no limits + DBG("Input\n"); break; - case MODE_OUTPUT: // no limits + case MODE_OUTPUT: // remove pullup/pulldown for PP + DBG("Output\n"); + if(pcfg->otype == OUTPUT_PP) pcfg->pull = PULL_NONE; break; default: + DBG("Wrong\n"); return FALSE; } + pcfg->enable = 1; // don't forget to set enable flag! + the_conf.pinconfig[port][pin] = *pcfg; + DBG("All OK\n"); return TRUE; } +// reinit all GPIO registers due to config; also configure (if need) USART1/2, SPI1 and I2C1 int gpio_reinit(){ + bzero(monitor_mask, sizeof(monitor_mask)); + bzero(oldstates, sizeof(oldstates)); for(int port = 0; port < 2; port++){ GPIO_TypeDef *gpio = (port == 0) ? GPIOA : GPIOB; for(int pin = 0; pin < 16; pin++){ pinconfig_t *cfg = &the_conf.pinconfig[port][pin]; - int shift2 = pin << 1; if(!cfg->enable) continue; + const pinprops_t *props = &pin_props[port][pin]; + if(cfg->mode == MODE_AF && (cfg->af >= FUNC_AMOUNT || + !((1<af) & props->funcs) || + (cfg->afno != props->AF[cfg->af]))){ // wrong configuration -> don't mind AF, make FLIN + DBG("Wrong AF config -> FL IN\n"); + cfg->af = FUNC_AIN; + cfg->afno = 0; + cfg->mode = MODE_INPUT; + cfg->monitor = 0; + cfg->speed = SPEED_LOW; + cfg->pull = PULL_NONE; + } + int shift2 = pin << 1; gpio->MODER = (gpio->MODER & ~(3 << shift2))| (cfg->mode << shift2); gpio->OTYPER = (gpio->OTYPER & ~(1 << pin)) | (cfg->otype << pin); gpio->OSPEEDR = (gpio->OSPEEDR & ~(3 << shift2)) | (cfg->speed << shift2); gpio->PUPDR = (gpio->PUPDR & ~(3 << shift2)) | (cfg->pull << shift2); if(pin < 8){ int shift4 = pin << 4; - gpio->AFR[0] = (gpio->AFR[0] & ~(0xf << shift4)) | (cfg->af << shift4); + gpio->AFR[0] = (gpio->AFR[0] & ~(0xf << shift4)) | (cfg->afno << shift4); }else{ int shift4 = (pin - 8) << 4; - gpio->AFR[1] = (gpio->AFR[1] & ~(0xf << shift4)) | (cfg->af << shift4); + gpio->AFR[1] = (gpio->AFR[1] & ~(0xf << shift4)) | (cfg->afno << shift4); } + if(cfg->monitor && cfg->mode != MODE_AF) monitor_mask[port] |= (1 << pin); } } // TODO: configure USART, SPI etc + // also chech cfg->monitor! return TRUE; } + +// get MODER for current pin +TRUE_INLINE uint32_t get_moder(volatile GPIO_TypeDef * GPIOx, uint8_t pin){ + return (GPIOx->MODER >> (pin << 1)) & 3; +} + +/** + * @brief pin_out - change pin value + * @param port - 0 for GPIOA, 1 for GPIOB + * @param pin - 0..15 + * @param newval - 0 or 1 (reset/set) + * @return FALSE if pin isn't OUT or other err + * here I check real current settings by GPIOx->MODER + */ +int pin_out(uint8_t port, uint8_t pin, uint8_t newval){ + if(port > 1 || pin > 15) return FALSE; + volatile GPIO_TypeDef * GPIOx = (port == 0) ? GPIOA : GPIOB; + uint16_t mask = 1 << pin; + uint32_t moder = get_moder(GPIOx, pin); + if(moder != MODE_OUTPUT) return FALSE; + if(newval) GPIOx->BSRR = mask; + else GPIOx->BRR = mask; + return TRUE; +} + +/** + * @brief pin_in - get current pin's value (0/1 for regular GPIO, 0..4095 for ADC) + * @param port - 0..1 + * @param pin - 0..15 + * @return value or -1 if pin have AF or don't used + */ +int16_t pin_in(uint8_t port, uint8_t pin){ + if(port > 1 || pin > 15) return -1; + volatile GPIO_TypeDef * GPIOx = (port == 0) ? GPIOA : GPIOB; + uint32_t moder = get_moder(GPIOx, pin); + int16_t val = -1; + switch(moder){ // check REAL pin config + case MODE_INPUT: + case MODE_OUTPUT: + if(GPIOx->IDR & (1< 1) return 0; + if(0 == monitor_mask[port]) return 0; // nothing to monitor + volatile GPIO_TypeDef * GPIOx = (port == 0) ? GPIOA : GPIOB; + uint32_t moder = GPIOx->MODER; + uint16_t curpinbit = 1; // shift each iteration + uint16_t *oldstate = oldstates[port]; + uint16_t alert = 0; + for(int pin = 0; pin < 16; ++pin, curpinbit <<= 1, moder >>= 2){ + uint8_t curm = moder & 3; + if((curm == MODE_AF) || 0 == (monitor_mask[port] & curpinbit)) continue; // monitor also OUT (if OD) + // TODO: add AIN + if(curm == MODE_ANALOG) continue; + uint16_t curval = (GPIOx->IDR & curpinbit) ? 1 : 0; + if(oldstate[pin] != curval){ + oldstate[pin] = curval; + alert |= curpinbit; + } + } + return alert; +} diff --git a/F0:F030,F042,F072/usbcan_gpio/gpio.h b/F0:F030,F042,F072/usbcan_gpio/gpio.h index 3ff2d77..a35c602 100644 --- a/F0:F030,F042,F072/usbcan_gpio/gpio.h +++ b/F0:F030,F042,F072/usbcan_gpio/gpio.h @@ -20,6 +20,17 @@ #include +#ifdef EBUG +#define USBIF IGPIO +#include "strfunc.h" +#define DBG(x) SEND(x) +#define DBGNL() NL() +#else +#define DBG(x) +#define DBGNL() +#endif + + // MODER typedef enum{ MODE_INPUT = 0, @@ -48,11 +59,15 @@ typedef enum{ SPEED_HIGH = 3 } pinspeed_t; -enum FuncShifts{ // shift 1 by this to get "canUSART" etc; not more than 7! +// !!! FuncNames means position of bit in funcvalues_t.flags! +enum FuncNames{ // shift 1 by this to get "canUSART" etc; not more than 7! + FUNC_AIN = 0, FUNC_USART = 1, FUNC_SPI = 2, + FUNC_I2C = 3, + FUNC_AMOUNT // just for arrays' sizes }; - +/* typedef union{ struct{ uint8_t canADC : 1; @@ -61,14 +76,15 @@ typedef union{ }; uint8_t flags; } funcvalues_t; - +*/ typedef struct{ uint8_t enable : 1; // [immutable!] pin config avialable (==1 for PA0-PA3, PA5-PA7, PA9, PA10, PB0-PB7, PB10, PB11, ==0 for rest) pinmode_t mode : 2; pinpull_t pull : 2; pinout_t otype : 1; pinspeed_t speed : 2; - uint8_t af : 3; // alternate function number (only if mode == MODE_AF) + uint8_t afno : 3; // alternate function number (only if mode == MODE_AF) + uint8_t af : 3; // alternate function name (`FuncNames`) uint8_t monitor : 1; // monitor changes } pinconfig_t; @@ -88,5 +104,31 @@ typedef struct{ uint8_t enabled : 1; } spiconfig_t; -int is_func_allowed(uint8_t port, uint8_t pin, pinconfig_t *pcfg); +// strings for keywords +extern const char *str_keywords[]; +#define KEYWORDS \ +KW(AIN) \ +KW(IN) \ +KW(OUT) \ +KW(AF) \ +KW(PU)\ +KW(PD) \ +KW(FL) \ +KW(PP) \ +KW(OD) \ +KW(USART) \ +KW(SPI) \ +KW(I2C) \ +KW(MONITOR) + +enum{ // indexes of string keywords +#define KW(k) STR_ ## k, + KEYWORDS +#undef KW +}; + +int set_pinfunc(uint8_t port, uint8_t pin, pinconfig_t *pcfg); int gpio_reinit(); +int pin_out(uint8_t port, uint8_t pin, uint8_t newval); +int16_t pin_in(uint8_t port, uint8_t pin); +uint16_t gpio_alert(uint8_t port); diff --git a/F0:F030,F042,F072/usbcan_gpio/gpioproto.c b/F0:F030,F042,F072/usbcan_gpio/gpioproto.c deleted file mode 100644 index 0485fdd..0000000 --- a/F0:F030,F042,F072/usbcan_gpio/gpioproto.c +++ /dev/null @@ -1,151 +0,0 @@ -/* - * This file is part of the usbcangpio project. - * Copyright 2026 Edward V. Emelianov . - * - * 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 . - */ - -#include - -#include "can.h" -#include "flash.h" -#include "gpioproto.h" - -#define USBIF IGPIO -#include "strfunc.h" - -extern volatile uint32_t Tms; -static const char *const sOKn = "OK\n", *const sERRn = "ERR\n"; - -const char *helpstring = - REPOURL - "d - dump flash\n" - "ix - rename interface number x (0 - CAN, 1 - GPIO)\n" - "Cx - starting CAN bus speed (kBaud)\n" - "Dx - send text x to CAN USB interface\n" - "E - erase storage\n" - "F - reinit configurations from flash\n" - "R - soft reset\n" - "S - store new parameters into flash\n" - "T - print current Tms\n" -; - -static void dumpflash(){ - SEND("userconf_sz="); SEND(u2str(the_conf.userconf_sz)); - SEND("\ncurrentconfidx="); SENDn(i2str(currentconfidx)); - for(int i = 0; i < InterfacesAmount; ++i){ - SEND("interface"); PUTCHAR('0' + i); - PUTCHAR('='); - int l = the_conf.iIlengths[i] / 2; - char *ptr = (char*) the_conf.iInterface[i]; - for(int j = 0; j < l; ++j){ - PUTCHAR(*ptr); - ptr += 2; - } - NL(); - } - SEND("canspeed="); SENDn(u2str(the_conf.CANspeed)); -} - -// set new interface name -static const char* setiface(char *str){ - if(!str || !*str) goto err; - uint32_t N; - const char *nxt = getnum(str, &N); - if(!nxt || nxt == str || N >= InterfacesAmount) goto err; - //nxt = strchr(nxt, '='); - //if(!nxt || !*(++nxt)) goto err; - nxt = omit_spaces(nxt); - if(!nxt || !*nxt) goto err; - int l = strlen(nxt); - if(l > MAX_IINTERFACE_SZ) goto err; - the_conf.iIlengths[N] = (uint8_t) l * 2; - char *ptr = (char*)the_conf.iInterface[N]; - for(int i = 0; i < l; ++i){ - char c = *nxt++; - *ptr++ = (c > ' ') ? c : '_'; - *ptr++ = 0; - } - return sOKn; -err: - return sERRn; -} - -static const char* setCANspeed(char *buf){ - uint32_t N; - if(buf == getnum(buf, &N)) return sERRn; - if(N < CAN_MIN_SPEED || N > CAN_MAX_SPEED) return sERRn; - the_conf.CANspeed = N; - return sOKn; -} - -#include "hashparser.h" -static const char *cmd_parser(char *buf){ - if(!buf || !*buf) return NULL; - if(strlen(buf) > 1){ - chk(buf); - // "long" commands - char c = *buf++; - switch(c){ - case 'C': - return setCANspeed(buf); - case 'D': - if(USB_sendstr(ICAN, buf)) return sOKn; - else return sERRn; - case 'i': - return setiface(buf); - default: - // echo wrong data with terminating '\n' - SENDn(buf-1); - return NULL; - } - } - // "short" commands - switch(*buf){ - case 'd': - dumpflash(); - break; - case 'E': - if(erase_storage()) return sERRn; - return sOKn; - case 'F': - flashstorage_init(); - return sOKn; - case 'R': - NVIC_SystemReset(); - return NULL; - case 'S': - if(store_userconf()) return sERRn; - return sOKn; - case 'T': - SEND("T="); - SENDn(u2str(Tms)); - break; - default: // help - SEND(helpstring); - break; - } - return NULL; -} - -void GPIO_process(){ - char inbuff[MAXSTRLEN]; - int l = RECV(inbuff, MAXSTRLEN); - if(l == 0) return; - if(l < 0) SEND("ERROR: USB buffer overflow or string was too long\n"); - else{ - const char *ans = cmd_parser(inbuff); - if(ans) SEND(ans); - } -} diff --git a/F0:F030,F042,F072/usbcan_gpio/gpioproto.cpp b/F0:F030,F042,F072/usbcan_gpio/gpioproto.cpp new file mode 100644 index 0000000..8f6d180 --- /dev/null +++ b/F0:F030,F042,F072/usbcan_gpio/gpioproto.cpp @@ -0,0 +1,530 @@ +/* + * This file is part of the usbcangpio project. + * Copyright 2026 Edward V. Emelianov . + * + * 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 . + */ + +// !!! Some commands could change icoming string, so don't try to use it after function call !!! + +#include + +extern "C"{ +#include +#include "can.h" +#include "flash.h" +#include "gpioproto.h" +#include "gpio.h" +#include "gpioproto.h" +#undef USBIF +#define USBIF IGPIO +#include "strfunc.h" +} + +extern volatile uint32_t Tms; + +// TODO: add analog threshold! + +// list of all commands and handlers +#define COMMAND_TABLE \ + COMMAND(canspeed, "CAN bus speed setter/getter (kBaud, 10..1000)") \ + COMMAND(curcanspeed,"current CAN bus speed (interface speed, not settings)") \ + COMMAND(dumpconf, "dump current configuration") \ + COMMAND(eraseflash, "erase full flash storage") \ + COMMAND(help, "show this help") \ + COMMAND(mcureset, "reset MCU") \ + COMMAND(PA, "GPIOA setter/getter (type PA0=help for further info)") \ + COMMAND(PB, "GPIOB setter/getter") \ + COMMAND(readconf, "re-read config from flash") \ + COMMAND(reinit, "apply pin config") \ + COMMAND(saveconf, "save current user configuration into flash") \ + COMMAND(sendcan, "send all after '=' to CAN USB interface") \ + COMMAND(setiface, "set/get name of interface x (0 - CAN, 1 - GPIO)") \ + COMMAND(storeconf, "save config to flash") \ + COMMAND(time, "show current time (ms)") + +// COMMAND(USART, "Read USART data or send (USART=hex)") +// COMMAND(usartconf, "set USART params (e.g. usartconf=115200 8N1)") +// COMMAND(SPI, "Read SPI data or send (SPI=hex)") +// COMMAND(spiconf, "set SPI params") + + +typedef struct { + const char *name; + const char *desc; +} CmdInfo; + +// prototypes +#define COMMAND(name, desc) static errcodes_t cmd_ ## name(const char*, char*); + COMMAND_TABLE +#undef COMMAND + +static const CmdInfo cmdInfo[] = { // command name, description - for `help` +#define COMMAND(name, desc) { #name, desc }, + COMMAND_TABLE +#undef COMMAND +}; + +// pin settings parser +struct Keyword { + int index; // index in str_keywords + uint8_t group; + uint8_t value; +}; + +enum KeywordGroup { + GROUP_MODE, + GROUP_PULL, + GROUP_OTYPE, + GROUP_FUNC, + GROUP_MISC +}; + +enum MiscValues{ + MISC_MONITOR = 1, +}; + +static const Keyword keywords[] = { +#define KEY(x, g, v) { STR_ ## x, g, v}, + KEY(AIN, GROUP_MODE, MODE_ANALOG) + KEY(IN, GROUP_MODE, MODE_INPUT) + KEY(OUT, GROUP_MODE, MODE_OUTPUT) + KEY(AF, GROUP_MODE, MODE_AF) + KEY(PU, GROUP_PULL, PULL_UP) + KEY(PD, GROUP_PULL, PULL_DOWN) + KEY(FL, GROUP_PULL, PULL_NONE) + KEY(PP, GROUP_OTYPE, OUTPUT_PP) + KEY(OD, GROUP_OTYPE, OUTPUT_OD) + KEY(USART, GROUP_FUNC, FUNC_USART) + KEY(SPI, GROUP_FUNC, FUNC_SPI) + KEY(I2C, GROUP_FUNC, FUNC_I2C) + KEY(MONITOR, GROUP_MISC, MISC_MONITOR) +#undef K +}; +#define NUM_KEYWORDS (sizeof(keywords)/sizeof(keywords[0])) + +static const char* errtxt[ERR_AMOUNT] = { + [ERR_OK] = "OK", + [ERR_BADCMD] = "BADCMD", + [ERR_BADPAR] = "BADPAR", + [ERR_BADVAL] = "BADVAL", + [ERR_WRONGLEN] = "WRONGLEN", + [ERR_CANTRUN] = "CANTRUN", +}; + +static const char *pinhelp = + "Pin settings: PXx = MODE PULL OTYPE FUNC MISC (in any sequence), where\n" + " MODE: AIN, IN or OUT (analog in, digital in, output)\n" + " PULL: PU, PD or FL (pullup, pulldown, no pull - floating)\n" + " OTYPE: PP or OD (push-pull or open-drain)\n" + " FUNC: USART or SPI (enable alternate function and configure peripheal)\n" + " MISC: MONITOR (send data by USB as only state changed)\n\n" + ; + +static const char *EQ = " = "; // equal sign for getters + +/** + * @brief splitargs - get command parameter and setter from `args` + * @param args (i) - rest of string after command (like `1 = PU OD OUT`) + * @param parno (o) - parameter number or -1 if none + * @return setter (part after `=` without leading spaces) or NULL if none + */ +static char *splitargs(char *args, int32_t *parno){ + if(!args) return NULL; + uint32_t U32; + char *next = getnum(args, &U32); + int p = -1; + if(next != args && U32 <= MAXPARNO) p = U32; + if(parno) *parno = p; + next = strchr(next, '='); + if(next){ + DBG("next="); DBG(next); DBGNL(); + if(*(++next)) next = omit_spaces(next); + if(*next == 0) next = NULL; + } + DBG("next="); DBG(next); DBGNL(); + return next; +} + +/** + * @brief argsvals - split `args` into `parno` and setter's value + * @param args - rest of string after command + * @param parno (o) - parameter number or -1 if none + * @param parval - integer setter's value + * @return false if no setter or it's not a number, true - got setter's num + */ +static bool argsvals(char *args, int32_t *parno, int32_t *parval){ + char *setter = splitargs(args, parno); + if(!setter) return false; + int32_t I32; + char *next = getint(setter, &I32); + if(next != setter && parval){ + *parval = I32; + return true; + } + return false; +} + +// `port` and `pin` are checked in `parse_pin_command` +// `PAx = ` also printed there +static void pin_getter(uint8_t port, uint8_t pin){ + int16_t val = pin_in(port, pin); + if(val < 0) SENDn(errtxt[ERR_CANTRUN]); + SEND(port == 0 ? "PA" : "PB"); SEND(u2str((uint32_t)pin)); SEND(EQ); + SENDn(u2str((uint32_t)val)); +} + +// `port` and `pin` are checked in `parse_pin_command` +// set GPIO values (if *setter is 0/1) or configure it +static errcodes_t pin_setter(uint8_t port, uint8_t pin, char *setter){ + char _1st = *setter; + if(_1st == '0' || _1st == '1'){ // just set/clear pin state; throw out all text after "1"/"0" + DBG("set pin\n"); + if(pin_out(port, pin, _1st - '0')) return ERR_OK; + return ERR_CANTRUN; + } + if(strncmp(setter, "help", 4) == 0){ // send PIN help + SENDn(pinhelp); + return ERR_AMOUNT; + } + // complex setter: parse properties + uint8_t mode_set = 0xFF, pull_set = 0xFF, otype_set = 0xFF, func_set = 0xFF; + bool monitor = false; + char *saveptr, *token = strtok_r(setter, " ,", &saveptr); + while(token){ + size_t i = 0; + for(; i < NUM_KEYWORDS; i++){ + if(strcmp(token, str_keywords[keywords[i].index]) == 0){ + switch(keywords[i].group){ + case GROUP_MODE: + DBG("GROUP_MODE\n"); + if(mode_set != 0xFF) return ERR_BADVAL; // repeated similar group parameter + mode_set = keywords[i].value; + break; + case GROUP_PULL: + DBG("GROUP_PULL\n"); + if(pull_set != 0xFF) return ERR_BADVAL; + pull_set = keywords[i].value; + break; + case GROUP_OTYPE: + DBG("GROUP_OTYPE\n"); + if(otype_set != 0xFF) return ERR_BADVAL; + otype_set = keywords[i].value; + break; + case GROUP_FUNC: + DBG("GROUP_FUNC\n"); + if(func_set != 0xFF) return ERR_BADVAL; + func_set = keywords[i].value; + break; + case GROUP_MISC: + DBG("GROUP_MISC\n"); + if(keywords[i].value == MISC_MONITOR) monitor = true; + break; + } + break; + } + } + if(i == NUM_KEYWORDS) return ERR_BADVAL; // not found + token = strtok_r(NULL, " ,", &saveptr); + } + if(func_set != 0xFF) mode_set = MODE_AF; + if(mode_set == 0xFF) return ERR_BADVAL; // user forgot to set mode + // set defaults + if(pull_set == 0xFF) pull_set = PULL_NONE; + if(otype_set == 0xFF) otype_set = OUTPUT_PP; + // can also do something with `speed_set`, then remove SPEED_MEDIUM from `curconfig` + // check that current parameters combination is acceptable for current pin + pinconfig_t curconf; + curconf.mode = static_cast (mode_set); + curconf.pull = static_cast (pull_set); + curconf.otype = static_cast (otype_set); + curconf.speed = SPEED_MEDIUM; + curconf.af = func_set; + curconf.monitor = monitor; + if(!set_pinfunc(port, pin, &curconf)) return ERR_BADVAL; + return ERR_OK; +} + +// PAx [= aa], PBx [= bb] +static errcodes_t parse_pin_command(const char *cmd, char *args){ + if(!args) return ERR_BADPAR; // or maybe add list for all pins? + char port_char = cmd[1]; + if(port_char != 'A' && port_char != 'B') return ERR_BADCMD; + uint8_t port = (port_char == 'A') ? 0 : 1; + int32_t pin = -1; + char *setter = splitargs(args, &pin); + DBG("args="); DBG(args); DBG(", pin="); DBG(i2str(pin)); DBG(", setter="); DBG(setter); DBGNL(); + if(pin < 0 || pin > 15) return ERR_BADPAR; + pinconfig_t *pcfg = &the_conf.pinconfig[port][pin]; // just to check if pin can be configured + if(!pcfg->enable) return ERR_CANTRUN; // prohibited pin + if(!setter){ // simple getter -> get value and return ERR_AMOUNT as silence + DBG("Getter\n"); + pin_getter(port, pin); + return ERR_AMOUNT; + } + return pin_setter(port, pin, setter); +} + +static errcodes_t cmd_PA(const char *cmd, char *args){ + return parse_pin_command(cmd, args); +} +static errcodes_t cmd_PB(const char *cmd, char *args){ + return parse_pin_command(cmd, args); +} + +static errcodes_t cmd_reinit(const char _U_ *cmd, char _U_ *args){ + if(gpio_reinit()) return ERR_OK; + SEND("Can't reinit: check your configuration!\n"); + return ERR_AMOUNT; +} + +static errcodes_t cmd_storeconf(const char _U_ *cmd, char _U_ *args){ + if(!store_userconf()) return ERR_CANTRUN; + return ERR_OK; +} + +// canspeed = baudrate (kBaud) +static errcodes_t cmd_canspeed(const char *cmd, char *args){ + int32_t S; + if(argsvals(args, NULL, &S)){ + if(S < CAN_MIN_SPEED || S > CAN_MAX_SPEED) return ERR_BADVAL; + the_conf.CANspeed = S; + } + SEND(cmd); PUTCHAR('='); SENDn(u2str(the_conf.CANspeed)); + return ERR_AMOUNT; +} + +static errcodes_t cmd_curcanspeed(const char *cmd, char _U_ *args){ + SEND(cmd); PUTCHAR('='); SENDn(u2str(CAN_getspeed())); + return ERR_AMOUNT; +} + +static errcodes_t cmd_dumpconf(const char _U_ *cmd, char _U_ *args){ + SEND("userconf_sz="); SEND(u2str(the_conf.userconf_sz)); + SEND("\ncurrentconfidx="); SENDn(i2str(currentconfidx)); + for(int i = 0; i < InterfacesAmount; ++i){ + SEND("interface"); PUTCHAR('0' + i); + PUTCHAR('='); + int l = the_conf.iIlengths[i] / 2; + char *ptr = (char*) the_conf.iInterface[i]; + for(int j = 0; j < l; ++j){ + PUTCHAR(*ptr); + ptr += 2; + } + NL(); + } + SEND("canspeed="); SENDn(u2str(the_conf.CANspeed)); + SEND("Pin configuration:\n"); + for(int port = 0; port < 2; port++){ + char port_letter = (port == 0) ? 'A' : 'B'; + for(int pin = 0; pin < 16; pin++){ + pinconfig_t *p = &the_conf.pinconfig[port][pin]; + if(!p->enable) continue; + PUTCHAR('P'); PUTCHAR(port_letter); SEND(i2str(pin)); PUTCHAR('='); + switch(p->mode){ +#define S(k) SEND(str_keywords[STR_ ## k]) +#define SP(k) do{PUTCHAR(' '); S(k);}while(0) + case MODE_INPUT: + S(IN); + if(p->pull == PULL_UP) SP(PU); + else if (p->pull == PULL_DOWN) SP(PD); + else SP(FL); + break; + case MODE_OUTPUT: + S(OUT); + if(p->otype == OUTPUT_PP) SP(PP); + else SP(OD); + if(p->pull == PULL_UP) SP(PU); + else if (p->pull == PULL_DOWN) SP(PD); + break; + case MODE_ANALOG: + S(AIN); + break; + case MODE_AF: + switch(p->af){ + case FUNC_USART: S(USART); break; + case FUNC_SPI: S(SPI); break; + case FUNC_I2C: S(I2C); break; + default: SEND("UNKNOWN_AF"); + } + break; + } + /* + if(p->mode == MODE_OUTPUT || p->mode == MODE_AF){ + switch(p->speed){ + case SPEED_LOW: SEND(" LOWSPEED"); break; + case SPEED_MEDIUM: SEND(" MEDSPEED"); break; + case SPEED_HIGH: SEND(" HIGHSPEED"); break; + default: break; + } + }*/ + // Monitor + if(p->monitor) SP(MONITOR); + NL(); +#undef S +#undef SP + } + } + // here are usart/spi/i2c configurations +#if 0 + bool usart_enabled = false; + for (int port = 0; port < 2 && !usart_enabled; port++) { + for (int pin = 0; pin < 16; pin++) { + pinconfig_t *p = &the_conf.pinconfig[port][pin]; + if (p->enable && p->mode == MODE_AF && p->af == FUNC_USART) { + usart_enabled = true; + break; + } + } + } + if (usart_enabled) { + SEND("usart="); + // usart_config (baud, bits, parity, stopbits) + // e.g: SEND(u2str(usart_config.baudrate)); SEND(" "); + // SEND(i2str(usart_config.databits)); PUTCHAR(usart_config.parity); SEND(i2str(usart_config.stopbits)); + NL(); + } + bool spi_enabled = false; + for(int port = 0; port < 2 && !spi_enabled; port++){ + for (int pin = 0; pin < 16; pin++) { + pinconfig_t *p = &the_conf.pinconfig[port][pin]; + if (p->enable && p->mode == MODE_AF && p->af == FUNC_SPI) { + spi_enabled = true; + break; + } + } + } + if (spi_enabled) { + SEND("spi="); + // spi_config (speed, mode) + NL(); + } +#endif + return ERR_AMOUNT; +} + +static errcodes_t cmd_setiface(const char* cmd, char *args){ + int32_t N; + char *setter = splitargs(args, &N); + if(N < 0 || N >= InterfacesAmount) return ERR_BADPAR; + if(setter && *setter){ // setter + int l = strlen(setter); + if(l > MAX_IINTERFACE_SZ) return ERR_BADVAL; + the_conf.iIlengths[N] = (uint8_t) l * 2; + char *ptr = (char*)the_conf.iInterface[N]; + for(int i = 0; i < l; ++i){ + char c = *setter++; + *ptr++ = (c > ' ') ? c : '_'; + *ptr++ = 0; + } + } + // getter + SEND(cmd); PUTCHAR('='); + char *ptr = (char*) the_conf.iInterface[N]; + int l = the_conf.iIlengths[N] / 2; + for(int j = 0; j < l; ++j){ + PUTCHAR(*ptr); + ptr += 2; + } + NL(); + return ERR_AMOUNT; +} + +static errcodes_t cmd_sendcan(const char _U_ *cmd, char *args){ + if(!args) return ERR_BADVAL; + char *setter = splitargs(args, NULL); + if(!setter) return ERR_BADVAL; + if(USB_sendstr(ICAN, setter)) return ERR_OK; + return ERR_CANTRUN; +} + +static errcodes_t cmd_time(const char *cmd, char _U_ *args){ + SEND(cmd); PUTCHAR('='); SENDn(u2str(Tms)); + return ERR_AMOUNT; +} + +static errcodes_t cmd_mcureset(const char _U_ *cmd, char _U_ *args){ + NVIC_SystemReset(); + return ERR_CANTRUN; // never reached +} + +static errcodes_t cmd_readconf(const char _U_ *cmd, char _U_ *args){ + flashstorage_init(); + return ERR_OK; +} + +static errcodes_t cmd_saveconf(const char _U_ *cmd, char _U_ *args){ + if(store_userconf()) return ERR_CANTRUN; + return ERR_OK; +} + +static errcodes_t cmd_eraseflash(const char _U_ *cmd, char _U_ *args){ + if(erase_storage()) return ERR_CANTRUN; + return ERR_OK; +} + +static errcodes_t cmd_help(const char _U_ *cmd, char _U_ *args){ + SEND(REPOURL); + for(size_t i = 0; i < sizeof(cmdInfo)/sizeof(cmdInfo[0]); i++){ + SEND(cmdInfo[i].name); + SEND(" - "); + SENDn(cmdInfo[i].desc); + } + return ERR_AMOUNT; +} + +constexpr uint32_t hash(const char* str, uint32_t h = 0){ + return *str ? hash(str + 1, h + ((h << 7) ^ *str)) : h; +} + +// TODO: add checking real command length! + +static const char *cmd_parser(char *str){ + char command[CMD_MAXLEN+1]; + int i = 0; + while(*str > '@' && i < CMD_MAXLEN){ command[i++] = *str++; } + command[i] = 0; + while(*str && *str <= ' ') ++str; + char *restof = (char*) str; + uint32_t h = hash(command); + errcodes_t ecode = ERR_AMOUNT; + switch(h){ +#define COMMAND(name, desc) case hash(#name): ecode = cmd_ ## name(command, restof); break; + COMMAND_TABLE +#undef COMMAND + default: SEND("Unknown command, try 'help'\n"); break; + } + if(ecode < ERR_AMOUNT) return errtxt[ecode]; + return NULL; +} + +void GPIO_process(){ + char inbuff[MAXSTRLEN]; + int l = RECV(inbuff, MAXSTRLEN); + // TODO: check SPI/USART/I2C + for(uint8_t port = 0; port < 2; ++port){ + uint16_t alert = gpio_alert(port); + if(alert == 0) continue; + uint16_t pinmask = 1; + for(uint8_t i = 0; i < 16; ++i, pinmask <<= 1){ + if(alert & pinmask) pin_getter(port, i); + } + } + if(l == 0) return; + if(l < 0) SEND("ERROR: USB buffer overflow or string was too long\n"); + else{ + const char *ans = cmd_parser(inbuff); + if(ans) SENDn(ans); + } +} diff --git a/F0:F030,F042,F072/usbcan_gpio/gpioproto.h b/F0:F030,F042,F072/usbcan_gpio/gpioproto.h index 94bb96a..ff5390c 100644 --- a/F0:F030,F042,F072/usbcan_gpio/gpioproto.h +++ b/F0:F030,F042,F072/usbcan_gpio/gpioproto.h @@ -18,4 +18,20 @@ #pragma once +// error codes for answer message +typedef enum{ + ERR_OK, // all OK + ERR_BADCMD, // wrong command + ERR_BADPAR, // wrong parameter + ERR_BADVAL, // wrong value (for setter) + ERR_WRONGLEN, // wrong message length + ERR_CANTRUN, // can't run given command due to bad parameters or other + ERR_AMOUNT // amount of error codes or "send nothing" +} errcodes_t; + +// maximal length of command (without trailing zero) +#define CMD_MAXLEN 15 +// maximal available parameter number +#define MAXPARNO 255 + void GPIO_process(); diff --git a/F0:F030,F042,F072/usbcan_gpio/hardware.c b/F0:F030,F042,F072/usbcan_gpio/hardware.c index 74834b7..a64c064 100644 --- a/F0:F030,F042,F072/usbcan_gpio/hardware.c +++ b/F0:F030,F042,F072/usbcan_gpio/hardware.c @@ -21,6 +21,7 @@ uint8_t ledsON = 0; void gpio_setup(void){ + // enable all active GPIO clocking RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN; // Set LEDS (PB15/PA8) as output pin_set(LED0_port, LED0_pin); // clear LEDs diff --git a/F0:F030,F042,F072/usbcan_gpio/usb_dev.h b/F0:F030,F042,F072/usbcan_gpio/usb_dev.h index ab7b3f9..e40f06f 100644 --- a/F0:F030,F042,F072/usbcan_gpio/usb_dev.h +++ b/F0:F030,F042,F072/usbcan_gpio/usb_dev.h @@ -44,8 +44,8 @@ void linecoding_handler(uint8_t ifno, usb_LineCoding *lc); #define DISCONN_TMOUT (2) // sizes of ringbuffers for outgoing and incoming data -#define RBOUTSZ (1024) -#define RBINSZ (1024) +#define RBOUTSZ (512) +#define RBINSZ (256) #define newline(ifno) USB_putbyte(ifno, '\n') #define USND(ifno, s) do{USB_sendstr(ifno, s); USB_putbyte(ifno, '\n');}while(0) diff --git a/F0:F030,F042,F072/usbcan_gpio/usbcangpio.bin b/F0:F030,F042,F072/usbcan_gpio/usbcangpio.bin index ae2788162054d34ff2bc3874bfd6972d11837b7e..0dab055305a08a5541c7f857a0ed8eb210f4b7b3 100755 GIT binary patch delta 9127 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 delta 7824 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 Yn@nl^errZj8@?m{gRPR;?^-|qA714(O#lD@ diff --git a/F0:F030,F042,F072/usbcan_gpio/usbcangpio.creator.user b/F0:F030,F042,F072/usbcan_gpio/usbcangpio.creator.user index fa6f043..3ba89ad 100644 --- a/F0:F030,F042,F072/usbcan_gpio/usbcangpio.creator.user +++ b/F0:F030,F042,F072/usbcan_gpio/usbcangpio.creator.user @@ -1,6 +1,6 @@ - + EnvironmentId diff --git a/F0:F030,F042,F072/usbcan_gpio/usbcangpio.files b/F0:F030,F042,F072/usbcan_gpio/usbcangpio.files index 105b97b..5101e54 100644 --- a/F0:F030,F042,F072/usbcan_gpio/usbcangpio.files +++ b/F0:F030,F042,F072/usbcan_gpio/usbcangpio.files @@ -6,15 +6,13 @@ flash.c flash.h gpio.c gpio.h -gpioproto.c +gpioproto.cpp gpioproto.h hardware.c hardware.h hashgen/Readme hashgen/hashgen.c hashgen/mktestdic -hashparser.cpp -hashparser.h main.c ringbuffer.c ringbuffer.h diff --git a/F0:F030,F042,F072/usbcan_gpio/version.inc b/F0:F030,F042,F072/usbcan_gpio/version.inc index 323e780..d504585 100644 --- a/F0:F030,F042,F072/usbcan_gpio/version.inc +++ b/F0:F030,F042,F072/usbcan_gpio/version.inc @@ -1,2 +1,2 @@ -#define BUILD_NUMBER "105" -#define BUILD_DATE "2026-03-09" +#define BUILD_NUMBER "125" +#define BUILD_DATE "2026-03-10"