Skip to content

Commit 28dd812

Browse files
stickbreakerme-no-dev
authored andcommitted
Support CPU frequency changes in I2C (#2287)
**esp32-hal-i2c.c** * add callback for cpu frequency changes * adjust fifo thresholds based on cpu frequency and i2c bus frequency * reduce i2c bus frequency if differential is too small **Wire.h** * version to 1.1.0
1 parent 8b6483d commit 28dd812

File tree

2 files changed

+69
-17
lines changed

2 files changed

+69
-17
lines changed

Diff for: cores/esp32/esp32-hal-i2c.c

+67-16
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
#include "soc/i2c_struct.h"
2525
#include "soc/dport_reg.h"
2626
#include "esp_attr.h"
27-
27+
#include "esp32-hal-cpu.h" // cpu clock change support 31DEC2018
2828
//#define I2C_DEV(i) (volatile i2c_dev_t *)((i)?DR_REG_I2C1_EXT_BASE:DR_REG_I2C_EXT_BASE)
2929
//#define I2C_DEV(i) ((i2c_dev_t *)(REG_I2C_BASE(i)))
3030
#define I2C_SCL_IDX(p) ((p==0)?I2CEXT0_SCL_OUT_IDX:((p==1)?I2CEXT1_SCL_OUT_IDX:0))
@@ -206,8 +206,8 @@ static i2c_t _i2c_bus_array[2] = {
206206
{(volatile i2c_dev_t *)(DR_REG_I2C1_EXT_BASE_FIXED), 1, -1, -1,I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0,0}
207207
};
208208
#else
209-
#define I2C_MUTEX_LOCK() do {} while (xSemaphoreTake(i2c->lock, portMAX_DELAY) != pdPASS)
210-
#define I2C_MUTEX_UNLOCK() xSemaphoreGive(i2c->lock)
209+
#define I2C_MUTEX_LOCK() do {} while (xSemaphoreTakeRecursive(i2c->lock, portMAX_DELAY) != pdPASS)
210+
#define I2C_MUTEX_UNLOCK() xSemaphoreGiveRecursive(i2c->lock)
211211

212212
static i2c_t _i2c_bus_array[2] = {
213213
{(volatile i2c_dev_t *)(DR_REG_I2C_EXT_BASE_FIXED), NULL, 0, -1, -1, I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0,0,0},
@@ -445,6 +445,39 @@ static void IRAM_ATTR i2cTriggerDumps(i2c_t * i2c, uint8_t trigger, const char l
445445
}
446446
// end of debug support routines
447447

448+
/* Start of CPU Clock change Support
449+
*/
450+
451+
static void i2cApbChangeCallback(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){
452+
i2c_t* i2c = (i2c_t*) arg; // recover data
453+
if(i2c == NULL) { // point to peripheral control block does not exits
454+
return false;
455+
}
456+
uint32_t oldFreq=0;
457+
switch(ev_type){
458+
case APB_BEFORE_CHANGE :
459+
if(new_apb < 3000000) {// too slow
460+
log_e("apb speed %d too slow",new_apb);
461+
break;
462+
}
463+
I2C_MUTEX_LOCK(); // lock will spin until current transaction is completed
464+
break;
465+
case APB_AFTER_CHANGE :
466+
oldFreq = (i2c->dev->scl_low_period.period+i2c->dev->scl_high_period.period); //read old apbCycles
467+
if(oldFreq>0) { // was configured with value
468+
oldFreq = old_apb / oldFreq;
469+
i2cSetFrequency(i2c,oldFreq);
470+
}
471+
I2C_MUTEX_UNLOCK();
472+
break;
473+
default :
474+
log_e("unk ev %u",ev_type);
475+
I2C_MUTEX_UNLOCK();
476+
}
477+
return;
478+
}
479+
/* End of CPU Clock change Support
480+
*/
448481
static void IRAM_ATTR i2cSetCmd(i2c_t * i2c, uint8_t index, uint8_t op_code, uint8_t byte_num, bool ack_val, bool ack_exp, bool ack_check)
449482
{
450483
I2C_COMMAND_t cmd;
@@ -1221,6 +1254,12 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis)
12211254
I2C_MUTEX_UNLOCK();
12221255
return I2C_ERROR_MEMORY;
12231256
}
1257+
if( !addApbChangeCallback( i2c, i2cApbChangeCallback)) {
1258+
log_e("install apb Callback failed");
1259+
I2C_MUTEX_UNLOCK();
1260+
return I2C_ERROR_DEV;
1261+
}
1262+
12241263
}
12251264
//hang until it completes.
12261265

