From a0db0ccce79922c35529bac54fc503476049b624 Mon Sep 17 00:00:00 2001 From: chuck todd Date: Thu, 15 Mar 2018 16:41:30 -0600 Subject: [PATCH 01/14] v0.2.0 --- libraries/Wire/docs/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libraries/Wire/docs/README.md b/libraries/Wire/docs/README.md index 1633c17ee1f..ec490dad2ab 100644 --- a/libraries/Wire/docs/README.md +++ b/libraries/Wire/docs/README.md @@ -100,7 +100,7 @@ Wire.write(highByte(addr)); Wire.write(lowByte(addr)); uint8_t count=Wire.transact(len); // transact() does both Wire.endTransmission(false); and Wire.requestFrom(ID,len,true); -if(Wire.lastError != 0){ // complete/partial read failure +if(Wire.lastError() != 0){ // complete/partial read failure Serial.printf("Bad Stuff!! Read Failed lastError=%d\n",Wire.lastError()); } // some of the read may have executed @@ -118,13 +118,13 @@ Most were caused by incorrect coding of ReSTART operations, but, a Valid TimeOut The current library signals this occurence by returning I2C_ERROR_OK and a dataLength of 0(zero) back through the `Wire.requestFrom()` call. -### Alpha +### Beta -This **APLHA** release should be compiled with ESP32 Dev Module as its target, and -Set the "core debug level" to 'error' +This **BETA** release can be compiled with ESP32 Dev Module as its target. Selecting this board allow Core Debug Level to be selected. Setting the "core debug level" to 'error' will route verbose debug out Serial (uart0) when an i2c error occurs. -There is MINIMAL to NO ERROR detection, BUS, BUSY. because I have not encounter any of them! +This version V0.2.0 14MAR2018 includes code to handle `BUS_BUSY` conditions. This status is usually indicative of a hardware bus glitch. The most common way for a `BUS_BUSY` condition to be created, is when the i2c peripheral has detected a low going spike on SDA and intrepreted it as another i2c MASTER device acquiring the bus. It will wait FORE EVER for the other 'Master' to complete it's transaction. Since this was a temporary signal glitch, not a secondary Master preforming operations, the only way to clear the `BUS_BUSY` condition is to reset the i2c peripheral. So, when a `BUS_BUSY` conditions is detected, a hardware reset is performed. +This works great, as long as, there is not ACTUALLY another Master on the bus. If this Library is used in a Multi-Master i2c configuration, it will FAIL with continuous `ARBITRATION` failures, `BUS_BUSY` errors. Chuck. - \ No newline at end of file + From c571e1b715830909c585ee10c84eefd94a67f8fb Mon Sep 17 00:00:00 2001 From: chuck todd Date: Thu, 15 Mar 2018 20:02:02 -0600 Subject: [PATCH 02/14] Create Destructor for Wire Class. This destructor allows reassigning either hardware peripheral to the default Wire() objects. sequence: Wire.~TwoWire(); Wire = TwoWire(peripherial); // peripheral is either 0 or 1 Wire.begin(sda,scl); // sda, scl are the pins to use, when using peripheral 1, YOU MUST specifiy the pins, there ARE NO DEFAULT VALUES for peripheral 1. --- libraries/Wire/src/Wire.cpp | 26 ++++++++++++++++---------- libraries/Wire/src/Wire.h | 9 ++++++--- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp index c2faa2d5c3d..3d714fc1bc2 100644 --- a/libraries/Wire/src/Wire.cpp +++ b/libraries/Wire/src/Wire.cpp @@ -50,6 +50,16 @@ TwoWire::TwoWire(uint8_t bus_num) ,_dump(false) {} +TwoWire::~TwoWire(){ +flush(); +i2cDetachSCL(i2c,scl); // detach pins before resetting I2C perpherial +i2cDetachSDA(i2c,sda); // else a glitch will appear on the i2c bus +if(i2c){ + i2cReleaseAll(i2c); + i2c=NULL; + } +} + void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency) { if(sdaPin < 0) { @@ -102,6 +112,9 @@ bool TwoWire::initHardware(int sdaPin, int sclPin, uint32_t frequency){ sda = sdaPin; scl = sclPin; + +// 03/15/2018 What about MultiMaster? How can I be polite and still catch glitches? + // 03/10/2018 test I2C bus before attach. // if the bus is not 'clear' try the recommended recovery sequence, START, 9 Clocks, STOP digitalWrite(sda,HIGH); @@ -110,7 +123,7 @@ bool TwoWire::initHardware(int sdaPin, int sclPin, uint32_t frequency){ pinMode(scl,PULLUP|OPEN_DRAIN|OUTPUT|INPUT); if(!digitalRead(sda)||!digitalRead(scl)){ // bus in busy state - // Serial.printf("invalid state sda=%d, scl=%d\n",digitalRead(sda),digitalRead(scl)); + log_e("invalid state sda=%d, scl=%d\n",digitalRead(sda),digitalRead(scl)); digitalWrite(sda,HIGH); digitalWrite(scl,HIGH); delayMicroseconds(5); @@ -128,7 +141,7 @@ bool TwoWire::initHardware(int sdaPin, int sclPin, uint32_t frequency){ i2cAttachSCL(i2c, scl); if(!digitalRead(sda)||!digitalRead(scl)){ // bus in busy state -// Serial.println("Bus Invalid State, TwoWire() Can't init"); + log_e("Bus Invalid State, TwoWire() Can't init"); return false; // bus is busy } @@ -199,7 +212,7 @@ uint16_t TwoWire::requestFrom(uint16_t address, uint8_t * readBuff, uint16_t siz */ i2c_err_t TwoWire::writeTransmission(uint16_t address, uint8_t *buff, uint16_t size, bool sendStop){ // will destroy any partially created beginTransaction() - +log_i("i2c=%p",i2c); last_error=i2cAddQueueWrite(i2c,address,buff,size,sendStop,NULL); if(last_error==I2C_ERROR_OK){ //queued @@ -466,12 +479,5 @@ void TwoWire::flush(void) i2cFreeQueue(i2c); // cleanup } -void TwoWire::reset(void) -{ - i2cReleaseISR(i2c); // remove ISR from Interrupt chain,Delete EventGroup,Free Heap memory - i2cReset( i2c ); - i2c = NULL; - begin( sda, scl ); -} TwoWire Wire = TwoWire(0); diff --git a/libraries/Wire/src/Wire.h b/libraries/Wire/src/Wire.h index 04c53505b4e..78b909ab911 100644 --- a/libraries/Wire/src/Wire.h +++ b/libraries/Wire/src/Wire.h @@ -30,7 +30,7 @@ #include "freertos/queue.h" #include "Stream.h" -#define STICKBREAKER +#define STICKBREAKER V0.2.1 #define I2C_BUFFER_LENGTH 128 typedef void(*user_onRequest)(void); typedef void(*user_onReceive)(uint8_t*, int); @@ -69,6 +69,7 @@ class TwoWire: public Stream public: TwoWire(uint8_t bus_num); + ~TwoWire(); void begin(int sda=-1, int scl=-1, uint32_t frequency=100000); void setClock(uint32_t); void beginTransmission(uint16_t); @@ -113,8 +114,6 @@ class TwoWire: public Stream int peek(void); void flush(void); - void reset(void); - inline size_t write(const char * s) { return write((uint8_t*) s, strlen(s)); @@ -139,4 +138,8 @@ class TwoWire: public Stream extern TwoWire Wire; + +/* +V0.2.1 15MAR2018 Hardware reset, Glitch prevention, adding destructor for second i2c testing +*/ #endif From dd0ac4bd4f031d1c4c5be87375f755f8156c6562 Mon Sep 17 00:00:00 2001 From: chuck todd Date: Thu, 15 Mar 2018 20:05:41 -0600 Subject: [PATCH 03/14] Support for Wire Destructor These changes allow both i2c peripherals to be use, if a TwoWire() object is destroyed, the associated peripheral is reset and powered down. --- cores/esp32/esp32-hal-i2c.c | 67 ++++++++++++++++++++++++++++++------- cores/esp32/esp32-hal-i2c.h | 5 +-- 2 files changed, 57 insertions(+), 15 deletions(-) diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index 742194d2015..69c2dd2c76e 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -82,6 +82,8 @@ functional with Silicon date=0x16042000 */ static i2c_err_t i2cAddQueue(i2c_t * i2c,uint8_t mode, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen,bool sendStop, EventGroupHandle_t event){ + if(i2c==NULL) return I2C_ERROR_DEV; + I2C_DATA_QUEUE_t dqx; dqx.data = dataPtr; dqx.length = dataLen; @@ -100,6 +102,7 @@ if(event){// an eventGroup exist, so, initialize it } if(i2c->dq!=NULL){ // expand +//log_i("expand"); I2C_DATA_QUEUE_t* tq =(I2C_DATA_QUEUE_t*)realloc(i2c->dq,sizeof(I2C_DATA_QUEUE_t)*(i2c->queueCount +1)); if(tq!=NULL){// ok i2c->dq = tq; @@ -111,6 +114,7 @@ if(i2c->dq!=NULL){ // expand } } else { // first Time +//log_i("new"); i2c->queueCount=0; i2c->dq =(I2C_DATA_QUEUE_t*)malloc(sizeof(I2C_DATA_QUEUE_t)); if(i2c->dq!=NULL){ @@ -125,10 +129,12 @@ return I2C_ERROR_OK; } i2c_err_t i2cFreeQueue(i2c_t * i2c){ +if(i2c==NULL) return I2C_ERROR_DEV; // need to grab a MUTEX for exclusive Queue, // what out if ISR is running? i2c_err_t rc=I2C_ERROR_OK; if(i2c->dq!=NULL){ +// log_i("free"); // what about EventHandle? free(i2c->dq); i2c->dq = NULL; @@ -319,6 +325,9 @@ i2c_t * i2cInit(uint8_t i2c_num) //before this is called, pins should be detache } #endif I2C_MUTEX_LOCK(); + + i2cReleaseISR(i2c); // ISR exists, release it before disabling hardware + uint32_t old_clock = i2cGetFrequency(i2c); if(i2c_num == 0) { @@ -348,7 +357,7 @@ i2c_t * i2cInit(uint8_t i2c_num) //before this is called, pins should be detache return i2c; } - +/* unused 03/15/2018 void i2cInitFix(i2c_t * i2c){ if(i2c == NULL){ return; @@ -370,7 +379,8 @@ void i2cInitFix(i2c_t * i2c){ while ((!i2c->dev->command[2].done) && (--count > 0)); I2C_MUTEX_UNLOCK(); } - +/* + unused 03/15/2018 void i2cReset(i2c_t* i2c){ if(i2c == NULL){ return; @@ -382,6 +392,7 @@ void i2cReset(i2c_t* i2c){ periph_module_enable( moduleId ); I2C_MUTEX_UNLOCK(); } +*/ /* Stickbreaker ISR mode debug support */ @@ -615,7 +626,6 @@ log_n("Enable Core Debug Level \"Error\""); #endif } - void i2cDumpI2c(i2c_t * i2c){ log_e("i2c=%p",i2c); log_e("dev=%p date=%p",i2c->dev,i2c->dev->date); @@ -960,7 +970,6 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis) install ISR if necessary setup EventGroup handle bus busy? - do I load command[] or just pass that off to the ISR */ //log_e("procQueue i2c=%p",&i2c); *readCount = 0; //total reads accomplished in all queue elements @@ -968,11 +977,24 @@ if(i2c == NULL){ return I2C_ERROR_DEV; } if (i2c->dev->status_reg.bus_busy){ // return error, let TwoWire() handle resetting the hardware. +/* if multi master then this if should be changed to this 03/12/2018 + if(multiMaster){// try to let the bus clear by its self + uint32_t timeOutTick = millis(); + while((i2c->dev->status_reg.bus_busy)&&(millis()-timeOutTickdev->status_reg.bus_busy){ // still busy, so die + log_i("Bus busy, reinit"); + return I2C_ERROR_BUSY; + } +*/ log_i("Bus busy, reinit"); return I2C_ERROR_BUSY; } + I2C_MUTEX_LOCK(); -/* what about co-existance with SLAVE mode? +/* what about co-existence with SLAVE mode? Should I check if a slaveMode xfer is in progress and hang until it completes? if i2c->stage == I2C_RUNNING or I2C_SLAVE_ACTIVE @@ -987,7 +1009,7 @@ for(uint16_t i=0;inum] = 0; #endif -// EventGroup is used to signal transmisison completion from ISR +// EventGroup is used to signal transmission completion from ISR // not always reliable. Sometimes, the FreeRTOS scheduler is maxed out and refuses request // if that happens, this call hangs until the timeout period expires, then it continues. if(!i2c->i2c_event){ @@ -995,8 +1017,6 @@ if(!i2c->i2c_event){ } if(i2c->i2c_event) { uint32_t ret=xEventGroupClearBits(i2c->i2c_event, 0xFF); - -// log_e("after clearBits(%p)=%p",i2c->i2c_event,ret); } else {// failed to create EventGroup log_e("eventCreate failed=%p",i2c->i2c_event); @@ -1032,7 +1052,7 @@ i2c->queuePos=0; i2c->byteCnt=0; uint32_t totalBytes=0; // total number of bytes to be Moved! // convert address field to required I2C format -while(i2c->queuePos < i2c->queueCount){ +while(i2c->queuePos < i2c->queueCount){ // need to push these address modes upstream, to AddQueue I2C_DATA_QUEUE_t *tdq = &i2c->dq[i2c->queuePos++]; uint16_t taddr=0; if(tdq->ctrl.addrReq ==2){ // 10bit address @@ -1073,7 +1093,7 @@ i2c->dev->int_ena.val = I2C_RXFIFO_FULL_INT_ENA; // (BIT(0)) trigger emptyRxFifo() if(!i2c->intr_handle){ // create ISR for either peripheral - log_i("create ISR"); + // log_i("create ISR %d",i2c->num); uint32_t ret; switch(i2c->num){ case 0: @@ -1218,19 +1238,40 @@ I2C_MUTEX_UNLOCK(); return reason; } -i2c_err_t i2cReleaseISR(i2c_t * i2c){ +void i2cReleaseISR(i2c_t * i2c){ if(i2c->intr_handle){ +// log_i("Release ISR %d",i2c->num); esp_err_t error =esp_intr_free(i2c->intr_handle); // log_e("released ISR=%d",error); i2c->intr_handle=NULL; } +} + +void i2cReleaseAll(i2c_t *i2c){ // release all resources, power down peripheral +// gpio pins must be released BEFORE this function or a Glitch will appear + +I2C_MUTEX_LOCK(); + +i2cReleaseISR(i2c); + if(i2c->i2c_event){ vEventGroupDelete(i2c->i2c_event); i2c->i2c_event = NULL; } -return i2cFreeQueue(i2c); -} +i2cFreeQueue(i2c); + +// reset the I2C hardware and shut off the clock, power it down. +if(i2c->num == 0) { + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG,DPORT_I2C_EXT0_RST); //reset hardware + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG,DPORT_I2C_EXT0_CLK_EN); // shutdown hardware +} else { + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG,DPORT_I2C_EXT1_RST); //reset Hardware + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG,DPORT_I2C_EXT1_CLK_EN); // shutdown Hardware + } + +I2C_MUTEX_UNLOCK(); +} /* todo 24Nov17 Need to think about not usings I2C_MASTER_TRAN_COMP_INT_ST to adjust queuePos. This diff --git a/cores/esp32/esp32-hal-i2c.h b/cores/esp32/esp32-hal-i2c.h index 97230622a4b..453473e5844 100644 --- a/cores/esp32/esp32-hal-i2c.h +++ b/cores/esp32/esp32-hal-i2c.h @@ -148,11 +148,12 @@ typedef struct i2c_struct_t i2c_t; i2c_t * i2cInit(uint8_t i2c_num); +/* unused, 03/18/2018 fixed with V0.2.0 //call this after you setup the bus and pins to send empty packet //required because when pins are attached, they emit pulses that lock the bus void i2cInitFix(i2c_t * i2c); - void i2cReset(i2c_t* i2c); +*/ i2c_err_t i2cSetFrequency(i2c_t * i2c, uint32_t clk_speed); uint32_t i2cGetFrequency(i2c_t * i2c); @@ -167,7 +168,7 @@ i2c_err_t i2cProcQueue(i2c_t *i2c, uint32_t *readCount, uint16_t timeOutMillis); i2c_err_t i2cAddQueueWrite(i2c_t *i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen, bool SendStop, EventGroupHandle_t event); i2c_err_t i2cAddQueueRead(i2c_t *i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen, bool SendStop, EventGroupHandle_t event); i2c_err_t i2cFreeQueue(i2c_t *i2c); -i2c_err_t i2cReleaseISR(i2c_t *i2c); +void i2cReleaseAll(i2c_t *i2c); // free ISR, Free DQ, Power off peripheral clock. Must call i2cInit(),i2cSetFrequency() to recover //stickbreaker debug support void i2cDumpInts(uint8_t num); void i2cDumpI2c(i2c_t *i2c); From 5d2e3f4e300bc638f39a9205057ed794a9680837 Mon Sep 17 00:00:00 2001 From: chuck todd Date: Sun, 18 Mar 2018 14:11:52 -0600 Subject: [PATCH 04/14] Create README.md --- docs/stickbreaker/README.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/stickbreaker/README.md diff --git a/docs/stickbreaker/README.md b/docs/stickbreaker/README.md new file mode 100644 index 00000000000..ae31dd5cd0d --- /dev/null +++ b/docs/stickbreaker/README.md @@ -0,0 +1 @@ +# Stickbreaker Branch specific info. From ec01623e901f2a380254413d6c2f707253caa4d7 Mon Sep 17 00:00:00 2001 From: chuck todd Date: Sun, 18 Mar 2018 14:14:01 -0600 Subject: [PATCH 05/14] documentation support for Wiki --- docs/stickbreaker/ESP32DE_16.jpg | Bin 0 -> 18436 bytes docs/stickbreaker/ESP32DE_17.jpg | Bin 0 -> 18495 bytes docs/stickbreaker/ESP32DE_18.jpg | Bin 0 -> 18913 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/stickbreaker/ESP32DE_16.jpg create mode 100644 docs/stickbreaker/ESP32DE_17.jpg create mode 100644 docs/stickbreaker/ESP32DE_18.jpg diff --git a/docs/stickbreaker/ESP32DE_16.jpg b/docs/stickbreaker/ESP32DE_16.jpg new file mode 100644 index 0000000000000000000000000000000000000000..de609647183ca94ca94150dd0839dc87a7aeecc6 GIT binary patch literal 18436 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 Qc;%6p<4;^Nsd|6^*{lUDMgRZ+ literal 0 HcmV?d00001 diff --git a/docs/stickbreaker/ESP32DE_17.jpg b/docs/stickbreaker/ESP32DE_17.jpg new file mode 100644 index 0000000000000000000000000000000000000000..044b5e80f445952b349cca344463e127ee5daea0 GIT binary patch literal 18495 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 Date: Sun, 18 Mar 2018 14:27:18 -0600 Subject: [PATCH 06/14] Update README.md --- docs/stickbreaker/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/stickbreaker/README.md b/docs/stickbreaker/README.md index ae31dd5cd0d..fb2a0a915bf 100644 --- a/docs/stickbreaker/README.md +++ b/docs/stickbreaker/README.md @@ -1 +1,6 @@ # Stickbreaker Branch specific info. + +content: +* [ESP32DE_17.jpg](https://github.com/stickbreaker/arduino-esp32/blob/master/docs/stickbreaker/ESP32DE_17.jpg): Original Signal glitch on GPIO attach to peripheral +* [ESP32DE_16.jpg](https://github.com/stickbreaker/arduino-esp32/blob/master/docs/stickbreaker/ESP32DE_16.jpg): After changes suggested by @ESP32DE, over 50% reduction in glitch duration +* [ESP32DE_18.jpg](https://github.com/stickbreaker/arduino-esp32/blob/master/docs/stickbreaker/ESP32DE_18.jpg): Complete glitch prevention after additional experimentation by @stickbreaker From da6be5707c1fbba3ab9c519673e5dbe3d1270d45 Mon Sep 17 00:00:00 2001 From: chuck todd Date: Fri, 13 Apr 2018 17:34:46 -0600 Subject: [PATCH 07/14] Preserve Custom SDA,SCL and Frequency when calling Wire.begin() This change will not reset the SCL, SDA and Frequency values to their default value if `Wire.begin()` is call without any parameters. Arduino libraries that use `Wire()` usually have a call to `Wire.begin()` inside their initialization call. Without this change, any custom pin assignments are lost whenever `Wire.begin()` is called. --- libraries/Wire/src/Wire.cpp | 51 ++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp index 3d714fc1bc2..d504d977c7c 100644 --- a/libraries/Wire/src/Wire.cpp +++ b/libraries/Wire/src/Wire.cpp @@ -62,25 +62,37 @@ if(i2c){ void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency) { - if(sdaPin < 0) { - if(num == 0) { - sdaPin = SDA; - } else { - return; - } + if(sdaPin < 0) { // default param passed + if(num == 0) { + if(sda==-1) sdaPin = SDA; //use Default Pin + else sdaPin = sda; // reuse prior pin + } + else { + if(sda==-1) { + log_e("no Default SDA Pin for Second Peripheral"); + return; //no Default pin for Second Peripheral + } + else sdaPin = sda; // reuse prior pin + } } - if(sclPin < 0) { - if(num == 0) { - sclPin = SCL; - } else { - return; + if(sclPin < 0) { // default param passed + if(num == 0) { + if(scl==-1) sclPin = SCL; // use Default pin + else sclPin = scl; // reuse prior pin + } + else { + if(scl==-1){ + log_e("no Default SCL Pin for Second Peripheral"); + return; //no Default pin for Second Peripheral } + else sclPin = scl; // reuse prior pin + } } - - if(!initHardware(sdaPin, sclPin, frequency)) return; - flush(); + if(!initHardware(sdaPin, sclPin, frequency)) return; + + flush(); } @@ -97,8 +109,6 @@ void TwoWire::setClock(uint32_t frequency) i2cSetFrequency(i2c, frequency); } -/*@StickBreaker common handler for processing the queued commands -*/ bool TwoWire::initHardware(int sdaPin, int sclPin, uint32_t frequency){ i2cDetachSCL(i2c,scl); // detach pins before resetting I2C perpherial @@ -107,7 +117,12 @@ bool TwoWire::initHardware(int sdaPin, int sclPin, uint32_t frequency){ if(i2c == NULL) { return false; } - + + if(frequency==0) {// don't change existing frequency + frequency = i2cGetFrequency(i2c); + } + if(frequency==0) frequency = 100000L; // default to 100khz + i2cSetFrequency(i2c, frequency); sda = sdaPin; @@ -148,6 +163,8 @@ bool TwoWire::initHardware(int sdaPin, int sclPin, uint32_t frequency){ return true; } +/*@StickBreaker common handler for processing the queued commands +*/ i2c_err_t TwoWire::processQueue(uint32_t * readCount){ last_error=i2cProcQueue(i2c,readCount,_timeOutMillis); if(last_error==I2C_ERROR_BUSY){ // try to clear the bus From 9b1c895bfc32058b361cfa452a456d46bc9a549f Mon Sep 17 00:00:00 2001 From: chuck todd Date: Fri, 13 Apr 2018 17:49:12 -0600 Subject: [PATCH 08/14] Preserve custome SCL, SDA and Frequency when Wire.begin() called Changed how default parameters are interpreted. Now calling `Wire.begin()` without parameters will reuse the current values instead of resetting them to the default values. This change was prompted when it was found than custom pin assignments were being lost. The standard Arduino library practice is to issue a `Wire.begin()` call inside each library that uses `Wire()`. If a custom pin assignment was used, when these libraries were init'd, they caused a failure when they re-assigned the SDA and SCL pins. Changing Frequency back to 100khz was not a fatal problem. --- libraries/Wire/src/Wire.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libraries/Wire/src/Wire.h b/libraries/Wire/src/Wire.h index 78b909ab911..303d78956d4 100644 --- a/libraries/Wire/src/Wire.h +++ b/libraries/Wire/src/Wire.h @@ -70,8 +70,10 @@ class TwoWire: public Stream public: TwoWire(uint8_t bus_num); ~TwoWire(); - void begin(int sda=-1, int scl=-1, uint32_t frequency=100000); - void setClock(uint32_t); + void begin(int sda=-1, int scl=-1, uint32_t frequency=0); + //defaults bus:0 sda=SDA, scl=SCL, frequency =100khz via variant pins_arduino.h + // bus:1 unspecified, emits Log_E() + void setClock(uint32_t); // change bus clock without initing hardware void beginTransmission(uint16_t); uint8_t endTransmission(bool); uint8_t requestFrom(uint16_t address, uint8_t size, bool sendStop); @@ -88,7 +90,7 @@ class TwoWire: public Stream bool getDump(){return _dump;} void dumpInts(); void dumpI2C(){i2cDumpI2c(i2c);} - size_t getClock(); + size_t getClock(); // current bus clock rate in hz void setTimeOut(uint16_t timeOutMillis); uint16_t getTimeOut(); // @@ -140,6 +142,7 @@ extern TwoWire Wire; /* +V0.2.2 13APR2018 preserve custom SCL,SDA,Frequency when no parameters passed to begin() V0.2.1 15MAR2018 Hardware reset, Glitch prevention, adding destructor for second i2c testing */ #endif From 68185a0feecda78c5cf2877399aa664bec87b8c8 Mon Sep 17 00:00:00 2001 From: chuck todd Date: Fri, 13 Apr 2018 17:50:20 -0600 Subject: [PATCH 09/14] update version V0.2.2 --- libraries/Wire/src/Wire.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/Wire/src/Wire.h b/libraries/Wire/src/Wire.h index 303d78956d4..520b5acbd09 100644 --- a/libraries/Wire/src/Wire.h +++ b/libraries/Wire/src/Wire.h @@ -30,7 +30,7 @@ #include "freertos/queue.h" #include "Stream.h" -#define STICKBREAKER V0.2.1 +#define STICKBREAKER V0.2.2 #define I2C_BUFFER_LENGTH 128 typedef void(*user_onRequest)(void); typedef void(*user_onReceive)(uint8_t*, int); From a396ffc0ea5f7c9132fb07ebc2e74b42573c0dad Mon Sep 17 00:00:00 2001 From: chuck todd Date: Sun, 20 May 2018 09:19:02 -0600 Subject: [PATCH 10/14] Fix error msg, Compiler Warning Time out recover was generating a confusing error message when DEBUG level was set, Clarify meaning. Thanks to @valki2 When Arduino 1.8.5 configured for maximum compiler warning, my sloppy code generates multiple errors. :grimace: Thanks @PicoEmma I fixed most of the warnings, but I haven't figure out how to clear one type of them. I use a structure to decode a peripheral memory register, the compiler reports it as an: > C:\Users\e\Documents\Arduino\hardware\espressif\esp32\cores\esp32\esp32-hal-i2c.c: In function 'dumpCmdQueue': C:\Users\e\Documents\Arduino\hardware\espressif\esp32\cores\esp32\esp32-hal-i2c.c:399:17: warning: variable 'c' set but not used [-Wunused-but-set-variable] I2C_COMMAND_t c; ^ Chuck. --- cores/esp32/esp32-hal-i2c.c | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index 69c2dd2c76e..2db29fab95b 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -64,16 +64,16 @@ enum { #define I2C_MUTEX_UNLOCK() static i2c_t _i2c_bus_array[2] = { - {(volatile i2c_dev_t *)(DR_REG_I2C_EXT_BASE_FIXED), 0,I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0}, - {(volatile i2c_dev_t *)(DR_REG_I2C1_EXT_BASE_FIXED), 1,I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0} + {(volatile i2c_dev_t *)(DR_REG_I2C_EXT_BASE_FIXED), 0,I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0}, + {(volatile i2c_dev_t *)(DR_REG_I2C1_EXT_BASE_FIXED), 1,I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0} }; #else #define I2C_MUTEX_LOCK() do {} while (xSemaphoreTake(i2c->lock, portMAX_DELAY) != pdPASS) #define I2C_MUTEX_UNLOCK() xSemaphoreGive(i2c->lock) static i2c_t _i2c_bus_array[2] = { - {(volatile i2c_dev_t *)(DR_REG_I2C_EXT_BASE_FIXED), NULL, 0,I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0}, - {(volatile i2c_dev_t *)(DR_REG_I2C1_EXT_BASE_FIXED), NULL, 1,I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0} + {(volatile i2c_dev_t *)(DR_REG_I2C_EXT_BASE_FIXED), NULL, 0,I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0}, + {(volatile i2c_dev_t *)(DR_REG_I2C1_EXT_BASE_FIXED), NULL, 1,I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0} }; #endif @@ -628,7 +628,17 @@ log_n("Enable Core Debug Level \"Error\""); void i2cDumpI2c(i2c_t * i2c){ log_e("i2c=%p",i2c); -log_e("dev=%p date=%p",i2c->dev,i2c->dev->date); +char levelText[8]; +switch(ARDUHAL_LOG_LEVEL){ + case 0 : levelText = sprintf("NONE"); break; + case 1 : levelText = sprintf("ERROR"); break; + case 2 : levelText = sprintf("WARN"); break; + case 3 : levelText = sprintf("INFO"); break; + case 4 : levelText = sprintf("DEBUG"); break; + case 5 : levelText = sprintf("VERBOSE"); break; + default : levelText = sprintf("uk=%d",ARDUHAL_LOG_LEVEL); +} +log_e("dev=%p date=%p level=%s",i2c->dev,i2c->dev->date,levelText); #if !CONFIG_DISABLE_HAL_LOCKS log_e("lock=%p",i2c->lock); #endif @@ -820,12 +830,13 @@ static void IRAM_ATTR i2c_isr_handler_default(void* arg){ i2c_t* p_i2c = (i2c_t*) arg; // recover data uint32_t activeInt = p_i2c->dev->int_status.val&0x1FFF; -portBASE_TYPE HPTaskAwoken = pdFALSE,xResult; +portBASE_TYPE HPTaskAwoken = pdFALSE; if(p_i2c->stage==I2C_DONE){ //get Out log_e("eject int=%p, ena=%p",activeInt,p_i2c->dev->int_ena.val); p_i2c->dev->int_ena.val = 0; p_i2c->dev->int_clr.val = activeInt; //0x1FFF; + return; } while (activeInt != 0) { // Ordering of 'if(activeInt)' statements is important, don't change @@ -843,8 +854,7 @@ while (activeInt != 0) { // Ordering of 'if(activeInt)' statements is important, intBuff[intPos[p_i2c->num]][2][p_i2c->num] = xTaskGetTickCountFromISR(); // when IRQ fired #endif - uint32_t oldInt =activeInt; - + if (activeInt & I2C_TRANS_START_INT_ST_M) { // p_i2c->byteCnt=0; if(p_i2c->stage==I2C_STARTUP){ @@ -1017,6 +1027,7 @@ if(!i2c->i2c_event){ } if(i2c->i2c_event) { uint32_t ret=xEventGroupClearBits(i2c->i2c_event, 0xFF); + if(ret != ESP_OK) log_e("Unable to Clear Event Bits=%d",ret); } else {// failed to create EventGroup log_e("eventCreate failed=%p",i2c->i2c_event); @@ -1094,7 +1105,7 @@ i2c->dev->int_ena.val = if(!i2c->intr_handle){ // create ISR for either peripheral // log_i("create ISR %d",i2c->num); - uint32_t ret; + uint32_t ret=0xFFFFFFFF; // clear uninitialized var warning switch(i2c->num){ case 0: ret = esp_intr_alloc(ETS_I2C_EXT0_INTR_SOURCE, 0, &i2c_isr_handler_default, i2c, &i2c->intr_handle); @@ -1153,7 +1164,7 @@ if(eBits&EVENT_DONE){ // no gross timeout uint32_t expected =(totalBytes*10*1000)/i2cGetFrequency(i2c); if((tAfter-tBefore)>(expected+1)) { //used some of the timeout Period // expected can be zero due to small packets - log_e("TimeoutRecovery: expected=%ums, actual=%ums",expected,(tAfter-tBefore)); + log_d("used TimeoutRecovery: expected=%ums, actual=%ums, configured=%ums ",expected,(tAfter-tBefore),timeOutMillis); i2cDumpI2c(i2c); i2cDumpInts(i2c->num); } @@ -1242,7 +1253,7 @@ void i2cReleaseISR(i2c_t * i2c){ if(i2c->intr_handle){ // log_i("Release ISR %d",i2c->num); esp_err_t error =esp_intr_free(i2c->intr_handle); -// log_e("released ISR=%d",error); + if(error!=ESP_OK) log_e("Error releasing ISR=%d",error); i2c->intr_handle=NULL; } } From 9cb06b9ab0cc0ecd76cfc79ce55b228c0d53c1d8 Mon Sep 17 00:00:00 2001 From: chuck todd Date: Sun, 20 May 2018 09:23:38 -0600 Subject: [PATCH 11/14] Update to version V0.2.3 esp-hal-i2c.c: * fix timeout debug message @valki2 * fix compiler warnings @PicoEmma * add debug level display --- libraries/Wire/src/Wire.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/Wire/src/Wire.h b/libraries/Wire/src/Wire.h index 520b5acbd09..09c2ac6d4bb 100644 --- a/libraries/Wire/src/Wire.h +++ b/libraries/Wire/src/Wire.h @@ -30,7 +30,7 @@ #include "freertos/queue.h" #include "Stream.h" -#define STICKBREAKER V0.2.2 +#define STICKBREAKER "V0.2.3" #define I2C_BUFFER_LENGTH 128 typedef void(*user_onRequest)(void); typedef void(*user_onReceive)(uint8_t*, int); From 88a51e11425814afd01927adad29a671c1d2c8bb Mon Sep 17 00:00:00 2001 From: chuck todd Date: Sun, 20 May 2018 12:25:08 -0600 Subject: [PATCH 12/14] Brain Fade, Did a direct file edit, and of course I failed. --- cores/esp32/esp32-hal-i2c.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index 2db29fab95b..4d008c0fde7 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -630,13 +630,13 @@ void i2cDumpI2c(i2c_t * i2c){ log_e("i2c=%p",i2c); char levelText[8]; switch(ARDUHAL_LOG_LEVEL){ - case 0 : levelText = sprintf("NONE"); break; - case 1 : levelText = sprintf("ERROR"); break; - case 2 : levelText = sprintf("WARN"); break; - case 3 : levelText = sprintf("INFO"); break; - case 4 : levelText = sprintf("DEBUG"); break; - case 5 : levelText = sprintf("VERBOSE"); break; - default : levelText = sprintf("uk=%d",ARDUHAL_LOG_LEVEL); + case 0 : sprintf(levelText,"NONE"); break; + case 1 : sprintf(levelText,"ERROR"); break; + case 2 : sprintf(levelText,"WARN"); break; + case 3 : sprintf(levelText,"INFO"); break; + case 4 : sprintf(levelText,"DEBUG"); break; + case 5 : sprintf(levelText,"VERBOSE"); break; + default : sprintf(levelText,"uk=%d",ARDUHAL_LOG_LEVEL); } log_e("dev=%p date=%p level=%s",i2c->dev,i2c->dev->date,levelText); #if !CONFIG_DISABLE_HAL_LOCKS From 0db1b53175ae7f615008add77401ea4e16ee541d Mon Sep 17 00:00:00 2001 From: chuck todd Date: Wed, 23 May 2018 19:51:21 -0600 Subject: [PATCH 13/14] brainless blunder xEventGroupclearBits() does not return an error code, just the prior values of the flags. --- cores/esp32/esp32-hal-i2c.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index 4d008c0fde7..29eadcb036f 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -1026,8 +1026,7 @@ if(!i2c->i2c_event){ i2c->i2c_event = xEventGroupCreate(); } if(i2c->i2c_event) { - uint32_t ret=xEventGroupClearBits(i2c->i2c_event, 0xFF); - if(ret != ESP_OK) log_e("Unable to Clear Event Bits=%d",ret); + xEventGroupClearBits(i2c->i2c_event, 0xFF); } else {// failed to create EventGroup log_e("eventCreate failed=%p",i2c->i2c_event); From 2a2e699b44513f15187686a470597742047b4af6 Mon Sep 17 00:00:00 2001 From: chuck todd Date: Mon, 2 Jul 2018 10:08:11 -0600 Subject: [PATCH 14/14] Remove confusing Debug message If Core Debug Level is at DEBUG, a confusing debug message will be emitted if the I2C transaction takes longer complete than the calculated minimum time. This original debug message was just to prove that this new i2c code could correctly handle SCL stretching or interrupt latency issues. This delay is not a problem, or an error. Usually it is caused by a higher priory interrupt starving the i2c ISR. Usually WiFi is the culprit. As long of this delay is within the configured timeout (by default 50ms, or can be set with Wire.setTimeOut(milliseconds);) no problem will occur and the transaction will successfully complete. Chuck. --- cores/esp32/esp32-hal-i2c.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index 29eadcb036f..09c6caf91a4 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -1159,15 +1159,6 @@ if(!(eBits==EVENT_DONE)&&(eBits&~(EVENT_ERROR_NAK|EVENT_ERROR_DATA_NAK|EVENT_ERR } if(eBits&EVENT_DONE){ // no gross timeout -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG - uint32_t expected =(totalBytes*10*1000)/i2cGetFrequency(i2c); - if((tAfter-tBefore)>(expected+1)) { //used some of the timeout Period - // expected can be zero due to small packets - log_d("used TimeoutRecovery: expected=%ums, actual=%ums, configured=%ums ",expected,(tAfter-tBefore),timeOutMillis); - i2cDumpI2c(i2c); - i2cDumpInts(i2c->num); - } -#endif switch(i2c->error){ case I2C_OK : reason = I2C_ERROR_OK;