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 ae27881..0dab055 100755 Binary files a/F0:F030,F042,F072/usbcan_gpio/usbcangpio.bin and b/F0:F030,F042,F072/usbcan_gpio/usbcangpio.bin differ 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"