diff --git a/F0:F030,F042,F072/usbcan_gpio/flash.h b/F0:F030,F042,F072/usbcan_gpio/flash.h index 439b1e1..3672050 100644 --- a/F0:F030,F042,F072/usbcan_gpio/flash.h +++ b/F0:F030,F042,F072/usbcan_gpio/flash.h @@ -49,6 +49,7 @@ typedef struct __attribute__((packed, aligned(4))){ // gpio settings pinconfig_t pinconfig[2][16]; // GPIOA, GPIOB usartconf_t usartconfig; + uint8_t I2Cspeed; //spiconfig_t spiconfig; } user_conf; diff --git a/F0:F030,F042,F072/usbcan_gpio/gpio.c b/F0:F030,F042,F072/usbcan_gpio/gpio.c index 0dd658f..da2e031 100644 --- a/F0:F030,F042,F072/usbcan_gpio/gpio.c +++ b/F0:F030,F042,F072/usbcan_gpio/gpio.c @@ -22,6 +22,7 @@ #include "adc.h" #include "flash.h" #include "gpio.h" +#include "i2c.h" #include "pwm.h" #include "usart.h" @@ -39,12 +40,6 @@ typedef struct{ uint8_t AF[FUNC_AMOUNT]; // alternate function number for corresponding `FuncNames` number } pinprops_t; -#define CANADC(x) ((x) & (1< 1 || pin > 15 || !CANUSART(pin_props[port][pin].funcs)) return -1; int idx = -1; usart_props_t curprops = {0}; if(port == 0){ // GPIOA @@ -133,6 +139,37 @@ static int get_usart_index(uint8_t port, uint8_t pin, usart_props_t *p){ return idx; } +// return -1 if pin can't I2C, or return 0 and fill `p` +int get_i2c_index(uint8_t port, uint8_t pin, i2c_props_t *p){ + if(port > 1 || pin > 15 || !CANI2C(pin_props[port][pin].funcs)) return -1; + int idx = -1; // I2C1 is alone + i2c_props_t curprops = {0}; + if(port == 1){ // only GPIOB + switch(pin){ + case 6: // PB6 - I2C1_SCL + idx = 0; + curprops.isscl = 1; + break; + case 7: // PB7 - I2C1_SDA + idx = 0; + curprops.issda = 1; + break; + case 10: // PB10 - I2C1_SCL + idx = 0; + curprops.isscl = 1; + break; + case 11: // PB11 - I2C1_SDA + idx = 0; + curprops.issda = 1; + break; + default: + break; + } + } + if(p) *p = curprops; + return idx; +} + // default config static void defconfig(pinconfig_t *cfg){ if(!cfg) return; @@ -158,6 +195,8 @@ int chkpinconf(){ UC.RXen = 0; UC.TXen = 0; UC.monitor = 0; } 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) for(int port = 0; port < 2; ++port){ for(int pin = 0; pin < 16; ++pin){ pinconfig_t *cfg = &pinconfig[port][pin]; @@ -210,6 +249,41 @@ int chkpinconf(){ break; } } + break; + case FUNC_I2C:{ + i2c_props_t ip; + int i2c_idx = get_i2c_index(port, pin, &ip); + if(i2c_idx < 0){ + defconfig(cfg); + ret = FALSE; + break; + } + // maybe for 2 I2Cs + if(active_i2c == -1) active_i2c = i2c_idx; + else if(active_i2c != i2c_idx){ + // collision + defconfig(cfg); + ret = FALSE; + break; + } + if(ip.isscl){ + if(i2c_scl_pin != 0xFF){ // two SCLs + defconfig(cfg); + ret = FALSE; + break; + } + i2c_scl_pin = (port << 4) | pin; + } + if(ip.issda){ + if(i2c_sda_pin != 0xFF){ // two SDAs + defconfig(cfg); + ret = FALSE; + break; + } + i2c_sda_pin = (port << 4) | pin; + } + } + break; default: break; // later fill other functions } } @@ -226,11 +300,19 @@ int chkpinconf(){ get_defusartconf(&UC); // clear global configuration the_conf.usartconfig = UC; } + // check active I2C + if(active_i2c != -1){ + if(i2c_scl_pin == 0xFF || i2c_sda_pin == 0xFF){ + DBG("Need two pins for I2C\n"); + ret = FALSE; + haveI2C = 0; + }else haveI2C = 1; + }else i2c_stop(); return ret; } int is_disabled(uint8_t port, uint8_t pin){ - if(port > 1 || pin > 15) return FALSE; + if(port > 1 || pin > 15) return TRUE; if(the_conf.pinconfig[port][pin].enable) return FALSE; return TRUE; } @@ -371,6 +453,8 @@ int gpio_reinit(){ if(!usart_config(NULL)) ret = FALSE; else if(!usart_start()) ret = FALSE; }else usart_stop(); + if(haveI2C) i2c_setup((i2c_speed_t) the_conf.I2Cspeed); + else i2c_stop(); return ret; } diff --git a/F0:F030,F042,F072/usbcan_gpio/gpio.h b/F0:F030,F042,F072/usbcan_gpio/gpio.h index 2893c68..25fd875 100644 --- a/F0:F030,F042,F072/usbcan_gpio/gpio.h +++ b/F0:F030,F042,F072/usbcan_gpio/gpio.h @@ -69,6 +69,7 @@ typedef enum FuncNames{ // shift 1 by this to get "canUSART" etc; not more than FUNC_PWM = 4, FUNC_AMOUNT // just for arrays' sizes } funcnames_t; + /* typedef union{ struct{ @@ -91,6 +92,16 @@ typedef struct{ uint16_t threshold; // threshold for ADC measurement } pinconfig_t; +typedef struct{ + uint8_t isrx : 1; + uint8_t istx : 1; +} usart_props_t; + +typedef struct { + uint8_t isscl : 1; + uint8_t issda : 1; +} i2c_props_t; + /* typedef struct{ uint32_t speed; @@ -102,11 +113,15 @@ typedef struct{ */ int is_disabled(uint8_t port, uint8_t pin); +int pinfuncs(uint8_t port, uint8_t pin); int chkpinconf(); int set_pinfunc(uint8_t port, uint8_t pin, pinconfig_t *pcfg); 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 gpio_reinit(); int pin_out(uint8_t port, uint8_t pin, uint8_t newval); diff --git a/F0:F030,F042,F072/usbcan_gpio/gpioproto.cpp b/F0:F030,F042,F072/usbcan_gpio/gpioproto.cpp index 03d8255..04812e9 100644 --- a/F0:F030,F042,F072/usbcan_gpio/gpioproto.cpp +++ b/F0:F030,F042,F072/usbcan_gpio/gpioproto.cpp @@ -27,6 +27,7 @@ extern "C"{ #include "flash.h" #include "gpio.h" #include "gpioproto.h" +#include "i2c.h" #include "pwm.h" #include "usart.h" #undef USBIF @@ -52,10 +53,15 @@ static uint8_t hex_input_mode = 0; // ==0 for text input, 1 for HEX + text in qu COMMAND(eraseflash, "erase full flash storage") \ COMMAND(help, "show this help") \ COMMAND(hexinput, "input is text (0) or hex + text in quotes (1)") \ + COMMAND(iic, "write data over I2C: I2C=addr data (hex)") \ + COMMAND(iicread, "I2C read: I2Cread=addr nbytes (hex)") \ + COMMAND(iicreadreg, "I2C read register: I2Creadreg=addr reg nbytes (hex)") \ + COMMAND(iicscan, "Scan I2C bus for devices") \ COMMAND(mcutemp, "get MCU temperature (degC*10)") \ COMMAND(mcureset, "reset MCU") \ COMMAND(PA, "GPIOA setter/getter (type PA0=help for further info)") \ COMMAND(PB, "GPIOB setter/getter") \ + COMMAND(pinout, "list pinout with all available functions (or selected in setter, like pinout=USART,AIN") \ COMMAND(pwmmap, "show pins with PWM ability") \ COMMAND(readconf, "re-read config from flash") \ COMMAND(reinit, "apply pin config") \ @@ -112,7 +118,7 @@ enum MiscValues{ // TODO: add HEX input? #define KEYWORDS \ -KW(AIN) \ + KW(AIN) \ KW(IN) \ KW(OUT) \ KW(AF) \ @@ -131,11 +137,12 @@ KW(AIN) \ KW(HEX) \ KW(PWM) \ - enum{ // indexes of string keywords + +typedef enum{ // indexes of string keywords #define KW(k) STR_ ## k, KEYWORDS #undef KW - }; +} kwindex_t; // strings for keywords static const char *str_keywords[] = { @@ -194,6 +201,9 @@ static const char *pinhelp = ; static const char *EQ = " = "; // equal sign for getters +// token delimeters in setters +static const char *DELIM_ = " ,"; +static const char *COMMA = ", "; // comma before next val in list // send `command = ` #define CMDEQ() do{SEND(cmd); SEND(EQ);}while(0) @@ -326,7 +336,6 @@ static errcodes_t pin_setter(uint8_t port, uint8_t pin, char *setter){ uint32_t *pending_u32 = NULL; // -//- for uint32_t usartconf_t UsartConf; if(!get_curusartconf(&UsartConf)) return ERR_CANTRUN; -#define DELIM_ " ," char *saveptr, *token = strtok_r(setter, DELIM_, &saveptr); while(token){ if(pending_u16){ @@ -381,7 +390,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; + pending_u32 = &UsartConf.speed; // also used for I2C speed! break; case MISC_TEXT: // what to do, if textproto is set, but user wants binary? UsartConf.textproto = 1; @@ -403,6 +412,10 @@ 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 != 0xFF) mode_set = MODE_AF; if(mode_set == 0xFF) return ERR_BADVAL; // user forgot to set mode // set defaults @@ -579,6 +592,17 @@ static errcodes_t cmd_dumpconf(const char _U_ *cmd, char _U_ *args){ else if(!U.TXen && U.RXen) SEND(" RXONLY"); NL(); } + if(I2C1->CR1 & I2C_CR1_PE){ // I2C active, show its speed + SEND("iicspeed="); + switch(the_conf.I2Cspeed){ + case 0: SEND("10kHz"); break; + case 1: SEND("100kHz"); break; + case 2: SEND("400kHz"); break; + case 3: SEND("1MHz"); break; + default: SEND("unknown"); + } + NL(); + } #undef S #undef SP return ERR_AMOUNT; @@ -748,12 +772,146 @@ static errcodes_t cmd_USART(const char _U_ *cmd, char *args){ return ERR_AMOUNT; } +static errcodes_t cmd_iic(const char _U_ *cmd, char *args){ + if(!(I2C1->CR1 & I2C_CR1_PE)) return ERR_CANTRUN; + if(!args) return ERR_BADVAL; + char *setter = splitargs(args, NULL); + if(!setter) return ERR_BADVAL; + int len = parse_hex_data(setter, curbuf, MAXSTRLEN); + if(len < 1) return ERR_BADVAL; // need at least address + uint8_t addr = curbuf[0]; + if(addr > 0x7F) return ERR_BADVAL; // 7-битный адрес + if(len == 1){ // only address without data + return ERR_BADPAR; + } + addr <<= 1; // roll address to run i2c_write + if(!i2c_write(addr, curbuf + 1, len - 1)){ // len = address + data length + return ERR_CANTRUN; + } + return ERR_OK; +} + +static errcodes_t cmd_iicread(const char *cmd, char *args){ + if(!(I2C1->CR1 & I2C_CR1_PE)) return ERR_CANTRUN; + if(!args) return ERR_BADVAL; + char *setter = splitargs(args, NULL); + if(!setter) return ERR_BADVAL; + int len = parse_hex_data(setter, curbuf, MAXSTRLEN); + if(len != 2) return ERR_BADVAL; // address, amount of bytes + uint8_t addr = curbuf[0]; + uint8_t nbytes = curbuf[1]; + if(addr > 0x7F) return ERR_BADVAL; // allow to "read" 0 bytes (just get ACK) + addr <<= 1; + if(!i2c_read(addr, curbuf, nbytes)) return ERR_CANTRUN; + CMDEQ(); + if(nbytes < 9) NL(); + hexdump(sendfun, curbuf, nbytes); + return ERR_AMOUNT; +} + +static errcodes_t cmd_iicreadreg(const char *cmd, char *args){ + if(!(I2C1->CR1 & I2C_CR1_PE)) return ERR_CANTRUN; + if(!args) return ERR_BADVAL; + char *setter = splitargs(args, NULL); + if(!setter) return ERR_BADVAL; + int len = parse_hex_data(setter, curbuf, MAXSTRLEN); + if(len != 3) return ERR_BADVAL; // address, register, amount of bytes + uint8_t addr = curbuf[0]; + uint8_t nreg = curbuf[1]; + uint8_t nbytes = curbuf[2]; + if(addr > 0x7F || nbytes == 0) return ERR_BADVAL; + addr <<= 1; + if(!i2c_read_reg(addr, nreg, curbuf, nbytes)) return ERR_CANTRUN; + CMDEQ(); + if(nbytes < 9) NL(); + hexdump(sendfun, curbuf, nbytes); + return ERR_AMOUNT; +} + +static errcodes_t cmd_iicscan(const char _U_ *cmd, char _U_ *args){ + if(!(I2C1->CR1 & I2C_CR1_PE)) return ERR_CANTRUN; + i2c_init_scan_mode(); + return ERR_OK; +} + +// array for `cmd_pinout` +static kwindex_t func_array[FUNC_AMOUNT] = { + [FUNC_AIN] = STR_AIN, + [FUNC_USART] = STR_USART, + [FUNC_SPI] = STR_SPI, + [FUNC_I2C] = STR_I2C, + [FUNC_PWM] = STR_PWM, +}; + +static errcodes_t cmd_pinout(const char _U_ *cmd, char *args){ + uint8_t listmask = 0xff; // bitmask for funcnames_t + if(args && *args){ // change listmask by user choise + char *setter = splitargs(args, NULL); + if(!setter) return ERR_BADVAL; + char *saveptr, *token = strtok_r(setter, DELIM_, &saveptr); + listmask = 0; + while(token){ + int i = 0; + for(; i < FUNC_AMOUNT; ++i){ + if(0 == strcmp(token, str_keywords[func_array[i]])){ + listmask |= (1 << i); + break; + } + } + if(i == FUNC_AMOUNT) return ERR_BADVAL; // wrong argument + token = strtok_r(NULL, DELIM_, &saveptr); + } + if(listmask == 0) return ERR_BADVAL; + } + pwmtimer_t tp; // timers' pins + usart_props_t up; // USARTs' pins + i2c_props_t ip; // I2C's pins + + SEND("\nConfigurable pins (check collisions if functions have same name!):\n"); + for(int port = 0; port < 2; ++port){ + for(int pin = 0; pin < 16; ++pin){ + int funcs = pinfuncs(port, pin); + if(funcs == -1) continue; + uint8_t mask = (static_cast (funcs)) & listmask; + if(listmask != 0xff && !mask) continue; // no asked functions + SEND((port == 0) ? "PA" : "PB"); + SEND(u2str(pin)); + SEND(": "); + 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); + PUTCHAR('_'); PUTCHAR(up.isrx ? 'R' : 'T'); PUTCHAR('X'); + } + if(mask & (1 << FUNC_SPI)){ + SEND(COMMA); SEND(str_keywords[STR_SPI]); + // TODO: MISO/MOSI/SCL + } + if(mask & (1 << FUNC_I2C)){ + int idx = get_i2c_index(port, pin, &ip); + SEND(COMMA); SEND(str_keywords[STR_I2C]); PUTCHAR('1' + idx); + PUTCHAR('_'); + SEND(ip.isscl ? "SCL" : "SDA"); + } + if(mask & (1 << FUNC_PWM)){ + canPWM(port, pin, &tp); + SEND(COMMA); SEND(str_keywords[STR_PWM]); + SEND(u2str(tp.timidx)); // timidx == TIMNO! + PUTCHAR('_'); + PUTCHAR('1' + tp.chidx); + } + NL(); + } + } + 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 *CommandParser(char *str){ char command[CMD_MAXLEN+1]; int i = 0; @@ -787,11 +945,20 @@ void GPIO_process(){ l = usart_process(curbuf, MAXSTRLEN); if(l > 0) sendusartdata(curbuf, l); l = RECV((char*)curbuf, MAXSTRLEN); - if(l == 0) return; - if(l < 0) SEND("ERROR: USB buffer overflow or string was too long\n"); - else{ - const char *ans = CommandParser((char*)curbuf); - if(ans) SENDn(ans); + if(l){ + if(l < 0) SEND("ERROR: USB buffer overflow or string was too long\n"); + else{ + const char *ans = CommandParser((char*)curbuf); + if(ans) SENDn(ans); + } + } + if(I2C_scan_mode){ + uint8_t addr; + if(i2c_scan_next_addr(&addr)){ + SEND("foundaddr = "); + printuhex(addr); + NL(); + } } } diff --git a/F0:F030,F042,F072/usbcan_gpio/hardware.c b/F0:F030,F042,F072/usbcan_gpio/hardware.c index 8728d16..da8c165 100644 --- a/F0:F030,F042,F072/usbcan_gpio/hardware.c +++ b/F0:F030,F042,F072/usbcan_gpio/hardware.c @@ -36,7 +36,7 @@ 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 |= 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/i2c.c b/F0:F030,F042,F072/usbcan_gpio/i2c.c new file mode 100644 index 0000000..d2700ed --- /dev/null +++ b/F0:F030,F042,F072/usbcan_gpio/i2c.c @@ -0,0 +1,214 @@ +/* + * 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 "gpio.h" +#include "i2c.h" + +// fields position in I2C1->TIMINGR +#define I2C_TIMINGR_PRESC_Pos 28 +#define I2C_TIMINGR_SCLDEL_Pos 20 +#define I2C_TIMINGR_SDADEL_Pos 16 +#define I2C_TIMINGR_SCLH_Pos 8 +#define I2C_TIMINGR_SCLL_Pos 0 + +i2c_speed_t curI2Cspeed = I2C_SPEED_10K; +extern volatile uint32_t Tms; +static uint32_t cntr; +volatile uint8_t I2C_scan_mode = 0; // == 1 when I2C is in scan mode +static uint8_t i2caddr = I2C_ADDREND; // address for `scan`, not active + +void i2c_setup(i2c_speed_t speed){ + if(speed >= I2C_SPEED_1M) speed = curI2Cspeed; + else curI2Cspeed = speed; + uint8_t PRESC, SCLDEL = 4, SDADEL = 2, SCLH, SCLL; + I2C1->CR1 = 0; + // I2C + RCC->CFGR3 |= RCC_CFGR3_I2C1SW; // use sysclock for timing + switch(curI2Cspeed){ + case I2C_SPEED_10K: + PRESC = 0x0B; + SCLH = 0xC3; + SCLL = 0xC7; + break; + case I2C_SPEED_100K: + PRESC = 0x0B; + SCLH = 0x0F; + SCLL = 0x13; + break; + case I2C_SPEED_400K: + SDADEL = 3; + SCLDEL = 3; + PRESC = 5; + SCLH = 3; + SCLL = 9; + break; + case I2C_SPEED_1M: + default: + SDADEL = 0; + SCLDEL = 1; + PRESC = 5; + SCLH = 1; + SCLL = 3; + break; + } + I2C1->TIMINGR = (PRESC<CFGR1 &= ~SYSCFG_CFGR1_I2C_FMP_I2C1; + }else{ // activate FM+ + SYSCFG->CFGR1 |= SYSCFG_CFGR1_I2C_FMP_I2C1; + } + I2C1->ICR = 0xffff; // clear all errors + I2C1->CR1 = I2C_CR1_PE; +} + +void i2c_stop(){ + I2C1->CR1 = 0; +} + +/** + * write command byte to I2C + * @param addr - device address (TSYS01_ADDR0 or TSYS01_ADDR1) + * @param data - bytes to write + * @param nbytes - amount of bytes to write + * @param stop - to set STOP + * @return 0 if error + */ +static uint8_t i2c_writes(uint8_t addr, uint8_t *data, uint8_t nbytes, uint8_t stop){ + cntr = Tms; + I2C1->CR1 = 0; // clear busy flag + I2C1->ICR = 0x3f38; // clear all errors + I2C1->CR1 = I2C_CR1_PE; + while(I2C1->ISR & I2C_ISR_BUSY){ + IWDG->KR = IWDG_REFRESH; + if(Tms - cntr > I2C_TIMEOUT){ + DBG("Line busy\n"); + return 0; // check busy + }} + cntr = Tms; + while(I2C1->CR2 & I2C_CR2_START){ + IWDG->KR = IWDG_REFRESH; + if(Tms - cntr > I2C_TIMEOUT){ + return 0; // check start + }} + //I2C1->ICR = 0x3f38; // clear all errors + I2C1->CR2 = nbytes << 16 | addr; + if(stop) I2C1->CR2 |= I2C_CR2_AUTOEND; // autoend + // now start transfer + I2C1->CR2 |= I2C_CR2_START; + for(int i = 0; i < nbytes; ++i){ + cntr = Tms; + while(!(I2C1->ISR & I2C_ISR_TXIS)){ // ready to transmit + IWDG->KR = IWDG_REFRESH; + if(I2C1->ISR & I2C_ISR_NACKF){ + I2C1->ICR |= I2C_ICR_NACKCF; + DBG("NAK\n"); + return 0; + } + if(Tms - cntr > I2C_TIMEOUT){ + DBG("Timeout\n"); + return 0; + } + } + I2C1->TXDR = data[i]; // send data + } + // wait for data gone + while(I2C1->ISR & I2C_ISR_BUSY){ + IWDG->KR = IWDG_REFRESH; + if(Tms - cntr > I2C_TIMEOUT){break;} + } + return 1; +} + +uint8_t i2c_write(uint8_t addr, uint8_t *data, uint8_t nbytes){ + return i2c_writes(addr, data, nbytes, 1); +} + +/** + * read nbytes of data from I2C line + * `data` should be an array with at least `nbytes` length + * @return 1 if all OK, 0 if NACK or no device found + */ +static uint8_t i2c_readb(uint8_t addr, uint8_t *data, uint8_t nbytes, uint8_t busychk){ + if(busychk){ + cntr = Tms; + while(I2C1->ISR & I2C_ISR_BUSY){ + IWDG->KR = IWDG_REFRESH; + if(Tms - cntr > I2C_TIMEOUT){ + DBG("Line busy\n"); + return 0; // check busy + }} + } + cntr = Tms; + while(I2C1->CR2 & I2C_CR2_START){ + IWDG->KR = IWDG_REFRESH; + if(Tms - cntr > I2C_TIMEOUT){ + DBG("No start\n"); + return 0; // check start + }} + // read N bytes + I2C1->CR2 = (nbytes<<16) | addr | 1 | I2C_CR2_AUTOEND | I2C_CR2_RD_WRN; + I2C1->CR2 |= I2C_CR2_START; + uint8_t i; + for(i = 0; i < nbytes; ++i){ + cntr = Tms; + while(!(I2C1->ISR & I2C_ISR_RXNE)){ // wait for data + IWDG->KR = IWDG_REFRESH; + if(I2C1->ISR & I2C_ISR_NACKF){ + I2C1->ICR |= I2C_ICR_NACKCF; + DBG("NAK\n"); + return 0; + } + if(Tms - cntr > I2C_TIMEOUT){ + DBG("Timeout\n"); + return 0; + } + } + *data++ = I2C1->RXDR; + } + return 1; + } + +uint8_t i2c_read(uint8_t addr, uint8_t *data, uint8_t nbytes){ + return i2c_readb(addr, data, nbytes, 1); +} + +// read register reg +uint8_t i2c_read_reg(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t nbytes){ + if(!i2c_writes(addr, ®, 1, 0)) return 0; + return i2c_readb(addr, data, nbytes, 0); +} + +void i2c_init_scan_mode(){ + i2caddr = 1; + I2C_scan_mode = 1; +} + +// return 1 if next addr is active & return in as `addr` +// if addresses are over, return 1 and set addr to I2C_NOADDR +// if scan mode inactive, return 0 and set addr to I2C_NOADDR +int i2c_scan_next_addr(uint8_t *addr){ + *addr = i2caddr; + if(i2caddr == I2C_ADDREND){ + *addr = I2C_ADDREND; + I2C_scan_mode = 0; + return 0; + } + if(!i2c_read_reg((i2caddr++)<<1, 0, NULL, 0)) return 0; + return 1; +} diff --git a/F0:F030,F042,F072/usbcan_gpio/i2c.h b/F0:F030,F042,F072/usbcan_gpio/i2c.h new file mode 100644 index 0000000..0c2385e --- /dev/null +++ b/F0:F030,F042,F072/usbcan_gpio/i2c.h @@ -0,0 +1,47 @@ +/* + * 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 + +#define I2C_ADDREND (0x80) + +typedef enum{ + I2C_SPEED_10K, + I2C_SPEED_100K, + I2C_SPEED_400K, + I2C_SPEED_1M, + I2C_SPEED_AMOUNT +} i2c_speed_t; + +extern i2c_speed_t curI2Cspeed; +extern volatile uint8_t I2C_scan_mode; + +// timeout of I2C bus in ms +#define I2C_TIMEOUT (100) + +void i2c_setup(i2c_speed_t speed); +void i2c_stop(); +uint8_t i2c_read(uint8_t addr, uint8_t *data, uint8_t nbytes); +uint8_t i2c_read_reg(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t nbytes); +uint8_t i2c_write(uint8_t addr, uint8_t *data, uint8_t nbytes); + +void i2c_init_scan_mode(); +int i2c_scan_next_addr(uint8_t *addr); + diff --git a/F0:F030,F042,F072/usbcan_gpio/usbcangpio.bin b/F0:F030,F042,F072/usbcan_gpio/usbcangpio.bin index b58d711..e4d6aba 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 efeb3e1..cb78273 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 7e44667..05fc5ab 100644 --- a/F0:F030,F042,F072/usbcan_gpio/usbcangpio.files +++ b/F0:F030,F042,F072/usbcan_gpio/usbcangpio.files @@ -15,6 +15,8 @@ hardware.h hashgen/Readme hashgen/hashgen.c hashgen/mktestdic +i2c.c +i2c.h main.c pwm.c pwm.h diff --git a/F0:F030,F042,F072/usbcan_gpio/version.inc b/F0:F030,F042,F072/usbcan_gpio/version.inc index 34e7d71..a7a139e 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 "207" -#define BUILD_DATE "2026-03-15" +#define BUILD_NUMBER "217" +#define BUILD_DATE "2026-03-17"