Skip to content

Commit 9781382

Browse files
author
Chuck Todd
committed
Bus Timeout, Documentation, Code Ordering
Add I2C_ERROR_BUS_BUSY, improve documentation, Setting the bus speed below 10kHZ caused a Div0 error, Changed timeout calcuation to handle extremely slow speeds. Reorderd coding to remove Compiler warnings (@dstoiko)
1 parent b28fc5f commit 9781382

File tree

6 files changed

+194
-29
lines changed

6 files changed

+194
-29
lines changed

cores/esp32/esp32-hal-i2c.c

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -636,9 +636,14 @@ static void IRAM_ATTR fillTxFifo(i2c_t * i2c){
636636
/*11/15/2017 will assume that I cannot queue tx after a READ until READ completes
637637
11/23/2017 Seems to be a TX fifo problem, the SM sends 0x40 for last rxbyte, I
638638
enable txEmpty, filltx fires, but the SM has already sent a bogus byte out the BUS.
639-
I am going so see if I can overlap Tx/Rx/Tx in the fifo
639+
I am going so see if I can overlap Tx/Rx/Tx in the fifo
640+
12/01/2017 The Fifo's are independent, 32 bytes of tx and 32 bytes of Rx.
641+
overlap is not an issue, just keep them full/empty the status_reg.xx_fifo_cnt
642+
tells the truth. And the INT's fire correctly
640643
*/
641-
bool readEncountered = false;
644+
bool readEncountered = false; // 12/01/2017 this needs to be removed
645+
// it is nolonger necessary, the fifo's are independent. Run thru the dq's
646+
// until the cmd[] is full or the txFifo is full.
642647
uint16_t a=i2c->queuePos; // currently executing dq,
643648
bool full=!(i2c->dev->status_reg.tx_fifo_cnt<31);
644649
uint8_t cnt;
@@ -795,11 +800,8 @@ if(xResult == pdPASS){
795800
}
796801

797802
static void IRAM_ATTR i2c_isr_handler_default(void* arg){
798-
//log_e("isr Entry=%p",arg);
799803
i2c_t* p_i2c = (i2c_t*) arg; // recover data
800804
uint32_t activeInt = p_i2c->dev->int_status.val&0x1FFF;
801-
//log_e("int=%x",activeInt);
802-
//dumpI2c(p_i2c);
803805

804806
portBASE_TYPE HPTaskAwoken = pdFALSE,xResult;
805807

@@ -886,12 +888,11 @@ while (activeInt != 0) { // Ordering of 'if(activeInt)' statements is important,
886888
return;
887889
}
888890

889-
if (activeInt & I2C_TIME_OUT_INT_ST_M) {//fatal death Happens Here
890-
// let Gross timeout occure
891+
if (activeInt & I2C_TIME_OUT_INT_ST_M) {
892+
// let Gross timeout occur, Slave may release SCL before the configured timeout expires
893+
// the Statemachine only has a 13.1ms max timout, some Devices >500ms
891894
p_i2c->dev->int_clr.time_out =1;
892895
activeInt &=~I2C_TIME_OUT_INT_ST;
893-
// i2cIsrExit(p_i2c,EVENT_ERROR_TIMEOUT,true);
894-
// return;
895896
}
896897

897898
if (activeInt & I2C_TRANS_COMPLETE_INT_ST_M) {
@@ -973,7 +974,9 @@ i2c->stage = I2C_DONE; // until ready
973974
memset(intBuff,0,sizeof(intBuff));
974975
intPos=0;
975976
#endif
976-
977+
// EventGroup is used to signal transmisison completion from ISR
978+
// not always reliable. Sometimes, the FreeRTOS scheduler is maxed out and refuses request
979+
// if that happens, this call hangs until the timeout period expires, then it continues.
977980
if(!i2c->i2c_event){
978981
i2c->i2c_event = xEventGroupCreate();
979982
}
@@ -1046,7 +1049,7 @@ i2c->stage = I2C_STARTUP; // everything configured, now start the I2C StateMachi
10461049
i2c->dev->int_ena.val =
10471050
I2C_ACK_ERR_INT_ENA | // (BIT(10)) Causes Fatal Error Exit
10481051
I2C_TRANS_START_INT_ENA | // (BIT(9)) Triggered by trans_start=1, initial,END
1049-
I2C_TIME_OUT_INT_ENA | //(BIT(8)) causes Fatal error Exit
1052+
I2C_TIME_OUT_INT_ENA | //(BIT(8)) Trigger by SLAVE SCL stretching, NOT an ERROR
10501053
I2C_TRANS_COMPLETE_INT_ENA | // (BIT(7)) triggered by STOP, successful exit
10511054
I2C_MASTER_TRAN_COMP_INT_ENA | // (BIT(6)) counts each byte xfer'd, inc's queuePos
10521055
I2C_ARBITRATION_LOST_INT_ENA | // (BIT(5)) cause fatal error exit
@@ -1071,7 +1074,7 @@ if(!i2c->intr_handle){ // create ISR I2C_0 only,
10711074
// how many ticks should it take to transfer totalBytes thru the I2C hardware,
10721075
// add user supplied timeOutMillis to Calc Value
10731076

1074-
portTickType ticksTimeOut = ((totalBytes /(i2cGetFrequency(i2c)/(10*1000)))+timeOutMillis)/portTICK_PERIOD_MS;
1077+
portTickType ticksTimeOut = ((totalBytes*10*1000)/(i2cGetFrequency(i2c))+timeOutMillis)/portTICK_PERIOD_MS;
10751078
portTickType tBefore=xTaskGetTickCount();
10761079

10771080
//log_e("before startup @tick=%d will wait=%d",xTaskGetTickCount(),ticksTimeOut);
@@ -1094,11 +1097,24 @@ if(i2c->exitCode!=eBits){ // try to recover from O/S failure
10941097
}
10951098

10961099
if(!(eBits==EVENT_DONE)&&(eBits&~(EVENT_ERROR_NAK|EVENT_ERROR_DATA_NAK|EVENT_ERROR|EVENT_DONE))){ // not only Done, therefore error, exclude ADDR NAK, DATA_NAK
1100+
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR
10971101
i2cDumpI2c(i2c);
10981102
i2cDumpInts();
1103+
#else
1104+
log_n("I2C exitCode=%u",eBits);
1105+
#endif
10991106
}
11001107

11011108
if(eBits&EVENT_DONE){ // no gross timeout
1109+
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR
1110+
uint32_t expected =(totalBytes*10*1000)/i2cGetFrequency(i2c);
1111+
if((tAfter-tBefore)>(expected+1)) { //used some of the timeout Period
1112+
// expected can be zero due to small packets
1113+
log_e("TimeoutRecovery: expected=%ums, actual=%ums",expected,(tAfter-tBefore));
1114+
i2cDumpI2c(i2c);
1115+
i2cDumpInts();
1116+
}
1117+
#endif
11021118
switch(i2c->error){
11031119
case I2C_OK :
11041120
reason = I2C_ERROR_OK;
@@ -1126,11 +1142,25 @@ else { // GROSS timeout, shutdown ISR , report Timeout
11261142
i2c->stage = I2C_DONE;
11271143
i2c->dev->int_ena.val =0;
11281144
i2c->dev->int_clr.val = 0x1FFF;
1129-
reason = I2C_ERROR_TIMEOUT;
1130-
eBits = eBits | EVENT_ERROR_TIMEOUT|EVENT_ERROR|EVENT_DONE;
1131-
log_e(" Gross Timeout Dead st=0x%x, ed=0x%x, =%d, max=%d error=%d",tBefore,tAfter,(tAfter-tBefore),ticksTimeOut,i2c->error);
1145+
if((i2c->queuePos==0)&&(i2c->byteCnt==0)){ // Bus Busy no bytes Moved
1146+
reason = I2C_ERROR_BUSY;
1147+
eBits = eBits | EVENT_ERROR_BUS_BUSY|EVENT_ERROR|EVENT_DONE;
1148+
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR
1149+
log_e(" Busy Timeout start=0x%x, end=0x%x, =%d, max=%d error=%d",tBefore,tAfter,(tAfter-tBefore),ticksTimeOut,i2c->error);
11321150
i2cDumpI2c(i2c);
11331151
i2cDumpInts();
1152+
#endif
1153+
}
1154+
else { // just a timeout, some data made it out or in.
1155+
reason = I2C_ERROR_TIMEOUT;
1156+
eBits = eBits | EVENT_ERROR_TIMEOUT|EVENT_ERROR|EVENT_DONE;
1157+
1158+
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR
1159+
log_e(" Gross Timeout Dead start=0x%x, end=0x%x, =%d, max=%d error=%d",tBefore,tAfter,(tAfter-tBefore),ticksTimeOut,i2c->error);
1160+
i2cDumpI2c(i2c);
1161+
i2cDumpInts();
1162+
#endif
1163+
}
11341164
}
11351165

11361166
// offloading all EventGroups to dispatch, EventGroups in ISR is not always successful

cores/esp32/esp32-hal-i2c.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ typedef enum {
104104
// on Exit. Dispatcher will set bits for each dq before/after ISR completion
105105
#define EVENT_ERROR_NAK (BIT(0))
106106
#define EVENT_ERROR (BIT(1))
107+
#define EVENT_ERROR_BUS_BUSY (BIT(2))
107108
#define EVENT_RUNNING (BIT(3))
108109
#define EVENT_DONE (BIT(4))
109110
#define EVENT_IN_END (BIT(5))

libraries/Wire/docs/README.md

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ Wire.write(highByte(addr));
1414
Wire.write(lowByte(addr));
1515
uint8_t err =Wire.endTransmission(false); // don't send a STOP, just Pause I2C operations
1616
if(err==0){ // successfully set internal address pointer
17-
err=Wire.requestFrom(addr,len);
18-
if(err==0){ // read failed
17+
uint8_t count=Wire.requestFrom(addr,len);
18+
if(count==0){ // read failed
1919
Serial.print("Bad Stuff!! Read Failed\n");
2020
}
2121
else {// successful read
@@ -59,10 +59,10 @@ if(err == 7){ // Prior Operation has been queued
5959
// returned 0. So, if this if() was if(err==0), the Queue operation would be
6060
// considered an error. This is the primary Difference.
6161
62-
err=Wire.requestFrom(addr,len);
62+
uint8_t count=Wire.requestFrom(addr,len);
6363
if(Wire.lastError()!=0){ // complete/partial read failure
6464
Serial.printf("Bad Stuff!!\nRead of (%d) bytes read %d bytes\nFailed"
65-
" lastError=%d, text=%s\n", len, err, Wire.lastError(),
65+
" lastError=%d, text=%s\n", len, count, Wire.lastError(),
6666
Wire.getErrorText(Wire.lastError()));
6767
}
6868
// some of the read may have executed
@@ -82,7 +82,11 @@ Additionally this implementation of `Wire()` includes methods to handle local bu
8282
size_t transact(size_t readLen); // replacement for endTransmission(false),requestFrom(ID,readLen,true);
8383
size_t transact(uint8_t* readBuff, size_t readLen);// bigger Block read
8484
i2c_err_t lastError(); // Expose complete error
85+
char * getErrorText(uint8_t err); // return char pointer for text of err
86+
void dumpI2C(); // diagnostic dump of I2C control structure and buffers
8587
void dumpInts(); // diagnostic dump for the last 64 different i2c Interrupts
88+
void dumpOn(); // Execute dumpI2C() and dumpInts() after every I2C procQueue()
89+
void dumpOff(); // turn off dumpOn()
8690
size_t getClock(); // current i2c Clock rate
8791
void setTimeOut(uint16_t timeOutMillis); // allows users to configure Gross Timeout
8892
uint16_t getTimeOut();
@@ -95,7 +99,7 @@ Wire.beginTransmission(ID);
9599
Wire.write(highByte(addr));
96100
Wire.write(lowByte(addr));
97101
98-
uint8_t err=Wire.transact(len); // transact() does both Wire.endTransmission(false); and Wire.requestFrom(ID,len,true);
102+
uint8_t count=Wire.transact(len); // transact() does both Wire.endTransmission(false); and Wire.requestFrom(ID,len,true);
99103
if(Wire.lastError != 0){ // complete/partial read failure
100104
Serial.printf("Bad Stuff!! Read Failed lastError=%d\n",Wire.lastError());
101105
}
@@ -107,10 +111,20 @@ Serial.println();
107111
108112
```
109113

114+
### TIMEOUT's
115+
The ESP32 I2C hardware, what I call the StateMachine(SM) is not documented very well, most of the the corner conditions and errata has been discovered throught trial and error. TimeOuts have been the bain of our existance.
116+
117+
Most were caused by incorrect coding of ReSTART operations, but, a Valid TimeOut operation was discovered by @lonerzzz. He was using a temperature/humidity sensor that uses SCL clock stretching while it does a sample conversion. The issue we discovered was that the SM does not continue after the timeout. It treats the timeout as a failure. The SM's hardware timeout maxes out at 13.1ms, @lonerzzz sensor a (HTU21D), uses SCL stretching while it takes a measurement. These SCL stretching events can last for over 120ms. The SM will issue a TIMEOUT IRQ every 13.1ms while SCL is held low. After SCL is release the SM immediately terminates the current command queue by issuing a STOP. It does NOT continue with the READ command(a protcol violation). In @lonerzzz's case the sensor acknowledges its I2C ID and the READ command, then it starts the sample conversion while holding SCL Low. After it completes the conversion, the SCL signal is release. When the SM sees SCL go HIGH, it initates an Abort by immediately issuing a STOP signal. So, the Sample was taken but never read.
118+
119+
The current library signals this occurence by returning I2C_ERROR_OK and a dataLength of 0(zero) back through the `Wire.requestFrom()` call.
120+
121+
### Alpha
122+
110123
This **APLHA** release should be compiled with ESP32 Dev Module as its target, and
111124
Set the "core debug level" to 'error'
112125

113126
There is MINIMAL to NO ERROR detection, BUS, BUSY. because I have not encounter any of them!
114127

115128

116129
Chuck.
130+

libraries/Wire/examples/eeprom_size/eeprom_size.ino

Lines changed: 126 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,38 @@
11
#include <Wire.h>
22
// Connect 4.7k pullups on SDA, SCL
33
// for ESP32 SDA(pin 21), SCL(pin 22)
4-
// for AtMega328p SDA(Pin A5), SCL(pin A4)
5-
// for Mega2560 SDA(Pin 20), SCL(pin 21)
64

75
/* This sketch uses the address rollover of the 24LCxx EEPROMS to detect their size.
8-
The 24LC32 (4kByte) 0x0000 .. 0x0FFF, a Write to addres 0x1000 will actually
6+
This detection sequence will not work with small capacity EEPROMs 24LC01 .. 24LC16.
7+
These small EEPROMS use single byte addressing, To access more than 256 bytes,
8+
the lc04, lc08, and lc16 EEPROMs respond as if they were multiple lc02's with
9+
consective I2CDevice addresses.
10+
device I2Caddr Address Range
11+
24LC01 0x50 0x00 .. 0x7F
12+
24LC02 0x50 0x00 .. 0xFF
13+
24LC04 0x50 0x00 .. 0xFF
14+
0x51 0x00 .. 0xFF
15+
24LC08 0x50 0x00 .. 0xFF
16+
0x51 0x00 .. 0xFF
17+
0x52 0x00 .. 0xFF
18+
0x53 0x00 .. 0xFF
19+
24LC16 0x50 0x00 .. 0xFF
20+
0x51 0x00 .. 0xFF
21+
0x52 0x00 .. 0xFF
22+
0x53 0x00 .. 0xFF
23+
0x54 0x00 .. 0xFF
24+
0x55 0x00 .. 0xFF
25+
0x56 0x00 .. 0xFF
26+
0x57 0x00 .. 0xFF
27+
The 24LC32 with selectable I2C address 0x50..0x57 are 4kByte devices with an internal
28+
address range of 0x0000 .. 0x0FFF. A Write to address 0x1000 will actually
929
be stored in 0x0000. This allows us to read the value of 0x0000, compare
1030
it to the value read from 0x1000, if they are different, then this IC is
1131
not a 24LC32.
1232
If the Value is the same, then we have to change the byte at 0x1000 and
1333
see if the change is reflected in 0x0000. If 0x0000 changes, then we know
1434
that the chip is a 24LC32. We have to restore the 'changed' value so that
15-
the data in the EEPROM is not compromized.
35+
the data in the EEPROM is not compromised.
1636
1737
This pattern of read, compare, test, restore is used for each possible size.
1838
All that changes is the test Address, 0x1000, 0x2000, 0x4000, 0x8000.
@@ -27,7 +47,8 @@
2747
bool i2cReady(uint8_t ID){
2848
uint32_t timeout=millis();
2949
bool ready=false;
30-
while((millis()-timeout<100)&&(!ready)){
50+
while((millis()-timeout<10)&&(!ready)){ // try to evoke a response from the device.
51+
// If the it does not respond within 10ms return as a failure.
3152
Wire.beginTransmission(ID);
3253
int err=Wire.endTransmission();
3354
ready=(err==0);
@@ -38,6 +59,103 @@ while((millis()-timeout<100)&&(!ready)){
3859
return ready;
3960
}
4061

62+
void dispBuff(uint8_t *buf, uint16_t len,uint16_t offset){
63+
char asciibuf[100];
64+
uint8_t bufPos=0;
65+
uint16_t adr=0;
66+
asciibuf[0] ='\0';
67+
while(adr<len){
68+
if(((offset+adr)&0x1F)==0){
69+
if(asciibuf[0]) Serial.printf(" %s\n",asciibuf);
70+
Serial.printf("0x%04x:",(uint16_t)(offset+adr));
71+
bufPos=0;
72+
}
73+
Serial.printf(" %02x",buf[adr]);
74+
char ch=buf[adr];
75+
if((ch<32)||(ch>127)) ch ='.';
76+
bufPos+=sprintf(&asciibuf[bufPos],"%c",ch);
77+
adr++;
78+
}
79+
80+
while(bufPos<32){
81+
Serial.print(" ");
82+
bufPos++;
83+
}
84+
Serial.printf(" %s\n",asciibuf);
85+
}
86+
87+
/* detectWritePageSize() attempts to write a 256 byte block to an I2C EEPROM.
88+
This large block will use the side effect of the WritePage to detect it's size.
89+
EEPROM's have to erase a 'page' of data in their memory cell array before they
90+
can change it. To facilitate partial page writes they contain a temporary 'WritePage'
91+
that is used store the contents of the memory cells while their page is erase.
92+
When data is written to the device it is merged into this temporary buffer. If the
93+
amount of data written is longer than the temporary buffer it rolls over to the beginning
94+
of the temporary buffer. In a 24lc32, the standard WritePage is 32 bytes, if 256
95+
bytes of data is written to the device, only the last 32 bytes are stored.
96+
This side effect allow easy detect of the WritePage size.
97+
*/
98+
99+
uint16_t detectWritePageSize(uint16_t i2cID,bool verbose=false){
100+
if(verbose) Serial.printf("detecting WritePage Size for (0x%02x)\n",i2cID);
101+
uint16_t adr = 0,ps=0;
102+
randomSeed(micros());
103+
adr = random(256)*256; //address is selected at random for wear leveling purposes.
104+
uint8_t *buf =(uint8_t*)malloc(256); //restore buffer
105+
uint8_t *buf1 =(uint8_t*)malloc(258); // write buffer
106+
i2cReady(i2cID); // device may completing a Write Cycle, wait if necessary
107+
Wire.beginTransmission(i2cID);
108+
Wire.write(highByte(adr));
109+
Wire.write(lowByte(adr));
110+
uint16_t count = Wire.transact(buf,256);//save current EEPROM content for Restore
111+
if(Wire.lastError()==0){ // successful read, now we can try the test
112+
for(uint16_t a=0;a<256;a++){ //initialize a detectable pattern in the writeBuffer
113+
buf1[a+2]=a; // leave room for the the address
114+
}
115+
buf1[0] = highByte(adr);
116+
buf1[1] = lowByte(adr);
117+
Wire.writeTransmission(i2cID,buf1,258);
118+
if(Wire.lastError()==0){ // wait for the write to complete
119+
if(i2cReady(i2cID)){
120+
Wire.beginTransmission(i2cID);
121+
Wire.write(highByte(adr));
122+
Wire.write(lowByte(adr));
123+
Wire.transact(&buf1[2],256);
124+
if(Wire.lastError()==0){
125+
ps = 256-buf1[2]; // if the read succeeded, the byte read from offset 0x0
126+
// can be used to calculate the WritePage size. On a 24lc32 with a 32byte
127+
// WritePage it will contain 224, therefore 256-224 = 32 byte Writepage.
128+
if(verbose){
129+
Serial.printf("Origonal data\n",i2cID);
130+
dispBuff(buf,256,adr);
131+
Serial.printf("\n OverWritten with\n");
132+
dispBuff(&buf1[2],256,adr);
133+
Serial.printf("Calculated Write Page is %d\n",ps);
134+
}
135+
memmove(&buf1[2],buf,256); // copy the savebuffer back into
136+
if(i2cReady(i2cID)){
137+
Wire.writeTransmission(i2cID,buf1,ps+2); // two address bytes plus WritePage
138+
}
139+
if(Wire.lastError()!=0){
140+
Serial.printf("unable to Restore prom\n");
141+
if(i2cReady(i2cID)){
142+
Wire.beginTransmission(i2cID);
143+
Wire.write(highByte(adr));
144+
Wire.write(lowByte(adr));
145+
Wire.transact(&buf1[2],256);
146+
Serial.printf("\n Restored to\n");
147+
dispBuff(&buf1[2],256,adr);
148+
}
149+
}
150+
}
151+
}
152+
}
153+
}
154+
free(buf1);
155+
free(buf);
156+
return ps;
157+
}
158+
41159
/* eepromSize() only works on 24LC32 .. 24LC512 eeprom,
42160
the smaller 24LC01 .. 24LC16 use one byte addressings.
43161
*/
@@ -127,7 +245,8 @@ while(ID<0x58){
127245
}
128246
}while((testByte==zeroByte)&&(size>0));
129247
if(size==0) i += sprintf_P(&buf[i],PSTR("64k Bytes"));
130-
else i+=sprintf_P(&buf[i],PSTR("%dk Bytes"),size/1024);
248+
else i+=sprintf_P(&buf[i],PSTR("%2uk Bytes"),size/1024);
249+
i+=sprintf_P(&buf[i],PSTR(" WritePage=%3u"),detectWritePageSize(ID,false));
131250
if(!i2cReady(ID)){
132251
i+=sprintf_P(&buf[i],PSTR(" notReady3.\n"));
133252
Serial.print(buf);
@@ -139,7 +258,7 @@ while(ID<0x58){
139258
Wire.write((uint8_t)0); // set address ptr to 0, two bytes Low
140259
err=Wire.endTransmission();
141260
if(err==0){
142-
err= Wire.requestFrom(ID,1);
261+
err= Wire.requestFrom(ID,(uint8_t)1);
143262
if (err==1) {
144263
testByte = Wire.read();
145264
if(testByte != zeroByte){ //fix it

libraries/Wire/src/Wire.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,12 @@ TwoWire::TwoWire(uint8_t bus_num)
3939
,i2c(NULL)
4040
,rxIndex(0)
4141
,rxLength(0)
42+
,rxQueued(0)
4243
,txIndex(0)
4344
,txLength(0)
4445
,txAddress(0)
45-
,transmitting(0)
4646
,txQueued(0)
47-
,rxQueued(0)
47+
,transmitting(0)
4848
,_timeOutMillis(50)
4949
,last_error(I2C_ERROR_OK)
5050
,_dump(false)

0 commit comments

Comments
 (0)