I2C works

This commit is contained in:
Edward Emelianov
2026-03-17 21:17:45 +03:00
parent 03b05051aa
commit 165780bef9
11 changed files with 558 additions and 28 deletions

View File

@@ -49,6 +49,7 @@ typedef struct __attribute__((packed, aligned(4))){
// gpio settings // gpio settings
pinconfig_t pinconfig[2][16]; // GPIOA, GPIOB pinconfig_t pinconfig[2][16]; // GPIOA, GPIOB
usartconf_t usartconfig; usartconf_t usartconfig;
uint8_t I2Cspeed;
//spiconfig_t spiconfig; //spiconfig_t spiconfig;
} user_conf; } user_conf;

View File

@@ -22,6 +22,7 @@
#include "adc.h" #include "adc.h"
#include "flash.h" #include "flash.h"
#include "gpio.h" #include "gpio.h"
#include "i2c.h"
#include "pwm.h" #include "pwm.h"
#include "usart.h" #include "usart.h"
@@ -39,12 +40,6 @@ typedef struct{
uint8_t AF[FUNC_AMOUNT]; // alternate function number for corresponding `FuncNames` number uint8_t AF[FUNC_AMOUNT]; // alternate function number for corresponding `FuncNames` number
} pinprops_t; } pinprops_t;
#define CANADC(x) ((x) & (1<<FUNC_AIN))
#define CANUSART(x) ((x) & (1<<FUNC_USART))
#define CANSPI(x) ((x) & (1<<FUNC_SPI))
#define CANI2C(x) ((x) & (1<<FUNC_I2C))
#define CANPWM(x) ((x) & (1<<FUNC_PWM))
// AF for USART, SPI, I2C: // AF for USART, SPI, I2C:
#define _U(x) [FUNC_USART] = x #define _U(x) [FUNC_USART] = x
// _S(0) or _U(0) have no sence, but lets understand that this pin have SPI or USART // _S(0) or _U(0) have no sence, but lets understand that this pin have SPI or USART
@@ -81,17 +76,28 @@ static const pinprops_t pin_props[2][16] = {
#undef _S #undef _S
#undef _I #undef _I
typedef struct{ #define CANADC(x) ((x) & (1<<FUNC_AIN))
uint8_t isrx : 1; #define CANUSART(x) ((x) & (1<<FUNC_USART))
uint8_t istx : 1; #define CANSPI(x) ((x) & (1<<FUNC_SPI))
} usart_props_t; #define CANI2C(x) ((x) & (1<<FUNC_I2C))
#define CANPWM(x) ((x) & (1<<FUNC_PWM))
static uint8_t haveI2C = 0; // ==1 if chkpinconf found I2C
// return pin_props[port][pin].funcs for listing or -1 if disabled
int pinfuncs(uint8_t port, uint8_t pin){
if(is_disabled(port, pin)) return -1;
return (int) pin_props[port][pin].funcs;
}
/** /**
* @brief get_usart_index - get USART index (0 or 1 for USART1 or USART2) by given pin * @brief get_usart_index - get USART index (0 or 1 for USART1 or USART2) by given pin
* @param port * @param port
* @param pin * @param pin
* @return -1 if error * @return -1 if error
*/ */
static int get_usart_index(uint8_t port, uint8_t pin, usart_props_t *p){ int get_usart_index(uint8_t port, uint8_t pin, usart_props_t *p){
if(port > 1 || pin > 15 || !CANUSART(pin_props[port][pin].funcs)) return -1;
int idx = -1; int idx = -1;
usart_props_t curprops = {0}; usart_props_t curprops = {0};
if(port == 0){ // GPIOA 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 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 // default config
static void defconfig(pinconfig_t *cfg){ static void defconfig(pinconfig_t *cfg){
if(!cfg) return; if(!cfg) return;
@@ -158,6 +195,8 @@ int chkpinconf(){
UC.RXen = 0; UC.TXen = 0; UC.monitor = 0; 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_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 port = 0; port < 2; ++port){
for(int pin = 0; pin < 16; ++pin){ for(int pin = 0; pin < 16; ++pin){
pinconfig_t *cfg = &pinconfig[port][pin]; pinconfig_t *cfg = &pinconfig[port][pin];
@@ -210,6 +249,41 @@ int chkpinconf(){
break; 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 default: break; // later fill other functions
} }
} }
@@ -226,11 +300,19 @@ int chkpinconf(){
get_defusartconf(&UC); // clear global configuration get_defusartconf(&UC); // clear global configuration
the_conf.usartconfig = UC; 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; return ret;
} }
int is_disabled(uint8_t port, uint8_t pin){ 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; if(the_conf.pinconfig[port][pin].enable) return FALSE;
return TRUE; return TRUE;
} }
@@ -371,6 +453,8 @@ int gpio_reinit(){
if(!usart_config(NULL)) ret = FALSE; if(!usart_config(NULL)) ret = FALSE;
else if(!usart_start()) ret = FALSE; else if(!usart_start()) ret = FALSE;
}else usart_stop(); }else usart_stop();
if(haveI2C) i2c_setup((i2c_speed_t) the_conf.I2Cspeed);
else i2c_stop();
return ret; return ret;
} }

View File

@@ -69,6 +69,7 @@ typedef enum FuncNames{ // shift 1 by this to get "canUSART" etc; not more than
FUNC_PWM = 4, FUNC_PWM = 4,
FUNC_AMOUNT // just for arrays' sizes FUNC_AMOUNT // just for arrays' sizes
} funcnames_t; } funcnames_t;
/* /*
typedef union{ typedef union{
struct{ struct{
@@ -91,6 +92,16 @@ typedef struct{
uint16_t threshold; // threshold for ADC measurement uint16_t threshold; // threshold for ADC measurement
} pinconfig_t; } 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{ typedef struct{
uint32_t speed; uint32_t speed;
@@ -102,11 +113,15 @@ typedef struct{
*/ */
int is_disabled(uint8_t port, uint8_t pin); int is_disabled(uint8_t port, uint8_t pin);
int pinfuncs(uint8_t port, uint8_t pin);
int chkpinconf(); int chkpinconf();
int set_pinfunc(uint8_t port, uint8_t pin, pinconfig_t *pcfg); 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_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 gpio_reinit();
int pin_out(uint8_t port, uint8_t pin, uint8_t newval); int pin_out(uint8_t port, uint8_t pin, uint8_t newval);

