diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index 34e8e4d9973..09c6caf91a4 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 @@ -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; @@ -176,7 +182,7 @@ i2c_err_t i2cAttachSCL(i2c_t * i2c, int8_t scl) return I2C_ERROR_DEV; } digitalWrite(scl, HIGH); - pinMode(scl, OPEN_DRAIN | PULLUP); + pinMode(scl, OPEN_DRAIN | PULLUP | INPUT | OUTPUT); pinMatrixOutAttach(scl, I2C_SCL_IDX(i2c->num), false, false); pinMatrixInAttach(scl, I2C_SCL_IDX(i2c->num), false); return I2C_ERROR_OK; @@ -189,7 +195,7 @@ i2c_err_t i2cDetachSCL(i2c_t * i2c, int8_t scl) } pinMatrixOutDetach(scl, false, false); pinMatrixInDetach(I2C_SCL_IDX(i2c->num), false, false); - pinMode(scl, INPUT); + pinMode(scl, INPUT | PULLUP); return I2C_ERROR_OK; } @@ -199,7 +205,7 @@ i2c_err_t i2cAttachSDA(i2c_t * i2c, int8_t sda) return I2C_ERROR_DEV; } digitalWrite(sda, HIGH); - pinMode(sda, OPEN_DRAIN | PULLUP); + pinMode(sda, OPEN_DRAIN | PULLUP | INPUT | OUTPUT ); pinMatrixOutAttach(sda, I2C_SDA_IDX(i2c->num), false, false); pinMatrixInAttach(sda, I2C_SDA_IDX(i2c->num), false); return I2C_ERROR_OK; @@ -212,7 +218,7 @@ i2c_err_t i2cDetachSDA(i2c_t * i2c, int8_t sda) } pinMatrixOutDetach(sda, false, false); pinMatrixInDetach(I2C_SDA_IDX(i2c->num), false, false); - pinMode(sda, INPUT); + pinMode(sda, INPUT | PULLUP); return I2C_ERROR_OK; } @@ -287,8 +293,13 @@ uint32_t i2cGetFrequency(i2c_t * i2c) if(i2c == NULL){ return 0; } - - return APB_CLK_FREQ/(i2c->dev->scl_low_period.period+i2c->dev->scl_high_period.period); + uint32_t result = 0; + uint32_t old_count = (i2c->dev->scl_low_period.period+i2c->dev->scl_high_period.period); + if(old_count>0){ + result = APB_CLK_FREQ / old_count; + } + else result = 0; + return result; } /* @@ -297,7 +308,7 @@ uint32_t i2cGetFrequency(i2c_t * i2c) * addr_10bit_en - enable slave 10bit address mode. * */ // 24Nov17 only supports Master Mode -i2c_t * i2cInit(uint8_t i2c_num) +i2c_t * i2cInit(uint8_t i2c_num) //before this is called, pins should be detached, else glitch { if(i2c_num > 1){ return NULL; @@ -313,16 +324,21 @@ i2c_t * i2cInit(uint8_t i2c_num) } } #endif + I2C_MUTEX_LOCK(); + + i2cReleaseISR(i2c); // ISR exists, release it before disabling hardware + + uint32_t old_clock = i2cGetFrequency(i2c); if(i2c_num == 0) { + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG,DPORT_I2C_EXT0_RST); //reset hardware DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG,DPORT_I2C_EXT0_CLK_EN); - DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG,DPORT_I2C_EXT0_RST); + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG,DPORT_I2C_EXT0_RST);// release reset } else { + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG,DPORT_I2C_EXT1_RST); //reset Hardware DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG,DPORT_I2C_EXT1_CLK_EN); DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG,DPORT_I2C_EXT1_RST); } - - I2C_MUTEX_LOCK(); i2c->dev->ctr.val = 0; i2c->dev->ctr.ms_mode = 1; i2c->dev->ctr.sda_force_out = 1 ; @@ -336,10 +352,12 @@ i2c_t * i2cInit(uint8_t i2c_num) i2c->dev->slave_addr.val = 0; I2C_MUTEX_UNLOCK(); + + if(old_clock) i2cSetFrequency(i2c,old_clock); // reconfigure return i2c; } - +/* unused 03/15/2018 void i2cInitFix(i2c_t * i2c){ if(i2c == NULL){ return; @@ -361,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; @@ -373,13 +392,14 @@ void i2cReset(i2c_t* i2c){ periph_module_enable( moduleId ); I2C_MUTEX_UNLOCK(); } +*/ /* Stickbreaker ISR mode debug support */ -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO #define INTBUFFMAX 64 -static uint32_t intBuff[INTBUFFMAX][3]; -static uint32_t intPos=0; +static uint32_t intBuff[INTBUFFMAX][3][2]; +static uint32_t intPos[2]={0,0}; #endif /* Stickbreaker ISR mode debug support @@ -606,10 +626,19 @@ 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); +char levelText[8]; +switch(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 log_e("lock=%p",i2c->lock); #endif @@ -645,13 +674,10 @@ enable txEmpty, filltx fires, but the SM has already sent a bogus byte out the B overlap is not an issue, just keep them full/empty the status_reg.xx_fifo_cnt tells the truth. And the INT's fire correctly */ -bool readEncountered = false; // 12/01/2017 this needs to be removed -// it is nolonger necessary, the fifo's are independent. Run thru the dq's -// until the cmd[] is full or the txFifo is full. uint16_t a=i2c->queuePos; // currently executing dq, bool full=!(i2c->dev->status_reg.tx_fifo_cnt<31); uint8_t cnt; -while((a < i2c->queueCount)&&!(full || readEncountered)){ +while((a < i2c->queueCount) && !full){ I2C_DATA_QUEUE_t *tdq = &i2c->dq[a]; cnt=0; // add to address to fifo ctrl.addr already has R/W bit positioned correctly @@ -697,22 +723,19 @@ while((a < i2c->queueCount)&&!(full || readEncountered)){ } } } -//11/23/2017 overlap tx/rx/tx -// else readEncountered = true; - - if(full) readEncountered =false; //tx possibly needs more -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO // update debug buffer tx counts - cnt += intBuff[intPos][1]>>16; - intBuff[intPos][1] = (intBuff[intPos][1]&0xFFFF)|(cnt<<16); + cnt += intBuff[intPos[i2c->num]][1][i2c->num]>>16; + intBuff[intPos[i2c->num]][1][i2c->num] = (intBuff[intPos[i2c->num]][1][i2c->num]&0xFFFF)|(cnt<<16); + #endif - if(!(full||readEncountered)) a++; // check next buffer for tx + if(!full) a++; // check next buffer for tx } -if((!full) || readEncountered || (a >= i2c->queueCount)){// disable IRQ, the next dq will re-enable it +if(!full || (a >= i2c->queueCount)){// disable IRQ, the next dq will re-enable it i2c->dev->int_ena.tx_fifo_empty=0; } @@ -746,10 +769,10 @@ if(tdq->ctrl.mode==1) { // read moveCnt = (tdq->length - tdq->position); } } -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO // update Debug rxCount - cnt += (intBuff[intPos][1])&&0xffFF; - intBuff[intPos][1] = (intBuff[intPos][1]&0xFFFF0000)|cnt; + cnt += (intBuff[intPos[i2c->num]][1][i2c->num])&&0xffFF; + intBuff[intPos[i2c->num]][1][i2c->num] = (intBuff[intPos[i2c->num]][1][i2c->num]&0xFFFF0000)|cnt; #endif } else { @@ -807,32 +830,31 @@ 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; -// i2cDumpI2c(p_i2c); -// i2cDumpInts(); + return; } while (activeInt != 0) { // Ordering of 'if(activeInt)' statements is important, don't change -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR - if(activeInt==(intBuff[intPos][0]&0x1fff)){ - intBuff[intPos][0] = (((intBuff[intPos][0]>>16)+1)<<16)|activeInt; +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO + if(activeInt==(intBuff[intPos[p_i2c->num]][0][p_i2c->num]&0x1fff)){ + intBuff[intPos[p_i2c->num]][0][p_i2c->num] = (((intBuff[intPos[p_i2c->num]][0][p_i2c->num]>>16)+1)<<16)|activeInt; } else{ - intPos++; - intPos %= INTBUFFMAX; - intBuff[intPos][0]=(1<<16)|activeInt; - intBuff[intPos][1] = 0; + intPos[p_i2c->num]++; + intPos[p_i2c->num] %= INTBUFFMAX; + intBuff[intPos[p_i2c->num]][0][p_i2c->num] = (1<<16) | activeInt; + intBuff[intPos[p_i2c->num]][1][p_i2c->num] = 0; } - intBuff[intPos][2] = xTaskGetTickCountFromISR(); // when IRQ fired + 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){ @@ -940,13 +962,13 @@ while (activeInt != 0) { // Ordering of 'if(activeInt)' statements is important, } } -void i2cDumpInts(){ -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR +void i2cDumpInts(uint8_t num){ +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO uint32_t b; -log_e("row count INTR TX RX"); +log_e("%u row count INTR TX RX",num); for(uint32_t a=1;a<=INTBUFFMAX;a++){ - b=(a+intPos)%INTBUFFMAX; - if(intBuff[b][0]!=0) log_e("[%02d] 0x%04x 0x%04x 0x%04x 0x%04x 0x%08x",b,((intBuff[b][0]>>16)&0xFFFF),(intBuff[b][0]&0xFFFF),((intBuff[b][1]>>16)&0xFFFF),(intBuff[b][1]&0xFFFF),intBuff[b][2]); + b=(a+intPos[num])%INTBUFFMAX; + if(intBuff[b][0][num]!=0) log_e("[%02d] 0x%04x 0x%04x 0x%04x 0x%04x 0x%08x",b,((intBuff[b][0][num]>>16)&0xFFFF),(intBuff[b][0][num]&0xFFFF),((intBuff[b][1][num]>>16)&0xFFFF),(intBuff[b][1][num]&0xFFFF),intBuff[b][2][num]); } #else log_n("enable Core Debug Level \"Error\""); @@ -958,36 +980,53 @@ 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 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 */ i2c->stage = I2C_DONE; // until ready -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR -memset(intBuff,0,sizeof(intBuff)); -intPos=0; +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO +for(uint16_t i=0;inum] = 0; + intBuff[i][1][i2c->num] = 0; + intBuff[i][2][i2c->num] = 0; + } +intPos[i2c->num] = 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){ i2c->i2c_event = xEventGroupCreate(); } if(i2c->i2c_event) { - uint32_t ret=xEventGroupClearBits(i2c->i2c_event, 0xFF); - -// log_e("after clearBits(%p)=%p",i2c->i2c_event,ret); + xEventGroupClearBits(i2c->i2c_event, 0xFF); } else {// failed to create EventGroup log_e("eventCreate failed=%p",i2c->i2c_event); @@ -1023,7 +1062,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 @@ -1063,9 +1102,19 @@ i2c->dev->int_ena.val = I2C_TXFIFO_EMPTY_INT_ENA | // (BIT(1)) triggers fillTxFifo() I2C_RXFIFO_FULL_INT_ENA; // (BIT(0)) trigger emptyRxFifo() -if(!i2c->intr_handle){ // create ISR I2C_0 only, -// log_e("create ISR"); - uint32_t ret = esp_intr_alloc(ETS_I2C_EXT0_INTR_SOURCE, 0, &i2c_isr_handler_default, i2c, &i2c->intr_handle); +if(!i2c->intr_handle){ // create ISR for either peripheral + // log_i("create ISR %d",i2c->num); + 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); + break; + case 1: + ret = esp_intr_alloc(ETS_I2C_EXT1_INTR_SOURCE, 0, &i2c_isr_handler_default, i2c, &i2c->intr_handle); + break; + default :; + } + if(ret!=ESP_OK){ log_e("install interrupt handler Failed=%d",ret); I2C_MUTEX_UNLOCK(); @@ -1101,24 +1150,15 @@ if(i2c->exitCode!=eBits){ // try to recover from O/S failure } 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 -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO i2cDumpI2c(i2c); - i2cDumpInts(); + i2cDumpInts(i2c->num); #else - log_n("I2C exitCode=%u",eBits); + log_n("I2C exitCode=0x%x",eBits); #endif } 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_e("TimeoutRecovery: expected=%ums, actual=%ums",expected,(tAfter-tBefore)); - i2cDumpI2c(i2c); - i2cDumpInts(); - } -#endif switch(i2c->error){ case I2C_OK : reason = I2C_ERROR_OK; @@ -1152,7 +1192,7 @@ else { // GROSS timeout, shutdown ISR , report Timeout #if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR log_e(" Busy Timeout start=0x%x, end=0x%x, =%d, max=%d error=%d",tBefore,tAfter,(tAfter-tBefore),ticksTimeOut,i2c->error); i2cDumpI2c(i2c); - i2cDumpInts(); + i2cDumpInts(i2c->num); #endif } else { // just a timeout, some data made it out or in. @@ -1162,7 +1202,7 @@ else { // GROSS timeout, shutdown ISR , report Timeout #if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR log_e(" Gross Timeout Dead start=0x%x, end=0x%x, =%d, max=%d error=%d",tBefore,tAfter,(tAfter-tBefore),ticksTimeOut,i2c->error); i2cDumpI2c(i2c); - i2cDumpInts(); + i2cDumpInts(i2c->num); #endif } } @@ -1199,19 +1239,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); + if(error!=ESP_OK) log_e("Error releasing 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 bf75f0e6808..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,9 +168,9 @@ 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(); +void i2cDumpInts(uint8_t num); void i2cDumpI2c(i2c_t *i2c); #ifdef __cplusplus diff --git a/docs/stickbreaker/ESP32DE_16.jpg b/docs/stickbreaker/ESP32DE_16.jpg new file mode 100644 index 00000000000..de609647183 Binary files /dev/null and b/docs/stickbreaker/ESP32DE_16.jpg differ diff --git a/docs/stickbreaker/ESP32DE_17.jpg b/docs/stickbreaker/ESP32DE_17.jpg new file mode 100644 index 00000000000..044b5e80f44 Binary files /dev/null and b/docs/stickbreaker/ESP32DE_17.jpg differ diff --git a/docs/stickbreaker/ESP32DE_18.jpg b/docs/stickbreaker/ESP32DE_18.jpg new file mode 100644 index 00000000000..4dbc4622373 Binary files /dev/null and b/docs/stickbreaker/ESP32DE_18.jpg differ diff --git a/docs/stickbreaker/README.md b/docs/stickbreaker/README.md new file mode 100644 index 00000000000..fb2a0a915bf --- /dev/null +++ b/docs/stickbreaker/README.md @@ -0,0 +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 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 + diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp index fed35ba71cc..d504d977c7c 100644 --- a/libraries/Wire/src/Wire.cpp +++ b/libraries/Wire/src/Wire.cpp @@ -50,50 +50,50 @@ 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) { - if(num == 0) { - sdaPin = SDA; - } else { - return; - } - } - - if(sclPin < 0) { - if(num == 0) { - sclPin = SCL; - } 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(i2c == NULL) { - i2c = i2cInit(num); - if(i2c == NULL) { - 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 + } } - i2cSetFrequency(i2c, frequency); - - if(sda >= 0 && sda != sdaPin ) { - i2cDetachSDA(i2c, sda); - } - - if(scl >= 0 && scl != sclPin ) { - i2cDetachSCL(i2c, scl); - } - - sda = sdaPin; - scl = sclPin; - - i2cAttachSDA(i2c, sda); - i2cAttachSCL(i2c, scl); + if(!initHardware(sdaPin, sclPin, frequency)) return; - flush(); + flush(); - i2cInitFix(i2c); } void TwoWire::setTimeOut(uint16_t timeOutMillis){ @@ -108,20 +108,81 @@ void TwoWire::setClock(uint32_t frequency) { i2cSetFrequency(i2c, frequency); } + +bool TwoWire::initHardware(int sdaPin, int sclPin, uint32_t frequency){ + + i2cDetachSCL(i2c,scl); // detach pins before resetting I2C perpherial + i2cDetachSDA(i2c,sda); // else a glitch will appear on the i2c bus + i2c = i2cInit(num);// i2cInit() now performs a hardware reset + 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; + 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); + digitalWrite(scl,HIGH); + pinMode(sda,PULLUP|OPEN_DRAIN|OUTPUT|INPUT); + pinMode(scl,PULLUP|OPEN_DRAIN|OUTPUT|INPUT); + + if(!digitalRead(sda)||!digitalRead(scl)){ // bus in busy state + log_e("invalid state sda=%d, scl=%d\n",digitalRead(sda),digitalRead(scl)); + digitalWrite(sda,HIGH); + digitalWrite(scl,HIGH); + delayMicroseconds(5); + digitalWrite(sda,LOW); + for(uint8_t a=0; a<9;a++){ + delayMicroseconds(5); + digitalWrite(scl,LOW); + delayMicroseconds(5); + digitalWrite(scl,HIGH); + } + delayMicroseconds(5); + digitalWrite(sda,HIGH); + } + i2cAttachSDA(i2c, sda); + i2cAttachSCL(i2c, scl); + + if(!digitalRead(sda)||!digitalRead(scl)){ // bus in busy state + log_e("Bus Invalid State, TwoWire() Can't init"); + return false; // bus is busy + } + + return true; +} + /*@StickBreaker common handler for processing the queued commands */ i2c_err_t TwoWire::processQueue(uint32_t * readCount){ - last_error=i2cProcQueue(i2c,readCount,_timeOutMillis); - rxIndex = 0; - rxLength = rxQueued; - rxQueued = 0; - txQueued = 0; // the SendStop=true will restart all Queueing - if(_dump){ - i2cDumpI2c(i2c); - i2cDumpInts(); - } - i2cFreeQueue(i2c); - return last_error; + last_error=i2cProcQueue(i2c,readCount,_timeOutMillis); + if(last_error==I2C_ERROR_BUSY){ // try to clear the bus + if(initHardware(sda,scl,getClock())){ + last_error=i2cProcQueue(i2c,readCount,_timeOutMillis); + } + } + + rxIndex = 0; + rxLength = rxQueued; + rxQueued = 0; + txQueued = 0; // the SendStop=true will restart all Queueing + if(_dump){ + i2cDumpI2c(i2c); + i2cDumpInts(num); + } + i2cFreeQueue(i2c); + return last_error; } /* @stickBreaker 11/2017 fix for ReSTART timeout, ISR @@ -168,7 +229,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 @@ -205,7 +266,7 @@ return last_error; /*stickbreaker Dump i2c Interrupt buffer, i2c isr Debugging */ void TwoWire::dumpInts(){ - i2cDumpInts(); + i2cDumpInts(num); } /*stickbreaker i2c isr Debugging @@ -435,12 +496,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 89070b6fd0b..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 +#define STICKBREAKER "V0.2.3" #define I2C_BUFFER_LENGTH 128 typedef void(*user_onRequest)(void); typedef void(*user_onReceive)(uint8_t*, int); @@ -65,11 +65,15 @@ class TwoWire: public Stream i2c_err_t processQueue(uint32_t *readCount); uint16_t _timeOutMillis; bool _dump; + bool initHardware(int sdaPin, int sclPin, uint32_t frequency); public: TwoWire(uint8_t bus_num); - void begin(int sda=-1, int scl=-1, uint32_t frequency=100000); - void setClock(uint32_t); + ~TwoWire(); + 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); @@ -86,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(); // @@ -112,8 +116,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)); @@ -138,4 +140,9 @@ class TwoWire: public Stream 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