diff --git a/F0:F030,F042,F072/usbcan_gpio/flash.h b/F0:F030,F042,F072/usbcan_gpio/flash.h index 3672050..a7aa4c8 100644 --- a/F0:F030,F042,F072/usbcan_gpio/flash.h +++ b/F0:F030,F042,F072/usbcan_gpio/flash.h @@ -44,13 +44,13 @@ typedef struct __attribute__((packed, aligned(4))){ uint16_t userconf_sz; // "magick number" uint16_t CANspeed; // default CAN speed (in kBaud!!!) + uint32_t SPIspeed; // SPI speed, baud uint16_t iInterface[InterfacesAmount][MAX_IINTERFACE_SZ]; // we store Interface name here in UTF! uint8_t iIlengths[InterfacesAmount]; // length in BYTES (symbols amount x2)! // gpio settings - pinconfig_t pinconfig[2][16]; // GPIOA, GPIOB + pinconfig_t pinconfig[2][16]; // GPIOA, GPIOB usartconf_t usartconfig; - uint8_t I2Cspeed; - //spiconfig_t spiconfig; + uint8_t I2Cspeed; // I2C speed index } user_conf; extern user_conf the_conf; // global user config (read from FLASH to RAM) diff --git a/F0:F030,F042,F072/usbcan_gpio/gpio.c b/F0:F030,F042,F072/usbcan_gpio/gpio.c index da2e031..9866c07 100644 --- a/F0:F030,F042,F072/usbcan_gpio/gpio.c +++ b/F0:F030,F042,F072/usbcan_gpio/gpio.c @@ -24,6 +24,7 @@ #include "gpio.h" #include "i2c.h" #include "pwm.h" +#include "spi.h" #include "usart.h" static uint16_t monitor_mask[2] = {0}; // pins to monitor == 1 (ONLY GPIO and ADC) @@ -83,6 +84,7 @@ static const pinprops_t pin_props[2][16] = { #define CANPWM(x) ((x) & (1< 1 || pin > 15 || !CANI2C(pin_props[port][pin].funcs)) return -1; - int idx = -1; // I2C1 is alone + int idx = -1; // later we can add SPI2 support i2c_props_t curprops = {0}; if(port == 1){ // only GPIOB switch(pin){ @@ -170,6 +172,47 @@ int get_i2c_index(uint8_t port, uint8_t pin, i2c_props_t *p){ return idx; } +int get_spi_index(uint8_t port, uint8_t pin, spi_props_t *p){ + if(port > 1 || pin > 15 || !CANSPI(pin_props[port][pin].funcs)) return -1; + int idx = -1; + spi_props_t curprops = {0}; + if(port == 0){ // PA5-7 (SCK-MISO-MOSI) + switch(pin){ + case 5: + idx = 0; + curprops.issck =1; + break; + case 6: + idx = 0; + curprops.ismiso = 1; + break; + case 7: + idx = 0; + curprops.ismosi = 1; + break; + default: break; + } + }else if(port == 1){ // PB3-5 (SCK-MISO-MOSI) + switch(pin){ + case 3: + idx = 0; + curprops.issck =1; + break; + case 4: + idx = 0; + curprops.ismiso = 1; + break; + case 5: + idx = 0; + curprops.ismosi = 1; + break; + default: break; + } + } + if(p) *p = curprops; + return idx; +} + // default config static void defconfig(pinconfig_t *cfg){ if(!cfg) return; @@ -196,7 +239,9 @@ int chkpinconf(){ } int active_usart = -1; // number of USART if user selects it (we can't check it by UC->idx) int active_i2c = -1; - uint8_t i2c_scl_pin = 0xFF, i2c_sda_pin = 0xFF; // to check SCL/SDA collisions and (SCL&SDA) + int active_spi = -1; + i2c_props_t i2cprops = {0}; + spi_props_t spiprops = {0}; for(int port = 0; port < 2; ++port){ for(int pin = 0; pin < 16; ++pin){ pinconfig_t *cfg = &pinconfig[port][pin]; @@ -267,20 +312,60 @@ int chkpinconf(){ break; } if(ip.isscl){ - if(i2c_scl_pin != 0xFF){ // two SCLs + if(i2cprops.isscl){ // two SCLs defconfig(cfg); ret = FALSE; break; } - i2c_scl_pin = (port << 4) | pin; + i2cprops.isscl = 1; } if(ip.issda){ - if(i2c_sda_pin != 0xFF){ // two SDAs + if(i2cprops.issda){ // two SDAs defconfig(cfg); ret = FALSE; break; } - i2c_sda_pin = (port << 4) | pin; + i2cprops.issda = 1; + } + } + break; + case FUNC_SPI:{ + spi_props_t sp; + int spi_idx = get_spi_index(port, pin, &sp); + if(spi_idx < 0){ + defconfig(cfg); + ret = FALSE; + break; + } + if(active_spi == -1) active_spi = spi_idx; + else if(active_spi != spi_idx){ + defconfig(cfg); + ret = FALSE; + break; + } + if(sp.issck){ + if(spiprops.issck){ + defconfig(cfg); + ret = FALSE; + break; + } + spiprops.issck = 1; + } + if(sp.ismiso){ + if(spiprops.ismiso){ + defconfig(cfg); + ret = FALSE; + break; + } + spiprops.ismiso = 1; + } + if(sp.ismosi){ + if(spiprops.ismosi){ + defconfig(cfg); + ret = FALSE; + break; + } + spiprops.ismosi = 1; } } break; @@ -302,12 +387,23 @@ int chkpinconf(){ } // check active I2C if(active_i2c != -1){ - if(i2c_scl_pin == 0xFF || i2c_sda_pin == 0xFF){ + if(i2cprops.isscl && i2cprops.issda){ + haveI2C = 1; + }else{ DBG("Need two pins for I2C\n"); ret = FALSE; haveI2C = 0; - }else haveI2C = 1; - }else i2c_stop(); + } + } + if(active_spi != -1){ + if(spiprops.issck && (spiprops.ismiso || spiprops.ismosi)){ + haveSPI = 1; + }else{ + DBG("SPI needs SCK and MOSI or MISO\n"); + ret = FALSE; + haveSPI = 0; + } + } return ret; } @@ -455,6 +551,8 @@ int gpio_reinit(){ }else usart_stop(); if(haveI2C) i2c_setup((i2c_speed_t) the_conf.I2Cspeed); else i2c_stop(); + if(haveSPI) spi_setup(the_conf.SPIspeed); + else spi_stop(); return ret; } diff --git a/F0:F030,F042,F072/usbcan_gpio/gpio.h b/F0:F030,F042,F072/usbcan_gpio/gpio.h index 25fd875..448cb37 100644 --- a/F0:F030,F042,F072/usbcan_gpio/gpio.h +++ b/F0:F030,F042,F072/usbcan_gpio/gpio.h @@ -97,11 +97,17 @@ typedef struct{ uint8_t istx : 1; } usart_props_t; -typedef struct { +typedef struct{ uint8_t isscl : 1; uint8_t issda : 1; } i2c_props_t; +typedef struct{ + uint8_t issck : 1; + uint8_t ismiso : 1; + uint8_t ismosi : 1; +} spi_props_t; + /* typedef struct{ uint32_t speed; @@ -121,6 +127,7 @@ int get_curpinconf(uint8_t port, uint8_t pin, pinconfig_t *c); int get_usart_index(uint8_t port, uint8_t pin, usart_props_t *p); int get_i2c_index(uint8_t port, uint8_t pin, i2c_props_t *p); +int get_spi_index(uint8_t port, uint8_t pin, spi_props_t *p); int gpio_reinit(); diff --git a/F0:F030,F042,F072/usbcan_gpio/gpioproto.cpp b/F0:F030,F042,F072/usbcan_gpio/gpioproto.cpp index 04812e9..2bb344f 100644 --- a/F0:F030,F042,F072/usbcan_gpio/gpioproto.cpp +++ b/F0:F030,F042,F072/usbcan_gpio/gpioproto.cpp @@ -29,6 +29,7 @@ extern "C"{ #include "gpioproto.h" #include "i2c.h" #include "pwm.h" +#include "spi.h" #include "usart.h" #undef USBIF #define USBIF IGPIO @@ -68,6 +69,7 @@ static uint8_t hex_input_mode = 0; // ==0 for text input, 1 for HEX + text in qu 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(SPI, "transfer SPI data: SPI=data (hex)") \ COMMAND(storeconf, "save config to flash") \ COMMAND(time, "show current time (ms)") \ COMMAND(USART, "Read USART data or send (USART=hex)") \ @@ -334,6 +336,7 @@ static errcodes_t pin_setter(uint8_t port, uint8_t pin, char *setter){ bool monitor = false; uint16_t *pending_u16 = NULL; // pointer to uint16_t value, if !NULL, next token should be a number uint32_t *pending_u32 = NULL; // -//- for uint32_t + uint32_t wU32 = UINT32_MAX; // for pending usartconf_t UsartConf; if(!get_curusartconf(&UsartConf)) return ERR_CANTRUN; char *saveptr, *token = strtok_r(setter, DELIM_, &saveptr); @@ -390,7 +393,7 @@ static errcodes_t pin_setter(uint8_t port, uint8_t pin, char *setter){ pending_u16 = &curconf.threshold; break; case MISC_SPEED: - pending_u32 = &UsartConf.speed; // also used for I2C speed! + pending_u32 = &wU32; break; case MISC_TEXT: // what to do, if textproto is set, but user wants binary? UsartConf.textproto = 1; @@ -411,10 +414,16 @@ static errcodes_t pin_setter(uint8_t port, uint8_t pin, char *setter){ // check periferial settings before refresh pin data // check current USART settings - if(func_set == FUNC_USART && !chkusartconf(&UsartConf)) return ERR_BADVAL; - if(func_set == FUNC_I2C){ // check speed - i2c_speed_t s = (UsartConf.speed > I2C_SPEED_1M) ? I2C_SPEED_10K : static_cast (UsartConf.speed); - the_conf.I2Cspeed = static_cast (s); + if(func_set == FUNC_USART){ + if(wU32 != UINT32_MAX) UsartConf.speed = wU32; + if(!chkusartconf(&UsartConf)) return ERR_BADVAL; + }else if(func_set == FUNC_I2C){ // check speed + if(wU32 != UINT32_MAX){ + i2c_speed_t s = (wU32 > I2C_SPEED_1M) ? I2C_SPEED_10K : static_cast (wU32); + the_conf.I2Cspeed = static_cast (s); + } + }else if(func_set == FUNC_SPI){ + if(wU32 != UINT32_MAX) the_conf.SPIspeed = wU32; } if(func_set != 0xFF) mode_set = MODE_AF; if(mode_set == 0xFF) return ERR_BADVAL; // user forgot to set mode @@ -603,6 +612,10 @@ static errcodes_t cmd_dumpconf(const char _U_ *cmd, char _U_ *args){ } NL(); } + if(SPI1->CR1 & SPI_CR1_SPE){ + SEND("spispeed="); + SENDn(u2str(the_conf.SPIspeed)); + } #undef S #undef SP return ERR_AMOUNT; @@ -866,6 +879,7 @@ static errcodes_t cmd_pinout(const char _U_ *cmd, char *args){ pwmtimer_t tp; // timers' pins usart_props_t up; // USARTs' pins i2c_props_t ip; // I2C's pins + spi_props_t sp; SEND("\nConfigurable pins (check collisions if functions have same name!):\n"); for(int port = 0; port < 2; ++port){ @@ -880,14 +894,17 @@ static errcodes_t cmd_pinout(const char _U_ *cmd, char *args){ if(listmask == 0xff) SEND("GPIO"); // don't send "GPIO" for specific choice if(mask & (1 << FUNC_AIN)){ SEND(COMMA); SEND(str_keywords[STR_AIN]); } if(mask & (1 << FUNC_USART)){ // USARTn_aX (n - 1/2, a - R/T) - SEND(", "); int idx = get_usart_index(port, pin, &up); - SEND(str_keywords[STR_USART]); PUTCHAR('1' + idx); + SEND(COMMA); SEND(str_keywords[STR_USART]); PUTCHAR('1' + idx); PUTCHAR('_'); PUTCHAR(up.isrx ? 'R' : 'T'); PUTCHAR('X'); } if(mask & (1 << FUNC_SPI)){ - SEND(COMMA); SEND(str_keywords[STR_SPI]); - // TODO: MISO/MOSI/SCL + int idx = get_spi_index(port, pin, &sp); + SEND(COMMA); SEND(str_keywords[STR_SPI]); PUTCHAR('1' + idx); + PUTCHAR('_'); + if(sp.ismiso) SEND("MISO"); + else if(sp.ismosi) SEND("MOSI"); + else SEND("SCK"); } if(mask & (1 << FUNC_I2C)){ int idx = get_i2c_index(port, pin, &ip); @@ -908,6 +925,21 @@ static errcodes_t cmd_pinout(const char _U_ *cmd, char *args){ return ERR_AMOUNT; } +static errcodes_t cmd_SPI(const char *cmd, char *args){ + if(!args) return ERR_BADVAL; + if(!(SPI1->CR1 & SPI_CR1_SPE)) return ERR_CANTRUN; + char *setter = splitargs(args, NULL); + if(!setter) return ERR_BADVAL; + int len = parse_hex_data(setter, curbuf, MAXSTRLEN); + if(len <= 0) return ERR_BADVAL; + int got = spi_transfer(curbuf, curbuf, len); + if(-1 == got) return ERR_CANTRUN; + if(0 == got) return ERR_BUSY; + CMDEQ(); + hexdump(sendfun, curbuf, got); + return ERR_AMOUNT; +} + constexpr uint32_t hash(const char* str, uint32_t h = 0){ return *str ? hash(str + 1, h + ((h << 7) ^ *str)) : h; } diff --git a/F0:F030,F042,F072/usbcan_gpio/hardware.c b/F0:F030,F042,F072/usbcan_gpio/hardware.c index da8c165..75aba17 100644 --- a/F0:F030,F042,F072/usbcan_gpio/hardware.c +++ b/F0:F030,F042,F072/usbcan_gpio/hardware.c @@ -20,6 +20,7 @@ #include "gpioproto.h" #include "hardware.h" +const uint32_t peripherial_clock = 48000000; uint8_t ledsON = 0; TRUE_INLINE void pins_setup(){ // setup some common GPIO @@ -35,8 +36,10 @@ TRUE_INLINE void pins_setup(){ // setup some common GPIO void hardware_setup(){ // enable all active GPIO clocking RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | RCC_AHBENR_DMA1EN; - RCC->APB2ENR |= RCC_APB2ENR_USART1EN | RCC_APB2ENR_SYSCFGEN | RCC_APB2ENR_TIM1EN | RCC_APB2ENR_TIM16EN; - RCC->APB1ENR |= RCC_APB1ENR_USART2EN | RCC_APB1ENR_TIM2EN | RCC_APB1ENR_TIM3EN | RCC_APB1ENR_TIM14EN | RCC_APB1ENR_I2C1EN; + RCC->APB2ENR |= RCC_APB2ENR_USART1EN | RCC_APB2ENR_SYSCFGEN | RCC_APB2ENR_TIM1EN | RCC_APB2ENR_TIM16EN | + RCC_APB2ENR_SPI1EN; + RCC->APB1ENR |= RCC_APB1ENR_USART2EN | RCC_APB1ENR_TIM2EN | RCC_APB1ENR_TIM3EN | RCC_APB1ENR_TIM14EN | + RCC_APB1ENR_I2C1EN; pins_setup(); adc_setup(); GPIO_init(); diff --git a/F0:F030,F042,F072/usbcan_gpio/hardware.h b/F0:F030,F042,F072/usbcan_gpio/hardware.h index d70d44d..421669c 100644 --- a/F0:F030,F042,F072/usbcan_gpio/hardware.h +++ b/F0:F030,F042,F072/usbcan_gpio/hardware.h @@ -41,7 +41,7 @@ extern volatile uint32_t Tms; - +extern const uint32_t peripherial_clock; extern uint8_t ledsON; void hardware_setup(); diff --git a/F0:F030,F042,F072/usbcan_gpio/spi.c b/F0:F030,F042,F072/usbcan_gpio/spi.c new file mode 100644 index 0000000..0e09c83 --- /dev/null +++ b/F0:F030,F042,F072/usbcan_gpio/spi.c @@ -0,0 +1,76 @@ +/* + * 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 "hardware.h" +#include "spi.h" + +// get best prescaler to fit given frequency +static uint16_t get_baudrate_prescaler(uint32_t speed_hz){ + uint32_t freq = peripherial_clock; + uint32_t best_i = 7; + uint32_t best_err = 0xFFFFFFFF; + for(int i = 0; i < 8; i++){ + freq >>= 1; + uint32_t err = (freq > speed_hz) ? (freq - speed_hz) : (speed_hz - freq); + if(err < best_err){ + best_err = err; + best_i = i; + }else if(err > best_err) break; + } + return best_i; +} + + +// Master, 8bit, CPOL=0, CPHA=0, MSB first +void spi_setup(uint32_t speed){ // speed in Hz + RCC->APB2RSTR |= RCC_APB2RSTR_SPI1RST; + RCC->APB2RSTR = 0; + SPI1->CR1 = 0; + uint16_t br = get_baudrate_prescaler(speed); + SPI1->CR1 = SPI_CR1_MSTR | (br << 3) | SPI_CR1_SSM | SPI_CR1_SSI; + SPI1->CR2 = SPI_CR2_SSOE; + SPI1->CR1 |= SPI_CR1_SPE; +} + +void spi_stop(){ + SPI1->CR1 &= ~SPI_CR1_SPE; +} + +// return -1 if SPI isn't run or got error +int spi_transfer(const uint8_t *tx, uint8_t *rx, int len){ + if(len < 1 || !(SPI1->CR1 & SPI_CR1_SPE)) return -1; + int i; + for(i = 0; i < len; ++i){ + uint32_t timeout = 1000000; + while(!(SPI1->SR & SPI_SR_TXE)){ + if (--timeout == 0) return -1; // error by timeout: TX isn't ready + } + uint8_t out = (tx) ? tx[i] : 0; + *(uint8_t*)&SPI1->DR = out; // запись в DR + timeout = 1000000; + while(!(SPI1->SR & SPI_SR_RXNE)){ + if(--timeout == 0) return 0; + } + uint8_t in = *(uint8_t*)&SPI1->DR; // чтение из DR + if(rx) rx[i] = in; + } + //while(SPI1->SR & SPI_SR_BSY){ } + return i; +} diff --git a/F0:F030,F042,F072/usbcan_gpio/spi.h b/F0:F030,F042,F072/usbcan_gpio/spi.h new file mode 100644 index 0000000..50bbb93 --- /dev/null +++ b/F0:F030,F042,F072/usbcan_gpio/spi.h @@ -0,0 +1,25 @@ +/* + * 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 . + */ + +#pragma once + +#include + +void spi_setup(uint32_t speed); // speed in Hz +void spi_stop(); +int spi_transfer(const uint8_t *tx, uint8_t *rx, int len); diff --git a/F0:F030,F042,F072/usbcan_gpio/usart.c b/F0:F030,F042,F072/usbcan_gpio/usart.c index 23b3967..3d0c6b4 100644 --- a/F0:F030,F042,F072/usbcan_gpio/usart.c +++ b/F0:F030,F042,F072/usbcan_gpio/usart.c @@ -20,6 +20,7 @@ #include #include "flash.h" +#include "hardware.h" #include "ringbuffer.h" #include "usart.h" @@ -101,13 +102,12 @@ int usart_config(usartconf_t *uc){ if(curUSARTidx != -1) usart_stop(); // disable previous USART if enabled uint8_t No = usartconfig.idx; volatile USART_TypeDef *U = Usarts[No]; - uint32_t peripheral_clock = 48000000; // Disable USART while configuring U->CR1 = 0; U->ICR = 0xFFFFFFFF; // Clear all interrupt flags // Assuming oversampling by 16 (default after reset). For higher baud rates you might use by 8. - U->BRR = peripheral_clock / (usartconfig.speed); - usartconfig.speed= peripheral_clock / U->BRR; // fix for real speed + U->BRR = peripherial_clock / (usartconfig.speed); + usartconfig.speed = peripherial_clock / U->BRR; // fix for real speed uint32_t cr1 = 0, cr3 = 0; textformat = usartconfig.textproto; monitor = usartconfig.monitor; diff --git a/F0:F030,F042,F072/usbcan_gpio/usbcangpio.bin b/F0:F030,F042,F072/usbcan_gpio/usbcangpio.bin index e4d6aba..21b1057 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 cb78273..69793c4 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 05fc5ab..73f5be2 100644 --- a/F0:F030,F042,F072/usbcan_gpio/usbcangpio.files +++ b/F0:F030,F042,F072/usbcan_gpio/usbcangpio.files @@ -22,6 +22,8 @@ pwm.c pwm.h ringbuffer.c ringbuffer.h +spi.c +spi.h strfunc.c strfunc.h usart.c diff --git a/F0:F030,F042,F072/usbcan_gpio/version.inc b/F0:F030,F042,F072/usbcan_gpio/version.inc index a7a139e..6226929 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 "217" +#define BUILD_NUMBER "221" #define BUILD_DATE "2026-03-17"