View File

@@ -27,6 +27,7 @@ extern "C"{
#include "flash.h" #include "flash.h"
#include "gpio.h" #include "gpio.h"
#include "gpioproto.h" #include "gpioproto.h"
#include "i2c.h"
#include "pwm.h" #include "pwm.h"
#include "usart.h" #include "usart.h"
#undef USBIF #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(eraseflash, "erase full flash storage") \
COMMAND(help, "show this help") \ COMMAND(help, "show this help") \
COMMAND(hexinput, "input is text (0) or hex + text in quotes (1)") \ 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(mcutemp, "get MCU temperature (degC*10)") \
COMMAND(mcureset, "reset MCU") \ COMMAND(mcureset, "reset MCU") \
COMMAND(PA, "GPIOA setter/getter (type PA0=help for further info)") \ COMMAND(PA, "GPIOA setter/getter (type PA0=help for further info)") \
COMMAND(PB, "GPIOB setter/getter") \ 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(pwmmap, "show pins with PWM ability") \
COMMAND(readconf, "re-read config from flash") \ COMMAND(readconf, "re-read config from flash") \
COMMAND(reinit, "apply pin config") \ COMMAND(reinit, "apply pin config") \
@@ -112,7 +118,7 @@ enum MiscValues{
// TODO: add HEX input? // TODO: add HEX input?
#define KEYWORDS \ #define KEYWORDS \
KW(AIN) \ KW(AIN) \
KW(IN) \ KW(IN) \
KW(OUT) \ KW(OUT) \
KW(AF) \ KW(AF) \
@@ -131,11 +137,12 @@ KW(AIN) \
KW(HEX) \ KW(HEX) \
KW(PWM) \ KW(PWM) \
enum{ // indexes of string keywords
typedef enum{ // indexes of string keywords
#define KW(k) STR_ ## k, #define KW(k) STR_ ## k,
KEYWORDS KEYWORDS
#undef KW #undef KW
}; } kwindex_t;
// strings for keywords // strings for keywords
static const char *str_keywords[] = { static const char *str_keywords[] = {
@@ -194,6 +201,9 @@ static const char *pinhelp =
; ;
static const char *EQ = " = "; // equal sign for getters 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 = ` // send `command = `
#define CMDEQ() do{SEND(cmd); SEND(EQ);}while(0) #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 uint32_t *pending_u32 = NULL; // -//- for uint32_t
usartconf_t UsartConf; usartconf_t UsartConf;
if(!get_curusartconf(&UsartConf)) return ERR_CANTRUN; if(!get_curusartconf(&UsartConf)) return ERR_CANTRUN;
#define DELIM_ " ,"
char *saveptr, *token = strtok_r(setter, DELIM_, &saveptr); char *saveptr, *token = strtok_r(setter, DELIM_, &saveptr);
while(token){ while(token){
if(pending_u16){ if(pending_u16){
@@ -381,7 +390,7 @@ static errcodes_t pin_setter(uint8_t port, uint8_t pin, char *setter){
pending_u16 = &curconf.threshold; pending_u16 = &curconf.threshold;
break; break;
case MISC_SPEED: case MISC_SPEED:
pending_u32 = &UsartConf.speed; pending_u32 = &UsartConf.speed; // also used for I2C speed!
break; break;
case MISC_TEXT: // what to do, if textproto is set, but user wants binary? case MISC_TEXT: // what to do, if textproto is set, but user wants binary?
UsartConf.textproto = 1; 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 periferial settings before refresh pin data
// check current USART settings // check current USART settings
if(func_set == FUNC_USART && !chkusartconf(&UsartConf)) return ERR_BADVAL; 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 <i2c_speed_t> (UsartConf.speed);
the_conf.I2Cspeed = static_cast <uint8_t> (s);
}
if(func_set != 0xFF) mode_set = MODE_AF; if(func_set != 0xFF) mode_set = MODE_AF;
if(mode_set == 0xFF) return ERR_BADVAL; // user forgot to set mode if(mode_set == 0xFF) return ERR_BADVAL; // user forgot to set mode
// set defaults // 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"); else if(!U.TXen && U.RXen) SEND(" RXONLY");
NL(); 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 S
#undef SP #undef SP
return ERR_AMOUNT; return ERR_AMOUNT;
@@ -748,12 +772,146 @@ static errcodes_t cmd_USART(const char _U_ *cmd, char *args){
return ERR_AMOUNT; 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 <uint8_t> (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){ constexpr uint32_t hash(const char* str, uint32_t h = 0){
return *str ? hash(str + 1, h + ((h << 7) ^ *str)) : h; return *str ? hash(str + 1, h + ((h << 7) ^ *str)) : h;
} }
// TODO: add checking real command length!
static const char *CommandParser(char *str){ static const char *CommandParser(char *str){
char command[CMD_MAXLEN+1]; char command[CMD_MAXLEN+1];
int i = 0; int i = 0;
@@ -787,11 +945,20 @@ void GPIO_process(){
l = usart_process(curbuf, MAXSTRLEN); l = usart_process(curbuf, MAXSTRLEN);
if(l > 0) sendusartdata(curbuf, l); if(l > 0) sendusartdata(curbuf, l);
l = RECV((char*)curbuf, MAXSTRLEN); l = RECV((char*)curbuf, MAXSTRLEN);
if(l == 0) return; if(l){
if(l < 0) SEND("ERROR: USB buffer overflow or string was too long\n"); if(l < 0) SEND("ERROR: USB buffer overflow or string was too long\n");
else{ else{
const char *ans = CommandParser((char*)curbuf); const char *ans = CommandParser((char*)curbuf);
if(ans) SENDn(ans); if(ans) SENDn(ans);
}
}
if(I2C_scan_mode){
uint8_t addr;
if(i2c_scan_next_addr(&addr)){
SEND("foundaddr = ");
printuhex(addr);
NL();
}
} }
} }

View File

@@ -36,7 +36,7 @@ void hardware_setup(){
// enable all active GPIO clocking // enable all active GPIO clocking
RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | RCC_AHBENR_DMA1EN; 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->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(); pins_setup();
adc_setup(); adc_setup();
GPIO_init(); GPIO_init();

View File

@@ -0,0 +1,214 @@
/*
* This file is part of the usbcangpio project.
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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<<I2C_TIMINGR_PRESC_Pos) | (SCLDEL<<I2C_TIMINGR_SCLDEL_Pos) |
(SDADEL<<I2C_TIMINGR_SDADEL_Pos) | (SCLH<<I2C_TIMINGR_SCLH_Pos) | (SCLL<< I2C_TIMINGR_SCLL_Pos);
if(speed < I2C_SPEED_400K){
SYSCFG->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, &reg, 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;
}

View File

@@ -0,0 +1,47 @@
/*
* This file is part of the usbcangpio project.
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stm32f0.h>
#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);

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject> <!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 18.0.2, 2026-03-15T23:41:05. --> <!-- Written by QtCreator 18.0.2, 2026-03-17T00:10:23. -->
<qtcreator> <qtcreator>
<data> <data>
<variable>EnvironmentId</variable> <variable>EnvironmentId</variable>

View File

@@ -15,6 +15,8 @@ hardware.h
hashgen/Readme hashgen/Readme
hashgen/hashgen.c hashgen/hashgen.c
hashgen/mktestdic hashgen/mktestdic
i2c.c
i2c.h
main.c main.c
pwm.c pwm.c
pwm.h pwm.h

View File

@@ -1,2 +1,2 @@
#define BUILD_NUMBER "207" #define BUILD_NUMBER "217"
#define BUILD_DATE "2026-03-15" #define BUILD_DATE "2026-03-17"