Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions multi/stm32l4-multi/i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,20 @@ ssize_t i2c_writeReg(int i2c, unsigned char addr, unsigned char reg, const void
}


int i2c_setSpeed(int i2c, uint32_t speed, int rise_time)
{
if ((N_I2C_ACTIVE == 0) || (i2c < i2c1) || (i2c > MAX_I2C) || (i2cConfig[i2c] == 0)) {
return -EINVAL;
}

mutexLock(i2c_lock[i2cPos[i2c]]);
int ret = libi2c_setSpeed(&i2c_ctx[i2cPos[i2c]], speed, rise_time);
mutexUnlock(i2c_lock[i2cPos[i2c]]);

return ret;
}


void i2c_init(void)
{
int i2c;
Expand Down
3 changes: 3 additions & 0 deletions multi/stm32l4-multi/i2c.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ ssize_t i2c_write(int i2c, unsigned char addr, const void *buff, size_t len);
ssize_t i2c_writeReg(int i2c, unsigned char addr, unsigned char reg, const void *buff, size_t len);


int i2c_setSpeed(int i2c, uint32_t speed, int rise_time);


void i2c_init(void);


Expand Down
11 changes: 11 additions & 0 deletions multi/stm32l4-multi/libmulti/include/libmulti/libi2c.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ typedef struct {
volatile unsigned int *base;
volatile int err;
int clk;
uint32_t refclk_freq;

handle_t irqlock;
handle_t irqcond;
Expand All @@ -32,6 +33,13 @@ typedef struct {
enum { i2c1 = 0, i2c2, i2c3, i2c4 };


enum libi2c_speed {
libi2c_speed_standard = 0, /* Standard mode, 100 kHz */
libi2c_speed_fast, /* Fast mode, 400 kHz */
libi2c_speed_fastplus, /* Fast Plus mode, 1 MHz */
};


ssize_t libi2c_read(libi2c_ctx_t *ctx, unsigned char addr, void *buff, size_t len);


Expand All @@ -44,6 +52,9 @@ ssize_t libi2c_write(libi2c_ctx_t *ctx, unsigned char addr, const void *buff, si
ssize_t libi2c_writeReg(libi2c_ctx_t *ctx, unsigned char addr, unsigned char reg, const void *buff, size_t len);


int libi2c_setSpeed(libi2c_ctx_t *ctx, enum libi2c_speed speed, int rise_time);


int libi2c_init(libi2c_ctx_t *ctx, int i2c);


Expand Down
154 changes: 141 additions & 13 deletions multi/stm32l4-multi/libmulti/libi2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@

#define TIMEOUT (100 * 1000)

#define ANALOG_NOISE_FILTER 0
/* Regardless of reference clock frequency, the low period seems to be 3 cycles too long and high period 2 cycles too long.
* These extra delays were measured empirically, they are not mentioned in the documentation nor in the errata,
* and according to docs it seems they should not be necessary at all. */
#define SCLL_EXTRA_DELAY 3
#define SCLH_EXTRA_DELAY 2


static const struct libi2c_peripheralInfo {
void *base;
Expand Down Expand Up @@ -276,14 +283,138 @@ ssize_t libi2c_writeReg(libi2c_ctx_t *ctx, unsigned char addr, unsigned char reg
}


static uint32_t divide_ceil(uint32_t n, uint32_t d)
{
uint32_t res = (n + d - 1) / d;
return (res == 0) ? 1 : res;
}

/* Calculate values for TIMINGR register and DNF field of CR1 register.
* `refclk` - I2C reference clock in Hz
* `speed` - I2C speed mode
* `rise_time` - Rise time compensation.
* Should be set to time taken by the rising edge of SCLK to reach 70% amplitude.
* The unit is 1/16 microseconds (62.5 ns).
* If rise time is unknown, set to 0 - the clock speed will be in spec, but may be slower than optimal.
* Can be set to < 0 to slow down the clock more than required by spec.
* `timingr_val` - output for TIMINGR
* `digifilter` - output for DNF field
*/
static int libi2c_calculateTiming(uint32_t refclk, enum libi2c_speed speed, int rise_time, uint32_t *timingr_val, uint32_t *digifilter)
{
/* In constants below, each tick represents 0.0625 us, corresponding to frequency of 16 MHz */
/* Ticks per one clock period (from I2C protocol documentation) */
static const uint8_t lookup_tCLK[] = {
[libi2c_speed_standard] = 160, /* 10 us => 100 kHz */
[libi2c_speed_fast] = 40, /* 2.5 us => 400 kHz */
[libi2c_speed_fastplus] = 16, /* 1 us => 1 MHz */
};
/* Ticks to hold high state (from I2C protocol documentation) */
static const uint8_t lookup_tHIGH[] = {
[libi2c_speed_standard] = 65, /* 4 us minimum + 0.0625 safety margin */
[libi2c_speed_fast] = 11, /* 0.6 us minimum + 0.0625 safety margin */
[libi2c_speed_fastplus] = 5, /* 0.25 us minimum + 0.0625 safety margin */
};
/* Data set-up time (tSU;DAT from I2C protocol documentation) */
static const uint8_t lookup_tSCLDEL[] = {
[libi2c_speed_standard] = 4, /* 0.25 us */
[libi2c_speed_fast] = 2, /* 0.125 us >= 0.1 us minimum */
[libi2c_speed_fastplus] = 1, /* 0.625 us >= 0.05 us minimum */
};
/* Analog noise filter delays clock transitions by another 0.0625 us. */
static const uint32_t tANF = (ANALOG_NOISE_FILTER != 0) ? 1 : 0;

if ((speed < libi2c_speed_standard) || (speed > libi2c_speed_fastplus)) {
return -EINVAL;
}

/* Digital noise filter period - 0.125 us */
uint32_t tDNF = 2;

const int low_subtract = lookup_tHIGH[speed] + rise_time + tANF;
uint32_t tSCLL = (lookup_tCLK[speed] > low_subtract) ? (lookup_tCLK[speed] - low_subtract) : 1;
uint32_t tSCLH = lookup_tHIGH[speed] - tANF;
uint32_t tSCLDEL = lookup_tSCLDEL[speed];
uint32_t prescaler = 1;

/* Round the clock frequency to 1 MHz */
uint32_t m = divide_ceil(refclk, 1000000);
/* If actual frequency is different from our "base frequency", scale the results. */
if (m != 16) {
tDNF = divide_ceil(tDNF * m, 16);
if (tDNF > 15) {
tDNF = 15;
}

tSCLDEL = divide_ceil(tSCLDEL * m, 16);
tSCLL = divide_ceil(tSCLL * m, 16);
tSCLH = divide_ceil(tSCLH * m, 16);
}

tSCLL = (tSCLL > (tDNF + SCLL_EXTRA_DELAY)) ? (tSCLL - (tDNF + SCLL_EXTRA_DELAY)) : 1;
tSCLH = (tSCLH > (tDNF + SCLH_EXTRA_DELAY)) ? (tSCLH - (tDNF + SCLH_EXTRA_DELAY)) : 1;

/* tSCLDEL has a range of [1:16], but we can save some calculations by scaling it up to [16:256]
* just for the purpose of calculating prescaler. */
uint32_t longest = max(tSCLDEL * 16, max(tSCLL, tSCLH));
if (longest > 256) {
prescaler = divide_ceil(longest, 256);
}

if (prescaler > 16) {
/* Input frequency too fast */
return -EINVAL;
}

if (prescaler > 1) {
tSCLDEL = divide_ceil(tSCLDEL, prescaler);
tSCLL = divide_ceil(tSCLL, prescaler);
tSCLH = divide_ceil(tSCLH, prescaler);
}

*timingr_val =
(((prescaler - 1) & 0xf) << 28) |
(((tSCLDEL - 1) & 0xf) << 20) |
(((tSCLH - 1) & 0xff) << 8) |
(((tSCLL - 1) & 0xff) << 0);
*digifilter = tDNF;
return 0;
}


static int _libi2c_setSpeedInternal(libi2c_ctx_t *ctx, enum libi2c_speed speed, int rise_time)
{
int ret;
uint32_t timingr_val, digifilter;
dataBarier();
ret = libi2c_calculateTiming(ctx->refclk_freq, speed, rise_time, &timingr_val, &digifilter);
if (ret < 0) {
return ret;
}

uint32_t t = *(ctx->base + cr1) & ~(0xf << 8);
*(ctx->base + cr1) = t | (digifilter << 8);
*(ctx->base + timingr) = timingr_val;
dataBarier();
return 0;
}


int libi2c_setSpeed(libi2c_ctx_t *ctx, enum libi2c_speed speed, int rise_time)
{
devClk(ctx->clk, 1);
int ret = _libi2c_setSpeedInternal(ctx, speed, rise_time);
devClk(ctx->clk, 0);
return ret;
}


int libi2c_init(libi2c_ctx_t *ctx, int i2c)
{
int presc;
uint32_t refclk;
unsigned int t;

if (i2c < i2c1 || i2c > i2c4 || ctx == NULL) {
return -1;
return -EINVAL;
}

i2c -= i2c1;
Expand All @@ -292,8 +423,8 @@ int libi2c_init(libi2c_ctx_t *ctx, int i2c)
ctx->clk = i2cinfo[i2c].clk;

devClk(ctx->clk, 1);
if (libi2c_clockSetup(&i2cinfo[i2c], &refclk) < 0) {
return -1;
if (libi2c_clockSetup(&i2cinfo[i2c], &ctx->refclk_freq) < 0) {
return -EIO;
}

mutexCreate(&ctx->irqlock);
Expand All @@ -306,14 +437,11 @@ int libi2c_init(libi2c_ctx_t *ctx, int i2c)
dataBarier();

t = *(ctx->base + cr1) & ~0xfffdff;
*(ctx->base + cr1) = t | (1 << 12) | (0x7 << 8) | (1 << 7) | (1 << 6) | (1 << 4) | (1 << 2) | (1 << 1);

presc = ((refclk + 500 * 1000) / (1000 * 1000)) / 4;
t = *(ctx->base + timingr) & ~((0xf << 18) | 0xffffff);
*(ctx->base + timingr) = t | (((presc & 0xf) << 28) | (0x4 << 20) | (0x2 << 16) | (0xf << 8) | 0x13);
dataBarier();

/* Enable analog noise filter (if requested) and interrupts (ERR, TC, NACK, RX, TX).
* Note: Analog noise filter is 1 to disable. */
*(ctx->base + cr1) = t | ((ANALOG_NOISE_FILTER != 0) ? 0 : (1 << 12)) | (1 << 7) | (1 << 6) | (1 << 4) | (1 << 2) | (1 << 1);
int ret = _libi2c_setSpeedInternal(ctx, libi2c_speed_standard, 0);
devClk(ctx->clk, 0);

return 0;
return ret;
}
4 changes: 4 additions & 0 deletions multi/stm32l4-multi/stm32l4-multi.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ static void handleMsg(msg_t *msg)
err = i2c_writeReg(imsg->i2c_msg.i2c, imsg->i2c_msg.addr, imsg->i2c_msg.reg, msg->i.data, msg->i.size);
break;

case i2c_def:
err = i2c_setSpeed(imsg->i2c_def.i2c, imsg->i2c_def.speed, imsg->i2c_def.riseTime);
break;

case exti_def:
err = exti_configure(imsg->exti_def.line, imsg->exti_def.mode, imsg->exti_def.edge);
break;
Expand Down
11 changes: 10 additions & 1 deletion multi/stm32l4-multi/stm32l4-multi.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ enum { adc_get = 0, rtc_setcal, rtc_get, rtc_set, rtc_setalarm, i2c_get, i2c_get
i2c_set, i2c_setwreg, gpio_def, gpio_get, gpio_set, uart_def, uart_get, uart_set,
flash_get, flash_set, flash_info, spi_get, spi_set, spi_rw, spi_def, exti_def,
exti_map, otp_get, otp_set, rtc_setBackup, rtc_getBackup, flash_setRaw, flash_erase,
rng_get, pwm_def, pwm_setm, pwm_getm, pwm_getfreq, pwm_distim, pwm_dischn, pwm_bitseq };
rng_get, pwm_def, pwm_setm, pwm_getm, pwm_getfreq, pwm_distim, pwm_dischn, pwm_bitseq,
i2c_def };
/* clang-format on */

/* RTC */
Expand Down Expand Up @@ -58,6 +59,13 @@ typedef struct {
} __attribute__((packed)) i2cmsg_t;


typedef struct {
int i2c;
unsigned char speed;
int riseTime;
} __attribute__((packed)) i2cdef_t;


/* GPIO */


Expand Down Expand Up @@ -292,6 +300,7 @@ typedef struct {
int rtc_calib;
rtctimestamp_t rtc_timestamp;
i2cmsg_t i2c_msg;
i2cdef_t i2c_def;
uartget_t uart_get;
uartset_t uart_set;
uartdef_t uart_def;
Expand Down
Loading