mirror of
https://github.com/eddyem/stm32samples.git
synced 2026-03-20 08:40:57 +03:00
I2C works
This commit is contained in:
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
214
F0:F030,F042,F072/usbcan_gpio/i2c.c
Normal file
214
F0:F030,F042,F072/usbcan_gpio/i2c.c
Normal 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, ®, 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;
|
||||||
|
}
|
||||||
47
F0:F030,F042,F072/usbcan_gpio/i2c.h
Normal file
47
F0:F030,F042,F072/usbcan_gpio/i2c.h
Normal 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);
|
||||||
|
|
||||||
Binary file not shown.
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
#define BUILD_NUMBER "207"
|
#define BUILD_NUMBER "217"
|
||||||
#define BUILD_DATE "2026-03-15"
|
#define BUILD_DATE "2026-03-17"
|
||||||
|
|||||||
Reference in New Issue
Block a user