mirror of
https://github.com/eddyem/stm32samples.git
synced 2026-03-20 00:30:57 +03:00
I2C works
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -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<<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:
|
||||
#define _U(x) [FUNC_USART] = x
|
||||
// _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 _I
|
||||
|
||||
typedef struct{
|
||||
uint8_t isrx : 1;
|
||||
uint8_t istx : 1;
|
||||
} usart_props_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))
|
||||
|
||||
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
|
||||
* @param port
|
||||
* @param pin
|
||||
* @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;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 <i2c_speed_t> (UsartConf.speed);
|
||||
the_conf.I2Cspeed = static_cast <uint8_t> (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 <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){
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
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"?>
|
||||
<!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>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
|
||||
@@ -15,6 +15,8 @@ hardware.h
|
||||
hashgen/Readme
|
||||
hashgen/hashgen.c
|
||||
hashgen/mktestdic
|
||||
i2c.c
|
||||
i2c.h
|
||||
main.c
|
||||
pwm.c
|
||||
pwm.h
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
#define BUILD_NUMBER "207"
|
||||
#define BUILD_DATE "2026-03-15"
|
||||
#define BUILD_NUMBER "217"
|
||||
#define BUILD_DATE "2026-03-17"
|
||||
|
||||
Reference in New Issue
Block a user