@@ -1352,6 +1391,9 @@ static void i2cReleaseISR(i2c_t * i2c)
13521391
if(i2c->intr_handle) {
13531392
esp_intr_free(i2c->intr_handle);
13541393
i2c->intr_handle=NULL;
1394+
if (!removeApbChangeCallback( i2c, i2cApbChangeCallback)) {
1395+
log_e("unable to release apbCallback");
1396+
}
13551397
}
13561398
}
13571399

@@ -1437,15 +1479,15 @@ i2c_err_t i2cDetachSDA(i2c_t * i2c, int8_t sda)
14371479
* PUBLIC API
14381480
* */
14391481
// 24Nov17 only supports Master Mode
1440-
i2c_t * i2cInit(uint8_t i2c_num, int8_t sda, int8_t scl, uint32_t frequency) //before this is called, pins should be detached, else glitch
1441-
{
1482+
i2c_t * i2cInit(uint8_t i2c_num, int8_t sda, int8_t scl, uint32_t frequency) {
14421483
log_v("num=%d sda=%d scl=%d freq=%d",i2c_num, sda, scl, frequency);
14431484
if(i2c_num > 1) {
14441485
return NULL;
14451486
}
14461487

14471488
i2c_t * i2c = &_i2c_bus_array[i2c_num];
14481489

1490+
// pins should be detached, else glitch
14491491
if(i2c->sda >= 0){
14501492
i2cDetachSDA(i2c, i2c->sda);
14511493
}
@@ -1457,7 +1499,7 @@ i2c_t * i2cInit(uint8_t i2c_num, int8_t sda, int8_t scl, uint32_t frequency) //b
14571499

14581500
#if !CONFIG_DISABLE_HAL_LOCKS
14591501
if(i2c->lock == NULL) {
1460-
i2c->lock = xSemaphoreCreateMutex();
1502+
i2c->lock = xSemaphoreCreateRecursiveMutex();
14611503
if(i2c->lock == NULL) {
14621504
return NULL;
14631505
}
@@ -1604,7 +1646,8 @@ i2c_err_t i2cRead(i2c_t * i2c, uint16_t address, uint8_t* buff, uint16_t size, b
16041646
return last_error;
16051647
}
16061648

1607-
#define MIN_I2C_CLKS 100
1649+
#define MIN_I2C_CLKS 100 // minimum ratio between cpu and i2c Bus clocks
1650+
#define INTERRUPT_CYCLE_OVERHEAD 16000 // number of cpu clocks necessary to respond to interrupt
16081651
i2c_err_t i2cSetFrequency(i2c_t * i2c, uint32_t clk_speed)
16091652
{
16101653
if(i2c == NULL) {
@@ -1614,17 +1657,18 @@ i2c_err_t i2cSetFrequency(i2c_t * i2c, uint32_t clk_speed)
16141657
uint32_t period = (apb/clk_speed) / 2;
16151658

16161659
if((apb/8192 > clk_speed)||(apb/MIN_I2C_CLKS < clk_speed)){ //out of bounds
1617-
log_w("i2c freq(%d) out of bounds.vs APB Clock(%d), min=%d, max=%d",clk_speed,apb,(apb/8192),(apb/MIN_I2C_CLKS));
1660+
log_d("i2c freq(%d) out of bounds.vs APB Clock(%d), min=%d, max=%d",clk_speed,apb,(apb/8192),(apb/MIN_I2C_CLKS));
16181661
}
16191662
if(period < (MIN_I2C_CLKS/2) ){
16201663
period = (MIN_I2C_CLKS/2);
16211664
clk_speed = apb/(period*2);
1622-
log_w("APB Freq too slow, Reducing i2c Freq to %d Hz",clk_speed);
1665+
log_d("APB Freq too slow, Reducing i2c Freq to %d Hz",clk_speed);
16231666
} else if ( period> 4095) {
16241667
period = 4095;
16251668
clk_speed = apb/(period*2);
1626-
log_w("APB Freq too fast, Increasing i2c Freq to %d Hz",clk_speed);
1669+
log_d("APB Freq too fast, Increasing i2c Freq to %d Hz",clk_speed);
16271670
}
1671+
log_v("freq=%dHz",clk_speed);
16281672

16291673
uint32_t halfPeriod = period/2;
16301674
uint32_t quarterPeriod = period/4;
@@ -1633,14 +1677,19 @@ i2c_err_t i2cSetFrequency(i2c_t * i2c, uint32_t clk_speed)
16331677

16341678
I2C_FIFO_CONF_t f;
16351679

1636-
// Adjust Fifo thresholds based on frequency
16371680
f.val = i2c->dev->fifo_conf.val;
1638-
uint32_t a = (clk_speed / 50000L )+1;
1639-
if (a > 24) a=24;
1640-
f.rx_fifo_full_thrhd = 32 - a;
1641-
f.tx_fifo_empty_thrhd = a;
1681+
/* Adjust Fifo thresholds based on differential between cpu frequency and bus clock.
1682+
The fifo_delta is calculated such that at least INTERRUPT_CYCLE_OVERHEAD cpu clocks are
1683+
available when a Fifo interrupt is triggered. This allows enough room in the Fifo so that
1684+
interrupt latency does not cause a Fifo overflow/underflow event.
1685+
*/
1686+
log_v("cpu Freq=%dMhz, i2c Freq=%dHz",getCpuFrequencyMhz(),clk_speed);
1687+
uint32_t fifo_delta = (INTERRUPT_CYCLE_OVERHEAD/((getCpuFrequencyMhz()*1000000 / clk_speed)*10))+1;
1688+
if (fifo_delta > 24) fifo_delta=24;
1689+
f.rx_fifo_full_thrhd = 32 - fifo_delta;
1690+
f.tx_fifo_empty_thrhd = fifo_delta;
16421691
i2c->dev->fifo_conf.val = f.val; // set thresholds
1643-
log_v("Fifo threshold=%d",a);
1692+
log_v("Fifo delta=%d",fifo_delta);
16441693

16451694
//the clock num during SCL is low level
16461695
i2c->dev->scl_low_period.period = period;
@@ -1696,6 +1745,8 @@ uint32_t i2cGetStatus(i2c_t * i2c){
16961745
}
16971746
else return 0;
16981747
}
1748+
1749+
16991750
/* todo
17001751
22JUL18
17011752
need to add multi-thread capability, use dq.queueEvent as the group marker. When multiple threads

Diff for: libraries/Wire/src/Wire.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
#include "freertos/queue.h"
3131
#include "Stream.h"
3232

33-
#define STICKBREAKER V1.0.1
33+
#define STICKBREAKER 'V1.1.0'
3434
#define I2C_BUFFER_LENGTH 128
3535
typedef void(*user_onRequest)(void);
3636
typedef void(*user_onReceive)(uint8_t*, int);
@@ -138,6 +138,7 @@ extern TwoWire Wire1;
138138

139139

140140
/*
141+
V1.1.0 08JAN2019 Support CPU Clock frequency changes
141142
V1.0.2 30NOV2018 stop returning I2C_ERROR_CONTINUE on ReSTART operations, regain compatibility with Arduino libs
142143
V1.0.1 02AUG2018 First Fix after release, Correct ReSTART handling, change Debug control, change begin()
143144
to a function, this allow reporting if bus cannot be initialized, Wire.begin() can be used to recover

0 commit comments

Comments
 (0)