From bc3fe8434cfe61fb1a162f1e439e8a027727899d Mon Sep 17 00:00:00 2001 From: Chuck Todd Date: Mon, 13 Nov 2017 19:36:57 -0700 Subject: [PATCH 01/63] Interrupt Driven I2C Interrupt Driven I2c, Not for production, No Error handling, Does not Play Well with Original Wire.h --- cores/esp32/esp32-hal-i2c.c | 1169 ++++++++++++++++++++++-- cores/esp32/esp32-hal-i2c.h | 77 +- tools/sdk/include/soc/soc/i2c_reg.h | 2 +- tools/sdk/include/soc/soc/i2c_struct.h | 204 ++--- 4 files changed, 1249 insertions(+), 203 deletions(-) diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index dd3d35baafb..516c703466a 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -17,6 +17,7 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" +#include "freertos/event_groups.h" #include "rom/ets_sys.h" #include "driver/periph_ctrl.h" #include "soc/i2c_reg.h" @@ -30,15 +31,24 @@ #define DR_REG_I2C_EXT_BASE_FIXED 0x60013000 #define DR_REG_I2C1_EXT_BASE_FIXED 0x60027000 - -#define COMMAND_BUFFER_LENGTH 16 - + struct i2c_struct_t { i2c_dev_t * dev; #if !CONFIG_DISABLE_HAL_LOCKS xSemaphoreHandle lock; #endif uint8_t num; + I2C_MODE_t mode; + I2C_STAGE_t stage; + I2C_ERROR_t error; + EventGroupHandle_t i2c_event; // a way to monitor ISR process + // maybe use it to trigger callback for OnRequest() + intr_handle_t intr_handle; /*!< I2C interrupt handle*/ + I2C_DATA_QUEUE_t * dq; + uint16_t queueCount; + uint16_t queuePos; + uint16_t byteCnt; + }; enum { @@ -54,19 +64,106 @@ enum { #define I2C_MUTEX_UNLOCK() static i2c_t _i2c_bus_array[2] = { - {(volatile i2c_dev_t *)(DR_REG_I2C_EXT_BASE_FIXED), 0}, - {(volatile i2c_dev_t *)(DR_REG_I2C1_EXT_BASE_FIXED), 1} + {(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} }; #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}, - {(volatile i2c_dev_t *)(DR_REG_I2C1_EXT_BASE_FIXED), NULL, 1} + {(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} }; #endif + +//forward for ISR, added IRAM_ATTR? +static void IRAM_ATTR i2c_isr_handler_default(void* arg); + +static i2c_err_t i2cAddQueue(i2c_t * i2c,uint8_t mode, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen,bool sendStop){ + + I2C_DATA_QUEUE_t dqx; + dqx.data = dataPtr; + dqx.length = dataLen; + dqx.position = 0; + dqx.cmdBytesNeeded = dataLen; + dqx.ctrl.val = 0; + dqx.ctrl.addr = i2cDeviceAddr; + dqx.ctrl.mode = mode; + dqx.ctrl.stop= sendStop; + dqx.ctrl.addrReq = ((i2cDeviceAddr&0x7800)==0x7800)?2:1; // 10bit or 7bit address + dqx.queueLength = dataLen + dqx.ctrl.addrReq; + +if(i2c->dq!=NULL){ // 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; + memmove(&i2c->dq[i2c->queueCount++],&dqx,sizeof(I2C_DATA_QUEUE_t)); + } + else { // bad stuff, unable to allocate more memory! + log_e("realloc Failure"); + return I2C_ERROR_MEMORY; + } + } +else { // first Time + i2c->queueCount=0; + i2c->dq =(I2C_DATA_QUEUE_t*)malloc(sizeof(I2C_DATA_QUEUE_t)); + if(i2c->dq!=NULL){ + memmove(&i2c->dq[i2c->queueCount++],&dqx,sizeof(I2C_DATA_QUEUE_t)); + } + else { + log_e("malloc failure"); + return I2C_ERROR_MEMORY; + } + } +return I2C_ERROR_OK; +} + +i2c_err_t i2cFreeQueue(i2c_t * i2c){ +if(i2c->dq!=NULL){ + free(i2c->dq); + i2c->dq = NULL; + } +i2c->queueCount=0; +i2c->queuePos=0; +} + +i2c_err_t i2cAddQueueWrite(i2c_t * i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen,bool sendStop){ + + return i2cAddQueue(i2c,0,i2cDeviceAddr,dataPtr,dataLen,sendStop); +} + +i2c_err_t i2cAddQueueRead(i2c_t * i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen,bool sendStop){ + + return i2cAddQueue(i2c,1,i2cDeviceAddr,dataPtr,dataLen,sendStop); +} + +uint16_t i2cQueueReadPendingCount(i2c_t * i2c){ +uint16_t cnt=0; +uint16_t a=i2c->queuePos; +while(aqueueCount){ + if(i2c->dq[a].ctrl.mode==1){ + cnt += i2c->dq[a].length - i2c->dq[a].position; + } + a++; + } +return cnt; +} + +uint16_t i2cQueueReadCount(i2c_t * i2c){ +uint16_t cnt=0; +uint16_t a=0; +while( a < i2c->queueCount){ + if(i2c->dq[a].ctrl.mode==1){ + cnt += i2c->dq[a].position; //if postion is > 0 then some reads have happened + } + a++; + } +return cnt; +} + + i2c_err_t i2cAttachSCL(i2c_t * i2c, int8_t scl) { if(i2c == NULL){ @@ -121,33 +218,53 @@ i2c_err_t i2cDetachSDA(i2c_t * i2c, int8_t sda) * */ void i2cSetCmd(i2c_t * i2c, uint8_t index, uint8_t op_code, uint8_t byte_num, bool ack_val, bool ack_exp, bool ack_check) { - i2c->dev->command[index].val = 0; + I2C_COMMAND_t cmd; + cmd.val=0; + cmd.ack_en = ack_check; + cmd.ack_exp = ack_exp; + cmd.ack_val = ack_val; + cmd.byte_num = byte_num; + cmd.op_code = op_code; + i2c->dev->command[index].val = cmd.val; +/* i2c->dev->command[index].ack_en = ack_check; i2c->dev->command[index].ack_exp = ack_exp; i2c->dev->command[index].ack_val = ack_val; i2c->dev->command[index].byte_num = byte_num; i2c->dev->command[index].op_code = op_code; +*/ } -void i2cResetCmd(i2c_t * i2c) { - uint8_t i; +void i2cResetCmd(i2c_t * i2c){ + int i; for(i=0;i<16;i++){ i2c->dev->command[i].val = 0; } } -void i2cResetFiFo(i2c_t * i2c) { +void i2cResetFiFo(i2c_t * i2c) +{ + I2C_FIFO_CONF_t f; + f.val = i2c->dev->fifo_conf.val; + f.tx_fifo_rst = 1; + f.rx_fifo_rst = 1; + i2c->dev->fifo_conf.val = f.val; + f.tx_fifo_rst = 0; + f.rx_fifo_rst = 0; + i2c->dev->fifo_conf.val = f.val; +/* i2c->dev->fifo_conf.tx_fifo_rst = 1; i2c->dev->fifo_conf.tx_fifo_rst = 0; i2c->dev->fifo_conf.rx_fifo_rst = 1; i2c->dev->fifo_conf.rx_fifo_rst = 0; +*/ } -i2c_err_t i2cWrite(i2c_t * i2c, uint16_t address, bool addr_10bit, uint8_t * data, uint16_t len, bool sendStop) +i2c_err_t i2cWrite(i2c_t * i2c, uint16_t address, bool addr_10bit, uint8_t * data, uint8_t len, bool sendStop) { int i; - uint16_t index = 0; - uint16_t dataLen = len + (addr_10bit?2:1); + uint8_t index = 0; + uint8_t dataLen = len + (addr_10bit?2:1); address = (address << 1); if(i2c == NULL){ @@ -177,13 +294,10 @@ i2c_err_t i2cWrite(i2c_t * i2c, uint16_t address, bool addr_10bit, uint8_t * dat //CMD WRITE(ADDRESS + DATA) if(!index) { - if(addr_10bit){// address is leftshifted with Read/Write bit set - i2c->dev->fifo_data.data = (((address >> 8) & 0x6) | 0xF0); // send a9:a8 plus 1111 0xxW mask - i2c->dev->fifo_data.data = ((address >> 1) & 0xFF); // send a7:a0, remove W bit (7bit address style) - dataSend -= 2; - } - else { // 7bit address - i2c->dev->fifo_data.data = address & 0xFF; + i2c->dev->fifo_data.data = address & 0xFF; + dataSend--; + if(addr_10bit) { + i2c->dev->fifo_data.data = (address >> 8) & 0xFF; dataSend--; } } @@ -233,6 +347,7 @@ i2c_err_t i2cWrite(i2c_t * i2c, uint16_t address, bool addr_10bit, uint8_t * dat //Transmission did not finish and ACK_ERR is set if(i2c->dev->int_raw.ack_err) { log_w("Ack Error! Addr: %x", address >> 1); + i2c->dev->int_clr.ack_err = 1; // clear it while((i2c->dev->status_reg.bus_busy) && ((millis() - startAt)<50)); I2C_MUTEX_UNLOCK(); return I2C_ERROR_ACK; @@ -248,25 +363,12 @@ i2c_err_t i2cWrite(i2c_t * i2c, uint16_t address, bool addr_10bit, uint8_t * dat return I2C_ERROR_OK; } -uint8_t inc( uint8_t* index ) -{ - uint8_t i = index[ 0 ]; - if (++index[ 0 ] == COMMAND_BUFFER_LENGTH) - { - index[ 0 ] = 0; - } - - return i; -} - -i2c_err_t i2cRead(i2c_t * i2c, uint16_t address, bool addr_10bit, uint8_t * data, uint16_t len, bool sendStop) +i2c_err_t i2cRead(i2c_t * i2c, uint16_t address, bool addr_10bit, uint8_t * data, uint8_t len, bool sendStop) { address = (address << 1) | 1; uint8_t addrLen = (addr_10bit?2:1); - uint8_t amountRead[16]; - uint16_t index = 0; - uint8_t cmdIdx = 0, currentCmdIdx = 0, nextCmdCount; - bool stopped = false, isEndNear = false; + uint8_t index = 0; + uint8_t cmdIdx; uint8_t willRead; if(i2c == NULL){ @@ -286,45 +388,62 @@ i2c_err_t i2cRead(i2c_t * i2c, uint16_t address, bool addr_10bit, uint8_t * data i2cResetCmd(i2c); //CMD START - i2cSetCmd(i2c, cmdIdx++, I2C_CMD_RSTART, 0, false, false, false); + i2cSetCmd(i2c, 0, I2C_CMD_RSTART, 0, false, false, false); //CMD WRITE ADDRESS - if (addr_10bit) { // address is left-shifted with Read/Write bit set - i2c->dev->fifo_data.data = (((address >> 8) & 0x6) | 0xF1); // send a9:a8 plus 1111 0xxR mask - i2c->dev->fifo_data.data = ((address >> 1) & 0xFF); // send a7:a0, remove R bit (7bit address style) + i2c->dev->fifo_data.val = address & 0xFF; + if(addr_10bit) { + i2c->dev->fifo_data.val = (address >> 8) & 0xFF; } - else { // 7bit address - i2c->dev->fifo_data.data = address & 0xFF; - } - i2cSetCmd(i2c, cmdIdx++, I2C_CMD_WRITE, addrLen, false, false, true); - nextCmdCount = cmdIdx; + i2cSetCmd(i2c, 1, I2C_CMD_WRITE, addrLen, false, false, true); - //Clear Interrupts - i2c->dev->int_clr.val = 0x00001FFF; + while(len) { + cmdIdx = (index)?0:2; + willRead = (len > 32)?32:(len-1); + if(cmdIdx){ + i2cResetFiFo(i2c); + } + + if(willRead){ + i2cSetCmd(i2c, cmdIdx++, I2C_CMD_READ, willRead, false, false, false); + } + + if((len - willRead) > 1) { + i2cSetCmd(i2c, cmdIdx++, I2C_CMD_END, 0, false, false, false); + } else { + willRead++; + i2cSetCmd(i2c, cmdIdx++, I2C_CMD_READ, 1, true, false, false); + if(sendStop) { + i2cSetCmd(i2c, cmdIdx++, I2C_CMD_STOP, 0, false, false, false); + } + } + + //Clear Interrupts + i2c->dev->int_clr.val = 0xFFFFFFFF; + + //START Transmission + i2c->dev->ctr.trans_start = 1; - //START Transmission - i2c->dev->ctr.trans_start = 1; - while (!stopped) { //WAIT Transmission uint32_t startAt = millis(); while(1) { //have been looping for too long - if((millis() - startAt)>50) { - log_e("Timeout! Addr: %x, index %d", (address >> 1), index); + if((millis() - startAt)>50){ + log_e("Timeout! Addr: %x", address >> 1); I2C_MUTEX_UNLOCK(); return I2C_ERROR_BUS; } //Bus failed (maybe check for this while waiting? if(i2c->dev->int_raw.arbitration_lost) { - log_e("Bus Fail! Addr: %x", (address >> 1)); + log_e("Bus Fail! Addr: %x", address >> 1); I2C_MUTEX_UNLOCK(); return I2C_ERROR_BUS; } //Bus timeout if(i2c->dev->int_raw.time_out) { - log_e("Bus Timeout! Addr: %x, index %d", (address >> 1), index ); + log_e("Bus Timeout! Addr: %x", address >> 1); I2C_MUTEX_UNLOCK(); return I2C_ERROR_TIMEOUT; } @@ -332,51 +451,351 @@ i2c_err_t i2cRead(i2c_t * i2c, uint16_t address, bool addr_10bit, uint8_t * data //Transmission did not finish and ACK_ERR is set if(i2c->dev->int_raw.ack_err) { log_w("Ack Error! Addr: %x", address >> 1); - while((i2c->dev->status_reg.bus_busy) && ((millis() - startAt)<50)); I2C_MUTEX_UNLOCK(); return I2C_ERROR_ACK; } - // Save bytes from the buffer as they arrive instead of doing them at the end of the loop since there is no - // pause from an END operation in this approach. - if((!isEndNear) && (nextCmdCount < 2)) { - willRead = ((len>32)?32:len); - if (willRead > 0) { - if (willRead > 1) { - i2cSetCmd(i2c, cmdIdx, I2C_CMD_READ, (amountRead[ inc( &cmdIdx ) ] = willRead -1), false, false, false); - nextCmdCount++; - } - i2cSetCmd(i2c, cmdIdx, I2C_CMD_READ, (amountRead[ inc( &cmdIdx ) ] = 1), (len<=32), false, false); - nextCmdCount++; - len -= willRead; - } else { - i2cSetCmd(i2c, inc( &cmdIdx ), I2C_CMD_STOP, 0, false, false, false); - isEndNear = true; - nextCmdCount++; - } - } - - if(i2c->dev->command[currentCmdIdx].done) { - nextCmdCount--; - if (i2c->dev->command[currentCmdIdx].op_code == I2C_CMD_READ) { - while(amountRead[currentCmdIdx]>0) { - data[index++] = i2c->dev->fifo_data.val & 0xFF; - amountRead[currentCmdIdx]--; - } - i2cResetFiFo(i2c); - } else if (i2c->dev->command[currentCmdIdx].op_code == I2C_CMD_STOP) { - stopped = true; - } - inc( ¤tCmdIdx ); + if(i2c->dev->command[cmdIdx-1].done) { break; } } + + int i = 0; + while(idev->fifo_data.val & 0xFF; + } + len -= willRead; } I2C_MUTEX_UNLOCK(); - return I2C_ERROR_OK; } +void dumpCmdQueue(i2c_t *i2c){ +uint8_t i=0; +while(i<16){ + I2C_COMMAND_t c; + c.val=i2c->dev->command[i].val; + + log_e("[%2d] %c op[%d] val[%d] exp[%d] en[%d] bytes[%d]",i,(c.done?'Y':'N'), + c.op_code, + c.ack_val, + c.ack_exp, + c.ack_en, + c.byte_num); + i++; + } +} +uint32_t emptyCnt=0; +uint32_t maxRead=0; + +void emptyFifo(i2c_t* i2c,char* data, uint16_t len,uint16_t* index){ +uint32_t d; +uint32_t max=0; +while(i2c->dev->status_reg.rx_fifo_cnt>0){ + d = i2c->dev->fifo_data.val; + data[*index] = (d&0xff); + if(((*index)+1)maxRead) maxRead=max; +d = I2C_RXFIFO_FULL_INT_ST; +i2c->dev->int_clr.val =d; // processed! +//log_e("emptied %d",*index); +emptyCnt++; +} + +void fillFifo(i2c_t* i2c,uint8_t* data, uint16_t len,uint16_t* index){ +uint32_t d; +while((i2c->dev->status_reg.tx_fifo_cnt<0x1F)&&(*indexdev->fifo_data.val = d; + *index++; + } +d = I2C_TXFIFO_EMPTY_INT_ST; +i2c->dev->int_clr.val =d; // processed! +//log_e("Filled %d",*index); +} + +typedef enum{ + Stage_waitForBusIdle, + Stage_init, + Stage_StartMachine, + Stage_process, + Stage_done, + Stage_abort + }STAGES; + +void fillReadQueue(i2c_t *i2c, uint16_t *neededRead, uint8_t cmdIdx){ +/* Can I have another Sir? + ALL CMD queues must be terminated with either END or STOP. + if END is used, when refilling it next time, no entries from END to [15] can be used. + AND END must exit. The END command does not complete until the the + ctr->trans_start=1 has executed. + So, only refill from [0]..[14], leave [15] for a continuation if necessary. + As a corrilary, once END exists in [15], you do not need to overwrite it for the + next continuation. It is never modified. But, I update it every time because! + +*/ +uint8_t blkSize=0; // max is 255 +//log_e("needed=%2d index=%d",*neededRead,cmdIdx); +while((*neededRead>1)&&((cmdIdx)<15)) { // more bytes needed and room in cmd queue, leave room for END + // lets start with 3 bytes at a time, but may 255 is possible + blkSize = (*neededRead>35)?35:(*neededRead-1); // each read CMD can only read 32? + *neededRead -= blkSize; // + i2cSetCmd(i2c, (cmdIdx)++, I2C_CMD_READ, blkSize,false,false,false); // read cmd, this can't be the last read. + } +if((*neededRead==1)&&(cmdIdx==14)){ // stretch because Stop cannot be after or at END spot. + I2C_COMMAND_t cmdx; + cmdx.val =i2c->dev->command[cmdIdx-1].val; + if(cmdx.byte_num>1){ + cmdx.byte_num--; + i2c->dev->command[cmdIdx-1].val = cmdx.val; + } + else { // go back two + cmdx.val =i2c->dev->command[cmdIdx-2].val; + cmdx.byte_num--; + i2c->dev->command[cmdIdx-2].val = cmdx.val; + } + cmdx.byte_num=1; + i2c->dev->command[cmdIdx++].val = cmdx.val; + // now cmdIdx is 15 and a contuation will be added + } + +if(cmdIdx==15){ //need continuation +// cmd buffer is almost full, Add END as a continuation feature +// log_e("END at %d, left=%d",cmdIdx,neededRead); + i2cSetCmd(i2c, (cmdIdx)++,I2C_CMD_END, 0,false,false,false); + i2c->dev->int_ena.end_detect=1; //maybe? + i2c->dev->int_clr.end_detect=1; //maybe? + } + +if((*neededRead==1)&&((cmdIdx)<15)){// last Read cmd needs a NAK + // log_e("last Read"); + i2cSetCmd(i2c, (cmdIdx)++, I2C_CMD_READ, 1,true,false,false); + // send NAK to mark end of read + *neededRead=0; + } +if((*neededRead==0)&&((cmdIdx)<16)){// add Stop command + // What about SEND_STOP? If no Stop is sent, the Bus Buzy will Error on + // next call? Need bus ownership? +// log_e("STOP at %d",cmdIdx); + i2cSetCmd(i2c, (cmdIdx)++,I2C_CMD_STOP,0,false,false,false); + } +// log_e("loaded %d cmds",cmdIdx); +// dumpCmdQueue(i2c); + + +} + +i2c_err_t pollI2cRead(i2c_t * i2c, uint16_t address, bool addr_10bit, uint8_t * data, uint16_t len, bool sendStop) +{ +address = (address << 1) | 1; +uint8_t addrLen = (addr_10bit?2:1); +uint16_t index = 0; // pos in data for next byte +uint8_t cmdIdx=0; // next available entry in cmd buffer8 +uint16_t neededRead = len; // bytes left +uint32_t intbuf[32]; +uint16_t intIndex=0; +for(intIndex=0;intIndex<32;intIndex++){ + intbuf[intIndex]=intIndex; + } +intIndex = 0; + +if(i2c == NULL){ + return I2C_ERROR_DEV; + } + +I2C_MUTEX_LOCK(); + +emptyCnt = 0; +STAGES stage=Stage_init; // init +bool finished = false; +bool abort = false; +bool started = false; +uint8_t blkSize = 0; +uint32_t startAt = millis(),lastInt=0,activeInt=0; +i2c_err_t reason = I2C_ERROR_OK; +//uint8_t priorMode=i2c->dev->ctr.ms_mode; +while(!finished){ + if((millis()-startAt)>5000){ // timeout + if(reason == I2C_ERROR_OK) reason = I2C_ERROR_TIMEOUT; + // else leave reason alone + log_e("Stage[%d] millis(%ld) Timeout(%ld)! Addr: %x",stage,millis(),startAt, address >> 1); + stage= Stage_abort; + } + switch(stage){ + case Stage_waitForBusIdle : // bus busy on entry + // if last call had SEND_STOP=FALSE, this will always Error Out? + if (!i2c->dev->status_reg.bus_busy){ + stage = Stage_StartMachine; + reason = I2C_ERROR_OK; + } + else reason = I2C_ERROR_BUSY; + break; + + case Stage_init: // set up address + i2c->dev->ctr.trans_start=0; // Pause Machine + i2c->dev->timeout.tout = 0xFFFFF; // max 1M + I2C_FIFO_CONF_t f; + f.val = i2c->dev->fifo_conf.val; + f.rx_fifo_rst = 1; // fifo in reset + f.tx_fifo_rst = 1; // fifo in reset + f.nonfifo_en = 0; // use fifo mode + f.rx_fifo_full_thrhd = 6; // six bytes before INT is issued + f.fifo_addr_cfg_en = 0; // no directed access + i2c->dev->fifo_conf.val = f.val; // post them all + + f.rx_fifo_rst = 0; // release fifo + f.tx_fifo_rst = 0; + i2c->dev->fifo_conf.val = f.val; // post them all + + i2c->dev->int_ena.val = + I2C_ACK_ERR_INT_ENA | // (BIT(10)) + I2C_TRANS_START_INT_ENA | // (BIT(9)) + I2C_TIME_OUT_INT_ENA | //(BIT(8)) + I2C_TRANS_COMPLETE_INT_ENA | // (BIT(7)) + I2C_MASTER_TRAN_COMP_INT_ENA | // (BIT(6)) + I2C_ARBITRATION_LOST_INT_ENA | // (BIT(5)) + I2C_SLAVE_TRAN_COMP_INT_ENA | // (BIT(4)) + I2C_END_DETECT_INT_ENA | // (BIT(3)) + I2C_RXFIFO_OVF_INT_ENA | //(BIT(2)) + I2C_RXFIFO_FULL_INT_ENA; // (BIT(0)) + + + i2c->dev->int_clr.val = 0xFFFFFFFF; // kill them All! + i2c->dev->ctr.ms_mode = 1; // master! + //i2cResetFiFo(i2c); + //i2cResetCmd(i2c); + + //CMD START + i2cSetCmd(i2c, cmdIdx++, I2C_CMD_RSTART, 0, false, false, false); + + //CMD WRITE ADDRESS + if(addr_10bit){ + i2c->dev->fifo_data.data = ((address>>7)&0xff)|0x1; // high 2 bits plus read + i2c->dev->fifo_data.data = (address>>1)&0xff; // low 8 bits of address + } + else i2c->dev->fifo_data.data = address & 0xFF; // low 7bits plus read + + i2cSetCmd(i2c, cmdIdx++, I2C_CMD_WRITE, addrLen, false, false, true); //load address in cmdlist, validate (low) ack + // log_e("init(%d)",millis()); + fillReadQueue(i2c,&neededRead,cmdIdx); + stage = Stage_StartMachine; + break; + case Stage_StartMachine: + if((!started)&& (i2c->dev->status_reg.bus_busy)){ + // should not hang on END? because we own the bus! + log_e("bus_busy"); + stage = Stage_waitForBusIdle; + } + else { + started = true; +// i2c->dev->int_clr.val = 0xFFFFFFFF; // kill them All! +// log_e("started"); + i2c->dev->ctr.trans_start=1; // go for it + stage = Stage_process; + } + break; + case Stage_process: // Ok, Get to Work + activeInt = i2c->dev->int_status.val; // ok lets play! + if(activeInt==0) break; // nothing to do! + if(lastInt!=activeInt){ + intIndex++; + intIndex %= 32; + intbuf[intIndex] = activeInt; +// log_e("int=%08X",activeInt); + lastInt = activeInt; + } + else { // same int as last time, probably rx_fifo_full! + uint32_t x=(intbuf[intIndex]>>20); //12 bits should be enough! + x++; + x = x<<20; + intbuf[intIndex] = x | (intbuf[intIndex]&0x00ffffff); + } + + if(activeInt&I2C_RXFIFO_FULL_INT_ST){// rx has reached thrhd +// log_e("process"); + emptyFifo(i2c,data,len,&index); + startAt=millis(); // reset timeout + } + + if(activeInt&I2C_END_DETECT_INT_ST){ + i2c->dev->ctr.trans_start=0; + cmdIdx=0; + fillReadQueue(i2c,&neededRead,cmdIdx); + i2c->dev->int_ena.end_detect = 1; + i2c->dev->int_clr.end_detect = 1; + + stage = Stage_StartMachine; +// log_e("end"); + } + + if(activeInt&I2C_ARBITRATION_LOST_INT_ST){// abort transaction + // reset State Machine, Release Bus + stage = Stage_abort; + reason = I2C_ERROR_BUS; + log_e("Arbitration Lost Addr: %x", address >> 1); + } + else if(activeInt&I2C_TRANS_COMPLETE_INT_ST){// stop + stage = Stage_done; + reason = I2C_ERROR_OK; +// log_e("trans_Complete"); + } + else if(activeInt&I2C_TIME_OUT_INT_ST){// + stage = Stage_abort; + reason = I2C_ERROR_TIMEOUT; + + log_e("bit Timeout! int(%lx) stat(%lx) Addr: %x",activeInt, + i2c->dev->status_reg.val,address >> 1); + } + else if(activeInt&I2C_ACK_ERR_INT_ST){ + stage = Stage_abort; + reason = I2C_ERROR_ACK; + log_e("Ack Error Addr: %x", address >> 1); + } + i2c->dev->int_clr.val = activeInt; + break; + case Stage_done: // quiting Time! More Beer! +// log_e("done"); + emptyFifo(i2c,data,len,&index); + finished = true; + i2c->dev->int_clr.val = 0xFFFFFFFF; // kill them All! + break; + case Stage_abort : // on the Job accident, Call 911 + emptyFifo(i2c,data,len,&index); + i2c->dev->int_clr.val = 0xFFFFFFFF; // kill them All! + finished = true; + abort = true; + log_e("abort len=%d",index); + break; + default : + stage = Stage_abort; // should not happen + reason = I2C_ERROR_DEV; + log_e("Unknown Stage! Addr: %x", address >> 1); + } + } + +//i2c->dev->ctr.trans_start=0; // Pause Machine +i2c->dev->int_ena.val = 0; // disable all interrups +//i2c->dev->ctr.ms_mode=priorMode; // maybe also a slave? +if(reason!=I2C_ERROR_OK) { + log_e("exit =%d, cnt=%d got %d maxRead%d",reason,emptyCnt,index,maxRead); + uint16_t a=intIndex; + a = (a +1)%32; + while(a!=intIndex){ + if(intbuf[a] != a)log_e("[%02d]=0x%08lx",a,intbuf[a]); + a++; + a %=32; + } + if(intbuf[a] != a)log_e("[%02d]=0x%08lx",a,intbuf[a]); + dumpCmdQueue(i2c); + } +I2C_MUTEX_UNLOCK(); + +return reason; +} + i2c_err_t i2cSetFrequency(i2c_t * i2c, uint32_t clk_speed) { if(i2c == NULL){ @@ -450,7 +869,7 @@ i2c_t * i2cInit(uint8_t i2c_num, uint16_t slave_addr, bool addr_10bit_en) 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 = (slave_addr == 0); @@ -459,7 +878,7 @@ i2c_t * i2cInit(uint8_t i2c_num, uint16_t slave_addr, bool addr_10bit_en) i2c->dev->ctr.clk_en = 1; //the max clock number of receiving a data - i2c->dev->timeout.tout = 1048575;//clocks max=1048575 + i2c->dev->timeout.tout = 400000;//clocks max=1048575 //disable apb nonfifo access i2c->dev->fifo_conf.nonfifo_en = 0; @@ -507,3 +926,573 @@ void i2cReset(i2c_t* i2c){ I2C_MUTEX_UNLOCK(); } +//** 11/2017 Stickbreaker attempt at ISR for I2C hardware +// liberally stolen from ESP_IDF /drivers/i2c.c + +esp_err_t i2c_isr_free(intr_handle_t handle){ + +return esp_intr_free(handle); +} + +#define INTBUFFMAX 64 +static uint32_t intBuff[INTBUFFMAX][2]; +static uint32_t intPos=0; + +static void IRAM_ATTR fillCmdQueue(i2c_t * i2c, uint8_t cmdIdx){ +/* this function is call on initial i2cProcQueue() + or when a I2C_END_DETECT_INT occures +*/ + uint16_t qp = i2c->queuePos; + bool done; + bool needMoreCmds = false; + bool ena_rx=false; // if we add a read op, better enable Rx_Fifo + bool ena_tx=false; // if we add a Write op, better enable TX_Fifo + +while(!needMoreCmds&&(qp < i2c->queueCount)){ // check if more possible cmds + if(i2c->dq[qp].ctrl.stopCmdSent) { + qp++; + } + else needMoreCmds=true; + } +log_e("needMoreCmds=%d",needMoreCmds); +done=(!needMoreCmds)||(cmdIdx>14); + +while(!done){ // fill the command[] until either 0..14 or out of cmds +//CMD START + I2C_DATA_QUEUE_t *tdq=&i2c->dq[qp]; // simpler + + if(!tdq->ctrl.startCmdSent){ + i2cSetCmd(i2c, cmdIdx++, I2C_CMD_RSTART, 0, false, false, false); + tdq->ctrl.startCmdSent=1; + done = (cmdIdx>14); + } + +//CMD WRITE ADDRESS + if((!done)&&(tdq->ctrl.startCmdSent)){// have to leave room for continue + if(!tdq->ctrl.addrCmdSent){ + i2cSetCmd(i2c, cmdIdx++, I2C_CMD_WRITE, tdq->ctrl.addrReq, false, false, true); //load address in cmdlist, validate (low) ack + tdq->ctrl.addrCmdSent=1; + done =(cmdIdx>14); + ena_tx=true; + } + } + +/* Can I have another Sir? + ALL CMD queues must be terminated with either END or STOP. + if END is used, when refilling it next time, no entries from END to [15] can be used. + AND END must exit. The END command does not complete until the the + ctr->trans_start=1 has executed. + So, only refill from [0]..[14], leave [15] for a continuation if necessary. + As a corrilary, once END exists in [15], you do not need to overwrite it for the + next continuation. It is never modified. But, I update it every time because! + +*/ + if((!done)&&(tdq->ctrl.addrCmdSent)){ //room in command[] for at least One data (read/Write) cmd + uint8_t blkSize=0; // max is 255 + //log_e("needed=%2d index=%d",*neededRead,cmdIdx); + while(( tdq->cmdBytesNeeded > tdq->ctrl.mode )&&(!done )) { // more bytes needed and room in cmd queue, leave room for END + // lets start with 3 bytes at a time, but may 255 is possible + blkSize = (tdq->cmdBytesNeeded > 255)?255:(tdq->cmdBytesNeeded - tdq->ctrl.mode); // Last read cmd needs different ACK setting, so leave 1 byte remainer on reads + tdq->cmdBytesNeeded -= blkSize; // + if(tdq->ctrl.mode==1){ + i2cSetCmd(i2c, (cmdIdx)++, I2C_CMD_READ, blkSize,false,false,false); // read cmd, this can't be the last read. + ena_rx=true; + } + else {// write + i2cSetCmd(i2c, cmdIdx++, I2C_CMD_WRITE, blkSize, false, false, true); // check for Nak + ena_tx=true; + } + done = cmdIdx>14; + } + + if(!done){ // buffer is not filled completely + if((tdq->ctrl.mode==1)&&(tdq->cmdBytesNeeded==1)){ + i2cSetCmd(i2c, (cmdIdx)++, I2C_CMD_READ, 1,true,false,false); + // send NAK to mark end of read + tdq->cmdBytesNeeded=0; + done = cmdIdx > 14; + ena_rx=true; + } + tdq->ctrl.dataCmdSent=(tdq->cmdBytesNeeded==0); // enough command[] to send all data + } + + if((!done)&&(tdq->ctrl.dataCmdSent)){ // possibly add stop + if(tdq->ctrl.stop){ //send a stop + i2cSetCmd(i2c, (cmdIdx)++,I2C_CMD_STOP,0,false,false,false); + done = cmdIdx > 14; + tdq->ctrl.stopCmdSent = 1; + } + else {// dummy a stop because this is a restart + tdq->ctrl.stopCmdSent = 1; + } + } + } + + if(cmdIdx==15){ //need continuation, even if STOP is in 14, it will not matter + // cmd buffer is almost full, Add END as a continuation feature + // log_e("END at %d, left=%d",cmdIdx,neededRead); + i2cSetCmd(i2c, (cmdIdx)++,I2C_CMD_END, 0,false,false,false); + i2c->dev->int_ena.end_detect=1; //maybe? + i2c->dev->int_clr.end_detect=1; //maybe? + } + + if(!done){ + if(tdq->ctrl.stopCmdSent){ // this queue element has been completely added to command[] buffer + qp++; + if(qp < i2c->queueCount){ + tdq = &i2c->dq[qp]; + log_e("inc to next queue=%d",qp); + } + else done = true; + } + } + + }// while(!done) +if(ena_rx) i2c->dev->int_ena.rx_fifo_full = 1; +if(ena_tx) i2c->dev->int_ena.tx_fifo_empty = 1; +} + + +static void IRAM_ATTR dumpI2c(i2c_t * i2c){ +log_e("i2c=%p",i2c); +log_e("dev=%p",i2c->dev); +log_e("lock=%p",i2c->lock); +log_e("num=%d",i2c->num); +log_e("mode=%d",i2c->mode); +log_e("stage=%d",i2c->stage); +log_e("error=%d",i2c->error); +log_e("event=%p",i2c->i2c_event); +log_e("intr_handle=%p",i2c->intr_handle); +log_e("dq=%p",i2c->dq); +log_e("queueCount=%d",i2c->queueCount); +log_e("queuePos=%d",i2c->queuePos); +log_e("byteCnt=%d",i2c->byteCnt); +uint16_t a=0; +I2C_DATA_QUEUE_t *tdq; +while(aqueueCount){ + tdq=&i2c->dq[a]; + log_e("[%d] %x %c %s buf@=%p, len=%d, pos=%d",a,tdq->ctrl.addr,(tdq->ctrl.mode)?'R':'W',(tdq->ctrl.stop)?"STOP":"",tdq->data,tdq->length,tdq->position); + a++; + } +} + + +static void IRAM_ATTR fillTxFifo(i2c_t * i2c){ +uint16_t a=i2c->queuePos; +bool full=!(i2c->dev->status_reg.tx_fifo_cnt<31); +uint8_t cnt; +while((a < i2c->queueCount)&&(!full)){ + I2C_DATA_QUEUE_t *tdq = &i2c->dq[a]; + cnt=0; +// add to address to fifo + if(tdq->ctrl.addrSent < tdq->ctrl.addrReq){ // need to send address bytes + if(tdq->ctrl.addrReq==2){ //10bit + if(tdq->ctrl.addrSent==0){ //10bit highbyte needs sent + if(!full){ // room in fifo + i2c->dev->fifo_data.val = ((tdq->ctrl.addr>>8)&0xFF); + cnt++; + tdq->ctrl.addrSent=1; //10bit highbyte sent + } + } + full=!(i2c->dev->status_reg.tx_fifo_cnt<31); + + if(tdq->ctrl.addrSent==1){ //10bit Lowbyte needs sent + if(!full){ // room in fifo + i2c->dev->fifo_data.val = tdq->ctrl.addr&0xFF; + cnt++; + tdq->ctrl.addrSent=2; //10bit lowbyte sent + } + } + } + else { // 7bit} + if(tdq->ctrl.addrSent==0){ // 7bit Lowbyte needs sent + if(!full){ // room in fifo + i2c->dev->fifo_data.val = tdq->ctrl.addr&0xFF; + cnt++; + tdq->ctrl.addrSent=1; // 7bit lowbyte sent + } + } + } + } + full=!(i2c->dev->status_reg.tx_fifo_cnt<31); +// add write data to fifo + if(tdq->ctrl.mode==0){ // write + if(tdq->ctrl.addrSent == tdq->ctrl.addrReq){ //address has been sent, is Write Mode! + while((!full)&&(tdq->position < tdq->length)){ + i2c->dev->fifo_data.val = tdq->data[tdq->position++]; + cnt++; + full=!(i2c->dev->status_reg.tx_fifo_cnt<31); + } + } + } + cnt += intBuff[intPos][1]>>16; + intBuff[intPos][1] = (intBuff[intPos][1]&0xFFFF)|(cnt<<16); + if(!full) a++; // check next buffer for tx + } + +if(a < i2c->queueCount){ + i2c->dev->int_ena.tx_fifo_empty=1; + } +else { // no more data available, disable INT + i2c->dev->int_ena.tx_fifo_empty=0; + } +i2c->dev->int_clr.tx_fifo_empty=1; +} + +static void IRAM_ATTR emptyRxFifo(i2c_t * i2c){ +uint32_t d,cnt=0; +I2C_DATA_QUEUE_t *tdq =&i2c->dq[i2c->queuePos]; +if(tdq->ctrl.mode==1) { // read + while(i2c->dev->status_reg.rx_fifo_cnt>0){ + d = i2c->dev->fifo_data.val; + cnt++; + if(tdq->position < tdq->length) tdq->data[tdq->position++] = (d&0xFF); + } +// d = I2C_RXFIFO_FULL_INT_ST; +// i2c->dev->int_clr.val =d; // processed! + cnt += (intBuff[intPos][1])&&0xffFF; + intBuff[intPos][1] = (intBuff[intPos][1]&0xFFFF0000)|cnt; + } +else { + log_e("RxEmpty on TxBuffer? %d",i2c->queuePos); +// dumpI2c(i2c); + } +//log_e("emptied %d",*index); +} + +static void IRAM_ATTR nextQueue(i2c_t * i2c){ + /* This function is call when I2C_TRANS_START_INT is encountred + */ + // handle first queue entry correctly +if(i2c->stage != I2C_STARTUP){ + +//empty RXFIFO for current queue, what about First Time? + emptyRxFifo(i2c); +//roll to next queue entry + i2c->queuePos++; + } +// stuff txFifo with Addr +fillTxFifo(i2c); +} + +static void IRAM_ATTR i2c_isr_handler_default(void* arg){ +//log_e("isr Entry=%p",arg); +i2c_t* p_i2c = (i2c_t*) arg; // recover data +uint32_t activeInt = p_i2c->dev->int_status.val&0x1FFF; +//log_e("int=%x",activeInt); +//dumpI2c(p_i2c); + +portBASE_TYPE HPTaskAwoken = pdFALSE; +// be polite if someone more important wakes up. +//log_e("setbits(%p)=%p",p_i2c->i2c_event); + +xEventGroupSetBitsFromISR(p_i2c->i2c_event, EVENT_IN_IRQ, &HPTaskAwoken); +if (HPTaskAwoken == pdTRUE) {//higher pri task has awoken, jump to it + portYIELD_FROM_ISR(); + } +//log_e("stage"); + +if(p_i2c->stage==I2C_DONE){ //get Out + p_i2c->dev->int_ena.val = 0; + p_i2c->dev->int_clr.val = 0x1FFF; + log_e("eject int=%p",activeInt); + xEventGroupClearBitsFromISR(p_i2c->i2c_event, EVENT_IN_IRQ); + return; + } +while (activeInt != 0) { // pending + if(activeInt==(intBuff[intPos][0]&0x1fff)){ + intBuff[intPos][0] = (((intBuff[intPos][0]>>16)+1)<<16)|activeInt; + } + else{ + intPos++; + intPos %= INTBUFFMAX; + intBuff[intPos][0]=(1<<16)|activeInt; + } + + uint32_t oldInt =activeInt; + + if (activeInt & I2C_TRANS_START_INT_ST_M) { + // p_i2c->byteCnt=0; + // nextQueue(p_i2c); + if(p_i2c->stage==I2C_STARTUP){ + p_i2c->stage=I2C_RUNNING; + xEventGroupClearBitsFromISR(p_i2c->i2c_event, EVENT_DONE); + xEventGroupSetBitsFromISR(p_i2c->i2c_event, EVENT_RUNNING, &HPTaskAwoken); + if (HPTaskAwoken == pdTRUE) {//higher pri task has awoken, jump to it + portYIELD_FROM_ISR(); + } + } + activeInt &=~I2C_TRANS_START_INT_ST_M; + p_i2c->dev->int_ena.trans_start = 1; + p_i2c->dev->int_clr.trans_start = 1; + } + + if (activeInt & I2C_TXFIFO_EMPTY_INT_ST) {//should this be before Trans_start? + fillTxFifo(p_i2c); + activeInt&=~I2C_TXFIFO_EMPTY_INT_ST; + } + + if(activeInt & I2C_RXFIFO_FULL_INT_ST){ + emptyRxFifo(p_i2c); + p_i2c->dev->int_clr.rx_fifo_full=1; + p_i2c->dev->int_ena.rx_fifo_full=1; + + activeInt &=~I2C_RXFIFO_FULL_INT_ST; + } + + if(activeInt & I2C_MASTER_TRAN_COMP_INT_ST){ // each byte the master sends/recv + p_i2c->dev->int_clr.master_tran_comp = 1; + p_i2c->byteCnt++; + if(p_i2c->byteCnt > p_i2c->dq[p_i2c->queuePos].queueLength){// simulate Trans_start + p_i2c->byteCnt -= p_i2c->dq[p_i2c->queuePos].queueLength; + if(p_i2c->dq[p_i2c->queuePos].ctrl.mode==1){ + emptyRxFifo(p_i2c); + p_i2c->dev->int_clr.rx_fifo_full=1; + p_i2c->dev->int_ena.rx_fifo_full=1; + } + p_i2c->queuePos++; + p_i2c->dev->int_ena.tx_fifo_empty=1; + +// nextQueue(p_i2c); + + } + activeInt &=~I2C_MASTER_TRAN_COMP_INT_ST; + } + + if (activeInt & I2C_ACK_ERR_INT_ST_M) { + p_i2c->dev->int_clr.ack_err = 1; + if (p_i2c->mode == I2C_MASTER) { + p_i2c->error = I2C_ERROR; + log_e("AcK Err byteCnt=%d, queuepos=%d",p_i2c->byteCnt,p_i2c->queuePos); + if(p_i2c->byteCnt<2) p_i2c->error = I2C_ADDR_ACK; + else if((p_i2c->byteCnt == 2) && (p_i2c->dq[p_i2c->queuePos].ctrl.addrReq == 2)) p_i2c->error = I2C_ADDR_ACK; + else p_i2c->error = I2C_DATA_ACK; + } + p_i2c->stage = I2C_DONE; + p_i2c->dev->int_ena.val = 0; // kill them all + p_i2c->dev->int_clr.val = 0xFFFFFF; + xEventGroupClearBitsFromISR(p_i2c->i2c_event, EVENT_RUNNING|EVENT_IN_IRQ); + xEventGroupSetBitsFromISR(p_i2c->i2c_event, EVENT_DONE, &HPTaskAwoken); + if (HPTaskAwoken == pdTRUE) {//higher pri task has awoken, jump to it + portYIELD_FROM_ISR(); + } + return; + activeInt &=~I2C_ACK_ERR_INT_ST_M; + } + + if (activeInt & I2C_TIME_OUT_INT_ST_M) {//death Happens Here + p_i2c->dev->int_clr.time_out = 1; + p_i2c->error = I2C_TIMEOUT; + p_i2c->stage = I2C_DONE; + p_i2c->dev->int_ena.val = 0; // kill them all + p_i2c->dev->int_clr.val = 0xFFFFFF; + xEventGroupClearBitsFromISR(p_i2c->i2c_event, EVENT_RUNNING|EVENT_IN_IRQ); + xEventGroupSetBitsFromISR(p_i2c->i2c_event, EVENT_DONE, &HPTaskAwoken); + if (HPTaskAwoken == pdTRUE) {//higher pri task has awoken, jump to it + portYIELD_FROM_ISR(); + } + return; + activeInt &=~I2C_TIME_OUT_INT_ST_M; + } + + if (activeInt & I2C_TRANS_COMPLETE_INT_ST_M) { + if(p_i2c->dq[p_i2c->queuePos].ctrl.mode == 1) emptyRxFifo(p_i2c); // grab last few characters + p_i2c->stage = I2C_DONE; + p_i2c->dev->int_ena.val = 0; // shutdown interrupts + p_i2c->dev->int_clr.val = 0xFFFFFF; + xEventGroupClearBitsFromISR(p_i2c->i2c_event, EVENT_RUNNING); + xEventGroupSetBitsFromISR(p_i2c->i2c_event, EVENT_DONE, &HPTaskAwoken); + if (HPTaskAwoken == pdTRUE) {//higher pri task has awoken, jump to it + portYIELD_FROM_ISR(); + } + return; // no more work to do +/* + // how does a restart appear? + // how does slave mode act? + + if (p_i2c->mode == I2C_SLAVE) { // STOP detected + // empty fifo + // dispatch callback +*/ + activeInt &=~I2C_TRANS_COMPLETE_INT_ST_M; + } + + if (activeInt & I2C_ARBITRATION_LOST_INT_ST_M) { + p_i2c->dev->int_clr.arbitration_lost = 1; + p_i2c->error = I2C_ARBITRATION; + p_i2c->stage = I2C_DONE; + p_i2c->dev->int_ena.val = 0; // shutdown interrupts + p_i2c->dev->int_clr.val = 0xFFFFFF; + xEventGroupClearBitsFromISR(p_i2c->i2c_event, EVENT_RUNNING); + xEventGroupSetBitsFromISR(p_i2c->i2c_event, EVENT_DONE, &HPTaskAwoken); + if (HPTaskAwoken == pdTRUE) {//higher pri task has awoken, jump to it + portYIELD_FROM_ISR(); + } + return; // no more work to do + activeInt &=~I2C_ARBITRATION_LOST_INT_ST_M; + } + + if (activeInt & I2C_SLAVE_TRAN_COMP_INT_ST_M) { + p_i2c->dev->int_clr.slave_tran_comp = 1; + + } + + if (activeInt & I2C_END_DETECT_INT_ST_M) { + xEventGroupSetBitsFromISR(p_i2c->i2c_event, EVENT_IN_END, &HPTaskAwoken); + if (HPTaskAwoken == pdTRUE) {//higher pri task has awoken, jump to it + portYIELD_FROM_ISR(); + } + p_i2c->dev->int_ena.end_detect = 0; + p_i2c->dev->int_clr.end_detect = 1; + p_i2c->dev->ctr.trans_start=0; + fillCmdQueue(p_i2c,0); + p_i2c->dev->ctr.trans_start=1; // go for it + activeInt&=~I2C_END_DETECT_INT_ST_M; + // What about Tx, RX fifo fill? + xEventGroupClearBitsFromISR(p_i2c->i2c_event, EVENT_IN_END); + } + + if (activeInt & I2C_RXFIFO_OVF_INT_ST_M) { + p_i2c->dev->int_clr.rx_fifo_ovf = 1; + } + + if(activeInt){ // clear unhandled if possible? What about Disableing interrupt? + p_i2c->dev->int_clr.val = activeInt; + log_e("unknown int=%x",activeInt); + } + + activeInt = p_i2c->dev->int_status.val; // start all over + /* if((activeInt&oldInt)==oldInt){ + log_e("dup int old=%p, new=%p dup=%p",oldInt,activeInt,(oldInt&activeInt)); + } +*/ + } + +xEventGroupClearBitsFromISR(p_i2c->i2c_event, EVENT_IN_IRQ); +// and we're out! +} + +i2c_err_t i2cProcQueue(i2c_t * i2c){ +/* do the hard stuff here + 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); + +if(i2c == NULL){ + return I2C_ERROR_DEV; + } + +I2C_MUTEX_LOCK(); +i2c->stage = I2C_DONE; // until ready + +for(intPos=0;intPosi2c_event){ + log_e("create Event"); + i2c->i2c_event = xEventGroupCreate(); + log_e("event=%p",i2c->i2c_event); + } +if(i2c->i2c_event) { + uint32_t ret=xEventGroupClearBits(i2c->i2c_event, EVENT_IN_IRQ|EVENT_RUNNING|EVENT_DONE); + log_e("after clearBits(%p)=%p",i2c->i2c_event,ret); + } +uint32_t startAt = millis(); + +i2c_err_t reason = I2C_ERROR_OK; +i2c->mode = I2C_MASTER; + +i2c->dev->ctr.trans_start=0; // Pause Machine +i2c->dev->timeout.tout = 0xFFFFF; // max 1M +I2C_FIFO_CONF_t f; +f.val = i2c->dev->fifo_conf.val; +f.rx_fifo_rst = 1; // fifo in reset +f.tx_fifo_rst = 1; // fifo in reset +f.nonfifo_en = 0; // use fifo mode +f.rx_fifo_full_thrhd = 30; // six bytes before INT is issued +f.fifo_addr_cfg_en = 0; // no directed access +i2c->dev->fifo_conf.val = f.val; // post them all + +f.rx_fifo_rst = 0; // release fifo +f.tx_fifo_rst = 0; +i2c->dev->fifo_conf.val = f.val; // post them all + +i2c->dev->int_clr.val = 0xFFFFFFFF; // kill them All! +i2c->dev->ctr.ms_mode = 1; // master! +i2c->queuePos=0; +i2c->byteCnt=0; +// convert address field to required I2C format +while(i2c->queuePosqueueCount){ + I2C_DATA_QUEUE_t *tdq = &i2c->dq[i2c->queuePos++]; + uint16_t taddr=0; + if(tdq->ctrl.addrReq ==2){ // 10bit address + taddr =((tdq->ctrl.addr>>7)&0xFE) + |tdq->ctrl.mode; + taddr = (taddr <<8)||(tdq->ctrl.addr&0xFF); + } + else { // 7bit address + taddr = ((tdq->ctrl.addr<<1)&0xFE) + |tdq->ctrl.mode; + } + tdq->ctrl.addr = taddr; // all fixed with R/W bit + } +i2c->queuePos=0; + +fillCmdQueue(i2c,0); // start adding command[], END irq will keep it full + +//fillTxFifo(i2c); // I2C_TRANS_START_INT will do this + +i2c->stage = I2C_STARTUP; // + +i2c->dev->int_ena.val = + I2C_ACK_ERR_INT_ENA | // (BIT(10)) + I2C_TRANS_START_INT_ENA | // (BIT(9)) + I2C_TIME_OUT_INT_ENA | //(BIT(8)) + I2C_TRANS_COMPLETE_INT_ENA | // (BIT(7)) + I2C_MASTER_TRAN_COMP_INT_ENA | // (BIT(6)) + I2C_ARBITRATION_LOST_INT_ENA | // (BIT(5)) + I2C_SLAVE_TRAN_COMP_INT_ENA | // (BIT(4)) + I2C_END_DETECT_INT_ENA | // (BIT(3)) + I2C_RXFIFO_OVF_INT_ENA | //(BIT(2)) + I2C_TXFIFO_EMPTY_INT_ENA | // (BIT(1)) + I2C_RXFIFO_FULL_INT_ENA; // (BIT(0)) + + +dumpI2c(i2c); +dumpCmdQueue(i2c); +if(!i2c->intr_handle){ // create ISR +// 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(ret!=ESP_OK){ + log_e("install interrupt=%d",ret); + } + } + +log_e("before startup"); +i2c->dev->ctr.trans_start=1; // go for it +uint32_t eBits = xEventGroupWaitBits(i2c->i2c_event,EVENT_DONE,pdTRUE,pdTRUE,0xFF); +log_e("after Wait"); +dumpI2c(i2c); +uint32_t b; +for(uint32_t a=1;a<=INTBUFFMAX;a++){ + b=(a+intPos)%INTBUFFMAX; + if(intBuff[b][0]!=0) log_e("[%02d] 0x%08x 0x%08x",b,intBuff[b][0],intBuff[b][1]); + } + +if(eBits&EVENT_DONE){ // no gross timeout + reason = i2c->error; // not correct + } +else { // GROSS timeout, shutdown ISR , report Timeout + log_e(" Gross Timeout Dead"); + } +I2C_MUTEX_UNLOCK(); + +return reason; +} + diff --git a/cores/esp32/esp32-hal-i2c.h b/cores/esp32/esp32-hal-i2c.h index f26c7ab2a60..a4e0efa051a 100644 --- a/cores/esp32/esp32-hal-i2c.h +++ b/cores/esp32/esp32-hal-i2c.h @@ -21,6 +21,7 @@ extern "C" { #include #include +#include typedef enum { I2C_ERROR_OK, @@ -28,9 +29,67 @@ typedef enum { I2C_ERROR_ACK, I2C_ERROR_TIMEOUT, I2C_ERROR_BUS, - I2C_ERROR_BUSY + I2C_ERROR_BUSY, + I2C_ERROR_MEMORY, + I2C_ERROR_CONTINUE } i2c_err_t; +typedef enum { + //I2C_NONE=0, + I2C_STARTUP=1, + I2C_RUNNING, + I2C_DONE +} I2C_STAGE_t; + +typedef enum { + I2C_NONE=0, + I2C_MASTER, + I2C_SLAVE, + I2C_MASTERSLAVE +}I2C_MODE_t; + + +typedef enum { +// I2C_NONE=0, + I2C_OK=1, + I2C_ERROR, + I2C_ADDR_ACK, + I2C_DATA_ACK, + I2C_ARBITRATION, + I2C_TIMEOUT +}I2C_ERROR_t; + +// i2c_event bits +#define EVENT_IN_IRQ (BIT(2)) +#define EVENT_RUNNING (BIT(3)) +#define EVENT_DONE (BIT(4)) +#define EVENT_IN_END (BIT(5)) + +typedef union{ + struct { + uint32_t addr: 16; // I2C address, if 10bit must have 0x7800 mask applied, else 8bit + uint32_t mode: 1; // 0 write, 1 read + uint32_t stop: 1; // 0 no, 1 yes + uint32_t startCmdSent: 1; // START cmd has been added to command[] + uint32_t addrCmdSent: 1; // addr WRITE cmd has been added to command[] + uint32_t dataCmdSent: 1; // all necessary DATA(READ/WRITE) cmds added to command[] + uint32_t stopCmdSent: 1; // completed all necessary commands + uint32_t addrReq: 2; // number of addr bytes need to send address + uint32_t addrSent: 2; // number of addr bytes added to FIFO + uint32_t reserved_31: 6; + }; + uint32_t val; + }I2C_DATA_CTRL_t; + +typedef struct { + uint8_t *data; // datapointer for read/write buffer + uint16_t length; // size of data buffer + uint16_t position; // current position for next char in buffer (Slave direction on the bus. IT IS NOT limited to transactions referencing THIS SLAVE*/ uint32_t time_out: 1; /*when I2C takes more than time_out_reg clocks to receive a data then this register changes to high level.*/ uint32_t arb_lost: 1; /*when I2C lost control of SDA line this register changes to high level.*/ uint32_t bus_busy: 1; /*1:I2C bus is busy transferring data. 0:I2C bus is in idle state.*/ uint32_t slave_addressed: 1; /*when configured as i2c slave and the address send by master is equal to slave's address then this bit will be high level.*/ + /*chuck Slave Mode receive: non persistant, set when last byte received Matched Slave Id. As soon as first byte of data received, this bit is cleared. No Interrupt generated*/ uint32_t byte_trans: 1; /*This register changes to high level when one byte is transferred.*/ uint32_t reserved7: 1; uint32_t rx_fifo_cnt: 6; /*This register represent the amount of data need to send.*/ @@ -60,129 +101,72 @@ typedef volatile struct { uint32_t reserved31: 1; }; uint32_t val; - } status_reg; - union { - struct { - uint32_t tout: 20; /*This register is used to configure the max clock number of receiving a data, unit: APB clock cycle.*/ - uint32_t reserved20:12; - }; - uint32_t val; - } timeout; - union { - struct { - uint32_t addr: 15; /*when configured as i2c slave this register is used to configure slave's address.*/ - uint32_t reserved15: 16; - uint32_t en_10bit: 1; /*This register is used to enable slave 10bit address mode.*/ - }; - uint32_t val; - } slave_addr; - union { - struct { - uint32_t rx_fifo_start_addr: 5; /*This is the offset address of the last receiving data as described in nonfifo_rx_thres_register.*/ - uint32_t rx_fifo_end_addr: 5; /*This is the offset address of the first receiving data as described in nonfifo_rx_thres_register.*/ - uint32_t tx_fifo_start_addr: 5; /*This is the offset address of the first sending data as described in nonfifo_tx_thres register.*/ - uint32_t tx_fifo_end_addr: 5; /*This is the offset address of the last sending data as described in nonfifo_tx_thres register.*/ - uint32_t reserved20: 12; - }; - uint32_t val; - } fifo_st; - union { - struct { - uint32_t rx_fifo_full_thrhd: 5; - uint32_t tx_fifo_empty_thrhd:5; /*Config tx_fifo empty threhd value when using apb fifo access*/ - uint32_t nonfifo_en: 1; /*Set this bit to enble apb nonfifo access.*/ - uint32_t fifo_addr_cfg_en: 1; /*When this bit is set to 1 then the byte after address represent the offset address of I2C Slave's ram.*/ - uint32_t rx_fifo_rst: 1; /*Set this bit to reset rx fifo when using apb fifo access.*/ - uint32_t tx_fifo_rst: 1; /*Set this bit to reset tx fifo when using apb fifo access.*/ - uint32_t nonfifo_rx_thres: 6; /*when I2C receives more than nonfifo_rx_thres data it will produce rx_send_full_int_raw interrupt and update the current offset address of the receiving data.*/ - uint32_t nonfifo_tx_thres: 6; /*when I2C sends more than nonfifo_tx_thres data it will produce tx_send_empty_int_raw interrupt and update the current offset address of the sending data.*/ - uint32_t reserved26: 6; - }; - uint32_t val; - } fifo_conf; - union { - struct { - uint8_t data; /*The register represent the byte data read from rx_fifo when use apb fifo access*/ - uint8_t reserved[3]; - }; - uint32_t val; - } fifo_data; - union { + } I2C_STATUS_REG_t; + +typedef union { struct { uint32_t rx_fifo_full: 1; /*The raw interrupt status bit for rx_fifo full when use apb fifo access.*/ uint32_t tx_fifo_empty: 1; /*The raw interrupt status bit for tx_fifo empty when use apb fifo access.*/ + /*chuck will only clear if tx_fifo has more than fifo_conf.tx_fifo_empty_thrhd bytes in it.*/ uint32_t rx_fifo_ovf: 1; /*The raw interrupt status bit for receiving data overflow when use apb fifo access.*/ uint32_t end_detect: 1; /*The raw interrupt status bit for end_detect_int interrupt. when I2C deals with the END command it will produce end_detect_int interrupt.*/ uint32_t slave_tran_comp: 1; /*The raw interrupt status bit for slave_tran_comp_int interrupt. when I2C Slave detects the STOP bit it will produce slave_tran_comp_int interrupt.*/ + /*chuck Slave Mode: actually triggered after receipt of Slave Address. */ uint32_t arbitration_lost: 1; /*The raw interrupt status bit for arbitration_lost_int interrupt.when I2C lost the usage right of I2C BUS it will produce arbitration_lost_int interrupt.*/ uint32_t master_tran_comp: 1; /*The raw interrupt status bit for master_tra_comp_int interrupt. when I2C Master sends or receives a byte it will produce master_tran_comp_int interrupt.*/ uint32_t trans_complete: 1; /*The raw interrupt status bit for trans_complete_int interrupt. when I2C Master finished STOP command it will produce trans_complete_int interrupt.*/ + /*chuck Slave Mode: triggerd when STOP is seen on the Bus. ANY STOP including those generated by OTHER MASTERS TALKING with OTHER SLAVES */ uint32_t time_out: 1; /*The raw interrupt status bit for time_out_int interrupt. when I2C takes a lot of time to receive a data it will produce time_out_int interrupt.*/ uint32_t trans_start: 1; /*The raw interrupt status bit for trans_start_int interrupt. when I2C sends the START bit it will produce trans_start_int interrupt.*/ + /*chuck Only issued after ctr.trans_start=1 and a START has been sent. It does not fire on a ReSTART */ uint32_t ack_err: 1; /*The raw interrupt status bit for ack_err_int interrupt. when I2C receives a wrong ACK bit it will produce ack_err_int interrupt..*/ + /*chuck SLAVE MODE: triggered WHENEVER a NAK is seen on the BUS. If another master does an presense detect, this interrupt will be triggered on every failed acknowledgement*/ uint32_t rx_rec_full: 1; /*The raw interrupt status bit for rx_rec_full_int interrupt. when I2C receives more data than nonfifo_rx_thres it will produce rx_rec_full_int interrupt.*/ uint32_t tx_send_empty: 1; /*The raw interrupt status bit for tx_send_empty_int interrupt.when I2C sends more data than nonfifo_tx_thres it will produce tx_send_empty_int interrupt..*/ uint32_t reserved13: 19; }; uint32_t val; - } int_raw; - union { + } I2C_INTERRUPT_t; + +typedef volatile struct { + I2C_SCL_LOW_PERIOD_t scl_low_period; + I2C_CTR_t ctr; + I2C_STATUS_REG_t status_reg; + union { struct { - uint32_t rx_fifo_full: 1; /*Set this bit to clear the rx_fifo_full_int interrupt.*/ - uint32_t tx_fifo_empty: 1; /*Set this bit to clear the tx_fifo_empty_int interrupt.*/ - uint32_t rx_fifo_ovf: 1; /*Set this bit to clear the rx_fifo_ovf_int interrupt.*/ - uint32_t end_detect: 1; /*Set this bit to clear the end_detect_int interrupt.*/ - uint32_t slave_tran_comp: 1; /*Set this bit to clear the slave_tran_comp_int interrupt.*/ - uint32_t arbitration_lost: 1; /*Set this bit to clear the arbitration_lost_int interrupt.*/ - uint32_t master_tran_comp: 1; /*Set this bit to clear the master_tran_comp interrupt.*/ - uint32_t trans_complete: 1; /*Set this bit to clear the trans_complete_int interrupt.*/ - uint32_t time_out: 1; /*Set this bit to clear the time_out_int interrupt.*/ - uint32_t trans_start: 1; /*Set this bit to clear the trans_start_int interrupt.*/ - uint32_t ack_err: 1; /*Set this bit to clear the ack_err_int interrupt.*/ - uint32_t rx_rec_full: 1; /*Set this bit to clear the rx_rec_full_int interrupt.*/ - uint32_t tx_send_empty: 1; /*Set this bit to clear the tx_send_empty_int interrupt.*/ - uint32_t reserved13: 19; + uint32_t tout: 20; /*This register is used to configure the max clock number of receiving a data, unit: APB clock cycle.*/ + uint32_t reserved20:12; }; uint32_t val; - } int_clr; + } timeout; union { struct { - uint32_t rx_fifo_full: 1; /*The enable bit for rx_fifo_full_int interrupt.*/ - uint32_t tx_fifo_empty: 1; /*The enable bit for tx_fifo_empty_int interrupt.*/ - uint32_t rx_fifo_ovf: 1; /*The enable bit for rx_fifo_ovf_int interrupt.*/ - uint32_t end_detect: 1; /*The enable bit for end_detect_int interrupt.*/ - uint32_t slave_tran_comp: 1; /*The enable bit for slave_tran_comp_int interrupt.*/ - uint32_t arbitration_lost: 1; /*The enable bit for arbitration_lost_int interrupt.*/ - uint32_t master_tran_comp: 1; /*The enable bit for master_tran_comp_int interrupt.*/ - uint32_t trans_complete: 1; /*The enable bit for trans_complete_int interrupt.*/ - uint32_t time_out: 1; /*The enable bit for time_out_int interrupt.*/ - uint32_t trans_start: 1; /*The enable bit for trans_start_int interrupt.*/ - uint32_t ack_err: 1; /*The enable bit for ack_err_int interrupt.*/ - uint32_t rx_rec_full: 1; /*The enable bit for rx_rec_full_int interrupt.*/ - uint32_t tx_send_empty: 1; /*The enable bit for tx_send_empty_int interrupt.*/ - uint32_t reserved13: 19; + uint32_t addr: 16; /*when configured as i2c slave this register is used to configure slave's address.*/ + /*Stickchuck when using 10bit address: to comply with industry standard format, the bit order of address must be adjusted. + slave_addr.addr=((slaveId&0xff)<<7)|(((slaveId>>8)&0x3)|0x78); + The ( | 0x78) mask is used to avoid collision with 7bit device. 7bit device address are limited to 0x00..0x77*/ + + uint32_t reserved15: 15; + uint32_t en_10bit: 1; /*This register is used to enable slave 10bit address mode.*/ }; uint32_t val; - } int_ena; + } slave_addr; union { struct { - uint32_t rx_fifo_full: 1; /*The masked interrupt status for rx_fifo_full_int interrupt.*/ - uint32_t tx_fifo_empty: 1; /*The masked interrupt status for tx_fifo_empty_int interrupt.*/ - uint32_t rx_fifo_ovf: 1; /*The masked interrupt status for rx_fifo_ovf_int interrupt.*/ - uint32_t end_detect: 1; /*The masked interrupt status for end_detect_int interrupt.*/ - uint32_t slave_tran_comp: 1; /*The masked interrupt status for slave_tran_comp_int interrupt.*/ - uint32_t arbitration_lost: 1; /*The masked interrupt status for arbitration_lost_int interrupt.*/ - uint32_t master_tran_comp: 1; /*The masked interrupt status for master_tran_comp_int interrupt.*/ - uint32_t trans_complete: 1; /*The masked interrupt status for trans_complete_int interrupt.*/ - uint32_t time_out: 1; /*The masked interrupt status for time_out_int interrupt.*/ - uint32_t trans_start: 1; /*The masked interrupt status for trans_start_int interrupt.*/ - uint32_t ack_err: 1; /*The masked interrupt status for ack_err_int interrupt.*/ - uint32_t rx_rec_full: 1; /*The masked interrupt status for rx_rec_full_int interrupt.*/ - uint32_t tx_send_empty: 1; /*The masked interrupt status for tx_send_empty_int interrupt.*/ - uint32_t reserved13: 19; + uint32_t rx_fifo_start_addr: 5; /*This is the offset address of the last receiving data as described in nonfifo_rx_thres_register.*/ + uint32_t rx_fifo_end_addr: 5; /*This is the offset address of the first receiving data as described in nonfifo_rx_thres_register.*/ + uint32_t tx_fifo_start_addr: 5; /*This is the offset address of the first sending data as described in nonfifo_tx_thres register.*/ + uint32_t tx_fifo_end_addr: 5; /*This is the offset address of the last sending data as described in nonfifo_tx_thres register.*/ + uint32_t reserved20: 12; }; uint32_t val; - } int_status; + } fifo_st; + I2C_FIFO_CONF_t fifo_conf; + I2C_FIFO_DATA_t fifo_data; + I2C_INTERRUPT_t int_raw; + I2C_INTERRUPT_t int_clr; + I2C_INTERRUPT_t int_ena; + I2C_INTERRUPT_t int_status; union { struct { uint32_t time: 10; /*This register is used to configure the clock num I2C used to hold the data after the negedge of SCL.*/ @@ -249,18 +233,19 @@ typedef volatile struct { }; uint32_t val; } sda_filter_cfg; - union { +/* union { struct { - uint32_t byte_num: 8; /*Byte_num represent the number of data need to be send or data need to be received.*/ - uint32_t ack_en: 1; /*ack_check_en ack_exp and ack value are used to control the ack bit.*/ - uint32_t ack_exp: 1; /*ack_check_en ack_exp and ack value are used to control the ack bit.*/ - uint32_t ack_val: 1; /*ack_check_en ack_exp and ack value are used to control the ack bit.*/ - uint32_t op_code: 3; /*op_code is the command 0:RSTART 1:WRITE 2:READ 3:STOP . 4:END.*/ + uint32_t byte_num: 8; //Byte_num represent the number of data need to be send or data need to be received. + uint32_t ack_en: 1; //ack_check_en ack_exp and ack value are used to control the ack bit. + uint32_t ack_exp: 1; //ack_check_en ack_exp and ack value are used to control the ack bit. + uint32_t ack_val: 1; //ack_check_en ack_exp and ack value are used to control the ack bit. + uint32_t op_code: 3; //op_code is the command 0:RSTART 1:WRITE 2:READ 3:STOP . 4:END. uint32_t reserved14: 17; - uint32_t done: 1; /*When command0 is done in I2C Master mode this bit changes to high level.*/ + uint32_t done: 1; //When command0 is done in I2C Master mode this bit changes to high level. }; uint32_t val; - } command[16]; + } */ + I2C_COMMAND_t command[16]; uint32_t reserved_98; uint32_t reserved_9c; uint32_t reserved_a0; @@ -289,6 +274,7 @@ typedef volatile struct { uint32_t reserved_fc; uint32_t ram_data[32]; /*This the start address for ram when use apb nonfifo access.*/ } i2c_dev_t; + extern i2c_dev_t I2C0; extern i2c_dev_t I2C1; From d2efcfe13ca04538253de8ab6c17ca04b62a9b77 Mon Sep 17 00:00:00 2001 From: Chuck Todd Date: Mon, 13 Nov 2017 20:21:51 -0700 Subject: [PATCH 02/63] Support for Interrupt Support for interrupt --- libraries/Wire/src/Wire.cpp | 149 +++++++++++++++++++++++++++++++++++- libraries/Wire/src/Wire.h | 20 ++++- 2 files changed, 166 insertions(+), 3 deletions(-) diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp index ba670eaf63a..83c036b92dd 100644 --- a/libraries/Wire/src/Wire.cpp +++ b/libraries/Wire/src/Wire.cpp @@ -19,6 +19,7 @@ Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts Modified December 2014 by Ivan Grokhotkov (ivan@esp8266.com) - esp8266 support Modified April 2015 by Hrsto Gochkov (ficeto@ficeto.com) - alternative esp8266 support + Modified Nov 2017 by Chuck Todd (ctodd@cableone.net) - ESP32 support */ extern "C" { @@ -30,6 +31,31 @@ extern "C" { #include "esp32-hal-i2c.h" #include "Wire.h" #include "Arduino.h" +/* Declarations to support Slave Mode */ +#include "esp_attr.h" +#include "soc/i2c_reg.h" +#include "soc/i2c_struct.h" + +user_onRequest TwoWire::uReq[2]; +user_onReceive TwoWire::uRcv[2]; + +#define DR_REG_I2C_EXT_BASE_FIXED 0x60013000 +#define DR_REG_I2C1_EXT_BASE_FIXED 0x60027000 + +/* + +void IRAM_ATTR slave_isr_handler(void* arg){ + // ... +uint32_t num = (uint32_t)arg; +i2c_dev_t * dev; +if(num==0) dev=(volatile i2c_dev_t*)DR_REG_I2C_EXT_BASE_FIXED; +else dev=(volatile i2c_dev_t*)DR_REG_I2C1_EXT_BASE_FIXED; + +uint32_t stat = dev->int_status.val; + +} +*/ + TwoWire::TwoWire(uint8_t bus_num) :num(bus_num & 1) @@ -42,7 +68,10 @@ TwoWire::TwoWire(uint8_t bus_num) ,txLength(0) ,txAddress(0) ,transmitting(0) -{} + {} + +void TwoWire::onRequestService(void){} +void TwoWire::onReceiveService(uint8_t*buf, int count){} void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency) { @@ -68,6 +97,8 @@ void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency) return; } } + uReq[num] =NULL; + uRcv[num] =NULL; i2cSetFrequency(i2c, frequency); @@ -100,12 +131,126 @@ size_t TwoWire::requestFrom(uint8_t address, size_t size, bool sendStop) if(size > I2C_BUFFER_LENGTH) { size = I2C_BUFFER_LENGTH; } - size_t read = (i2cRead(i2c, address, false, rxBuffer, size, sendStop) == 0)?size:0; + last_error = i2cRead(i2c, address, false, rxBuffer, size, sendStop); + size_t read = (last_error == 0)?size:0; rxIndex = 0; rxLength = read; return read; } +size_t TwoWire::newRequestFrom(uint8_t address, size_t size, bool sendStop){ + + uint16_t cnt = queuedRxLength; // currently queued reads + if(cntI2C_BUFFER_LENGTH) + size = (I2C_BUFFER_LENGTH-cnt); + } + else { // no room to receive more! + log_e("no room %d",cnt); + cnt = 0; + rxIndex = 0; + rxLength = 0; + last_error = I2C_ERROR_MEMORY; + return cnt; + } + last_error =i2cAddQueueRead(i2c,address,&rxBuffer[cnt],size,sendStop); + if(last_error==I2C_ERROR_OK){ // successfully queued the read + queuedRxLength += size; + if(sendStop){ //now actually process the queued commands + last_error=i2cProcQueue(i2c); + if(last_error!=I2C_ERROR_OK){ + log_e("err=%d",last_error); + } + rxIndex = 0; + rxLength = i2cQueueReadCount(i2c); + queuedRxLength = 0; + cnt = rxLength; + i2cFreeQueue(i2c); + } + else { // stop not received, so wait for I2C stop, + last_error=I2C_ERROR_CONTINUE; + cnt = 0; + } + } + else {// only possible error is I2C_ERROR_MEMORY + cnt = 0; + } + return cnt; +} + +size_t TwoWire::newRequestFrom(uint8_t address, uint8_t* buf, size_t size, bool sendStop){ + rxIndex = 0; + rxLength = 0; + if(size==0){ + return 0; + } + last_error =pollI2cRead(i2c, address, false, buf, size, sendStop); + if(last_error!=0){ + log_e("err=%d",last_error); + } + size_t read = (last_error==0)?size:0; + return read; +} + +size_t TwoWire::transact(size_t readLen){ // Assumes Wire.beginTransaction(),Wire.write() +// this command replaces Wire.endTransmission(false) and Wire.requestFrom(readLen,true); + last_error =i2cAddQueueWrite(i2c,txAddress,txBuffer,txLength,false); //queue tx element + size_t cnt = 0; + if(last_error != I2C_ERROR_OK){ + log_e("writeQueue err=%d",last_error); + rxIndex = 0; + rxLength = 0; + queuedRxLength = 0; + txLength = 0; + i2cFreeQueue(i2c); + return cnt; + } + + if(readLen>I2C_BUFFER_LENGTH) readLen = I2C_BUFFER_LENGTH; + + last_error =i2cAddQueueRead(i2c,txAddress,rxBuffer,readLen,true); //queue rx element + if(last_error==I2C_ERROR_OK){ // successfully queued the read + last_error=i2cProcQueue(i2c); + if(last_error!=I2C_ERROR_OK){ + log_e("procqueue err=%d",last_error); + } + rxIndex = 0; + rxLength = i2cQueueReadCount(i2c); + cnt = rxLength; + i2cFreeQueue(i2c); + } + else {// only possible error is I2C_ERROR_MEMORY + log_e("readqueue err=%d",last_error); + cnt = 0; + rxIndex=0; + rxLength=0; + i2cFreeQueue(i2c); // cleanup + } + return cnt; +} + +uint8_t TwoWire::newEndTransmission(void){ // Assumes Wire.beginTransaction(),Wire.write() +// this command replaces Wire.endTransmission(true) + size_t cnt = 0; +last_error =i2cAddQueueWrite(i2c,txAddress,txBuffer,txLength,true); //queue tx element + +if(last_error == I2C_ERROR_OK){ + last_error=i2cProcQueue(i2c); + if(last_error!=I2C_ERROR_OK){ + log_e("procqueue err=%d",last_error); + } + i2cFreeQueue(i2c); + } + +txLength = 0; + +return last_error; +} + +i2c_err_t TwoWire::lastError(){ + return last_error; +} + uint8_t TwoWire::endTransmission(uint8_t sendStop) { int8_t ret = i2cWrite(i2c, txAddress, false, txBuffer, txLength, sendStop); diff --git a/libraries/Wire/src/Wire.h b/libraries/Wire/src/Wire.h index d9a7a752088..0595cf6ba01 100644 --- a/libraries/Wire/src/Wire.h +++ b/libraries/Wire/src/Wire.h @@ -30,6 +30,8 @@ #include "Stream.h" #define I2C_BUFFER_LENGTH 128 +typedef void(*user_onRequest)(void); +typedef void(*user_onReceive)(uint8_t*, int); class TwoWire: public Stream { @@ -42,6 +44,7 @@ class TwoWire: public Stream uint8_t rxBuffer[I2C_BUFFER_LENGTH]; uint16_t rxIndex; uint16_t rxLength; + uint16_t queuedRxLength; uint8_t txBuffer[I2C_BUFFER_LENGTH]; uint16_t txIndex; @@ -49,6 +52,11 @@ class TwoWire: public Stream uint8_t txAddress; uint8_t transmitting; + static user_onRequest uReq[2]; + static user_onReceive uRcv[2]; + void onRequestService(void); + void onReceiveService(uint8_t*, int); + i2c_err_t last_error; // from esp32-hal-i2c.h public: TwoWire(uint8_t bus_num); @@ -58,12 +66,22 @@ class TwoWire: public Stream void beginTransmission(int); uint8_t endTransmission(void); uint8_t endTransmission(uint8_t); + uint8_t newEndTransmission(void); size_t requestFrom(uint8_t address, size_t size, bool sendStop); - + + size_t newRequestFrom(uint8_t address, size_t size, bool sendStop); + size_t newRequestFrom(uint8_t address, uint8_t* buf, size_t size, bool sendStop); + size_t transact(size_t readLen); + i2c_err_t lastError(); + uint8_t requestFrom(uint8_t, uint8_t); uint8_t requestFrom(uint8_t, uint8_t, uint8_t); uint8_t requestFrom(int, int); uint8_t requestFrom(int, int, int); + + void onReceive( void (*)(int) ); + void onRequest( void (*)(void) ); + size_t write(uint8_t); size_t write(const uint8_t *, size_t); From 4f597d3d8919af9639aa2bf203ef24128b894db4 Mon Sep 17 00:00:00 2001 From: Chuck Todd Date: Thu, 16 Nov 2017 09:52:43 -0700 Subject: [PATCH 03/63] Alpha Release of ISR I2C rewrite A fork of Arduino-esp32 that consists of an I2C subsystem rewrite to utilize Interrupt driven I2C communication. This rewrite should solve the TIMEOUT errors. --- README2.md | 115 ++++++++ cores/esp32/esp32-hal-i2c.c | 536 ++++++++++++++++++++++++------------ cores/esp32/esp32-hal-i2c.h | 28 +- libraries/Wire/src/Wire.cpp | 232 ++++++++++++---- libraries/Wire/src/Wire.h | 22 +- 5 files changed, 677 insertions(+), 256 deletions(-) create mode 100644 README2.md diff --git a/README2.md b/README2.md new file mode 100644 index 00000000000..a46c8ee0b71 --- /dev/null +++ b/README2.md @@ -0,0 +1,115 @@ +# A fork of Espressif/arduino-esp32: +## i2c communications using a ISR + +The existing main fork of arduino-esp32 implement i2c communications using a polled monitoring of the I2C Hardware StateMachine. The implementation of the I2C ReSTART operations leaves the I2C Hardware StateMachine(SM) in an unstable configuration. The I2C protocol arbitrates exclusive ownership of the bus to a single Master Device from a `START` signal to the next `STOP`. A restart operation is just a start signal inserted into this `START` -> `STOP` flow. Schematically like this: + `START` (Read or Write) -> `ReSTART` (Read or Write) -> `STOP`. + + By I2C protocol as [UM10204 I2C-Bus Specification and User Manual](https://Fwww.nxp.com/docs/en/user-guide/UM10204.pdf), A valid I2C transaction must be a `START` at least one Data Byte `STOP`. The Arduino environment is base on the AVR processor series, These Atmel CPU's implementation of the the I2C protocol allows unlimited pauses between protocol elements. The Espressif implement enforces a Strict TimeOut between elements. This has resulted in perceived I2C bus stability issues ([834](https://github.com/espressif/arduino-esp32/issues/834),[811](https://github.com/espressif/arduino-esp32/issues/811),[741](https://github.com/espressif/arduino-esp32/issues/741),[682](https://github.com/espressif/arduino-esp32/issues/682),[659](https://github.com/espressif/arduino-esp32/issues/659) and many more. @lonerzzz create a partial solution [#751](https://github.com/espressif/arduino-esp32/pull/751). + I spend a couple of weeks investigating these problems, and discovered this Hard TimeOut was the basis of all of these problems. Once this Hard TimeOut limit is triggered the SM cannot recover without a complete reinitialization. +The existing Arduino code base is reliant on the AVR's ability to infinetly pausing a i2c transaction. The standard coding practice of: +```c++ +// set internal address pointer in I2C EEPROM from which to read +Wire.beginTransmission(ID); +Wire.write(highByte(addr)); +Wire.write(lowByte(addr)); +uint8_t err =Wire.endTransmission(false); // don't send a STOP, just Pause I2C operations +if(err==0){ // successfully set internal address pointer + err=Wire.requestFrom(addr,len); + if(err==0){ // read failed + Serial.print("Bad Stuff!! Read Failed\n"); + } + else {// successful read + while(Wire.avaiable()){ + Serial.print((char)Wire.read()); + } + Serial.println(); + } + } +``` +May not function correctly with the ESP32, actually *usually* will not function correctly. The current arduino-esp32 platform is built upon the espressif-idf which is built on FreeRTOS, a multi-process/processor operating system. The Arduino sketch is just one task executing on One of the Two processor cores. The TimeOut is triggered when the FreeRTOS Task Scheduler does a task switch between `Wire.endTransmission(false);` and the i2c activites inside `Wire.requestfrom()`. The maximum TimeOut interval is around 12ms. +My solution is to avoid this TimeOut from every being possible. I have changed how `Wire()` uses the SM. Specifically I have changed how a `ReSTART` operation is implemented. To avoid the TimeOut I have create a i2c queuing system that does not allow an i2c transaction to start unless a `STOP` has been issued. But, alas, this creates some incompatibilities with the pre-exisiting Arduino code base. The changes to the standard Arduino `Wire()` coding are minimal, but, they are necessary: +```c++ +// new, maybe Excessive Error Return codes for @stickbreaker:arduino-esp32 +typedef enum { + I2C_ERROR_OK=0, + I2C_ERROR_DEV, + I2C_ERROR_ACK, + I2C_ERROR_TIMEOUT, + I2C_ERROR_BUS, + I2C_ERROR_BUSY, + I2C_ERROR_MEMORY, + I2C_ERROR_CONTINUE, + I2C_ERROR_MISSING_WRITE, + I2C_ERROR_NO_BEGIN +} i2c_err_t; + +// set internal address pointer in I2C EEPROM from which to read +Wire.beginTransmission(ID); +Wire.write(highByte(addr)); +Wire.write(lowByte(addr)); +uint8_t err =Wire.endTransmission(false); // don't send a STOP, just Pause I2C operations + +//err will be I2C_ERROR_CONTINUE (7), an indication that the preceding +//transaction has been Queued, NOT EXECUTED + +if(err==7){ // Prior Operation has been queued, it is NOT guaranteed that it will +// successfully occur! + err=Wire.requestFrom(addr,len); + if(err!=len){ // complete/partial read failure + Serial.print("Bad Stuff!! Read Failed lastError="); + Serial.print(Wire.lastError(),DEC); + } + // some of the read may have executed + while(Wire.avaiable()){ + Serial.print((char)Wire.read()); + } + Serial.println(); + } +``` + +Additionally I have expanded the ability of `Wire()` to handle larger Reads and Writes: up to 64k-1, I have tested READs of these sizes, but the largest single WRITE i have tested is 128bytes. I can send more data to a 24LC512, but it will only store the last 128bytes. + +I have create a few new methods for Wire: +```c++ + uint8_t oldEndTransmission(uint8_t); //released implementation + size_t oldRequestFrom(uint8_t address, size_t size, bool sendStop); //released implementation +//@stickBreaker for big blocks and ISR model + uint8_t writeTransaction(uint8_t address, uint8_t* buff, size_t size, bool sendStop);// big block handling + size_t requestFrom(uint8_t address, size_t size, bool sendStop); + size_t requestFrom(uint8_t address, uint8_t* buf, size_t size, bool sendStop); + size_t polledRequestFrom(uint8_t address, uint8_t* buf, size_t size, bool sendStop);//a BigBlock test case Not USING ISR + size_t transact(size_t readLen); // replacement for endTransmission(false),requestFrom(ID,readLen,true); + size_t transact(uint8_t* readBuff, size_t readLen);// bigger Block read + i2c_err_t lastError(); // Expose complete error + void dumpInts(); // diagnostic dump for the last 64 different i2c Interrupts + size_t getClock(); // current i2c Clock rate +``` + +`transact()` coding is: +```c++ +// set internal address pointer in I2C EEPROM from which to read +Wire.beginTransmission(ID); +Wire.write(highByte(addr)); +Wire.write(lowByte(addr)); + +uint8_t err=Wire.transact(len); +if(err!=len){ // complete/partial read failure + Serial.print("Bad Stuff!! Read Failed lastError="); + Serial.print(Wire.lastError(),DEC); + } + // some of the read may have executed +while(Wire.avaiable()){ + Serial.print((char)Wire.read()); + } +Serial.println(); + +``` + +This **APLHA** release should be compiled with ESP32 Dev Module as its target, and +Set the "core debug level" to 'error' + +There is MINIMAL to NO ERROR detection, BUS, BUSY. because I have not encounter any of them! + + + +Chuck. \ No newline at end of file diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index 516c703466a..0e48a27ad67 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -77,11 +77,9 @@ static i2c_t _i2c_bus_array[2] = { }; #endif - -//forward for ISR, added IRAM_ATTR? -static void IRAM_ATTR i2c_isr_handler_default(void* arg); - -static i2c_err_t i2cAddQueue(i2c_t * i2c,uint8_t mode, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen,bool sendStop){ +/* Stickbreaker added for ISR 11/2017 +*/ +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){ I2C_DATA_QUEUE_t dqx; dqx.data = dataPtr; @@ -94,6 +92,11 @@ static i2c_err_t i2cAddQueue(i2c_t * i2c,uint8_t mode, uint16_t i2cDeviceAddr, u dqx.ctrl.stop= sendStop; dqx.ctrl.addrReq = ((i2cDeviceAddr&0x7800)==0x7800)?2:1; // 10bit or 7bit address dqx.queueLength = dataLen + dqx.ctrl.addrReq; + dqx.queueEvent = event; + +if(event){// an eventGroup exist, so, initialize it + xEventGroupClearBits(event, 0x3F); // all of them + } if(i2c->dq!=NULL){ // expand I2C_DATA_QUEUE_t* tq =(I2C_DATA_QUEUE_t*)realloc(i2c->dq,sizeof(I2C_DATA_QUEUE_t)*(i2c->queueCount +1)); @@ -119,9 +122,29 @@ else { // first Time } return I2C_ERROR_OK; } +/* walk thru dq elements returning the buffer* and length, savePtr=NULL to start +*/ +i2c_err_t i2cGetReadQueue(i2c_t *i2c, uint8_t** buffPtr, uint16_t* lenPtr,uint8_t *savePtr){ + uint8_t a = *savePtr; +while((aqueueCount)&&(i2c->dq[a].ctrl.mode!=1)) a++; +if(a < i2c->queueCount){// found one + *buffPtr = i2c->dq[a].data; + *lenPtr = i2c->dq[a].length; + a++; + *savePtr = a; + return I2C_ERROR_OK; + } +else {//not found + *buffPtr = NULL; + *lenPtr = 0; + *savePtr = a; + return I2C_ERROR_MEMORY; + } +} i2c_err_t i2cFreeQueue(i2c_t * i2c){ if(i2c->dq!=NULL){ +// what about EventHandle? free(i2c->dq); i2c->dq = NULL; } @@ -129,14 +152,14 @@ i2c->queueCount=0; i2c->queuePos=0; } -i2c_err_t i2cAddQueueWrite(i2c_t * i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen,bool sendStop){ +i2c_err_t i2cAddQueueWrite(i2c_t * i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen,bool sendStop,EventGroupHandle_t event){ - return i2cAddQueue(i2c,0,i2cDeviceAddr,dataPtr,dataLen,sendStop); + return i2cAddQueue(i2c,0,i2cDeviceAddr,dataPtr,dataLen,sendStop,event); } -i2c_err_t i2cAddQueueRead(i2c_t * i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen,bool sendStop){ +i2c_err_t i2cAddQueueRead(i2c_t * i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen,bool sendStop,EventGroupHandle_t event){ - return i2cAddQueue(i2c,1,i2cDeviceAddr,dataPtr,dataLen,sendStop); + return i2cAddQueue(i2c,1,i2cDeviceAddr,dataPtr,dataLen,sendStop,event); } uint16_t i2cQueueReadPendingCount(i2c_t * i2c){ @@ -162,7 +185,7 @@ while( a < i2c->queueCount){ } return cnt; } - +// Stickbreaker i2c_err_t i2cAttachSCL(i2c_t * i2c, int8_t scl) { @@ -471,6 +494,8 @@ i2c_err_t i2cRead(i2c_t * i2c, uint16_t address, bool addr_10bit, uint8_t * data return I2C_ERROR_OK; } +/* Stickbreaker Debug code +*/ void dumpCmdQueue(i2c_t *i2c){ uint8_t i=0; while(i<16){ @@ -486,9 +511,14 @@ while(i<16){ i++; } } + +/* Stickbreaker debug support +*/ uint32_t emptyCnt=0; uint32_t maxRead=0; +/* Stickbreaker poll mode suppot +*/ void emptyFifo(i2c_t* i2c,char* data, uint16_t len,uint16_t* index){ uint32_t d; uint32_t max=0; @@ -505,6 +535,8 @@ i2c->dev->int_clr.val =d; // processed! emptyCnt++; } +/* Stickbreaker poll mode suppot +*/ void fillFifo(i2c_t* i2c,uint8_t* data, uint16_t len,uint16_t* index){ uint32_t d; while((i2c->dev->status_reg.tx_fifo_cnt<0x1F)&&(*indexdev->int_clr.val =d; // processed! //log_e("Filled %d",*index); } +/* Stickbreaker poll mode suppot +*/ typedef enum{ Stage_waitForBusIdle, Stage_init, @@ -526,6 +560,8 @@ typedef enum{ Stage_abort }STAGES; +/* Stickbreaker poll mode suppot +*/ void fillReadQueue(i2c_t *i2c, uint16_t *neededRead, uint8_t cmdIdx){ /* Can I have another Sir? ALL CMD queues must be terminated with either END or STOP. @@ -588,6 +624,8 @@ if((*neededRead==0)&&((cmdIdx)<16)){// add Stop command } +/* Stickbreaker poll mode suppot +*/ i2c_err_t pollI2cRead(i2c_t * i2c, uint16_t address, bool addr_10bit, uint8_t * data, uint16_t len, bool sendStop) { address = (address << 1) | 1; @@ -928,25 +966,28 @@ void i2cReset(i2c_t* i2c){ //** 11/2017 Stickbreaker attempt at ISR for I2C hardware // liberally stolen from ESP_IDF /drivers/i2c.c - esp_err_t i2c_isr_free(intr_handle_t handle){ return esp_intr_free(handle); } +/* Stickbreaker ISR mode debug support +*/ #define INTBUFFMAX 64 static uint32_t intBuff[INTBUFFMAX][2]; static uint32_t intPos=0; -static void IRAM_ATTR fillCmdQueue(i2c_t * i2c, uint8_t cmdIdx){ +/* Stickbreaker ISR mode support +*/ +static void IRAM_ATTR fillCmdQueue(i2c_t * i2c, uint8_t cmdIdx, bool INTS){ /* this function is call on initial i2cProcQueue() or when a I2C_END_DETECT_INT occures */ uint16_t qp = i2c->queuePos; bool done; bool needMoreCmds = false; - bool ena_rx=false; // if we add a read op, better enable Rx_Fifo - bool ena_tx=false; // if we add a Write op, better enable TX_Fifo + bool ena_rx=false; // if we add a read op, better enable Rx_Fifo IRQ + bool ena_tx=false; // if we add a Write op, better enable TX_Fifo IRQ while(!needMoreCmds&&(qp < i2c->queueCount)){ // check if more possible cmds if(i2c->dq[qp].ctrl.stopCmdSent) { @@ -954,67 +995,68 @@ while(!needMoreCmds&&(qp < i2c->queueCount)){ // check if more possible cmds } else needMoreCmds=true; } -log_e("needMoreCmds=%d",needMoreCmds); +//log_e("needMoreCmds=%d",needMoreCmds); done=(!needMoreCmds)||(cmdIdx>14); while(!done){ // fill the command[] until either 0..14 or out of cmds //CMD START I2C_DATA_QUEUE_t *tdq=&i2c->dq[qp]; // simpler - if(!tdq->ctrl.startCmdSent){ + if(!tdq->ctrl.startCmdSent){// has this dq element's START command been added? i2cSetCmd(i2c, cmdIdx++, I2C_CMD_RSTART, 0, false, false, false); tdq->ctrl.startCmdSent=1; done = (cmdIdx>14); } //CMD WRITE ADDRESS - if((!done)&&(tdq->ctrl.startCmdSent)){// have to leave room for continue + if((!done)&&(tdq->ctrl.startCmdSent)){// have to leave room for continue, and START must have been sent! if(!tdq->ctrl.addrCmdSent){ i2cSetCmd(i2c, cmdIdx++, I2C_CMD_WRITE, tdq->ctrl.addrReq, false, false, true); //load address in cmdlist, validate (low) ack tdq->ctrl.addrCmdSent=1; done =(cmdIdx>14); - ena_tx=true; + ena_tx=true; // tx Data necessary } } /* Can I have another Sir? ALL CMD queues must be terminated with either END or STOP. - if END is used, when refilling it next time, no entries from END to [15] can be used. - AND END must exit. The END command does not complete until the the + if END is used, when refilling the cmd[] next time, no entries from END to [15] can be used. + AND the END continue. The END command does not complete until the the ctr->trans_start=1 has executed. So, only refill from [0]..[14], leave [15] for a continuation if necessary. As a corrilary, once END exists in [15], you do not need to overwrite it for the - next continuation. It is never modified. But, I update it every time because! + next continuation. It is never modified. But, I update it every time because it might + actually be the first time! */ if((!done)&&(tdq->ctrl.addrCmdSent)){ //room in command[] for at least One data (read/Write) cmd - uint8_t blkSize=0; // max is 255 + uint8_t blkSize=0; // max is 255? does numBytes =0 actually mean 256? haven't tried it. //log_e("needed=%2d index=%d",*neededRead,cmdIdx); while(( tdq->cmdBytesNeeded > tdq->ctrl.mode )&&(!done )) { // more bytes needed and room in cmd queue, leave room for END - // lets start with 3 bytes at a time, but may 255 is possible blkSize = (tdq->cmdBytesNeeded > 255)?255:(tdq->cmdBytesNeeded - tdq->ctrl.mode); // Last read cmd needs different ACK setting, so leave 1 byte remainer on reads tdq->cmdBytesNeeded -= blkSize; // - if(tdq->ctrl.mode==1){ + if(tdq->ctrl.mode==1){ //read mode i2cSetCmd(i2c, (cmdIdx)++, I2C_CMD_READ, blkSize,false,false,false); // read cmd, this can't be the last read. - ena_rx=true; + ena_rx=true; // need to enable rxFifo IRQ } else {// write i2cSetCmd(i2c, cmdIdx++, I2C_CMD_WRITE, blkSize, false, false, true); // check for Nak - ena_tx=true; + ena_tx=true; // need to enable txFifo IRQ } - done = cmdIdx>14; + done = cmdIdx>14; //have to leave room for END } if(!done){ // buffer is not filled completely - if((tdq->ctrl.mode==1)&&(tdq->cmdBytesNeeded==1)){ + if((tdq->ctrl.mode==1)&&(tdq->cmdBytesNeeded==1)){ //special last read byte NAK i2cSetCmd(i2c, (cmdIdx)++, I2C_CMD_READ, 1,true,false,false); // send NAK to mark end of read tdq->cmdBytesNeeded=0; done = cmdIdx > 14; ena_rx=true; } - tdq->ctrl.dataCmdSent=(tdq->cmdBytesNeeded==0); // enough command[] to send all data } + + tdq->ctrl.dataCmdSent=(tdq->cmdBytesNeeded==0); // enough command[] to send all data if((!done)&&(tdq->ctrl.dataCmdSent)){ // possibly add stop if(tdq->ctrl.stop){ //send a stop @@ -1041,18 +1083,22 @@ while(!done){ // fill the command[] until either 0..14 or out of cmds qp++; if(qp < i2c->queueCount){ tdq = &i2c->dq[qp]; - log_e("inc to next queue=%d",qp); +// log_e("inc to next queue=%d",qp); } else done = true; } } }// while(!done) -if(ena_rx) i2c->dev->int_ena.rx_fifo_full = 1; -if(ena_tx) i2c->dev->int_ena.tx_fifo_empty = 1; +if(INTS){ + if(ena_rx) i2c->dev->int_ena.rx_fifo_full = 1; + if(ena_tx) i2c->dev->int_ena.tx_fifo_empty = 1; + } } +/* Stickbreaker ISR mode debug support +*/ static void IRAM_ATTR dumpI2c(i2c_t * i2c){ log_e("i2c=%p",i2c); log_e("dev=%p",i2c->dev); @@ -1061,7 +1107,7 @@ log_e("num=%d",i2c->num); log_e("mode=%d",i2c->mode); log_e("stage=%d",i2c->stage); log_e("error=%d",i2c->error); -log_e("event=%p",i2c->i2c_event); +log_e("event=%p bits=%x",i2c->i2c_event,(i2c->i2c_event)?xEventGroupGetBits(i2c->i2c_event):0); log_e("intr_handle=%p",i2c->intr_handle); log_e("dq=%p",i2c->dq); log_e("queueCount=%d",i2c->queueCount); @@ -1071,17 +1117,30 @@ uint16_t a=0; I2C_DATA_QUEUE_t *tdq; while(aqueueCount){ tdq=&i2c->dq[a]; - log_e("[%d] %x %c %s buf@=%p, len=%d, pos=%d",a,tdq->ctrl.addr,(tdq->ctrl.mode)?'R':'W',(tdq->ctrl.stop)?"STOP":"",tdq->data,tdq->length,tdq->position); + log_e("[%d] %x %c %s buf@=%p, len=%d, pos=%d, eventH=%p bits=%x",a,tdq->ctrl.addr,(tdq->ctrl.mode)?'R':'W',(tdq->ctrl.stop)?"STOP":"",tdq->data,tdq->length,tdq->position,tdq->queueEvent,(tdq->queueEvent)?xEventGroupGetBits(tdq->queueEvent):0); a++; } } +/* Stickbreaker ISR mode support +*/ static void IRAM_ATTR fillTxFifo(i2c_t * i2c){ -uint16_t a=i2c->queuePos; +/* need to test overlapping RX->TX fifo operations, + Currently, this function attempts to queue all possible tx elements into the Fifo. + What happens when WRITE 10, READ 20, Write 10? + (Write Addr, Write 10),(Write addr, Read 20) (Write addr, Write 10). + I know everything will work up to the End of the Read 20, but I am unsure + what will happen to the third command, will the Read 20 overwrite the previously + queued (write addr, write 10) of the Third command? I need to test! + */ +/*11/15/2017 will assume that I cannot queue tx after a READ until READ completes +*/ +bool readEncountered = false; +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)){ +while((a < i2c->queueCount)&&!(full || readEncountered)){ I2C_DATA_QUEUE_t *tdq = &i2c->dq[a]; cnt=0; // add to address to fifo @@ -1125,55 +1184,95 @@ while((a < i2c->queueCount)&&(!full)){ } } } + else readEncountered = true; + + if(full) readEncountered =false; //tx possibly needs more + cnt += intBuff[intPos][1]>>16; intBuff[intPos][1] = (intBuff[intPos][1]&0xFFFF)|(cnt<<16); - if(!full) a++; // check next buffer for tx + if(!(full||readEncountered)) a++; // check next buffer for tx } -if(a < i2c->queueCount){ - i2c->dev->int_ena.tx_fifo_empty=1; - } -else { // no more data available, disable INT +if((!full) || readEncountered || (a >= i2c->queueCount)){// disable IRQ, the next dq will re-enable it i2c->dev->int_ena.tx_fifo_empty=0; } + i2c->dev->int_clr.tx_fifo_empty=1; } +/* Stickbreaker ISR mode support +*/ static void IRAM_ATTR emptyRxFifo(i2c_t * i2c){ -uint32_t d,cnt=0; -I2C_DATA_QUEUE_t *tdq =&i2c->dq[i2c->queuePos]; + uint32_t d, cnt=0, moveCnt; + I2C_DATA_QUEUE_t *tdq =&i2c->dq[i2c->queuePos]; + +moveCnt = i2c->dev->status_reg.rx_fifo_cnt;//no need to check the reg until this many are read + if(tdq->ctrl.mode==1) { // read - while(i2c->dev->status_reg.rx_fifo_cnt>0){ - d = i2c->dev->fifo_data.val; - cnt++; - if(tdq->position < tdq->length) tdq->data[tdq->position++] = (d&0xFF); + while(moveCnt > 0){ + while(moveCnt > 0){ + d = i2c->dev->fifo_data.val; + moveCnt--; + cnt++; + if(tdq->position < tdq->length) tdq->data[tdq->position++] = (d&0xFF); + else { // discard? what Happened to get more than requested? + log_e("discard 0x%x",d); + } + } + // see if any more chars showed up while empting Fifo. + moveCnt = i2c->dev->status_reg.rx_fifo_cnt; } -// d = I2C_RXFIFO_FULL_INT_ST; -// i2c->dev->int_clr.val =d; // processed! cnt += (intBuff[intPos][1])&&0xffFF; intBuff[intPos][1] = (intBuff[intPos][1]&0xFFFF0000)|cnt; } else { - log_e("RxEmpty on TxBuffer? %d",i2c->queuePos); + log_e("RxEmpty(%d) call on TxBuffer? dq=%d",moveCnt,i2c->queuePos); // dumpI2c(i2c); } //log_e("emptied %d",*index); } + +static void i2cIsrExit(i2c_t * i2c,const uint32_t eventCode,bool Fatal){ +switch(eventCode){ + case EVENT_DONE: + i2c->error = I2C_OK; + break; + case EVENT_ERROR_NAK : + i2c->error =I2C_ADDR_NAK; + break; + case EVENT_ERROR_DATA_NAK : + i2c->error =I2C_DATA_NAK; + break; + case EVENT_ERROR_TIMEOUT : + i2c->error = I2C_TIMEOUT; + break; + case EVENT_ERROR_ARBITRATION: + i2c->error = I2C_ARBITRATION; + break; + default : + i2c->error = I2C_ERROR; + } +uint32_t exitCode = EVENT_DONE | eventCode |(Fatal?EVENT_ERROR:0); -static void IRAM_ATTR nextQueue(i2c_t * i2c){ - /* This function is call when I2C_TRANS_START_INT is encountred - */ - // handle first queue entry correctly -if(i2c->stage != I2C_STARTUP){ - -//empty RXFIFO for current queue, what about First Time? - emptyRxFifo(i2c); -//roll to next queue entry - i2c->queuePos++; +i2c->dev->int_ena.val = 0; // shutdown interrupts +i2c->dev->int_clr.val = 0x1FFFF; +i2c->stage = I2C_DONE; + +portBASE_TYPE HPTaskAwoken = pdFALSE; + +if(i2c->dq[i2c->queuePos].queueEvent){ // this data element has an event, use it. + HPTaskAwoken = pdFALSE; + xEventGroupClearBitsFromISR(i2c->dq[i2c->queuePos].queueEvent, EVENT_RUNNING); + xEventGroupSetBitsFromISR(i2c->dq[i2c->queuePos].queueEvent, exitCode,&HPTaskAwoken); + if(HPTaskAwoken == pdTRUE) portYIELD_FROM_ISR(); } -// stuff txFifo with Addr -fillTxFifo(i2c); -} +xEventGroupClearBitsFromISR(i2c->i2c_event, EVENT_RUNNING); +HPTaskAwoken = pdFALSE; +xEventGroupSetBitsFromISR(i2c->i2c_event, exitCode, &HPTaskAwoken); +if (HPTaskAwoken == pdTRUE) {//higher pri task has awoken, jump to it + portYIELD_FROM_ISR(); + } +} static void IRAM_ATTR i2c_isr_handler_default(void* arg){ //log_e("isr Entry=%p",arg); @@ -1183,6 +1282,7 @@ uint32_t activeInt = p_i2c->dev->int_status.val&0x1FFF; //dumpI2c(p_i2c); portBASE_TYPE HPTaskAwoken = pdFALSE; +/* don't need this Event_in_irq // be polite if someone more important wakes up. //log_e("setbits(%p)=%p",p_i2c->i2c_event); @@ -1191,15 +1291,18 @@ if (HPTaskAwoken == pdTRUE) {//higher pri task has awoken, jump to it portYIELD_FROM_ISR(); } //log_e("stage"); +*/ 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 = 0x1FFF; - log_e("eject int=%p",activeInt); - xEventGroupClearBitsFromISR(p_i2c->i2c_event, EVENT_IN_IRQ); + p_i2c->dev->int_clr.val = activeInt; //0x1FFF; + dumpI2c(p_i2c); + i2cDumpInts(); +// xEventGroupClearBitsFromISR(p_i2c->i2c_event, EVENT_IN_IRQ); return; } -while (activeInt != 0) { // pending +while (activeInt != 0) { // Ordering of 'if(activeInt)' statements is important, don't change if(activeInt==(intBuff[intPos][0]&0x1fff)){ intBuff[intPos][0] = (((intBuff[intPos][0]>>16)+1)<<16)|activeInt; } @@ -1207,137 +1310,125 @@ while (activeInt != 0) { // pending intPos++; intPos %= INTBUFFMAX; intBuff[intPos][0]=(1<<16)|activeInt; + intBuff[intPos][1] = 0; } uint32_t oldInt =activeInt; if (activeInt & I2C_TRANS_START_INT_ST_M) { // p_i2c->byteCnt=0; - // nextQueue(p_i2c); if(p_i2c->stage==I2C_STARTUP){ p_i2c->stage=I2C_RUNNING; - xEventGroupClearBitsFromISR(p_i2c->i2c_event, EVENT_DONE); + xEventGroupClearBitsFromISR(p_i2c->i2c_event, EVENT_DONE); //why? + if(p_i2c->dq[p_i2c->queuePos].queueEvent){ // this data element has an event, use it. + HPTaskAwoken = pdFALSE; + xEventGroupSetBitsFromISR(p_i2c->dq[p_i2c->queuePos].queueEvent, EVENT_RUNNING, + &HPTaskAwoken); + if(HPTaskAwoken == pdTRUE) portYIELD_FROM_ISR(); + } + HPTaskAwoken = pdFALSE; xEventGroupSetBitsFromISR(p_i2c->i2c_event, EVENT_RUNNING, &HPTaskAwoken); if (HPTaskAwoken == pdTRUE) {//higher pri task has awoken, jump to it portYIELD_FROM_ISR(); } } activeInt &=~I2C_TRANS_START_INT_ST_M; - p_i2c->dev->int_ena.trans_start = 1; - p_i2c->dev->int_clr.trans_start = 1; + p_i2c->dev->int_ena.trans_start = 1; // already enabled? why Again? + p_i2c->dev->int_clr.trans_start = 1; // so that will trigger after next 'END' } if (activeInt & I2C_TXFIFO_EMPTY_INT_ST) {//should this be before Trans_start? - fillTxFifo(p_i2c); + fillTxFifo(p_i2c); //fillTxFifo will enable/disable/clear interrupt activeInt&=~I2C_TXFIFO_EMPTY_INT_ST; } if(activeInt & I2C_RXFIFO_FULL_INT_ST){ emptyRxFifo(p_i2c); p_i2c->dev->int_clr.rx_fifo_full=1; - p_i2c->dev->int_ena.rx_fifo_full=1; + p_i2c->dev->int_ena.rx_fifo_full=1; //why? activeInt &=~I2C_RXFIFO_FULL_INT_ST; } - + if(activeInt & I2C_MASTER_TRAN_COMP_INT_ST){ // each byte the master sends/recv p_i2c->dev->int_clr.master_tran_comp = 1; + p_i2c->byteCnt++; + if(p_i2c->byteCnt > p_i2c->dq[p_i2c->queuePos].queueLength){// simulate Trans_start + p_i2c->byteCnt -= p_i2c->dq[p_i2c->queuePos].queueLength; - if(p_i2c->dq[p_i2c->queuePos].ctrl.mode==1){ + + if(p_i2c->dq[p_i2c->queuePos].ctrl.mode==1){ // grab last characters for this dq emptyRxFifo(p_i2c); p_i2c->dev->int_clr.rx_fifo_full=1; p_i2c->dev->int_ena.rx_fifo_full=1; } - p_i2c->queuePos++; - p_i2c->dev->int_ena.tx_fifo_empty=1; - -// nextQueue(p_i2c); + + if(p_i2c->dq[p_i2c->queuePos].queueEvent){ // this data element has an event, use it. + HPTaskAwoken = pdFALSE; + xEventGroupClearBitsFromISR(p_i2c->dq[p_i2c->queuePos].queueEvent, EVENT_RUNNING); + xEventGroupSetBitsFromISR(p_i2c->dq[p_i2c->queuePos].queueEvent, EVENT_DONE, + &HPTaskAwoken); + if(HPTaskAwoken == pdTRUE) portYIELD_FROM_ISR(); + } + + p_i2c->queuePos++; //inc to next dq + + if(p_i2c->dq[p_i2c->queuePos].queueEvent){ // this data element has an event, use it. + HPTaskAwoken = pdFALSE; + xEventGroupSetBitsFromISR(p_i2c->dq[p_i2c->queuePos].queueEvent, EVENT_RUNNING, + &HPTaskAwoken); + if(HPTaskAwoken == pdTRUE) portYIELD_FROM_ISR(); + } + + if(p_i2c->queuePos < p_i2c->queueCount) // load next dq address field + data + p_i2c->dev->int_ena.tx_fifo_empty=1; } activeInt &=~I2C_MASTER_TRAN_COMP_INT_ST; } - - if (activeInt & I2C_ACK_ERR_INT_ST_M) { - p_i2c->dev->int_clr.ack_err = 1; + + if (activeInt & I2C_ACK_ERR_INT_ST_M) {//fatal error, abort i2c service if (p_i2c->mode == I2C_MASTER) { - p_i2c->error = I2C_ERROR; - log_e("AcK Err byteCnt=%d, queuepos=%d",p_i2c->byteCnt,p_i2c->queuePos); - if(p_i2c->byteCnt<2) p_i2c->error = I2C_ADDR_ACK; - else if((p_i2c->byteCnt == 2) && (p_i2c->dq[p_i2c->queuePos].ctrl.addrReq == 2)) p_i2c->error = I2C_ADDR_ACK; - else p_i2c->error = I2C_DATA_ACK; - } - p_i2c->stage = I2C_DONE; - p_i2c->dev->int_ena.val = 0; // kill them all - p_i2c->dev->int_clr.val = 0xFFFFFF; - xEventGroupClearBitsFromISR(p_i2c->i2c_event, EVENT_RUNNING|EVENT_IN_IRQ); - xEventGroupSetBitsFromISR(p_i2c->i2c_event, EVENT_DONE, &HPTaskAwoken); - if (HPTaskAwoken == pdTRUE) {//higher pri task has awoken, jump to it - portYIELD_FROM_ISR(); + // log_e("AcK Err byteCnt=%d, queuepos=%d",p_i2c->byteCnt,p_i2c->queuePos); + if(p_i2c->byteCnt==1) i2cIsrExit(p_i2c,EVENT_ERROR_NAK,true); + else if((p_i2c->byteCnt == 2) && (p_i2c->dq[p_i2c->queuePos].ctrl.addrReq == 2)) + i2cIsrExit(p_i2c,EVENT_ERROR_NAK,true); + else i2cIsrExit(p_i2c,EVENT_ERROR_DATA_NAK,true); } return; - activeInt &=~I2C_ACK_ERR_INT_ST_M; } - - if (activeInt & I2C_TIME_OUT_INT_ST_M) {//death Happens Here - p_i2c->dev->int_clr.time_out = 1; - p_i2c->error = I2C_TIMEOUT; - p_i2c->stage = I2C_DONE; - p_i2c->dev->int_ena.val = 0; // kill them all - p_i2c->dev->int_clr.val = 0xFFFFFF; - xEventGroupClearBitsFromISR(p_i2c->i2c_event, EVENT_RUNNING|EVENT_IN_IRQ); - xEventGroupSetBitsFromISR(p_i2c->i2c_event, EVENT_DONE, &HPTaskAwoken); - if (HPTaskAwoken == pdTRUE) {//higher pri task has awoken, jump to it - portYIELD_FROM_ISR(); - } + + if (activeInt & I2C_TIME_OUT_INT_ST_M) {//fatal death Happens Here + i2cIsrExit(p_i2c,EVENT_ERROR_TIMEOUT,true); return; - activeInt &=~I2C_TIME_OUT_INT_ST_M; } if (activeInt & I2C_TRANS_COMPLETE_INT_ST_M) { if(p_i2c->dq[p_i2c->queuePos].ctrl.mode == 1) emptyRxFifo(p_i2c); // grab last few characters - p_i2c->stage = I2C_DONE; - p_i2c->dev->int_ena.val = 0; // shutdown interrupts - p_i2c->dev->int_clr.val = 0xFFFFFF; - xEventGroupClearBitsFromISR(p_i2c->i2c_event, EVENT_RUNNING); - xEventGroupSetBitsFromISR(p_i2c->i2c_event, EVENT_DONE, &HPTaskAwoken); - if (HPTaskAwoken == pdTRUE) {//higher pri task has awoken, jump to it - portYIELD_FROM_ISR(); - } + i2cIsrExit(p_i2c,EVENT_DONE,false); return; // no more work to do /* - // how does a restart appear? // how does slave mode act? - if (p_i2c->mode == I2C_SLAVE) { // STOP detected // empty fifo // dispatch callback */ - activeInt &=~I2C_TRANS_COMPLETE_INT_ST_M; } - if (activeInt & I2C_ARBITRATION_LOST_INT_ST_M) { - p_i2c->dev->int_clr.arbitration_lost = 1; - p_i2c->error = I2C_ARBITRATION; - p_i2c->stage = I2C_DONE; - p_i2c->dev->int_ena.val = 0; // shutdown interrupts - p_i2c->dev->int_clr.val = 0xFFFFFF; - xEventGroupClearBitsFromISR(p_i2c->i2c_event, EVENT_RUNNING); - xEventGroupSetBitsFromISR(p_i2c->i2c_event, EVENT_DONE, &HPTaskAwoken); - if (HPTaskAwoken == pdTRUE) {//higher pri task has awoken, jump to it - portYIELD_FROM_ISR(); - } + if (activeInt & I2C_ARBITRATION_LOST_INT_ST_M) { //fatal + i2cIsrExit(p_i2c,EVENT_ERROR_ARBITRATION,true); return; // no more work to do - activeInt &=~I2C_ARBITRATION_LOST_INT_ST_M; } if (activeInt & I2C_SLAVE_TRAN_COMP_INT_ST_M) { p_i2c->dev->int_clr.slave_tran_comp = 1; - +// need to complete this ! } if (activeInt & I2C_END_DETECT_INT_ST_M) { + HPTaskAwoken = pdFALSE; xEventGroupSetBitsFromISR(p_i2c->i2c_event, EVENT_IN_END, &HPTaskAwoken); if (HPTaskAwoken == pdTRUE) {//higher pri task has awoken, jump to it portYIELD_FROM_ISR(); @@ -1345,33 +1436,42 @@ while (activeInt != 0) { // pending p_i2c->dev->int_ena.end_detect = 0; p_i2c->dev->int_clr.end_detect = 1; p_i2c->dev->ctr.trans_start=0; - fillCmdQueue(p_i2c,0); + fillCmdQueue(p_i2c,0,true); p_i2c->dev->ctr.trans_start=1; // go for it activeInt&=~I2C_END_DETECT_INT_ST_M; // What about Tx, RX fifo fill? xEventGroupClearBitsFromISR(p_i2c->i2c_event, EVENT_IN_END); } - if (activeInt & I2C_RXFIFO_OVF_INT_ST_M) { + if (activeInt & I2C_RXFIFO_OVF_INT_ST_M) { //should never happen, I always use Fifo p_i2c->dev->int_clr.rx_fifo_ovf = 1; } - if(activeInt){ // clear unhandled if possible? What about Disableing interrupt? + if(activeInt){ // clear unhandled if possible? What about Disabling interrupt? p_i2c->dev->int_clr.val = activeInt; log_e("unknown int=%x",activeInt); } - activeInt = p_i2c->dev->int_status.val; // start all over + activeInt = p_i2c->dev->int_status.val; // start all over if another interrupt happened /* if((activeInt&oldInt)==oldInt){ log_e("dup int old=%p, new=%p dup=%p",oldInt,activeInt,(oldInt&activeInt)); } */ } -xEventGroupClearBitsFromISR(p_i2c->i2c_event, EVENT_IN_IRQ); +//xEventGroupClearBitsFromISR(p_i2c->i2c_event, EVENT_IN_IRQ); // and we're out! } +void i2cDumpInts(){ +uint32_t b; +log_e("row count INTR TX RX"); +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",b,((intBuff[b][0]>>16)&0xFFFF),(intBuff[b][0]&0xFFFF),((intBuff[b][1]>>16)&0xFFFF),(intBuff[b][1]&0xFFFF)); + } +} + i2c_err_t i2cProcQueue(i2c_t * i2c){ /* do the hard stuff here install ISR if necessary @@ -1379,13 +1479,18 @@ i2c_err_t i2cProcQueue(i2c_t * i2c){ handle bus busy? do I load command[] or just pass that off to the ISR */ -log_e("procQueue i2c=%p",&i2c); +//log_e("procQueue i2c=%p",&i2c); if(i2c == NULL){ return I2C_ERROR_DEV; } I2C_MUTEX_LOCK(); +/* what about co-existance 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 for(intPos=0;intPosi2c_event){ - log_e("create Event"); i2c->i2c_event = xEventGroupCreate(); - log_e("event=%p",i2c->i2c_event); } if(i2c->i2c_event) { - uint32_t ret=xEventGroupClearBits(i2c->i2c_event, EVENT_IN_IRQ|EVENT_RUNNING|EVENT_DONE); - log_e("after clearBits(%p)=%p",i2c->i2c_event,ret); + 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); + return I2C_ERROR_MEMORY; } -uint32_t startAt = millis(); i2c_err_t reason = I2C_ERROR_OK; i2c->mode = I2C_MASTER; i2c->dev->ctr.trans_start=0; // Pause Machine -i2c->dev->timeout.tout = 0xFFFFF; // max 1M +i2c->dev->timeout.tout = 0xFFFFF; // max 13ms I2C_FIFO_CONF_t f; f.val = i2c->dev->fifo_conf.val; f.rx_fifo_rst = 1; // fifo in reset f.tx_fifo_rst = 1; // fifo in reset f.nonfifo_en = 0; // use fifo mode -f.rx_fifo_full_thrhd = 30; // six bytes before INT is issued +// need to adjust threshold based on I2C clock rate, at 100k, 30 usually works, +// sometimes the emptyRx() actually moves 31 bytes +// it hasn't overflowed yet, I cannot tell if the new byte is added while +// emptyRX() is executing or before? +f.rx_fifo_full_thrhd = 30; // 30 bytes before INT is issued f.fifo_addr_cfg_en = 0; // no directed access i2c->dev->fifo_conf.val = f.val; // post them all @@ -1427,72 +1537,134 @@ i2c->dev->int_clr.val = 0xFFFFFFFF; // kill them All! i2c->dev->ctr.ms_mode = 1; // master! 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->queuePosqueueCount){ +while(i2c->queuePos < i2c->queueCount){ I2C_DATA_QUEUE_t *tdq = &i2c->dq[i2c->queuePos++]; uint16_t taddr=0; if(tdq->ctrl.addrReq ==2){ // 10bit address - taddr =((tdq->ctrl.addr>>7)&0xFE) + taddr =((tdq->ctrl.addr >> 7) & 0xFE) |tdq->ctrl.mode; - taddr = (taddr <<8)||(tdq->ctrl.addr&0xFF); + taddr = (taddr <<8) || (tdq->ctrl.addr&0xFF); } else { // 7bit address taddr = ((tdq->ctrl.addr<<1)&0xFE) |tdq->ctrl.mode; } tdq->ctrl.addr = taddr; // all fixed with R/W bit + totalBytes += tdq->queueLength; // total number of byte to be moved! } i2c->queuePos=0; -fillCmdQueue(i2c,0); // start adding command[], END irq will keep it full - -//fillTxFifo(i2c); // I2C_TRANS_START_INT will do this +fillCmdQueue(i2c,0,false); // start adding command[], END irq will keep it full +//Data Fifo will be filled after trans_start is issued -i2c->stage = I2C_STARTUP; // +i2c->stage = I2C_STARTUP; // everything configured, now start the I2C StateMachine, and +// As soon as interrupts are enabled, the ISR will start handling them. +// it should receive a TXFIFO_EMPTY immediately, even before it +// receives the TRANS_START i2c->dev->int_ena.val = - I2C_ACK_ERR_INT_ENA | // (BIT(10)) - I2C_TRANS_START_INT_ENA | // (BIT(9)) - I2C_TIME_OUT_INT_ENA | //(BIT(8)) - I2C_TRANS_COMPLETE_INT_ENA | // (BIT(7)) - I2C_MASTER_TRAN_COMP_INT_ENA | // (BIT(6)) - I2C_ARBITRATION_LOST_INT_ENA | // (BIT(5)) - I2C_SLAVE_TRAN_COMP_INT_ENA | // (BIT(4)) - I2C_END_DETECT_INT_ENA | // (BIT(3)) - I2C_RXFIFO_OVF_INT_ENA | //(BIT(2)) - I2C_TXFIFO_EMPTY_INT_ENA | // (BIT(1)) - I2C_RXFIFO_FULL_INT_ENA; // (BIT(0)) - - -dumpI2c(i2c); -dumpCmdQueue(i2c); -if(!i2c->intr_handle){ // create ISR + I2C_ACK_ERR_INT_ENA | // (BIT(10)) Causes Fatal Error Exit + I2C_TRANS_START_INT_ENA | // (BIT(9)) Triggered by trans_start=1, initial,END + I2C_TIME_OUT_INT_ENA | //(BIT(8)) causes Fatal error Exit + I2C_TRANS_COMPLETE_INT_ENA | // (BIT(7)) triggered by STOP, successful exit + I2C_MASTER_TRAN_COMP_INT_ENA | // (BIT(6)) counts each byte xfer'd, inc's queuePos + I2C_ARBITRATION_LOST_INT_ENA | // (BIT(5)) cause fatal error exit + I2C_SLAVE_TRAN_COMP_INT_ENA | // (BIT(4)) unhandled + I2C_END_DETECT_INT_ENA | // (BIT(3)) refills cmd[] list + I2C_RXFIFO_OVF_INT_ENA | //(BIT(2)) unhandled + 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(ret!=ESP_OK){ - log_e("install interrupt=%d",ret); + log_e("install interrupt handler Failed=%d",ret); + return I2C_ERROR_MEMORY; } } - -log_e("before startup"); + +//hang until it completes. + +// how many ticks should it take to transfer totalBytes thru the I2C hardware, +// add 50ms just for kicks + +portTickType ticksTimeOut = ((totalBytes /(i2cGetFrequency(i2c)/(10*1000)))+50)/portTICK_PERIOD_MS; +portTickType tBefore=xTaskGetTickCount(); + +//log_e("before startup @tick=%d will wait=%d",xTaskGetTickCount(),ticksTimeOut); + i2c->dev->ctr.trans_start=1; // go for it -uint32_t eBits = xEventGroupWaitBits(i2c->i2c_event,EVENT_DONE,pdTRUE,pdTRUE,0xFF); -log_e("after Wait"); -dumpI2c(i2c); + +uint32_t eBits = xEventGroupWaitBits(i2c->i2c_event,EVENT_DONE,pdFALSE,pdTRUE,ticksTimeOut); + +//log_e("after WaitBits=%x @tick=%d",eBits,xTaskGetTickCount()); + +portTickType tAfter=xTaskGetTickCount(); + uint32_t b; -for(uint32_t a=1;a<=INTBUFFMAX;a++){ - b=(a+intPos)%INTBUFFMAX; - if(intBuff[b][0]!=0) log_e("[%02d] 0x%08x 0x%08x",b,intBuff[b][0],intBuff[b][1]); +if(!(eBits==EVENT_DONE)&&(eBits&~(EVENT_ERROR_NAK|EVENT_ERROR|EVENT_DONE))){ // not only Done, therefore error, exclude ADDR NAK + dumpI2c(i2c); + i2cDumpInts(); } - + if(eBits&EVENT_DONE){ // no gross timeout - reason = i2c->error; // not correct + switch(i2c->error){ + case I2C_OK : + reason = I2C_ERROR_OK; + break; + case I2C_ERROR : + reason = I2C_ERROR_DEV; + break; + case I2C_ADDR_NAK: + reason = I2C_ERROR_ACK; + break; + case I2C_DATA_NAK: + reason = I2C_ERROR_ACK; + break; + case I2C_ARBITRATION: + reason = I2C_ERROR_BUS; + break; + case I2C_TIMEOUT: + reason = I2C_ERROR_TIMEOUT; + break; + default : + reason = I2C_ERROR_DEV; + } } else { // GROSS timeout, shutdown ISR , report Timeout - log_e(" Gross Timeout Dead"); + i2c->stage = I2C_DONE; + i2c->dev->int_ena.val =0; + i2c->dev->int_clr.val = 0x1FFF; + reason = I2C_ERROR_TIMEOUT; + eBits = eBits | EVENT_ERROR_TIMEOUT|EVENT_ERROR|EVENT_DONE; + log_e(" Gross Timeout Dead st=%d, ed=%d, =%d, max=%d error=%d",tBefore,tAfter,(tAfter-tBefore),ticksTimeOut,i2c->error); + dumpI2c(i2c); + i2cDumpInts(); } -I2C_MUTEX_UNLOCK(); +// if error, need to trigger all succeeding dataQueue events with the EVENT_ERROR_PREV +if(eBits&EVENT_ERROR){// every dq past the error point needs it's EventGroup Released + b=i2c->queuePos + 1; + while(b < i2c->queueCount){ + if(i2c->dq[b].queueEvent){ // this data queue element has an EventGroup + xEventGroupSetBits(i2c->dq[b].queueEvent,eBits|EVENT_ERROR_PREV); + } + b++; + } + } + +I2C_MUTEX_UNLOCK(); return reason; } +i2c_err_t i2cReleaseISR(i2c_t * i2c){ +if(i2c->intr_handle){ + esp_err_t error =i2c_isr_free(i2c->intr_handle); +// log_e("released ISR=%d",error); + i2c->intr_handle=NULL; + } +return I2C_ERROR_OK; +} \ No newline at end of file diff --git a/cores/esp32/esp32-hal-i2c.h b/cores/esp32/esp32-hal-i2c.h index a4e0efa051a..888de9210d0 100644 --- a/cores/esp32/esp32-hal-i2c.h +++ b/cores/esp32/esp32-hal-i2c.h @@ -22,16 +22,21 @@ extern "C" { #include #include #include +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" + typedef enum { - I2C_ERROR_OK, + I2C_ERROR_OK=0, I2C_ERROR_DEV, I2C_ERROR_ACK, I2C_ERROR_TIMEOUT, I2C_ERROR_BUS, I2C_ERROR_BUSY, I2C_ERROR_MEMORY, - I2C_ERROR_CONTINUE + I2C_ERROR_CONTINUE, + I2C_ERROR_MISSING_WRITE, + I2C_ERROR_NO_BEGIN } i2c_err_t; typedef enum { @@ -53,17 +58,22 @@ typedef enum { // I2C_NONE=0, I2C_OK=1, I2C_ERROR, - I2C_ADDR_ACK, - I2C_DATA_ACK, + I2C_ADDR_NAK, + I2C_DATA_NAK, I2C_ARBITRATION, I2C_TIMEOUT }I2C_ERROR_t; // i2c_event bits -#define EVENT_IN_IRQ (BIT(2)) +#define EVENT_ERROR_NAK (BIT(0)) +#define EVENT_ERROR (BIT(1)) #define EVENT_RUNNING (BIT(3)) #define EVENT_DONE (BIT(4)) #define EVENT_IN_END (BIT(5)) +#define EVENT_ERROR_PREV (BIT(6)) +#define EVENT_ERROR_TIMEOUT (BIT(7)) +#define EVENT_ERROR_ARBITRATION (BIT(8)) +#define EVENT_ERROR_DATA_NAK (BIT(9)) typedef union{ struct { @@ -88,6 +98,7 @@ typedef struct { uint16_t cmdBytesNeeded; // number of data bytes needing (READ/WRITE)Command[] uint16_t queueLength; I2C_DATA_CTRL_t ctrl; + EventGroupHandle_t queueEvent; }I2C_DATA_QUEUE_t; struct i2c_struct_t; @@ -112,11 +123,14 @@ i2c_err_t i2cRead(i2c_t * i2c, uint16_t address, bool addr_10bit, uint8_t * data //Stickbreakers attempt to read big blocks i2c_err_t pollI2cRead(i2c_t * i2c, uint16_t address, bool addr_10bit, uint8_t * data, uint16_t len, bool sendStop); i2c_err_t i2cProcQueue(i2c_t *i2c); -i2c_err_t i2cAddQueueWrite(i2c_t *i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen, bool SendStop); -i2c_err_t i2cAddQueueRead(i2c_t *i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen, bool SendStop); +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); uint16_t i2cQueueReadPendingCount(i2c_t *i2c); uint16_t i2cQueueReadCount(i2c_t *i2c); +i2c_err_t i2cGetReadQueue(i2c_t *i2c, uint8_t** buffPtr, uint16_t* lenPtr,uint8_t *savePtr); +void i2cDumpInts(); static void IRAM_ATTR i2c_isr_handler_default(void* arg); //ISR diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp index 83c036b92dd..da38dc39e51 100644 --- a/libraries/Wire/src/Wire.cpp +++ b/libraries/Wire/src/Wire.cpp @@ -68,6 +68,8 @@ TwoWire::TwoWire(uint8_t bus_num) ,txLength(0) ,txAddress(0) ,transmitting(0) + ,txQueued(0) + ,rxQueued(0) {} void TwoWire::onRequestService(void){} @@ -125,9 +127,12 @@ void TwoWire::setClock(uint32_t frequency) { i2cSetFrequency(i2c, frequency); } - -size_t TwoWire::requestFrom(uint8_t address, size_t size, bool sendStop) +/* Original requestFrom() 11/2017 before ISR +*/ +size_t TwoWire::oldRequestFrom(uint8_t address, size_t size, bool sendStop) { + i2cReleaseISR(i2c); + if(size > I2C_BUFFER_LENGTH) { size = I2C_BUFFER_LENGTH; } @@ -138,9 +143,11 @@ size_t TwoWire::requestFrom(uint8_t address, size_t size, bool sendStop) return read; } -size_t TwoWire::newRequestFrom(uint8_t address, size_t size, bool sendStop){ +/* @stickBreaker 11/2017 fix for ReSTART timeout, ISR +*/ +size_t TwoWire::requestFrom(uint8_t address, size_t size, bool sendStop){ - uint16_t cnt = queuedRxLength; // currently queued reads + uint16_t cnt = rxQueued; // currently queued reads if(cntI2C_BUFFER_LENGTH) size = (I2C_BUFFER_LENGTH-cnt); @@ -150,20 +157,20 @@ size_t TwoWire::newRequestFrom(uint8_t address, size_t size, bool sendStop){ cnt = 0; rxIndex = 0; rxLength = 0; + rxQueued = 0; last_error = I2C_ERROR_MEMORY; + i2cFreeQueue(i2c); return cnt; } - last_error =i2cAddQueueRead(i2c,address,&rxBuffer[cnt],size,sendStop); + + last_error =i2cAddQueueRead(i2c,address,&rxBuffer[cnt],size,sendStop,NULL); if(last_error==I2C_ERROR_OK){ // successfully queued the read - queuedRxLength += size; + rxQueued += size; if(sendStop){ //now actually process the queued commands last_error=i2cProcQueue(i2c); - if(last_error!=I2C_ERROR_OK){ - log_e("err=%d",last_error); - } rxIndex = 0; rxLength = i2cQueueReadCount(i2c); - queuedRxLength = 0; + rxQueued = 0; cnt = rxLength; i2cFreeQueue(i2c); } @@ -178,7 +185,96 @@ size_t TwoWire::newRequestFrom(uint8_t address, size_t size, bool sendStop){ return cnt; } -size_t TwoWire::newRequestFrom(uint8_t address, uint8_t* buf, size_t size, bool sendStop){ +size_t TwoWire::requestFrom(uint8_t address, uint8_t * readBuff, size_t size, bool sendStop){ + size_t cnt=0; + last_error =i2cAddQueueRead(i2c,address,readBuff,size,sendStop,NULL); + if(last_error==I2C_ERROR_OK){ // successfully queued the read + if(sendStop){ //now actually process the queued commands + last_error=i2cProcQueue(i2c); + rxIndex = 0; + rxLength = 0; + txQueued = 0; // the SendStop=true will restart are Queueing + rxQueued = 0; + cnt = i2cQueueReadCount(i2c); + + //what about mix local buffers and Wire Buffers? + //Handled it by verifying location, only contiguous sections of + //rxBuffer are counted. + uint8_t *savePtr; + uint16_t len; + uint8_t idx=0; + while(I2C_ERROR_OK==i2cGetReadQueue(i2c,&savePtr,&len,&idx)){ + if(savePtr==(rxBuffer+rxLength)){ + rxLength = rxLength + len; + } + } + i2cFreeQueue(i2c); + } + else { // stop not received, so wait for I2C stop, + last_error=I2C_ERROR_CONTINUE; + cnt = 0; + } + } + else {// only possible error is I2C_ERROR_MEMORY + cnt = 0; + } + return cnt; +} + +uint8_t TwoWire::writeTransaction(uint8_t address, uint8_t *buff, size_t size, bool sendStop){ +// will destroy any partially created beginTransaction() + +last_error=i2cAddQueueWrite(i2c,address,buff,size,sendStop,NULL); + +if(last_error==I2C_ERROR_OK){ //queued + if(sendStop){ //now actually process the queued commands, including READs + last_error=i2cProcQueue(i2c); + rxIndex = 0; + rxLength = 0; + txQueued = 0; // the SendStop=true will restart all Queueing + rxQueued = 0; + + //what about mix local buffers and Wire Buffers? + //Handled it by verifying location, only contiguous sections of + //rxBuffer are counted. + uint8_t *savePtr; + uint16_t len; + uint8_t idx=0; + while(I2C_ERROR_OK==i2cGetReadQueue(i2c,&savePtr,&len,&idx)){ + if(savePtr==(rxBuffer+rxLength)){ + rxLength = rxLength + len; + } + } + i2cFreeQueue(i2c); + } + else { // stop not received, so wait for I2C stop, + last_error=I2C_ERROR_CONTINUE; + } + } +txIndex=0; +txLength=0; +transmitting = 0; +return static_cast(last_error); +} + +/*stickbreaker Dump i2c Interrupt buffer, i2c isr Debugging +*/ +void TwoWire::dumpInts(){ + i2cDumpInts(); +} + +/*stickbreaker i2c isr Debugging +*/ +size_t TwoWire::getClock(){ + return i2cGetFrequency(i2c); +} + +/*stickbreaker using the i2c hardware without an ISR +*/ +size_t TwoWire::polledRequestFrom(uint8_t address, uint8_t* buf, size_t size, bool sendStop){ + + i2cReleaseISR(i2c); + rxIndex = 0; rxLength = 0; if(size==0){ @@ -192,58 +288,71 @@ size_t TwoWire::newRequestFrom(uint8_t address, uint8_t* buf, size_t size, bool return read; } +/*stickbreaker simple ReSTART handling using internal Wire data buffers +*/ size_t TwoWire::transact(size_t readLen){ // Assumes Wire.beginTransaction(),Wire.write() // this command replaces Wire.endTransmission(false) and Wire.requestFrom(readLen,true); - last_error =i2cAddQueueWrite(i2c,txAddress,txBuffer,txLength,false); //queue tx element - size_t cnt = 0; - if(last_error != I2C_ERROR_OK){ - log_e("writeQueue err=%d",last_error); - rxIndex = 0; - rxLength = 0; - queuedRxLength = 0; - txLength = 0; - i2cFreeQueue(i2c); - return cnt; - } - - if(readLen>I2C_BUFFER_LENGTH) readLen = I2C_BUFFER_LENGTH; - - last_error =i2cAddQueueRead(i2c,txAddress,rxBuffer,readLen,true); //queue rx element - if(last_error==I2C_ERROR_OK){ // successfully queued the read - last_error=i2cProcQueue(i2c); - if(last_error!=I2C_ERROR_OK){ - log_e("procqueue err=%d",last_error); - } - rxIndex = 0; - rxLength = i2cQueueReadCount(i2c); - cnt = rxLength; - i2cFreeQueue(i2c); - } - else {// only possible error is I2C_ERROR_MEMORY - log_e("readqueue err=%d",last_error); - cnt = 0; - rxIndex=0; - rxLength=0; - i2cFreeQueue(i2c); // cleanup - } - return cnt; +if(transmitting){ + last_error = static_cast(endTransmission(false)); + } + +if(last_error==I2C_ERROR_CONTINUE){ // must have queued the Write + size_t cnt = requestFrom(txAddress,readLen,true); + return cnt; + } +else { + last_error = I2C_ERROR_MISSING_WRITE; + return 0; + } } -uint8_t TwoWire::newEndTransmission(void){ // Assumes Wire.beginTransaction(),Wire.write() -// this command replaces Wire.endTransmission(true) - size_t cnt = 0; -last_error =i2cAddQueueWrite(i2c,txAddress,txBuffer,txLength,true); //queue tx element +/*stickbreaker isr ReSTART with external read Buffer +*/ +size_t TwoWire::transact(uint8_t * readBuff, size_t readLen){ // Assumes Wire.beginTransaction(),Wire.write() +// this command replaces Wire.endTransmission(false) and Wire.requestFrom(readLen,true); +if(transmitting){ + last_error = static_cast(endTransmission(false)); + } -if(last_error == I2C_ERROR_OK){ - last_error=i2cProcQueue(i2c); - if(last_error!=I2C_ERROR_OK){ - log_e("procqueue err=%d",last_error); - } - i2cFreeQueue(i2c); +if(last_error==I2C_ERROR_CONTINUE){ // must have queued the write + size_t cnt = requestFrom(txAddress,readBuff,readLen,true); + return cnt; + } +else { + last_error = I2C_ERROR_MISSING_WRITE; + return 0; } +} -txLength = 0; +/*stickbreaker isr +*/ +uint8_t TwoWire::endTransmission(uint8_t sendStop){ // Assumes Wire.beginTransaction(), Wire.write() +// this command replaces Wire.endTransmission(true) +if(transmitting==1){ + last_error =i2cAddQueueWrite(i2c,txAddress,&txBuffer[txQueued],txLength,sendStop,NULL); //queue tx element + + if(last_error == I2C_ERROR_OK){ + if(sendStop){ + last_error=i2cProcQueue(i2c); + txQueued = 0; + i2cFreeQueue(i2c); + } + else { // queued because it had sendStop==false + // txlength is howmany bytes in txbufferhave been use + txQueued = txLength; + last_error = I2C_ERROR_CONTINUE; + } + } + } +else { + last_error= I2C_ERROR_NO_BEGIN; + txQueued = 0; + i2cFreeQueue(i2c); // cleanup + } +txIndex = 0; +txLength =0; +transmitting = 0; return last_error; } @@ -251,8 +360,10 @@ i2c_err_t TwoWire::lastError(){ return last_error; } -uint8_t TwoWire::endTransmission(uint8_t sendStop) +uint8_t TwoWire::oldEndTransmission(uint8_t sendStop) { + //disable ISR + i2cReleaseISR(i2c); int8_t ret = i2cWrite(i2c, txAddress, false, txBuffer, txLength, sendStop); txIndex = 0; txLength = 0; @@ -277,15 +388,15 @@ uint8_t TwoWire::requestFrom(int address, int quantity) uint8_t TwoWire::requestFrom(int address, int quantity, int sendStop) { - return requestFrom(static_cast(address), static_cast(quantity), static_cast(sendStop)); + return static_cast(requestFrom(static_cast(address), static_cast(quantity), static_cast(sendStop))); } void TwoWire::beginTransmission(uint8_t address) { transmitting = 1; txAddress = address; - txIndex = 0; - txLength = 0; + txIndex = txQueued; // allow multiple beginTransmission(),write(),endTransmission(false) until endTransmission(true) + txLength = txQueued; } void TwoWire::beginTransmission(int address) @@ -354,6 +465,9 @@ void TwoWire::flush(void) rxLength = 0; txIndex = 0; txLength = 0; + rxQueued = 0; + txQueued = 0; + i2cFreeQueue(i2c); // cleanup } void TwoWire::reset(void) diff --git a/libraries/Wire/src/Wire.h b/libraries/Wire/src/Wire.h index 0595cf6ba01..277a2561dde 100644 --- a/libraries/Wire/src/Wire.h +++ b/libraries/Wire/src/Wire.h @@ -44,19 +44,20 @@ class TwoWire: public Stream uint8_t rxBuffer[I2C_BUFFER_LENGTH]; uint16_t rxIndex; uint16_t rxLength; - uint16_t queuedRxLength; + uint16_t rxQueued; //@stickBreaker uint8_t txBuffer[I2C_BUFFER_LENGTH]; uint16_t txIndex; uint16_t txLength; uint8_t txAddress; + uint16_t txQueued; //@stickbreaker uint8_t transmitting; static user_onRequest uReq[2]; static user_onReceive uRcv[2]; void onRequestService(void); void onReceiveService(uint8_t*, int); - i2c_err_t last_error; // from esp32-hal-i2c.h + i2c_err_t last_error; // @stickBreaker from esp32-hal-i2c.h public: TwoWire(uint8_t bus_num); @@ -66,14 +67,19 @@ class TwoWire: public Stream void beginTransmission(int); uint8_t endTransmission(void); uint8_t endTransmission(uint8_t); - uint8_t newEndTransmission(void); - size_t requestFrom(uint8_t address, size_t size, bool sendStop); - - size_t newRequestFrom(uint8_t address, size_t size, bool sendStop); - size_t newRequestFrom(uint8_t address, uint8_t* buf, size_t size, bool sendStop); + uint8_t oldEndTransmission(uint8_t); + size_t oldRequestFrom(uint8_t address, size_t size, bool sendStop); +//@stickBreaker for big blocks and ISR model + uint8_t writeTransaction(uint8_t address, uint8_t* buff, size_t size, bool sendStop); + size_t requestFrom(uint8_t address, size_t size, bool sendStop); + size_t requestFrom(uint8_t address, uint8_t* buf, size_t size, bool sendStop); + size_t polledRequestFrom(uint8_t address, uint8_t* buf, size_t size, bool sendStop); size_t transact(size_t readLen); + size_t transact(uint8_t* readBuff, size_t readLen); i2c_err_t lastError(); - + void dumpInts(); + size_t getClock(); +// uint8_t requestFrom(uint8_t, uint8_t); uint8_t requestFrom(uint8_t, uint8_t, uint8_t); uint8_t requestFrom(int, int); From 832a0eb851120ad91811ad4d1cc5c854b77f83e0 Mon Sep 17 00:00:00 2001 From: chuck todd Date: Thu, 16 Nov 2017 10:01:40 -0700 Subject: [PATCH 04/63] Update README2.md sp and formatting --- README2.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README2.md b/README2.md index a46c8ee0b71..e03b45ac073 100644 --- a/README2.md +++ b/README2.md @@ -4,9 +4,9 @@ The existing main fork of arduino-esp32 implement i2c communications using a polled monitoring of the I2C Hardware StateMachine. The implementation of the I2C ReSTART operations leaves the I2C Hardware StateMachine(SM) in an unstable configuration. The I2C protocol arbitrates exclusive ownership of the bus to a single Master Device from a `START` signal to the next `STOP`. A restart operation is just a start signal inserted into this `START` -> `STOP` flow. Schematically like this: `START` (Read or Write) -> `ReSTART` (Read or Write) -> `STOP`. - By I2C protocol as [UM10204 I2C-Bus Specification and User Manual](https://Fwww.nxp.com/docs/en/user-guide/UM10204.pdf), A valid I2C transaction must be a `START` at least one Data Byte `STOP`. The Arduino environment is base on the AVR processor series, These Atmel CPU's implementation of the the I2C protocol allows unlimited pauses between protocol elements. The Espressif implement enforces a Strict TimeOut between elements. This has resulted in perceived I2C bus stability issues ([834](https://github.com/espressif/arduino-esp32/issues/834),[811](https://github.com/espressif/arduino-esp32/issues/811),[741](https://github.com/espressif/arduino-esp32/issues/741),[682](https://github.com/espressif/arduino-esp32/issues/682),[659](https://github.com/espressif/arduino-esp32/issues/659) and many more. @lonerzzz create a partial solution [#751](https://github.com/espressif/arduino-esp32/pull/751). + By I2C protocol as [UM10204 I2C-Bus Specification and User Manual](https://Fwww.nxp.com/docs/en/user-guide/UM10204.pdf), A valid I2C transaction must be a `START` at least one Data Byte `STOP`. The Arduino environment is base on the AVR processor series, These Atmel CPU's implementation of the the I2C protocol allows unlimited pauses between protocol elements. The Espressif implement enforces a Strict TimeOut between elements. This has resulted in perceived I2C bus stability issues ([834](https://github.com/espressif/arduino-esp32/issues/834),[811](https://github.com/espressif/arduino-esp32/issues/811),[741](https://github.com/espressif/arduino-esp32/issues/741),[682](https://github.com/espressif/arduino-esp32/issues/682),[659](https://github.com/espressif/arduino-esp32/issues/659) and many more. @lonerzzz created a partial solution [#751](https://github.com/espressif/arduino-esp32/pull/751). I spend a couple of weeks investigating these problems, and discovered this Hard TimeOut was the basis of all of these problems. Once this Hard TimeOut limit is triggered the SM cannot recover without a complete reinitialization. -The existing Arduino code base is reliant on the AVR's ability to infinetly pausing a i2c transaction. The standard coding practice of: +The existing Arduino code base is reliant on the AVR's ability to infintely pause a i2c transaction. The standard coding practice of: ```c++ // set internal address pointer in I2C EEPROM from which to read Wire.beginTransmission(ID); @@ -75,12 +75,12 @@ I have create a few new methods for Wire: size_t oldRequestFrom(uint8_t address, size_t size, bool sendStop); //released implementation //@stickBreaker for big blocks and ISR model uint8_t writeTransaction(uint8_t address, uint8_t* buff, size_t size, bool sendStop);// big block handling - size_t requestFrom(uint8_t address, size_t size, bool sendStop); - size_t requestFrom(uint8_t address, uint8_t* buf, size_t size, bool sendStop); - size_t polledRequestFrom(uint8_t address, uint8_t* buf, size_t size, bool sendStop);//a BigBlock test case Not USING ISR - size_t transact(size_t readLen); // replacement for endTransmission(false),requestFrom(ID,readLen,true); - size_t transact(uint8_t* readBuff, size_t readLen);// bigger Block read - i2c_err_t lastError(); // Expose complete error + size_t requestFrom(uint8_t address, size_t size, bool sendStop); + size_t requestFrom(uint8_t address, uint8_t* buf, size_t size, bool sendStop); + size_t polledRequestFrom(uint8_t address, uint8_t* buf, size_t size, bool sendStop);//a BigBlock test case Not USING ISR + size_t transact(size_t readLen); // replacement for endTransmission(false),requestFrom(ID,readLen,true); + size_t transact(uint8_t* readBuff, size_t readLen);// bigger Block read + i2c_err_t lastError(); // Expose complete error void dumpInts(); // diagnostic dump for the last 64 different i2c Interrupts size_t getClock(); // current i2c Clock rate ``` @@ -112,4 +112,4 @@ There is MINIMAL to NO ERROR detection, BUS, BUSY. because I have not encounter -Chuck. \ No newline at end of file +Chuck. From 8ac6be6845400782f13f9e986884a06279fa3d39 Mon Sep 17 00:00:00 2001 From: Chuck Todd Date: Thu, 16 Nov 2017 10:04:55 -0700 Subject: [PATCH 05/63] Substitute ReadMe Substitute my ReadMe for the Arduio-esp32 default --- README.md | 135 +++++++++++++++++++++++++++++++++++++------------- README2.md | 115 ------------------------------------------ README_OLD.md | 48 ++++++++++++++++++ 3 files changed, 149 insertions(+), 149 deletions(-) delete mode 100644 README2.md create mode 100644 README_OLD.md diff --git a/README.md b/README.md index 4c0c046e5a8..e03b45ac073 100644 --- a/README.md +++ b/README.md @@ -1,48 +1,115 @@ -# Arduino core for ESP32 WiFi chip +# A fork of Espressif/arduino-esp32: +## i2c communications using a ISR -[![Build Status](https://travis-ci.org/espressif/arduino-esp32.svg?branch=master)](https://travis-ci.org/espressif/arduino-esp32) +The existing main fork of arduino-esp32 implement i2c communications using a polled monitoring of the I2C Hardware StateMachine. The implementation of the I2C ReSTART operations leaves the I2C Hardware StateMachine(SM) in an unstable configuration. The I2C protocol arbitrates exclusive ownership of the bus to a single Master Device from a `START` signal to the next `STOP`. A restart operation is just a start signal inserted into this `START` -> `STOP` flow. Schematically like this: + `START` (Read or Write) -> `ReSTART` (Read or Write) -> `STOP`. + + By I2C protocol as [UM10204 I2C-Bus Specification and User Manual](https://Fwww.nxp.com/docs/en/user-guide/UM10204.pdf), A valid I2C transaction must be a `START` at least one Data Byte `STOP`. The Arduino environment is base on the AVR processor series, These Atmel CPU's implementation of the the I2C protocol allows unlimited pauses between protocol elements. The Espressif implement enforces a Strict TimeOut between elements. This has resulted in perceived I2C bus stability issues ([834](https://github.com/espressif/arduino-esp32/issues/834),[811](https://github.com/espressif/arduino-esp32/issues/811),[741](https://github.com/espressif/arduino-esp32/issues/741),[682](https://github.com/espressif/arduino-esp32/issues/682),[659](https://github.com/espressif/arduino-esp32/issues/659) and many more. @lonerzzz created a partial solution [#751](https://github.com/espressif/arduino-esp32/pull/751). + I spend a couple of weeks investigating these problems, and discovered this Hard TimeOut was the basis of all of these problems. Once this Hard TimeOut limit is triggered the SM cannot recover without a complete reinitialization. +The existing Arduino code base is reliant on the AVR's ability to infintely pause a i2c transaction. The standard coding practice of: +```c++ +// set internal address pointer in I2C EEPROM from which to read +Wire.beginTransmission(ID); +Wire.write(highByte(addr)); +Wire.write(lowByte(addr)); +uint8_t err =Wire.endTransmission(false); // don't send a STOP, just Pause I2C operations +if(err==0){ // successfully set internal address pointer + err=Wire.requestFrom(addr,len); + if(err==0){ // read failed + Serial.print("Bad Stuff!! Read Failed\n"); + } + else {// successful read + while(Wire.avaiable()){ + Serial.print((char)Wire.read()); + } + Serial.println(); + } + } +``` +May not function correctly with the ESP32, actually *usually* will not function correctly. The current arduino-esp32 platform is built upon the espressif-idf which is built on FreeRTOS, a multi-process/processor operating system. The Arduino sketch is just one task executing on One of the Two processor cores. The TimeOut is triggered when the FreeRTOS Task Scheduler does a task switch between `Wire.endTransmission(false);` and the i2c activites inside `Wire.requestfrom()`. The maximum TimeOut interval is around 12ms. +My solution is to avoid this TimeOut from every being possible. I have changed how `Wire()` uses the SM. Specifically I have changed how a `ReSTART` operation is implemented. To avoid the TimeOut I have create a i2c queuing system that does not allow an i2c transaction to start unless a `STOP` has been issued. But, alas, this creates some incompatibilities with the pre-exisiting Arduino code base. The changes to the standard Arduino `Wire()` coding are minimal, but, they are necessary: +```c++ +// new, maybe Excessive Error Return codes for @stickbreaker:arduino-esp32 +typedef enum { + I2C_ERROR_OK=0, + I2C_ERROR_DEV, + I2C_ERROR_ACK, + I2C_ERROR_TIMEOUT, + I2C_ERROR_BUS, + I2C_ERROR_BUSY, + I2C_ERROR_MEMORY, + I2C_ERROR_CONTINUE, + I2C_ERROR_MISSING_WRITE, + I2C_ERROR_NO_BEGIN +} i2c_err_t; -### Need help or have a question? Join the chat at [![https://gitter.im/espressif/arduino-esp32](https://badges.gitter.im/espressif/arduino-esp32.svg)](https://gitter.im/espressif/arduino-esp32?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +// set internal address pointer in I2C EEPROM from which to read +Wire.beginTransmission(ID); +Wire.write(highByte(addr)); +Wire.write(lowByte(addr)); +uint8_t err =Wire.endTransmission(false); // don't send a STOP, just Pause I2C operations -## Contents -- [Development Status](#development-status) -- [Installation Instructions](#installation-instructions) -- [Decoding Exceptions](#decoding-exceptions) -- [Issue/Bug report template](#issuebug-report-template) -- [ESP32Dev Board PINMAP](#esp32dev-board-pinmap) +//err will be I2C_ERROR_CONTINUE (7), an indication that the preceding +//transaction has been Queued, NOT EXECUTED -## Development Status -Most of the framework is implemented. Most noticable is the missing analogWrite. While analogWrite is on it's way, there are a few other options that you can use: -- 16 channels [LEDC](cores/esp32/esp32-hal-ledc.h) which is PWM -- 8 channels [SigmaDelta](cores/esp32/esp32-hal-sigmadelta.h) which uses SigmaDelta modulation -- 2 channels [DAC](cores/esp32/esp32-hal-dac.h) which gives real analog output +if(err==7){ // Prior Operation has been queued, it is NOT guaranteed that it will +// successfully occur! + err=Wire.requestFrom(addr,len); + if(err!=len){ // complete/partial read failure + Serial.print("Bad Stuff!! Read Failed lastError="); + Serial.print(Wire.lastError(),DEC); + } + // some of the read may have executed + while(Wire.avaiable()){ + Serial.print((char)Wire.read()); + } + Serial.println(); + } +``` -## Installation Instructions +Additionally I have expanded the ability of `Wire()` to handle larger Reads and Writes: up to 64k-1, I have tested READs of these sizes, but the largest single WRITE i have tested is 128bytes. I can send more data to a 24LC512, but it will only store the last 128bytes. -- Using Arduino IDE - + [Instructions for Windows](docs/arduino-ide/windows.md) - + [Instructions for Mac](docs/arduino-ide/mac.md) - + [Instructions for Debian/Ubuntu Linux](docs/arduino-ide/debian_ubuntu.md) - + [Instructions for Fedora](docs/arduino-ide/fedora.md) - + [Instructions for openSUSE](docs/arduino-ide/opensuse.md) -- [Using PlatformIO](docs/platformio.md) -- [Building with make](docs/make.md) -- [Using as ESP-IDF component](docs/esp-idf_component.md) +I have create a few new methods for Wire: +```c++ + uint8_t oldEndTransmission(uint8_t); //released implementation + size_t oldRequestFrom(uint8_t address, size_t size, bool sendStop); //released implementation +//@stickBreaker for big blocks and ISR model + uint8_t writeTransaction(uint8_t address, uint8_t* buff, size_t size, bool sendStop);// big block handling + size_t requestFrom(uint8_t address, size_t size, bool sendStop); + size_t requestFrom(uint8_t address, uint8_t* buf, size_t size, bool sendStop); + size_t polledRequestFrom(uint8_t address, uint8_t* buf, size_t size, bool sendStop);//a BigBlock test case Not USING ISR + size_t transact(size_t readLen); // replacement for endTransmission(false),requestFrom(ID,readLen,true); + size_t transact(uint8_t* readBuff, size_t readLen);// bigger Block read + i2c_err_t lastError(); // Expose complete error + void dumpInts(); // diagnostic dump for the last 64 different i2c Interrupts + size_t getClock(); // current i2c Clock rate +``` -#### Decoding exceptions +`transact()` coding is: +```c++ +// set internal address pointer in I2C EEPROM from which to read +Wire.beginTransmission(ID); +Wire.write(highByte(addr)); +Wire.write(lowByte(addr)); -You can use [EspExceptionDecoder](https://github.com/me-no-dev/EspExceptionDecoder) to get meaningful call trace. +uint8_t err=Wire.transact(len); +if(err!=len){ // complete/partial read failure + Serial.print("Bad Stuff!! Read Failed lastError="); + Serial.print(Wire.lastError(),DEC); + } + // some of the read may have executed +while(Wire.avaiable()){ + Serial.print((char)Wire.read()); + } +Serial.println(); -#### Issue/Bug report template -Before reporting an issue, make sure you've searched for similar one that was already created. Also make sure to go through all the issues labelled as [for reference](https://github.com/espressif/arduino-esp32/issues?utf8=%E2%9C%93&q=is%3Aissue%20label%3A%22for%20reference%22%20). +``` -Finally, if you're sure no one else had the issue, follow the [ISSUE_TEMPLATE](docs/ISSUE_TEMPLATE.md) while reporting any issue. +This **APLHA** release should be compiled with ESP32 Dev Module as its target, and +Set the "core debug level" to 'error' +There is MINIMAL to NO ERROR detection, BUS, BUSY. because I have not encounter any of them! -## ESP32Dev Board PINMAP -![Pin Functions](docs/esp32_pinmap.png) -## Hint - -Sometimes to program ESP32 via serial you must keep GPIO0 LOW during the programming process +Chuck. diff --git a/README2.md b/README2.md deleted file mode 100644 index e03b45ac073..00000000000 --- a/README2.md +++ /dev/null @@ -1,115 +0,0 @@ -# A fork of Espressif/arduino-esp32: -## i2c communications using a ISR - -The existing main fork of arduino-esp32 implement i2c communications using a polled monitoring of the I2C Hardware StateMachine. The implementation of the I2C ReSTART operations leaves the I2C Hardware StateMachine(SM) in an unstable configuration. The I2C protocol arbitrates exclusive ownership of the bus to a single Master Device from a `START` signal to the next `STOP`. A restart operation is just a start signal inserted into this `START` -> `STOP` flow. Schematically like this: - `START` (Read or Write) -> `ReSTART` (Read or Write) -> `STOP`. - - By I2C protocol as [UM10204 I2C-Bus Specification and User Manual](https://Fwww.nxp.com/docs/en/user-guide/UM10204.pdf), A valid I2C transaction must be a `START` at least one Data Byte `STOP`. The Arduino environment is base on the AVR processor series, These Atmel CPU's implementation of the the I2C protocol allows unlimited pauses between protocol elements. The Espressif implement enforces a Strict TimeOut between elements. This has resulted in perceived I2C bus stability issues ([834](https://github.com/espressif/arduino-esp32/issues/834),[811](https://github.com/espressif/arduino-esp32/issues/811),[741](https://github.com/espressif/arduino-esp32/issues/741),[682](https://github.com/espressif/arduino-esp32/issues/682),[659](https://github.com/espressif/arduino-esp32/issues/659) and many more. @lonerzzz created a partial solution [#751](https://github.com/espressif/arduino-esp32/pull/751). - I spend a couple of weeks investigating these problems, and discovered this Hard TimeOut was the basis of all of these problems. Once this Hard TimeOut limit is triggered the SM cannot recover without a complete reinitialization. -The existing Arduino code base is reliant on the AVR's ability to infintely pause a i2c transaction. The standard coding practice of: -```c++ -// set internal address pointer in I2C EEPROM from which to read -Wire.beginTransmission(ID); -Wire.write(highByte(addr)); -Wire.write(lowByte(addr)); -uint8_t err =Wire.endTransmission(false); // don't send a STOP, just Pause I2C operations -if(err==0){ // successfully set internal address pointer - err=Wire.requestFrom(addr,len); - if(err==0){ // read failed - Serial.print("Bad Stuff!! Read Failed\n"); - } - else {// successful read - while(Wire.avaiable()){ - Serial.print((char)Wire.read()); - } - Serial.println(); - } - } -``` -May not function correctly with the ESP32, actually *usually* will not function correctly. The current arduino-esp32 platform is built upon the espressif-idf which is built on FreeRTOS, a multi-process/processor operating system. The Arduino sketch is just one task executing on One of the Two processor cores. The TimeOut is triggered when the FreeRTOS Task Scheduler does a task switch between `Wire.endTransmission(false);` and the i2c activites inside `Wire.requestfrom()`. The maximum TimeOut interval is around 12ms. -My solution is to avoid this TimeOut from every being possible. I have changed how `Wire()` uses the SM. Specifically I have changed how a `ReSTART` operation is implemented. To avoid the TimeOut I have create a i2c queuing system that does not allow an i2c transaction to start unless a `STOP` has been issued. But, alas, this creates some incompatibilities with the pre-exisiting Arduino code base. The changes to the standard Arduino `Wire()` coding are minimal, but, they are necessary: -```c++ -// new, maybe Excessive Error Return codes for @stickbreaker:arduino-esp32 -typedef enum { - I2C_ERROR_OK=0, - I2C_ERROR_DEV, - I2C_ERROR_ACK, - I2C_ERROR_TIMEOUT, - I2C_ERROR_BUS, - I2C_ERROR_BUSY, - I2C_ERROR_MEMORY, - I2C_ERROR_CONTINUE, - I2C_ERROR_MISSING_WRITE, - I2C_ERROR_NO_BEGIN -} i2c_err_t; - -// set internal address pointer in I2C EEPROM from which to read -Wire.beginTransmission(ID); -Wire.write(highByte(addr)); -Wire.write(lowByte(addr)); -uint8_t err =Wire.endTransmission(false); // don't send a STOP, just Pause I2C operations - -//err will be I2C_ERROR_CONTINUE (7), an indication that the preceding -//transaction has been Queued, NOT EXECUTED - -if(err==7){ // Prior Operation has been queued, it is NOT guaranteed that it will -// successfully occur! - err=Wire.requestFrom(addr,len); - if(err!=len){ // complete/partial read failure - Serial.print("Bad Stuff!! Read Failed lastError="); - Serial.print(Wire.lastError(),DEC); - } - // some of the read may have executed - while(Wire.avaiable()){ - Serial.print((char)Wire.read()); - } - Serial.println(); - } -``` - -Additionally I have expanded the ability of `Wire()` to handle larger Reads and Writes: up to 64k-1, I have tested READs of these sizes, but the largest single WRITE i have tested is 128bytes. I can send more data to a 24LC512, but it will only store the last 128bytes. - -I have create a few new methods for Wire: -```c++ - uint8_t oldEndTransmission(uint8_t); //released implementation - size_t oldRequestFrom(uint8_t address, size_t size, bool sendStop); //released implementation -//@stickBreaker for big blocks and ISR model - uint8_t writeTransaction(uint8_t address, uint8_t* buff, size_t size, bool sendStop);// big block handling - size_t requestFrom(uint8_t address, size_t size, bool sendStop); - size_t requestFrom(uint8_t address, uint8_t* buf, size_t size, bool sendStop); - size_t polledRequestFrom(uint8_t address, uint8_t* buf, size_t size, bool sendStop);//a BigBlock test case Not USING ISR - size_t transact(size_t readLen); // replacement for endTransmission(false),requestFrom(ID,readLen,true); - size_t transact(uint8_t* readBuff, size_t readLen);// bigger Block read - i2c_err_t lastError(); // Expose complete error - void dumpInts(); // diagnostic dump for the last 64 different i2c Interrupts - size_t getClock(); // current i2c Clock rate -``` - -`transact()` coding is: -```c++ -// set internal address pointer in I2C EEPROM from which to read -Wire.beginTransmission(ID); -Wire.write(highByte(addr)); -Wire.write(lowByte(addr)); - -uint8_t err=Wire.transact(len); -if(err!=len){ // complete/partial read failure - Serial.print("Bad Stuff!! Read Failed lastError="); - Serial.print(Wire.lastError(),DEC); - } - // some of the read may have executed -while(Wire.avaiable()){ - Serial.print((char)Wire.read()); - } -Serial.println(); - -``` - -This **APLHA** release should be compiled with ESP32 Dev Module as its target, and -Set the "core debug level" to 'error' - -There is MINIMAL to NO ERROR detection, BUS, BUSY. because I have not encounter any of them! - - - -Chuck. diff --git a/README_OLD.md b/README_OLD.md new file mode 100644 index 00000000000..4c0c046e5a8 --- /dev/null +++ b/README_OLD.md @@ -0,0 +1,48 @@ +# Arduino core for ESP32 WiFi chip + +[![Build Status](https://travis-ci.org/espressif/arduino-esp32.svg?branch=master)](https://travis-ci.org/espressif/arduino-esp32) + +### Need help or have a question? Join the chat at [![https://gitter.im/espressif/arduino-esp32](https://badges.gitter.im/espressif/arduino-esp32.svg)](https://gitter.im/espressif/arduino-esp32?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +## Contents +- [Development Status](#development-status) +- [Installation Instructions](#installation-instructions) +- [Decoding Exceptions](#decoding-exceptions) +- [Issue/Bug report template](#issuebug-report-template) +- [ESP32Dev Board PINMAP](#esp32dev-board-pinmap) + +## Development Status +Most of the framework is implemented. Most noticable is the missing analogWrite. While analogWrite is on it's way, there are a few other options that you can use: +- 16 channels [LEDC](cores/esp32/esp32-hal-ledc.h) which is PWM +- 8 channels [SigmaDelta](cores/esp32/esp32-hal-sigmadelta.h) which uses SigmaDelta modulation +- 2 channels [DAC](cores/esp32/esp32-hal-dac.h) which gives real analog output + +## Installation Instructions + +- Using Arduino IDE + + [Instructions for Windows](docs/arduino-ide/windows.md) + + [Instructions for Mac](docs/arduino-ide/mac.md) + + [Instructions for Debian/Ubuntu Linux](docs/arduino-ide/debian_ubuntu.md) + + [Instructions for Fedora](docs/arduino-ide/fedora.md) + + [Instructions for openSUSE](docs/arduino-ide/opensuse.md) +- [Using PlatformIO](docs/platformio.md) +- [Building with make](docs/make.md) +- [Using as ESP-IDF component](docs/esp-idf_component.md) + +#### Decoding exceptions + +You can use [EspExceptionDecoder](https://github.com/me-no-dev/EspExceptionDecoder) to get meaningful call trace. + +#### Issue/Bug report template +Before reporting an issue, make sure you've searched for similar one that was already created. Also make sure to go through all the issues labelled as [for reference](https://github.com/espressif/arduino-esp32/issues?utf8=%E2%9C%93&q=is%3Aissue%20label%3A%22for%20reference%22%20). + +Finally, if you're sure no one else had the issue, follow the [ISSUE_TEMPLATE](docs/ISSUE_TEMPLATE.md) while reporting any issue. + + +## ESP32Dev Board PINMAP + +![Pin Functions](docs/esp32_pinmap.png) + +## Hint + +Sometimes to program ESP32 via serial you must keep GPIO0 LOW during the programming process From 1a458dccb501d2dd5dae2828d70c9940e9dad31a Mon Sep 17 00:00:00 2001 From: chuck todd Date: Thu, 16 Nov 2017 21:44:55 -0700 Subject: [PATCH 06/63] Missing Reset of txQueue in requestFrom(ID,Len) --- libraries/Wire/src/Wire.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp index da38dc39e51..f9673873aa8 100644 --- a/libraries/Wire/src/Wire.cpp +++ b/libraries/Wire/src/Wire.cpp @@ -158,6 +158,7 @@ size_t TwoWire::requestFrom(uint8_t address, size_t size, bool sendStop){ rxIndex = 0; rxLength = 0; rxQueued = 0; + txQueued = 0; last_error = I2C_ERROR_MEMORY; i2cFreeQueue(i2c); return cnt; From 7753177e88837b0ec169186ae54b43acea46aa62 Mon Sep 17 00:00:00 2001 From: chuck todd Date: Thu, 16 Nov 2017 21:54:18 -0700 Subject: [PATCH 07/63] Wrong Spot! Put the txQueue=0; in the correct place! --- libraries/Wire/src/Wire.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp index f9673873aa8..31a2c4bb2a7 100644 --- a/libraries/Wire/src/Wire.cpp +++ b/libraries/Wire/src/Wire.cpp @@ -158,7 +158,6 @@ size_t TwoWire::requestFrom(uint8_t address, size_t size, bool sendStop){ rxIndex = 0; rxLength = 0; rxQueued = 0; - txQueued = 0; last_error = I2C_ERROR_MEMORY; i2cFreeQueue(i2c); return cnt; @@ -172,6 +171,7 @@ size_t TwoWire::requestFrom(uint8_t address, size_t size, bool sendStop){ rxIndex = 0; rxLength = i2cQueueReadCount(i2c); rxQueued = 0; + txQueued = 0; cnt = rxLength; i2cFreeQueue(i2c); } From 1cd9441758618ec2d0c2dd42d3def7fc094794b6 Mon Sep 17 00:00:00 2001 From: chuck todd Date: Fri, 17 Nov 2017 04:48:57 -0700 Subject: [PATCH 08/63] Incorrect block length The length queued would be the sum of all prior blocks, instead of just this block --- libraries/Wire/src/Wire.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp index 31a2c4bb2a7..f8d0efdff97 100644 --- a/libraries/Wire/src/Wire.cpp +++ b/libraries/Wire/src/Wire.cpp @@ -331,7 +331,7 @@ uint8_t TwoWire::endTransmission(uint8_t sendStop){ // Assumes Wire.beginTransac // this command replaces Wire.endTransmission(true) if(transmitting==1){ - last_error =i2cAddQueueWrite(i2c,txAddress,&txBuffer[txQueued],txLength,sendStop,NULL); //queue tx element + last_error =i2cAddQueueWrite(i2c,txAddress,&txBuffer[txQueued],txLength-teQueued,sendStop,NULL); //queue tx element if(last_error == I2C_ERROR_OK){ if(sendStop){ From a3a4736d063777a2b02d39df660a60e3f28ad174 Mon Sep 17 00:00:00 2001 From: chuck todd Date: Fri, 17 Nov 2017 04:49:25 -0700 Subject: [PATCH 09/63] sp --- libraries/Wire/src/Wire.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp index f8d0efdff97..fca0769a1a8 100644 --- a/libraries/Wire/src/Wire.cpp +++ b/libraries/Wire/src/Wire.cpp @@ -331,7 +331,7 @@ uint8_t TwoWire::endTransmission(uint8_t sendStop){ // Assumes Wire.beginTransac // this command replaces Wire.endTransmission(true) if(transmitting==1){ - last_error =i2cAddQueueWrite(i2c,txAddress,&txBuffer[txQueued],txLength-teQueued,sendStop,NULL); //queue tx element + last_error =i2cAddQueueWrite(i2c,txAddress,&txBuffer[txQueued],txLength-txQueued,sendStop,NULL); //queue tx element if(last_error == I2C_ERROR_OK){ if(sendStop){ From d4416bc2dae01bf845e24589388b76d11c3c3a4c Mon Sep 17 00:00:00 2001 From: chuck todd Date: Fri, 17 Nov 2017 11:58:39 -0700 Subject: [PATCH 10/63] Update README.md match current error coding --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index e03b45ac073..a43f69cf905 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,6 @@ typedef enum { I2C_ERROR_BUSY, I2C_ERROR_MEMORY, I2C_ERROR_CONTINUE, - I2C_ERROR_MISSING_WRITE, I2C_ERROR_NO_BEGIN } i2c_err_t; From 123d25713969aff4af89728d7ff46153a8ce120f Mon Sep 17 00:00:00 2001 From: chuck todd Date: Fri, 17 Nov 2017 15:36:10 -0700 Subject: [PATCH 11/63] solve missing return code error i2cFreeQueue() was not explicitly returning a result, this cause -Werror flag to abort compilation. As noted by @PhilColbert --- cores/esp32/esp32-hal-i2c.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index 0e48a27ad67..b859af1ab21 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -143,6 +143,7 @@ else {//not found } i2c_err_t i2cFreeQueue(i2c_t * i2c){ +i2c_err_t rc=I2C_ERROR_OK; if(i2c->dq!=NULL){ // what about EventHandle? free(i2c->dq); @@ -150,6 +151,7 @@ if(i2c->dq!=NULL){ } i2c->queueCount=0; i2c->queuePos=0; +return rc; } i2c_err_t i2cAddQueueWrite(i2c_t * i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen,bool sendStop,EventGroupHandle_t event){ @@ -1667,4 +1669,4 @@ if(i2c->intr_handle){ i2c->intr_handle=NULL; } return I2C_ERROR_OK; -} \ No newline at end of file +} From b3131f51842aa783a28ab55e7d9e6d00ed306a0c Mon Sep 17 00:00:00 2001 From: Chuck Todd Date: Mon, 20 Nov 2017 13:26:07 -0700 Subject: [PATCH 12/63] EventGroup fix EventGroups are not completely reliable for ISR, It seems the WiFi library starves the FreeRTOS Timer Daemon. The Timer Daemon's command Queue overflows, so EventGroup changes from ISR fail to be executed. This patch changes how EventGroups are used for Signalling. --- cores/esp32/esp32-hal-i2c.c | 135 ++++++++++++++++-------------------- cores/esp32/esp32-hal-i2c.h | 5 +- 2 files changed, 63 insertions(+), 77 deletions(-) diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index b859af1ab21..cce24c4a683 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -48,6 +48,7 @@ struct i2c_struct_t { uint16_t queueCount; uint16_t queuePos; uint16_t byteCnt; + uint32_t exitCode; }; @@ -95,7 +96,7 @@ static i2c_err_t i2cAddQueue(i2c_t * i2c,uint8_t mode, uint16_t i2cDeviceAddr, u dqx.queueEvent = event; if(event){// an eventGroup exist, so, initialize it - xEventGroupClearBits(event, 0x3F); // all of them + xEventGroupClearBits(event, EVENT_MASK); // all of them } if(i2c->dq!=NULL){ // expand @@ -1209,6 +1210,10 @@ static void IRAM_ATTR emptyRxFifo(i2c_t * i2c){ I2C_DATA_QUEUE_t *tdq =&i2c->dq[i2c->queuePos]; moveCnt = i2c->dev->status_reg.rx_fifo_cnt;//no need to check the reg until this many are read +if(moveCnt > (tdq->length - tdq->position)){ //makesure they go in this dq + // part of these reads go into the next dq + moveCnt = (tdq->length - tdq->position); + } if(tdq->ctrl.mode==1) { // read while(moveCnt > 0){ @@ -1216,13 +1221,14 @@ if(tdq->ctrl.mode==1) { // read d = i2c->dev->fifo_data.val; moveCnt--; cnt++; - if(tdq->position < tdq->length) tdq->data[tdq->position++] = (d&0xFF); - else { // discard? what Happened to get more than requested? - log_e("discard 0x%x",d); - } + tdq->data[tdq->position++] = (d&0xFF); } // see if any more chars showed up while empting Fifo. moveCnt = i2c->dev->status_reg.rx_fifo_cnt; + if(moveCnt > (tdq->length - tdq->position)){ //makesure they go in this dq + // part of these reads go into the next dq + moveCnt = (tdq->length - tdq->position); + } } cnt += (intBuff[intPos][1])&&0xffFF; intBuff[intPos][1] = (intBuff[intPos][1]&0xFFFF0000)|cnt; @@ -1235,6 +1241,7 @@ else { } static void i2cIsrExit(i2c_t * i2c,const uint32_t eventCode,bool Fatal){ + switch(eventCode){ case EVENT_DONE: i2c->error = I2C_OK; @@ -1256,24 +1263,25 @@ switch(eventCode){ } uint32_t exitCode = EVENT_DONE | eventCode |(Fatal?EVENT_ERROR:0); +if(i2c->dq[i2c->queuePos].ctrl.mode == 1) emptyRxFifo(i2c); // grab last few characters + i2c->dev->int_ena.val = 0; // shutdown interrupts i2c->dev->int_clr.val = 0x1FFFF; i2c->stage = I2C_DONE; +i2c->exitCode = exitCode; //true eventcode -portBASE_TYPE HPTaskAwoken = pdFALSE; - -if(i2c->dq[i2c->queuePos].queueEvent){ // this data element has an event, use it. - HPTaskAwoken = pdFALSE; - xEventGroupClearBitsFromISR(i2c->dq[i2c->queuePos].queueEvent, EVENT_RUNNING); - xEventGroupSetBitsFromISR(i2c->dq[i2c->queuePos].queueEvent, exitCode,&HPTaskAwoken); - if(HPTaskAwoken == pdTRUE) portYIELD_FROM_ISR(); - } -xEventGroupClearBitsFromISR(i2c->i2c_event, EVENT_RUNNING); +portBASE_TYPE HPTaskAwoken = pdFALSE,xResult; +// try to notify Dispatch we are done, +// else the 50ms timeout will recover the APP, just alittle slower HPTaskAwoken = pdFALSE; -xEventGroupSetBitsFromISR(i2c->i2c_event, exitCode, &HPTaskAwoken); -if (HPTaskAwoken == pdTRUE) {//higher pri task has awoken, jump to it - portYIELD_FROM_ISR(); +xResult = xEventGroupSetBitsFromISR(i2c->i2c_event, exitCode, &HPTaskAwoken); +if(xResult == pdPASS){ + if(HPTaskAwoken==pdTRUE) { + portYIELD_FROM_ISR(); +// log_e("Yield to Higher"); + } } + } static void IRAM_ATTR i2c_isr_handler_default(void* arg){ @@ -1283,17 +1291,7 @@ uint32_t activeInt = p_i2c->dev->int_status.val&0x1FFF; //log_e("int=%x",activeInt); //dumpI2c(p_i2c); -portBASE_TYPE HPTaskAwoken = pdFALSE; -/* don't need this Event_in_irq -// be polite if someone more important wakes up. -//log_e("setbits(%p)=%p",p_i2c->i2c_event); - -xEventGroupSetBitsFromISR(p_i2c->i2c_event, EVENT_IN_IRQ, &HPTaskAwoken); -if (HPTaskAwoken == pdTRUE) {//higher pri task has awoken, jump to it - portYIELD_FROM_ISR(); - } -//log_e("stage"); -*/ +portBASE_TYPE HPTaskAwoken = pdFALSE,xResult; if(p_i2c->stage==I2C_DONE){ //get Out log_e("eject int=%p, ena=%p",activeInt,p_i2c->dev->int_ena.val); @@ -1301,7 +1299,6 @@ if(p_i2c->stage==I2C_DONE){ //get Out p_i2c->dev->int_clr.val = activeInt; //0x1FFF; dumpI2c(p_i2c); i2cDumpInts(); -// xEventGroupClearBitsFromISR(p_i2c->i2c_event, EVENT_IN_IRQ); return; } while (activeInt != 0) { // Ordering of 'if(activeInt)' statements is important, don't change @@ -1321,19 +1318,8 @@ while (activeInt != 0) { // Ordering of 'if(activeInt)' statements is important, // p_i2c->byteCnt=0; if(p_i2c->stage==I2C_STARTUP){ p_i2c->stage=I2C_RUNNING; - xEventGroupClearBitsFromISR(p_i2c->i2c_event, EVENT_DONE); //why? - if(p_i2c->dq[p_i2c->queuePos].queueEvent){ // this data element has an event, use it. - HPTaskAwoken = pdFALSE; - xEventGroupSetBitsFromISR(p_i2c->dq[p_i2c->queuePos].queueEvent, EVENT_RUNNING, - &HPTaskAwoken); - if(HPTaskAwoken == pdTRUE) portYIELD_FROM_ISR(); - } - HPTaskAwoken = pdFALSE; - xEventGroupSetBitsFromISR(p_i2c->i2c_event, EVENT_RUNNING, &HPTaskAwoken); - if (HPTaskAwoken == pdTRUE) {//higher pri task has awoken, jump to it - portYIELD_FROM_ISR(); - } } + activeInt &=~I2C_TRANS_START_INT_ST_M; p_i2c->dev->int_ena.trans_start = 1; // already enabled? why Again? p_i2c->dev->int_clr.trans_start = 1; // so that will trigger after next 'END' @@ -1367,23 +1353,8 @@ while (activeInt != 0) { // Ordering of 'if(activeInt)' statements is important, p_i2c->dev->int_ena.rx_fifo_full=1; } - if(p_i2c->dq[p_i2c->queuePos].queueEvent){ // this data element has an event, use it. - HPTaskAwoken = pdFALSE; - xEventGroupClearBitsFromISR(p_i2c->dq[p_i2c->queuePos].queueEvent, EVENT_RUNNING); - xEventGroupSetBitsFromISR(p_i2c->dq[p_i2c->queuePos].queueEvent, EVENT_DONE, - &HPTaskAwoken); - if(HPTaskAwoken == pdTRUE) portYIELD_FROM_ISR(); - } - p_i2c->queuePos++; //inc to next dq - if(p_i2c->dq[p_i2c->queuePos].queueEvent){ // this data element has an event, use it. - HPTaskAwoken = pdFALSE; - xEventGroupSetBitsFromISR(p_i2c->dq[p_i2c->queuePos].queueEvent, EVENT_RUNNING, - &HPTaskAwoken); - if(HPTaskAwoken == pdTRUE) portYIELD_FROM_ISR(); - } - if(p_i2c->queuePos < p_i2c->queueCount) // load next dq address field + data p_i2c->dev->int_ena.tx_fifo_empty=1; @@ -1408,7 +1379,6 @@ while (activeInt != 0) { // Ordering of 'if(activeInt)' statements is important, } if (activeInt & I2C_TRANS_COMPLETE_INT_ST_M) { - if(p_i2c->dq[p_i2c->queuePos].ctrl.mode == 1) emptyRxFifo(p_i2c); // grab last few characters i2cIsrExit(p_i2c,EVENT_DONE,false); return; // no more work to do /* @@ -1430,11 +1400,6 @@ while (activeInt != 0) { // Ordering of 'if(activeInt)' statements is important, } if (activeInt & I2C_END_DETECT_INT_ST_M) { - HPTaskAwoken = pdFALSE; - xEventGroupSetBitsFromISR(p_i2c->i2c_event, EVENT_IN_END, &HPTaskAwoken); - if (HPTaskAwoken == pdTRUE) {//higher pri task has awoken, jump to it - portYIELD_FROM_ISR(); - } p_i2c->dev->int_ena.end_detect = 0; p_i2c->dev->int_clr.end_detect = 1; p_i2c->dev->ctr.trans_start=0; @@ -1442,27 +1407,22 @@ while (activeInt != 0) { // Ordering of 'if(activeInt)' statements is important, p_i2c->dev->ctr.trans_start=1; // go for it activeInt&=~I2C_END_DETECT_INT_ST_M; // What about Tx, RX fifo fill? - xEventGroupClearBitsFromISR(p_i2c->i2c_event, EVENT_IN_END); } if (activeInt & I2C_RXFIFO_OVF_INT_ST_M) { //should never happen, I always use Fifo p_i2c->dev->int_clr.rx_fifo_ovf = 1; + p_i2c->dev->int_ena.rx_fifo_ovf = 0; // disable it } if(activeInt){ // clear unhandled if possible? What about Disabling interrupt? p_i2c->dev->int_clr.val = activeInt; log_e("unknown int=%x",activeInt); + // disable unhandled IRQ, + p_i2c->dev->int_ena.val = p_i2c->dev->int_ena.val & (~activeInt); } activeInt = p_i2c->dev->int_status.val; // start all over if another interrupt happened - /* if((activeInt&oldInt)==oldInt){ - log_e("dup int old=%p, new=%p dup=%p",oldInt,activeInt,(oldInt&activeInt)); - } -*/ } - -//xEventGroupClearBitsFromISR(p_i2c->i2c_event, EVENT_IN_IRQ); -// and we're out! } void i2cDumpInts(){ @@ -1506,10 +1466,12 @@ 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); + I2C_MUTEX_UNLOCK(); return I2C_ERROR_MEMORY; } @@ -1561,6 +1523,7 @@ i2c->queuePos=0; fillCmdQueue(i2c,0,false); // start adding command[], END irq will keep it full //Data Fifo will be filled after trans_start is issued +i2c->exitCode=0; i2c->stage = I2C_STARTUP; // everything configured, now start the I2C StateMachine, and // As soon as interrupts are enabled, the ISR will start handling them. // it should receive a TXFIFO_EMPTY immediately, even before it @@ -1584,6 +1547,7 @@ if(!i2c->intr_handle){ // create ISR I2C_0 only, uint32_t ret = esp_intr_alloc(ETS_I2C_EXT0_INTR_SOURCE, 0, &i2c_isr_handler_default, i2c, &i2c->intr_handle); if(ret!=ESP_OK){ log_e("install interrupt handler Failed=%d",ret); + I2C_MUTEX_UNLOCK(); return I2C_ERROR_MEMORY; } } @@ -1593,6 +1557,8 @@ if(!i2c->intr_handle){ // create ISR I2C_0 only, // how many ticks should it take to transfer totalBytes thru the I2C hardware, // add 50ms just for kicks + + portTickType ticksTimeOut = ((totalBytes /(i2cGetFrequency(i2c)/(10*1000)))+50)/portTICK_PERIOD_MS; portTickType tBefore=xTaskGetTickCount(); @@ -1607,6 +1573,14 @@ uint32_t eBits = xEventGroupWaitBits(i2c->i2c_event,EVENT_DONE,pdFALSE,pdTRUE,ti portTickType tAfter=xTaskGetTickCount(); uint32_t b; +// if xEventGroupSetBitsFromISR() failed, the ISR could have succeeded but never been +// able to mark the success + +if(i2c->exitCode!=eBits){ // try to recover from O/S failure +// log_e("EventGroup Failed:%p!=%p",eBits,i2c->exitCode); + eBits=i2c->exitCode; + } + if(!(eBits==EVENT_DONE)&&(eBits&~(EVENT_ERROR_NAK|EVENT_ERROR|EVENT_DONE))){ // not only Done, therefore error, exclude ADDR NAK dumpI2c(i2c); i2cDumpInts(); @@ -1647,15 +1621,28 @@ else { // GROSS timeout, shutdown ISR , report Timeout i2cDumpInts(); } +// offloading all EventGroups to dispatch, EventGroups in ISR is not always successful +// 11/20/2017 // if error, need to trigger all succeeding dataQueue events with the EVENT_ERROR_PREV -if(eBits&EVENT_ERROR){// every dq past the error point needs it's EventGroup Released - b=i2c->queuePos + 1; - while(b < i2c->queueCount){ + +b = 0; +while(b < i2c->queueCount){ + if(b < i2c->queuePos){ // before any error + if(i2c->dq[b].queueEvent){ // this data queue element has an EventGroup + xEventGroupSetBits(i2c->dq[b].queueEvent,EVENT_DONE); + } + } + else if(b == i2c->queuePos){ // last processed queue + if(i2c->dq[b].queueEvent){ // this data queue element has an EventGroup + xEventGroupSetBits(i2c->dq[b].queueEvent,eBits); + } + } + else{ // never processed queues if(i2c->dq[b].queueEvent){ // this data queue element has an EventGroup xEventGroupSetBits(i2c->dq[b].queueEvent,eBits|EVENT_ERROR_PREV); } - b++; } + b++; } I2C_MUTEX_UNLOCK(); diff --git a/cores/esp32/esp32-hal-i2c.h b/cores/esp32/esp32-hal-i2c.h index 888de9210d0..575d2d419bc 100644 --- a/cores/esp32/esp32-hal-i2c.h +++ b/cores/esp32/esp32-hal-i2c.h @@ -35,7 +35,6 @@ typedef enum { I2C_ERROR_BUSY, I2C_ERROR_MEMORY, I2C_ERROR_CONTINUE, - I2C_ERROR_MISSING_WRITE, I2C_ERROR_NO_BEGIN } i2c_err_t; @@ -67,14 +66,14 @@ typedef enum { // i2c_event bits #define EVENT_ERROR_NAK (BIT(0)) #define EVENT_ERROR (BIT(1)) -#define EVENT_RUNNING (BIT(3)) +//#define EVENT_RUNNING (BIT(3)) #define EVENT_DONE (BIT(4)) #define EVENT_IN_END (BIT(5)) #define EVENT_ERROR_PREV (BIT(6)) #define EVENT_ERROR_TIMEOUT (BIT(7)) #define EVENT_ERROR_ARBITRATION (BIT(8)) #define EVENT_ERROR_DATA_NAK (BIT(9)) - +#define EVENT_MASK 0x3F typedef union{ struct { uint32_t addr: 16; // I2C address, if 10bit must have 0x7800 mask applied, else 8bit From abeb0c202abff1c4e781b914a15936b8c9a0cedd Mon Sep 17 00:00:00 2001 From: Chuck Todd Date: Mon, 20 Nov 2017 20:18:42 -0700 Subject: [PATCH 13/63] forgotten S Should have been pushed earler --- libraries/Wire/src/Wire.cpp | 125 +++++++++++++++--------------------- libraries/Wire/src/Wire.h | 1 + 2 files changed, 51 insertions(+), 75 deletions(-) diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp index fca0769a1a8..5a9c82f5c73 100644 --- a/libraries/Wire/src/Wire.cpp +++ b/libraries/Wire/src/Wire.cpp @@ -142,74 +142,64 @@ size_t TwoWire::oldRequestFrom(uint8_t address, size_t size, bool sendStop) rxLength = read; return read; } +/*@StickBreaker common handler for processing the queued commands +*/ +i2c_err_t TwoWire::processQueue(uint16_t * readCount){ + last_error=i2cProcQueue(i2c); + rxIndex = 0; + rxLength = 0; + txQueued = 0; // the SendStop=true will restart all Queueing + rxQueued = 0; + *readCount = i2cQueueReadCount(i2c); // do I want to exclude all Local buffers from this? +// currently 17/NOV/2017 this count is of all bytes read from I2C, for this queue of commands. + + + //what about mix local buffers and Wire Buffers? + //Handled it by verifying location, only contiguous sections of + //rxBuffer are available for Wire.read(). Any local buffer requestfrom(id,*char..) is + // completed already, only the ones using Wire's buffers need to be considered + + uint8_t *savePtr; + uint16_t len; + uint8_t idx=0; + while(I2C_ERROR_OK==i2cGetReadQueue(i2c,&savePtr,&len,&idx)){ + if(savePtr==(rxBuffer+rxLength)){ + rxLength = rxLength + len; + } + } + i2cFreeQueue(i2c); + return last_error; +} + + /* @stickBreaker 11/2017 fix for ReSTART timeout, ISR */ size_t TwoWire::requestFrom(uint8_t address, size_t size, bool sendStop){ +//use internal Wire rxBuffer, multiple requestFrom()'s may be pending, try to share rxBuffer - uint16_t cnt = rxQueued; // currently queued reads - if(cntI2C_BUFFER_LENGTH) size = (I2C_BUFFER_LENGTH-cnt); } else { // no room to receive more! - log_e("no room %d",cnt); + log_e("rxBuff overflow %d",cnt+size); cnt = 0; - rxIndex = 0; - rxLength = 0; - rxQueued = 0; last_error = I2C_ERROR_MEMORY; - i2cFreeQueue(i2c); + flush(); return cnt; } - - last_error =i2cAddQueueRead(i2c,address,&rxBuffer[cnt],size,sendStop,NULL); - if(last_error==I2C_ERROR_OK){ // successfully queued the read - rxQueued += size; - if(sendStop){ //now actually process the queued commands - last_error=i2cProcQueue(i2c); - rxIndex = 0; - rxLength = i2cQueueReadCount(i2c); - rxQueued = 0; - txQueued = 0; - cnt = rxLength; - i2cFreeQueue(i2c); - } - else { // stop not received, so wait for I2C stop, - last_error=I2C_ERROR_CONTINUE; - cnt = 0; - } - } - else {// only possible error is I2C_ERROR_MEMORY - cnt = 0; - } - return cnt; -} + + return requestFrom(address, &rxBuffer[cnt],size,sendStop); + } size_t TwoWire::requestFrom(uint8_t address, uint8_t * readBuff, size_t size, bool sendStop){ - size_t cnt=0; + uint16_t cnt=0; last_error =i2cAddQueueRead(i2c,address,readBuff,size,sendStop,NULL); if(last_error==I2C_ERROR_OK){ // successfully queued the read if(sendStop){ //now actually process the queued commands - last_error=i2cProcQueue(i2c); - rxIndex = 0; - rxLength = 0; - txQueued = 0; // the SendStop=true will restart are Queueing - rxQueued = 0; - cnt = i2cQueueReadCount(i2c); - - //what about mix local buffers and Wire Buffers? - //Handled it by verifying location, only contiguous sections of - //rxBuffer are counted. - uint8_t *savePtr; - uint16_t len; - uint8_t idx=0; - while(I2C_ERROR_OK==i2cGetReadQueue(i2c,&savePtr,&len,&idx)){ - if(savePtr==(rxBuffer+rxLength)){ - rxLength = rxLength + len; - } - } - i2cFreeQueue(i2c); + last_error = processQueue(&cnt); } else { // stop not received, so wait for I2C stop, last_error=I2C_ERROR_CONTINUE; @@ -229,24 +219,8 @@ last_error=i2cAddQueueWrite(i2c,address,buff,size,sendStop,NULL); if(last_error==I2C_ERROR_OK){ //queued if(sendStop){ //now actually process the queued commands, including READs - last_error=i2cProcQueue(i2c); - rxIndex = 0; - rxLength = 0; - txQueued = 0; // the SendStop=true will restart all Queueing - rxQueued = 0; - - //what about mix local buffers and Wire Buffers? - //Handled it by verifying location, only contiguous sections of - //rxBuffer are counted. - uint8_t *savePtr; - uint16_t len; - uint8_t idx=0; - while(I2C_ERROR_OK==i2cGetReadQueue(i2c,&savePtr,&len,&idx)){ - if(savePtr==(rxBuffer+rxLength)){ - rxLength = rxLength + len; - } - } - i2cFreeQueue(i2c); + uint16_t dummy; + last_error=processQueue(&dummy); } else { // stop not received, so wait for I2C stop, last_error=I2C_ERROR_CONTINUE; @@ -302,7 +276,7 @@ if(last_error==I2C_ERROR_CONTINUE){ // must have queued the Write return cnt; } else { - last_error = I2C_ERROR_MISSING_WRITE; + last_error = I2C_ERROR_NO_BEGIN; return 0; } } @@ -320,7 +294,7 @@ if(last_error==I2C_ERROR_CONTINUE){ // must have queued the write return cnt; } else { - last_error = I2C_ERROR_MISSING_WRITE; + last_error = I2C_ERROR_NO_BEGIN; return 0; } } @@ -335,9 +309,8 @@ if(transmitting==1){ if(last_error == I2C_ERROR_OK){ if(sendStop){ - last_error=i2cProcQueue(i2c); - txQueued = 0; - i2cFreeQueue(i2c); + uint16_t dummy; + last_error = processQueue(&dummy); } else { // queued because it had sendStop==false // txlength is howmany bytes in txbufferhave been use @@ -348,8 +321,7 @@ if(transmitting==1){ } else { last_error= I2C_ERROR_NO_BEGIN; - txQueued = 0; - i2cFreeQueue(i2c); // cleanup + flush(); } txIndex = 0; txLength =0; @@ -398,6 +370,9 @@ void TwoWire::beginTransmission(uint8_t address) txAddress = address; txIndex = txQueued; // allow multiple beginTransmission(),write(),endTransmission(false) until endTransmission(true) txLength = txQueued; + if(txLength!=0) + log_e("multiple beginTransmissions starting at %d",txQueued); + } void TwoWire::beginTransmission(int address) diff --git a/libraries/Wire/src/Wire.h b/libraries/Wire/src/Wire.h index 277a2561dde..3347f1d37a5 100644 --- a/libraries/Wire/src/Wire.h +++ b/libraries/Wire/src/Wire.h @@ -58,6 +58,7 @@ class TwoWire: public Stream void onRequestService(void); void onReceiveService(uint8_t*, int); i2c_err_t last_error; // @stickBreaker from esp32-hal-i2c.h + i2c_err_t processQueue(uint16_t *readCount); public: TwoWire(uint8_t bus_num); From b5d115e219a434447de4f33f988dd19c960b43f9 Mon Sep 17 00:00:00 2001 From: Chuck Todd Date: Tue, 21 Nov 2017 13:57:44 -0700 Subject: [PATCH 14/63] cleanup Get my dirty fingers out of the SDK. This commit should return the SDK files (i2c_struct.h,i2c_reg.h) back to factory defaults. The required typedef's were duplicated in esp32-hal-i2c.h instead. With this Commit, the only changes are in Wire.h, Wire.cpp, esp32-hal-i2c.h, esp32-hal-i2c.c --- cores/esp32/esp32-hal-i2c.c | 26 ++-- cores/esp32/esp32-hal-i2c.h | 62 ++++++-- tools/sdk/include/soc/soc/i2c_reg.h | 2 +- tools/sdk/include/soc/soc/i2c_struct.h | 204 +++++++++++++------------ 4 files changed, 179 insertions(+), 115 deletions(-) diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index cce24c4a683..8b4eecbc950 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -1001,9 +1001,9 @@ while(!needMoreCmds&&(qp < i2c->queueCount)){ // check if more possible cmds //log_e("needMoreCmds=%d",needMoreCmds); done=(!needMoreCmds)||(cmdIdx>14); -while(!done){ // fill the command[] until either 0..14 or out of cmds +while(!done){ // fill the command[] until either 0..14 filled or out of cmds and last cmd is STOP //CMD START - I2C_DATA_QUEUE_t *tdq=&i2c->dq[qp]; // simpler + I2C_DATA_QUEUE_t *tdq=&i2c->dq[qp]; // simpler coding if(!tdq->ctrl.startCmdSent){// has this dq element's START command been added? i2cSetCmd(i2c, cmdIdx++, I2C_CMD_RSTART, 0, false, false, false); @@ -1023,9 +1023,12 @@ while(!done){ // fill the command[] until either 0..14 or out of cmds /* Can I have another Sir? ALL CMD queues must be terminated with either END or STOP. - if END is used, when refilling the cmd[] next time, no entries from END to [15] can be used. - AND the END continue. The END command does not complete until the the - ctr->trans_start=1 has executed. + + If END is used, when refilling the cmd[] next time, no entries from END to [15] can be used. + AND the cmd[] must be filled starting at [0] with commands. Either fill all 15 [0]..[14] and leave the + END in [15] or include a STOP in one of the positions [0]..[14]. Any entries after a STOP are IGNORED byte the StateMachine. + The END operation does not complete until ctr->trans_start=1 has been issued. + So, only refill from [0]..[14], leave [15] for a continuation if necessary. As a corrilary, once END exists in [15], you do not need to overwrite it for the next continuation. It is never modified. But, I update it every time because it might @@ -1093,7 +1096,7 @@ while(!done){ // fill the command[] until either 0..14 or out of cmds } }// while(!done) -if(INTS){ +if(INTS){ // don't want to prematurely enable fifo ints until ISR is ready to handle it. if(ena_rx) i2c->dev->int_ena.rx_fifo_full = 1; if(ena_tx) i2c->dev->int_ena.tx_fifo_empty = 1; } @@ -1146,7 +1149,7 @@ uint8_t cnt; while((a < i2c->queueCount)&&!(full || readEncountered)){ I2C_DATA_QUEUE_t *tdq = &i2c->dq[a]; cnt=0; -// add to address to fifo +// add to address to fifo ctrl.addr already has R/W bit positioned correctly if(tdq->ctrl.addrSent < tdq->ctrl.addrReq){ // need to send address bytes if(tdq->ctrl.addrReq==2){ //10bit if(tdq->ctrl.addrSent==0){ //10bit highbyte needs sent @@ -1178,6 +1181,8 @@ while((a < i2c->queueCount)&&!(full || readEncountered)){ } full=!(i2c->dev->status_reg.tx_fifo_cnt<31); // add write data to fifo +//21NOV2017 might want to look into using local capacity counter instead of reading status_reg +// a double while loop, like emptyRxFifo() if(tdq->ctrl.mode==0){ // write if(tdq->ctrl.addrSent == tdq->ctrl.addrReq){ //address has been sent, is Write Mode! while((!full)&&(tdq->position < tdq->length)){ @@ -1190,9 +1195,11 @@ while((a < i2c->queueCount)&&!(full || readEncountered)){ else readEncountered = true; if(full) readEncountered =false; //tx possibly needs more - + +// update debug buffer tx counts cnt += intBuff[intPos][1]>>16; intBuff[intPos][1] = (intBuff[intPos][1]&0xFFFF)|(cnt<<16); + if(!(full||readEncountered)) a++; // check next buffer for tx } @@ -1230,6 +1237,7 @@ if(tdq->ctrl.mode==1) { // read moveCnt = (tdq->length - tdq->position); } } +// update Debug rxCount cnt += (intBuff[intPos][1])&&0xffFF; intBuff[intPos][1] = (intBuff[intPos][1]&0xFFFF0000)|cnt; } @@ -1656,4 +1664,4 @@ if(i2c->intr_handle){ i2c->intr_handle=NULL; } return I2C_ERROR_OK; -} +} \ No newline at end of file diff --git a/cores/esp32/esp32-hal-i2c.h b/cores/esp32/esp32-hal-i2c.h index 575d2d419bc..110f70f8a01 100644 --- a/cores/esp32/esp32-hal-i2c.h +++ b/cores/esp32/esp32-hal-i2c.h @@ -11,6 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +// modified Nov 2017 by Chuck Todd to support Interrupt Driven I/O #ifndef _ESP32_HAL_I2C_H_ #define _ESP32_HAL_I2C_H_ @@ -25,7 +26,41 @@ extern "C" { #include "freertos/FreeRTOS.h" #include "freertos/event_groups.h" - +// start from tools/sdk/include/soc/soc/i2c_struct.h + +typedef union { + struct { + uint32_t byte_num: 8; /*Byte_num represent the number of data need to be send or data need to be received.*/ + uint32_t ack_en: 1; /*ack_check_en ack_exp and ack value are used to control the ack bit.*/ + uint32_t ack_exp: 1; /*ack_check_en ack_exp and ack value are used to control the ack bit.*/ + uint32_t ack_val: 1; /*ack_check_en ack_exp and ack value are used to control the ack bit.*/ + uint32_t op_code: 3; /*op_code is the command 0:RSTART 1:WRITE 2:READ 3:STOP . 4:END.*/ + uint32_t reserved14: 17; + uint32_t done: 1; /*When command0 is done in I2C Master mode this bit changes to high level.*/ + }; + uint32_t val; + } I2C_COMMAND_t; + +typedef union { + struct { + uint32_t rx_fifo_full_thrhd: 5; + uint32_t tx_fifo_empty_thrhd:5; //Config tx_fifo empty threhd value when using apb fifo access * / + uint32_t nonfifo_en: 1; //Set this bit to enble apb nonfifo access. * / + uint32_t fifo_addr_cfg_en: 1; //When this bit is set to 1 then the byte after address represent the offset address of I2C Slave's ram. * / + uint32_t rx_fifo_rst: 1; //Set this bit to reset rx fifo when using apb fifo access. * / + // chuck while this bit is 1, the RX fifo is held in REST, Toggle it * / + uint32_t tx_fifo_rst: 1; //Set this bit to reset tx fifo when using apb fifo access. * / + // chuck while this bit is 1, the TX fifo is held in REST, Toggle it * / + uint32_t nonfifo_rx_thres: 6; //when I2C receives more than nonfifo_rx_thres data it will produce rx_send_full_int_raw interrupt and update the current offset address of the receiving data.* / + uint32_t nonfifo_tx_thres: 6; //when I2C sends more than nonfifo_tx_thres data it will produce tx_send_empty_int_raw interrupt and update the current offset address of the sending data. * / + uint32_t reserved26: 6; + }; + uint32_t val; + } I2C_FIFO_CONF_t; + +// end from tools/sdk/include/soc/soc/i2c_struct.h + +// External Wire.h equivalent error Codes typedef enum { I2C_ERROR_OK=0, I2C_ERROR_DEV, @@ -38,6 +73,7 @@ typedef enum { I2C_ERROR_NO_BEGIN } i2c_err_t; +// sync between dispatch(i2cProcQueue) and worker(i2c_isr_handler_default) typedef enum { //I2C_NONE=0, I2C_STARTUP=1, @@ -52,7 +88,7 @@ typedef enum { I2C_MASTERSLAVE }I2C_MODE_t; - +// internal Error condition typedef enum { // I2C_NONE=0, I2C_OK=1, @@ -63,10 +99,12 @@ typedef enum { I2C_TIMEOUT }I2C_ERROR_t; -// i2c_event bits +// i2c_event bits for EVENTGROUP bits +// needed to minimize change events, FreeRTOS Daemon overload, so ISR will only set values +// on Exit. Dispatcher will set bits for each dq before/after ISR completion #define EVENT_ERROR_NAK (BIT(0)) #define EVENT_ERROR (BIT(1)) -//#define EVENT_RUNNING (BIT(3)) +#define EVENT_RUNNING (BIT(3)) #define EVENT_DONE (BIT(4)) #define EVENT_IN_END (BIT(5)) #define EVENT_ERROR_PREV (BIT(6)) @@ -74,11 +112,13 @@ typedef enum { #define EVENT_ERROR_ARBITRATION (BIT(8)) #define EVENT_ERROR_DATA_NAK (BIT(9)) #define EVENT_MASK 0x3F + +// control record for each dq entry typedef union{ struct { uint32_t addr: 16; // I2C address, if 10bit must have 0x7800 mask applied, else 8bit - uint32_t mode: 1; // 0 write, 1 read - uint32_t stop: 1; // 0 no, 1 yes + uint32_t mode: 1; // transaction direction 0 write, 1 read + uint32_t stop: 1; // sendStop 0 no, 1 yes uint32_t startCmdSent: 1; // START cmd has been added to command[] uint32_t addrCmdSent: 1; // addr WRITE cmd has been added to command[] uint32_t dataCmdSent: 1; // all necessary DATA(READ/WRITE) cmds added to command[] @@ -89,15 +129,17 @@ typedef union{ }; uint32_t val; }I2C_DATA_CTRL_t; - + +// individual dq element typedef struct { uint8_t *data; // datapointer for read/write buffer uint16_t length; // size of data buffer uint16_t position; // current position for next char in buffer (Slave direction on the bus. IT IS NOT limited to transactions referencing THIS SLAVE*/ uint32_t time_out: 1; /*when I2C takes more than time_out_reg clocks to receive a data then this register changes to high level.*/ uint32_t arb_lost: 1; /*when I2C lost control of SDA line this register changes to high level.*/ uint32_t bus_busy: 1; /*1:I2C bus is busy transferring data. 0:I2C bus is in idle state.*/ uint32_t slave_addressed: 1; /*when configured as i2c slave and the address send by master is equal to slave's address then this bit will be high level.*/ - /*chuck Slave Mode receive: non persistant, set when last byte received Matched Slave Id. As soon as first byte of data received, this bit is cleared. No Interrupt generated*/ uint32_t byte_trans: 1; /*This register changes to high level when one byte is transferred.*/ uint32_t reserved7: 1; uint32_t rx_fifo_cnt: 6; /*This register represent the amount of data need to send.*/ @@ -101,72 +60,129 @@ typedef union { uint32_t reserved31: 1; }; uint32_t val; - } I2C_STATUS_REG_t; - -typedef union { + } status_reg; + union { + struct { + uint32_t tout: 20; /*This register is used to configure the max clock number of receiving a data, unit: APB clock cycle.*/ + uint32_t reserved20:12; + }; + uint32_t val; + } timeout; + union { + struct { + uint32_t addr: 15; /*when configured as i2c slave this register is used to configure slave's address.*/ + uint32_t reserved15: 16; + uint32_t en_10bit: 1; /*This register is used to enable slave 10bit address mode.*/ + }; + uint32_t val; + } slave_addr; + union { + struct { + uint32_t rx_fifo_start_addr: 5; /*This is the offset address of the last receiving data as described in nonfifo_rx_thres_register.*/ + uint32_t rx_fifo_end_addr: 5; /*This is the offset address of the first receiving data as described in nonfifo_rx_thres_register.*/ + uint32_t tx_fifo_start_addr: 5; /*This is the offset address of the first sending data as described in nonfifo_tx_thres register.*/ + uint32_t tx_fifo_end_addr: 5; /*This is the offset address of the last sending data as described in nonfifo_tx_thres register.*/ + uint32_t reserved20: 12; + }; + uint32_t val; + } fifo_st; + union { + struct { + uint32_t rx_fifo_full_thrhd: 5; + uint32_t tx_fifo_empty_thrhd:5; /*Config tx_fifo empty threhd value when using apb fifo access*/ + uint32_t nonfifo_en: 1; /*Set this bit to enble apb nonfifo access.*/ + uint32_t fifo_addr_cfg_en: 1; /*When this bit is set to 1 then the byte after address represent the offset address of I2C Slave's ram.*/ + uint32_t rx_fifo_rst: 1; /*Set this bit to reset rx fifo when using apb fifo access.*/ + uint32_t tx_fifo_rst: 1; /*Set this bit to reset tx fifo when using apb fifo access.*/ + uint32_t nonfifo_rx_thres: 6; /*when I2C receives more than nonfifo_rx_thres data it will produce rx_send_full_int_raw interrupt and update the current offset address of the receiving data.*/ + uint32_t nonfifo_tx_thres: 6; /*when I2C sends more than nonfifo_tx_thres data it will produce tx_send_empty_int_raw interrupt and update the current offset address of the sending data.*/ + uint32_t reserved26: 6; + }; + uint32_t val; + } fifo_conf; + union { + struct { + uint8_t data; /*The register represent the byte data read from rx_fifo when use apb fifo access*/ + uint8_t reserved[3]; + }; + uint32_t val; + } fifo_data; + union { struct { uint32_t rx_fifo_full: 1; /*The raw interrupt status bit for rx_fifo full when use apb fifo access.*/ uint32_t tx_fifo_empty: 1; /*The raw interrupt status bit for tx_fifo empty when use apb fifo access.*/ - /*chuck will only clear if tx_fifo has more than fifo_conf.tx_fifo_empty_thrhd bytes in it.*/ uint32_t rx_fifo_ovf: 1; /*The raw interrupt status bit for receiving data overflow when use apb fifo access.*/ uint32_t end_detect: 1; /*The raw interrupt status bit for end_detect_int interrupt. when I2C deals with the END command it will produce end_detect_int interrupt.*/ uint32_t slave_tran_comp: 1; /*The raw interrupt status bit for slave_tran_comp_int interrupt. when I2C Slave detects the STOP bit it will produce slave_tran_comp_int interrupt.*/ - /*chuck Slave Mode: actually triggered after receipt of Slave Address. */ uint32_t arbitration_lost: 1; /*The raw interrupt status bit for arbitration_lost_int interrupt.when I2C lost the usage right of I2C BUS it will produce arbitration_lost_int interrupt.*/ uint32_t master_tran_comp: 1; /*The raw interrupt status bit for master_tra_comp_int interrupt. when I2C Master sends or receives a byte it will produce master_tran_comp_int interrupt.*/ uint32_t trans_complete: 1; /*The raw interrupt status bit for trans_complete_int interrupt. when I2C Master finished STOP command it will produce trans_complete_int interrupt.*/ - /*chuck Slave Mode: triggerd when STOP is seen on the Bus. ANY STOP including those generated by OTHER MASTERS TALKING with OTHER SLAVES */ uint32_t time_out: 1; /*The raw interrupt status bit for time_out_int interrupt. when I2C takes a lot of time to receive a data it will produce time_out_int interrupt.*/ uint32_t trans_start: 1; /*The raw interrupt status bit for trans_start_int interrupt. when I2C sends the START bit it will produce trans_start_int interrupt.*/ - /*chuck Only issued after ctr.trans_start=1 and a START has been sent. It does not fire on a ReSTART */ uint32_t ack_err: 1; /*The raw interrupt status bit for ack_err_int interrupt. when I2C receives a wrong ACK bit it will produce ack_err_int interrupt..*/ - /*chuck SLAVE MODE: triggered WHENEVER a NAK is seen on the BUS. If another master does an presense detect, this interrupt will be triggered on every failed acknowledgement*/ uint32_t rx_rec_full: 1; /*The raw interrupt status bit for rx_rec_full_int interrupt. when I2C receives more data than nonfifo_rx_thres it will produce rx_rec_full_int interrupt.*/ uint32_t tx_send_empty: 1; /*The raw interrupt status bit for tx_send_empty_int interrupt.when I2C sends more data than nonfifo_tx_thres it will produce tx_send_empty_int interrupt..*/ uint32_t reserved13: 19; }; uint32_t val; - } I2C_INTERRUPT_t; - -typedef volatile struct { - I2C_SCL_LOW_PERIOD_t scl_low_period; - I2C_CTR_t ctr; - I2C_STATUS_REG_t status_reg; - union { + } int_raw; + union { struct { - uint32_t tout: 20; /*This register is used to configure the max clock number of receiving a data, unit: APB clock cycle.*/ - uint32_t reserved20:12; + uint32_t rx_fifo_full: 1; /*Set this bit to clear the rx_fifo_full_int interrupt.*/ + uint32_t tx_fifo_empty: 1; /*Set this bit to clear the tx_fifo_empty_int interrupt.*/ + uint32_t rx_fifo_ovf: 1; /*Set this bit to clear the rx_fifo_ovf_int interrupt.*/ + uint32_t end_detect: 1; /*Set this bit to clear the end_detect_int interrupt.*/ + uint32_t slave_tran_comp: 1; /*Set this bit to clear the slave_tran_comp_int interrupt.*/ + uint32_t arbitration_lost: 1; /*Set this bit to clear the arbitration_lost_int interrupt.*/ + uint32_t master_tran_comp: 1; /*Set this bit to clear the master_tran_comp interrupt.*/ + uint32_t trans_complete: 1; /*Set this bit to clear the trans_complete_int interrupt.*/ + uint32_t time_out: 1; /*Set this bit to clear the time_out_int interrupt.*/ + uint32_t trans_start: 1; /*Set this bit to clear the trans_start_int interrupt.*/ + uint32_t ack_err: 1; /*Set this bit to clear the ack_err_int interrupt.*/ + uint32_t rx_rec_full: 1; /*Set this bit to clear the rx_rec_full_int interrupt.*/ + uint32_t tx_send_empty: 1; /*Set this bit to clear the tx_send_empty_int interrupt.*/ + uint32_t reserved13: 19; }; uint32_t val; - } timeout; + } int_clr; union { struct { - uint32_t addr: 16; /*when configured as i2c slave this register is used to configure slave's address.*/ - /*Stickchuck when using 10bit address: to comply with industry standard format, the bit order of address must be adjusted. - slave_addr.addr=((slaveId&0xff)<<7)|(((slaveId>>8)&0x3)|0x78); - The ( | 0x78) mask is used to avoid collision with 7bit device. 7bit device address are limited to 0x00..0x77*/ - - uint32_t reserved15: 15; - uint32_t en_10bit: 1; /*This register is used to enable slave 10bit address mode.*/ + uint32_t rx_fifo_full: 1; /*The enable bit for rx_fifo_full_int interrupt.*/ + uint32_t tx_fifo_empty: 1; /*The enable bit for tx_fifo_empty_int interrupt.*/ + uint32_t rx_fifo_ovf: 1; /*The enable bit for rx_fifo_ovf_int interrupt.*/ + uint32_t end_detect: 1; /*The enable bit for end_detect_int interrupt.*/ + uint32_t slave_tran_comp: 1; /*The enable bit for slave_tran_comp_int interrupt.*/ + uint32_t arbitration_lost: 1; /*The enable bit for arbitration_lost_int interrupt.*/ + uint32_t master_tran_comp: 1; /*The enable bit for master_tran_comp_int interrupt.*/ + uint32_t trans_complete: 1; /*The enable bit for trans_complete_int interrupt.*/ + uint32_t time_out: 1; /*The enable bit for time_out_int interrupt.*/ + uint32_t trans_start: 1; /*The enable bit for trans_start_int interrupt.*/ + uint32_t ack_err: 1; /*The enable bit for ack_err_int interrupt.*/ + uint32_t rx_rec_full: 1; /*The enable bit for rx_rec_full_int interrupt.*/ + uint32_t tx_send_empty: 1; /*The enable bit for tx_send_empty_int interrupt.*/ + uint32_t reserved13: 19; }; uint32_t val; - } slave_addr; + } int_ena; union { struct { - uint32_t rx_fifo_start_addr: 5; /*This is the offset address of the last receiving data as described in nonfifo_rx_thres_register.*/ - uint32_t rx_fifo_end_addr: 5; /*This is the offset address of the first receiving data as described in nonfifo_rx_thres_register.*/ - uint32_t tx_fifo_start_addr: 5; /*This is the offset address of the first sending data as described in nonfifo_tx_thres register.*/ - uint32_t tx_fifo_end_addr: 5; /*This is the offset address of the last sending data as described in nonfifo_tx_thres register.*/ - uint32_t reserved20: 12; + uint32_t rx_fifo_full: 1; /*The masked interrupt status for rx_fifo_full_int interrupt.*/ + uint32_t tx_fifo_empty: 1; /*The masked interrupt status for tx_fifo_empty_int interrupt.*/ + uint32_t rx_fifo_ovf: 1; /*The masked interrupt status for rx_fifo_ovf_int interrupt.*/ + uint32_t end_detect: 1; /*The masked interrupt status for end_detect_int interrupt.*/ + uint32_t slave_tran_comp: 1; /*The masked interrupt status for slave_tran_comp_int interrupt.*/ + uint32_t arbitration_lost: 1; /*The masked interrupt status for arbitration_lost_int interrupt.*/ + uint32_t master_tran_comp: 1; /*The masked interrupt status for master_tran_comp_int interrupt.*/ + uint32_t trans_complete: 1; /*The masked interrupt status for trans_complete_int interrupt.*/ + uint32_t time_out: 1; /*The masked interrupt status for time_out_int interrupt.*/ + uint32_t trans_start: 1; /*The masked interrupt status for trans_start_int interrupt.*/ + uint32_t ack_err: 1; /*The masked interrupt status for ack_err_int interrupt.*/ + uint32_t rx_rec_full: 1; /*The masked interrupt status for rx_rec_full_int interrupt.*/ + uint32_t tx_send_empty: 1; /*The masked interrupt status for tx_send_empty_int interrupt.*/ + uint32_t reserved13: 19; }; uint32_t val; - } fifo_st; - I2C_FIFO_CONF_t fifo_conf; - I2C_FIFO_DATA_t fifo_data; - I2C_INTERRUPT_t int_raw; - I2C_INTERRUPT_t int_clr; - I2C_INTERRUPT_t int_ena; - I2C_INTERRUPT_t int_status; + } int_status; union { struct { uint32_t time: 10; /*This register is used to configure the clock num I2C used to hold the data after the negedge of SCL.*/ @@ -233,19 +249,18 @@ typedef volatile struct { }; uint32_t val; } sda_filter_cfg; -/* union { + union { struct { - uint32_t byte_num: 8; //Byte_num represent the number of data need to be send or data need to be received. - uint32_t ack_en: 1; //ack_check_en ack_exp and ack value are used to control the ack bit. - uint32_t ack_exp: 1; //ack_check_en ack_exp and ack value are used to control the ack bit. - uint32_t ack_val: 1; //ack_check_en ack_exp and ack value are used to control the ack bit. - uint32_t op_code: 3; //op_code is the command 0:RSTART 1:WRITE 2:READ 3:STOP . 4:END. + uint32_t byte_num: 8; /*Byte_num represent the number of data need to be send or data need to be received.*/ + uint32_t ack_en: 1; /*ack_check_en ack_exp and ack value are used to control the ack bit.*/ + uint32_t ack_exp: 1; /*ack_check_en ack_exp and ack value are used to control the ack bit.*/ + uint32_t ack_val: 1; /*ack_check_en ack_exp and ack value are used to control the ack bit.*/ + uint32_t op_code: 3; /*op_code is the command 0:RSTART 1:WRITE 2:READ 3:STOP . 4:END.*/ uint32_t reserved14: 17; - uint32_t done: 1; //When command0 is done in I2C Master mode this bit changes to high level. + uint32_t done: 1; /*When command0 is done in I2C Master mode this bit changes to high level.*/ }; uint32_t val; - } */ - I2C_COMMAND_t command[16]; + } command[16]; uint32_t reserved_98; uint32_t reserved_9c; uint32_t reserved_a0; @@ -274,7 +289,6 @@ typedef volatile struct { uint32_t reserved_fc; uint32_t ram_data[32]; /*This the start address for ram when use apb nonfifo access.*/ } i2c_dev_t; - extern i2c_dev_t I2C0; extern i2c_dev_t I2C1; From e5f56ebf0ddb419a5fbe378477e36c0cb7d42316 Mon Sep 17 00:00:00 2001 From: Chuck Todd Date: Fri, 24 Nov 2017 08:27:55 -0700 Subject: [PATCH 15/63] cleanup, fix another cmd[] corner, add example I2C sketch Cull unused code, Remove Polling Mode, add correct 10bit Master Read, Found new corner condition where the cmd[] cannot have an END preceded by a START. If this START END exists, the SM will not respond to ctr.trans_start=1; A TimeOut is guaranteed. with SCL clocking and SDA LOW. Updated library.properties to V.2.0, added Keywords --- cores/esp32/esp32-hal-i2c.c | 765 +++--------------- cores/esp32/esp32-hal-i2c.h | 21 +- .../Wire/examples/eeprom_size/eeprom_size.ino | 173 ++++ libraries/Wire/examples/i2c_scan/i2c_scan.ino | 30 + libraries/Wire/keywords.txt | 15 +- libraries/Wire/library.properties | 12 +- libraries/Wire/src/Wire.cpp | 98 ++- libraries/Wire/src/Wire.h | 13 +- 8 files changed, 422 insertions(+), 705 deletions(-) create mode 100644 libraries/Wire/examples/eeprom_size/eeprom_size.ino create mode 100644 libraries/Wire/examples/i2c_scan/i2c_scan.ino diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index 8b4eecbc950..d310792c0dd 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -49,7 +49,6 @@ struct i2c_struct_t { uint16_t queuePos; uint16_t byteCnt; uint32_t exitCode; - }; enum { @@ -91,7 +90,7 @@ static i2c_err_t i2cAddQueue(i2c_t * i2c,uint8_t mode, uint16_t i2cDeviceAddr, u dqx.ctrl.addr = i2cDeviceAddr; dqx.ctrl.mode = mode; dqx.ctrl.stop= sendStop; - dqx.ctrl.addrReq = ((i2cDeviceAddr&0x7800)==0x7800)?2:1; // 10bit or 7bit address + dqx.ctrl.addrReq = ((i2cDeviceAddr&0xFC00)==0x7800)?2:1; // 10bit or 7bit address dqx.queueLength = dataLen + dqx.ctrl.addrReq; dqx.queueEvent = event; @@ -123,27 +122,10 @@ else { // first Time } return I2C_ERROR_OK; } -/* walk thru dq elements returning the buffer* and length, savePtr=NULL to start -*/ -i2c_err_t i2cGetReadQueue(i2c_t *i2c, uint8_t** buffPtr, uint16_t* lenPtr,uint8_t *savePtr){ - uint8_t a = *savePtr; -while((aqueueCount)&&(i2c->dq[a].ctrl.mode!=1)) a++; -if(a < i2c->queueCount){// found one - *buffPtr = i2c->dq[a].data; - *lenPtr = i2c->dq[a].length; - a++; - *savePtr = a; - return I2C_ERROR_OK; - } -else {//not found - *buffPtr = NULL; - *lenPtr = 0; - *savePtr = a; - return I2C_ERROR_MEMORY; - } -} i2c_err_t i2cFreeQueue(i2c_t * i2c){ + // 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){ // what about EventHandle? @@ -152,41 +134,33 @@ if(i2c->dq!=NULL){ } i2c->queueCount=0; i2c->queuePos=0; +// release Mutex return rc; } i2c_err_t i2cAddQueueWrite(i2c_t * i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen,bool sendStop,EventGroupHandle_t event){ + // need to grab a MUTEX for exclusive Queue, + // what out if ISR is running? return i2cAddQueue(i2c,0,i2cDeviceAddr,dataPtr,dataLen,sendStop,event); } i2c_err_t i2cAddQueueRead(i2c_t * i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen,bool sendStop,EventGroupHandle_t event){ - - return i2cAddQueue(i2c,1,i2cDeviceAddr,dataPtr,dataLen,sendStop,event); -} +// need to grab a MUTEX for exclusive Queue, +// what out if ISR is running? -uint16_t i2cQueueReadPendingCount(i2c_t * i2c){ -uint16_t cnt=0; -uint16_t a=i2c->queuePos; -while(aqueueCount){ - if(i2c->dq[a].ctrl.mode==1){ - cnt += i2c->dq[a].length - i2c->dq[a].position; - } - a++; - } -return cnt; -} -uint16_t i2cQueueReadCount(i2c_t * i2c){ -uint16_t cnt=0; -uint16_t a=0; -while( a < i2c->queueCount){ - if(i2c->dq[a].ctrl.mode==1){ - cnt += i2c->dq[a].position; //if postion is > 0 then some reads have happened + //10bit read is kind of weird, first you do a 0byte Write with 10bit + // address, then a ReSTART then a 7bit Read using the the upper 7bit + + // readBit. + if((i2cDeviceAddr &0xFC00)==0x7800){ // ten bit read + i2c_err_t err = i2cAddQueue(i2c,0,i2cDeviceAddr,NULL,0,false,event); + if(err==I2C_ERROR_OK){ + return i2cAddQueue(i2c,1,(i2cDeviceAddr>>8),dataPtr,dataLen,sendStop,event); + } + else return err; } - a++; - } -return cnt; + return i2cAddQueue(i2c,1,i2cDeviceAddr,dataPtr,dataLen,sendStop,event); } // Stickbreaker @@ -252,20 +226,6 @@ void i2cSetCmd(i2c_t * i2c, uint8_t index, uint8_t op_code, uint8_t byte_num, bo cmd.byte_num = byte_num; cmd.op_code = op_code; i2c->dev->command[index].val = cmd.val; -/* - i2c->dev->command[index].ack_en = ack_check; - i2c->dev->command[index].ack_exp = ack_exp; - i2c->dev->command[index].ack_val = ack_val; - i2c->dev->command[index].byte_num = byte_num; - i2c->dev->command[index].op_code = op_code; -*/ -} - -void i2cResetCmd(i2c_t * i2c){ - int i; - for(i=0;i<16;i++){ - i2c->dev->command[i].val = 0; - } } void i2cResetFiFo(i2c_t * i2c) @@ -278,563 +238,6 @@ void i2cResetFiFo(i2c_t * i2c) f.tx_fifo_rst = 0; f.rx_fifo_rst = 0; i2c->dev->fifo_conf.val = f.val; -/* - i2c->dev->fifo_conf.tx_fifo_rst = 1; - i2c->dev->fifo_conf.tx_fifo_rst = 0; - i2c->dev->fifo_conf.rx_fifo_rst = 1; - i2c->dev->fifo_conf.rx_fifo_rst = 0; -*/ -} - -i2c_err_t i2cWrite(i2c_t * i2c, uint16_t address, bool addr_10bit, uint8_t * data, uint8_t len, bool sendStop) -{ - int i; - uint8_t index = 0; - uint8_t dataLen = len + (addr_10bit?2:1); - address = (address << 1); - - if(i2c == NULL){ - return I2C_ERROR_DEV; - } - - I2C_MUTEX_LOCK(); - - if (i2c->dev->status_reg.bus_busy == 1) - { - log_e( "Busy Timeout! Addr: %x", address >> 1 ); - I2C_MUTEX_UNLOCK(); - return I2C_ERROR_BUSY; - } - - while(dataLen) { - uint8_t willSend = (dataLen > 32)?32:dataLen; - uint8_t dataSend = willSend; - - i2cResetFiFo(i2c); - i2cResetCmd(i2c); - //Clear Interrupts - i2c->dev->int_clr.val = 0xFFFFFFFF; - - //CMD START - i2cSetCmd(i2c, 0, I2C_CMD_RSTART, 0, false, false, false); - - //CMD WRITE(ADDRESS + DATA) - if(!index) { - i2c->dev->fifo_data.data = address & 0xFF; - dataSend--; - if(addr_10bit) { - i2c->dev->fifo_data.data = (address >> 8) & 0xFF; - dataSend--; - } - } - i = 0; - while(idev->fifo_data.val = data[index++]; - while(i2c->dev->status_reg.tx_fifo_cnt < i); - } - i2cSetCmd(i2c, 1, I2C_CMD_WRITE, willSend, false, false, true); - dataLen -= willSend; - - //CMD STOP or CMD END if there is more data - if(dataLen || !sendStop) { - i2cSetCmd(i2c, 2, I2C_CMD_END, 0, false, false, false); - } else if(sendStop) { - i2cSetCmd(i2c, 2, I2C_CMD_STOP, 0, false, false, false); - } - - //START Transmission - i2c->dev->ctr.trans_start = 1; - - //WAIT Transmission - uint32_t startAt = millis(); - while(1) { - //have been looping for too long - if((millis() - startAt)>50){ - log_e("Timeout! Addr: %x", address >> 1); - I2C_MUTEX_UNLOCK(); - return I2C_ERROR_BUS; - } - - //Bus failed (maybe check for this while waiting? - if(i2c->dev->int_raw.arbitration_lost) { - log_e("Bus Fail! Addr: %x", address >> 1); - I2C_MUTEX_UNLOCK(); - return I2C_ERROR_BUS; - } - - //Bus timeout - if(i2c->dev->int_raw.time_out) { - log_e("Bus Timeout! Addr: %x", address >> 1); - I2C_MUTEX_UNLOCK(); - return I2C_ERROR_TIMEOUT; - } - - //Transmission did not finish and ACK_ERR is set - if(i2c->dev->int_raw.ack_err) { - log_w("Ack Error! Addr: %x", address >> 1); - i2c->dev->int_clr.ack_err = 1; // clear it - while((i2c->dev->status_reg.bus_busy) && ((millis() - startAt)<50)); - I2C_MUTEX_UNLOCK(); - return I2C_ERROR_ACK; - } - - if((sendStop && i2c->dev->command[2].done) || !i2c->dev->status_reg.bus_busy){ - break; - } - } - - } - I2C_MUTEX_UNLOCK(); - return I2C_ERROR_OK; -} - -i2c_err_t i2cRead(i2c_t * i2c, uint16_t address, bool addr_10bit, uint8_t * data, uint8_t len, bool sendStop) -{ - address = (address << 1) | 1; - uint8_t addrLen = (addr_10bit?2:1); - uint8_t index = 0; - uint8_t cmdIdx; - uint8_t willRead; - - if(i2c == NULL){ - return I2C_ERROR_DEV; - } - - I2C_MUTEX_LOCK(); - - if (i2c->dev->status_reg.bus_busy == 1) - { - log_w( "Busy Timeout! Addr: %x", address >> 1 ); - I2C_MUTEX_UNLOCK(); - return I2C_ERROR_BUSY; - } - - i2cResetFiFo(i2c); - i2cResetCmd(i2c); - - //CMD START - i2cSetCmd(i2c, 0, I2C_CMD_RSTART, 0, false, false, false); - - //CMD WRITE ADDRESS - i2c->dev->fifo_data.val = address & 0xFF; - if(addr_10bit) { - i2c->dev->fifo_data.val = (address >> 8) & 0xFF; - } - i2cSetCmd(i2c, 1, I2C_CMD_WRITE, addrLen, false, false, true); - - while(len) { - cmdIdx = (index)?0:2; - willRead = (len > 32)?32:(len-1); - if(cmdIdx){ - i2cResetFiFo(i2c); - } - - if(willRead){ - i2cSetCmd(i2c, cmdIdx++, I2C_CMD_READ, willRead, false, false, false); - } - - if((len - willRead) > 1) { - i2cSetCmd(i2c, cmdIdx++, I2C_CMD_END, 0, false, false, false); - } else { - willRead++; - i2cSetCmd(i2c, cmdIdx++, I2C_CMD_READ, 1, true, false, false); - if(sendStop) { - i2cSetCmd(i2c, cmdIdx++, I2C_CMD_STOP, 0, false, false, false); - } - } - - //Clear Interrupts - i2c->dev->int_clr.val = 0xFFFFFFFF; - - //START Transmission - i2c->dev->ctr.trans_start = 1; - - //WAIT Transmission - uint32_t startAt = millis(); - while(1) { - //have been looping for too long - if((millis() - startAt)>50){ - log_e("Timeout! Addr: %x", address >> 1); - I2C_MUTEX_UNLOCK(); - return I2C_ERROR_BUS; - } - - //Bus failed (maybe check for this while waiting? - if(i2c->dev->int_raw.arbitration_lost) { - log_e("Bus Fail! Addr: %x", address >> 1); - I2C_MUTEX_UNLOCK(); - return I2C_ERROR_BUS; - } - - //Bus timeout - if(i2c->dev->int_raw.time_out) { - log_e("Bus Timeout! Addr: %x", address >> 1); - I2C_MUTEX_UNLOCK(); - return I2C_ERROR_TIMEOUT; - } - - //Transmission did not finish and ACK_ERR is set - if(i2c->dev->int_raw.ack_err) { - log_w("Ack Error! Addr: %x", address >> 1); - I2C_MUTEX_UNLOCK(); - return I2C_ERROR_ACK; - } - - if(i2c->dev->command[cmdIdx-1].done) { - break; - } - } - - int i = 0; - while(idev->fifo_data.val & 0xFF; - } - len -= willRead; - } - I2C_MUTEX_UNLOCK(); - return I2C_ERROR_OK; -} - -/* Stickbreaker Debug code -*/ -void dumpCmdQueue(i2c_t *i2c){ -uint8_t i=0; -while(i<16){ - I2C_COMMAND_t c; - c.val=i2c->dev->command[i].val; - - log_e("[%2d] %c op[%d] val[%d] exp[%d] en[%d] bytes[%d]",i,(c.done?'Y':'N'), - c.op_code, - c.ack_val, - c.ack_exp, - c.ack_en, - c.byte_num); - i++; - } -} - -/* Stickbreaker debug support -*/ -uint32_t emptyCnt=0; -uint32_t maxRead=0; - -/* Stickbreaker poll mode suppot -*/ -void emptyFifo(i2c_t* i2c,char* data, uint16_t len,uint16_t* index){ -uint32_t d; -uint32_t max=0; -while(i2c->dev->status_reg.rx_fifo_cnt>0){ - d = i2c->dev->fifo_data.val; - data[*index] = (d&0xff); - if(((*index)+1)maxRead) maxRead=max; -d = I2C_RXFIFO_FULL_INT_ST; -i2c->dev->int_clr.val =d; // processed! -//log_e("emptied %d",*index); -emptyCnt++; -} - -/* Stickbreaker poll mode suppot -*/ -void fillFifo(i2c_t* i2c,uint8_t* data, uint16_t len,uint16_t* index){ -uint32_t d; -while((i2c->dev->status_reg.tx_fifo_cnt<0x1F)&&(*indexdev->fifo_data.val = d; - *index++; - } -d = I2C_TXFIFO_EMPTY_INT_ST; -i2c->dev->int_clr.val =d; // processed! -//log_e("Filled %d",*index); -} - -/* Stickbreaker poll mode suppot -*/ -typedef enum{ - Stage_waitForBusIdle, - Stage_init, - Stage_StartMachine, - Stage_process, - Stage_done, - Stage_abort - }STAGES; - -/* Stickbreaker poll mode suppot -*/ -void fillReadQueue(i2c_t *i2c, uint16_t *neededRead, uint8_t cmdIdx){ -/* Can I have another Sir? - ALL CMD queues must be terminated with either END or STOP. - if END is used, when refilling it next time, no entries from END to [15] can be used. - AND END must exit. The END command does not complete until the the - ctr->trans_start=1 has executed. - So, only refill from [0]..[14], leave [15] for a continuation if necessary. - As a corrilary, once END exists in [15], you do not need to overwrite it for the - next continuation. It is never modified. But, I update it every time because! - -*/ -uint8_t blkSize=0; // max is 255 -//log_e("needed=%2d index=%d",*neededRead,cmdIdx); -while((*neededRead>1)&&((cmdIdx)<15)) { // more bytes needed and room in cmd queue, leave room for END - // lets start with 3 bytes at a time, but may 255 is possible - blkSize = (*neededRead>35)?35:(*neededRead-1); // each read CMD can only read 32? - *neededRead -= blkSize; // - i2cSetCmd(i2c, (cmdIdx)++, I2C_CMD_READ, blkSize,false,false,false); // read cmd, this can't be the last read. - } -if((*neededRead==1)&&(cmdIdx==14)){ // stretch because Stop cannot be after or at END spot. - I2C_COMMAND_t cmdx; - cmdx.val =i2c->dev->command[cmdIdx-1].val; - if(cmdx.byte_num>1){ - cmdx.byte_num--; - i2c->dev->command[cmdIdx-1].val = cmdx.val; - } - else { // go back two - cmdx.val =i2c->dev->command[cmdIdx-2].val; - cmdx.byte_num--; - i2c->dev->command[cmdIdx-2].val = cmdx.val; - } - cmdx.byte_num=1; - i2c->dev->command[cmdIdx++].val = cmdx.val; - // now cmdIdx is 15 and a contuation will be added - } - -if(cmdIdx==15){ //need continuation -// cmd buffer is almost full, Add END as a continuation feature -// log_e("END at %d, left=%d",cmdIdx,neededRead); - i2cSetCmd(i2c, (cmdIdx)++,I2C_CMD_END, 0,false,false,false); - i2c->dev->int_ena.end_detect=1; //maybe? - i2c->dev->int_clr.end_detect=1; //maybe? - } - -if((*neededRead==1)&&((cmdIdx)<15)){// last Read cmd needs a NAK - // log_e("last Read"); - i2cSetCmd(i2c, (cmdIdx)++, I2C_CMD_READ, 1,true,false,false); - // send NAK to mark end of read - *neededRead=0; - } -if((*neededRead==0)&&((cmdIdx)<16)){// add Stop command - // What about SEND_STOP? If no Stop is sent, the Bus Buzy will Error on - // next call? Need bus ownership? -// log_e("STOP at %d",cmdIdx); - i2cSetCmd(i2c, (cmdIdx)++,I2C_CMD_STOP,0,false,false,false); - } -// log_e("loaded %d cmds",cmdIdx); -// dumpCmdQueue(i2c); - - -} - -/* Stickbreaker poll mode suppot -*/ -i2c_err_t pollI2cRead(i2c_t * i2c, uint16_t address, bool addr_10bit, uint8_t * data, uint16_t len, bool sendStop) -{ -address = (address << 1) | 1; -uint8_t addrLen = (addr_10bit?2:1); -uint16_t index = 0; // pos in data for next byte -uint8_t cmdIdx=0; // next available entry in cmd buffer8 -uint16_t neededRead = len; // bytes left -uint32_t intbuf[32]; -uint16_t intIndex=0; -for(intIndex=0;intIndex<32;intIndex++){ - intbuf[intIndex]=intIndex; - } -intIndex = 0; - -if(i2c == NULL){ - return I2C_ERROR_DEV; - } - -I2C_MUTEX_LOCK(); - -emptyCnt = 0; -STAGES stage=Stage_init; // init -bool finished = false; -bool abort = false; -bool started = false; -uint8_t blkSize = 0; -uint32_t startAt = millis(),lastInt=0,activeInt=0; -i2c_err_t reason = I2C_ERROR_OK; -//uint8_t priorMode=i2c->dev->ctr.ms_mode; -while(!finished){ - if((millis()-startAt)>5000){ // timeout - if(reason == I2C_ERROR_OK) reason = I2C_ERROR_TIMEOUT; - // else leave reason alone - log_e("Stage[%d] millis(%ld) Timeout(%ld)! Addr: %x",stage,millis(),startAt, address >> 1); - stage= Stage_abort; - } - switch(stage){ - case Stage_waitForBusIdle : // bus busy on entry - // if last call had SEND_STOP=FALSE, this will always Error Out? - if (!i2c->dev->status_reg.bus_busy){ - stage = Stage_StartMachine; - reason = I2C_ERROR_OK; - } - else reason = I2C_ERROR_BUSY; - break; - - case Stage_init: // set up address - i2c->dev->ctr.trans_start=0; // Pause Machine - i2c->dev->timeout.tout = 0xFFFFF; // max 1M - I2C_FIFO_CONF_t f; - f.val = i2c->dev->fifo_conf.val; - f.rx_fifo_rst = 1; // fifo in reset - f.tx_fifo_rst = 1; // fifo in reset - f.nonfifo_en = 0; // use fifo mode - f.rx_fifo_full_thrhd = 6; // six bytes before INT is issued - f.fifo_addr_cfg_en = 0; // no directed access - i2c->dev->fifo_conf.val = f.val; // post them all - - f.rx_fifo_rst = 0; // release fifo - f.tx_fifo_rst = 0; - i2c->dev->fifo_conf.val = f.val; // post them all - - i2c->dev->int_ena.val = - I2C_ACK_ERR_INT_ENA | // (BIT(10)) - I2C_TRANS_START_INT_ENA | // (BIT(9)) - I2C_TIME_OUT_INT_ENA | //(BIT(8)) - I2C_TRANS_COMPLETE_INT_ENA | // (BIT(7)) - I2C_MASTER_TRAN_COMP_INT_ENA | // (BIT(6)) - I2C_ARBITRATION_LOST_INT_ENA | // (BIT(5)) - I2C_SLAVE_TRAN_COMP_INT_ENA | // (BIT(4)) - I2C_END_DETECT_INT_ENA | // (BIT(3)) - I2C_RXFIFO_OVF_INT_ENA | //(BIT(2)) - I2C_RXFIFO_FULL_INT_ENA; // (BIT(0)) - - - i2c->dev->int_clr.val = 0xFFFFFFFF; // kill them All! - i2c->dev->ctr.ms_mode = 1; // master! - //i2cResetFiFo(i2c); - //i2cResetCmd(i2c); - - //CMD START - i2cSetCmd(i2c, cmdIdx++, I2C_CMD_RSTART, 0, false, false, false); - - //CMD WRITE ADDRESS - if(addr_10bit){ - i2c->dev->fifo_data.data = ((address>>7)&0xff)|0x1; // high 2 bits plus read - i2c->dev->fifo_data.data = (address>>1)&0xff; // low 8 bits of address - } - else i2c->dev->fifo_data.data = address & 0xFF; // low 7bits plus read - - i2cSetCmd(i2c, cmdIdx++, I2C_CMD_WRITE, addrLen, false, false, true); //load address in cmdlist, validate (low) ack - // log_e("init(%d)",millis()); - fillReadQueue(i2c,&neededRead,cmdIdx); - stage = Stage_StartMachine; - break; - case Stage_StartMachine: - if((!started)&& (i2c->dev->status_reg.bus_busy)){ - // should not hang on END? because we own the bus! - log_e("bus_busy"); - stage = Stage_waitForBusIdle; - } - else { - started = true; -// i2c->dev->int_clr.val = 0xFFFFFFFF; // kill them All! -// log_e("started"); - i2c->dev->ctr.trans_start=1; // go for it - stage = Stage_process; - } - break; - case Stage_process: // Ok, Get to Work - activeInt = i2c->dev->int_status.val; // ok lets play! - if(activeInt==0) break; // nothing to do! - if(lastInt!=activeInt){ - intIndex++; - intIndex %= 32; - intbuf[intIndex] = activeInt; -// log_e("int=%08X",activeInt); - lastInt = activeInt; - } - else { // same int as last time, probably rx_fifo_full! - uint32_t x=(intbuf[intIndex]>>20); //12 bits should be enough! - x++; - x = x<<20; - intbuf[intIndex] = x | (intbuf[intIndex]&0x00ffffff); - } - - if(activeInt&I2C_RXFIFO_FULL_INT_ST){// rx has reached thrhd -// log_e("process"); - emptyFifo(i2c,data,len,&index); - startAt=millis(); // reset timeout - } - - if(activeInt&I2C_END_DETECT_INT_ST){ - i2c->dev->ctr.trans_start=0; - cmdIdx=0; - fillReadQueue(i2c,&neededRead,cmdIdx); - i2c->dev->int_ena.end_detect = 1; - i2c->dev->int_clr.end_detect = 1; - - stage = Stage_StartMachine; -// log_e("end"); - } - - if(activeInt&I2C_ARBITRATION_LOST_INT_ST){// abort transaction - // reset State Machine, Release Bus - stage = Stage_abort; - reason = I2C_ERROR_BUS; - log_e("Arbitration Lost Addr: %x", address >> 1); - } - else if(activeInt&I2C_TRANS_COMPLETE_INT_ST){// stop - stage = Stage_done; - reason = I2C_ERROR_OK; -// log_e("trans_Complete"); - } - else if(activeInt&I2C_TIME_OUT_INT_ST){// - stage = Stage_abort; - reason = I2C_ERROR_TIMEOUT; - - log_e("bit Timeout! int(%lx) stat(%lx) Addr: %x",activeInt, - i2c->dev->status_reg.val,address >> 1); - } - else if(activeInt&I2C_ACK_ERR_INT_ST){ - stage = Stage_abort; - reason = I2C_ERROR_ACK; - log_e("Ack Error Addr: %x", address >> 1); - } - i2c->dev->int_clr.val = activeInt; - break; - case Stage_done: // quiting Time! More Beer! -// log_e("done"); - emptyFifo(i2c,data,len,&index); - finished = true; - i2c->dev->int_clr.val = 0xFFFFFFFF; // kill them All! - break; - case Stage_abort : // on the Job accident, Call 911 - emptyFifo(i2c,data,len,&index); - i2c->dev->int_clr.val = 0xFFFFFFFF; // kill them All! - finished = true; - abort = true; - log_e("abort len=%d",index); - break; - default : - stage = Stage_abort; // should not happen - reason = I2C_ERROR_DEV; - log_e("Unknown Stage! Addr: %x", address >> 1); - } - } - -//i2c->dev->ctr.trans_start=0; // Pause Machine -i2c->dev->int_ena.val = 0; // disable all interrups -//i2c->dev->ctr.ms_mode=priorMode; // maybe also a slave? -if(reason!=I2C_ERROR_OK) { - log_e("exit =%d, cnt=%d got %d maxRead%d",reason,emptyCnt,index,maxRead); - uint16_t a=intIndex; - a = (a +1)%32; - while(a!=intIndex){ - if(intbuf[a] != a)log_e("[%02d]=0x%08lx",a,intbuf[a]); - a++; - a %=32; - } - if(intbuf[a] != a)log_e("[%02d]=0x%08lx",a,intbuf[a]); - dumpCmdQueue(i2c); - } -I2C_MUTEX_UNLOCK(); - -return reason; } i2c_err_t i2cSetFrequency(i2c_t * i2c, uint32_t clk_speed) @@ -885,8 +288,8 @@ uint32_t i2cGetFrequency(i2c_t * i2c) * slave_addr - I2C Address * addr_10bit_en - enable slave 10bit address mode. * */ - -i2c_t * i2cInit(uint8_t i2c_num, uint16_t slave_addr, bool addr_10bit_en) +// 24Nov17 only supports Master Mode +i2c_t * i2cInit(uint8_t i2c_num) { if(i2c_num > 1){ return NULL; @@ -913,7 +316,7 @@ i2c_t * i2cInit(uint8_t i2c_num, uint16_t slave_addr, bool addr_10bit_en) I2C_MUTEX_LOCK(); i2c->dev->ctr.val = 0; - i2c->dev->ctr.ms_mode = (slave_addr == 0); + i2c->dev->ctr.ms_mode = 1; i2c->dev->ctr.sda_force_out = 1 ; i2c->dev->ctr.scl_force_out = 1 ; i2c->dev->ctr.clk_en = 1; @@ -924,10 +327,6 @@ i2c_t * i2cInit(uint8_t i2c_num, uint16_t slave_addr, bool addr_10bit_en) i2c->dev->fifo_conf.nonfifo_en = 0; i2c->dev->slave_addr.val = 0; - if (slave_addr) { - i2c->dev->slave_addr.addr = slave_addr; - i2c->dev->slave_addr.en_10bit = addr_10bit_en; - } I2C_MUTEX_UNLOCK(); return i2c; @@ -939,7 +338,6 @@ void i2cInitFix(i2c_t * i2c){ } I2C_MUTEX_LOCK(); i2cResetFiFo(i2c); - i2cResetCmd(i2c); i2c->dev->int_clr.val = 0xFFFFFFFF; i2cSetCmd(i2c, 0, I2C_CMD_RSTART, 0, false, false, false); i2c->dev->fifo_data.data = 0; @@ -977,15 +375,16 @@ return esp_intr_free(handle); /* Stickbreaker ISR mode debug support */ #define INTBUFFMAX 64 -static uint32_t intBuff[INTBUFFMAX][2]; +static uint32_t intBuff[INTBUFFMAX][3]; static uint32_t intPos=0; /* Stickbreaker ISR mode support */ -static void IRAM_ATTR fillCmdQueue(i2c_t * i2c, uint8_t cmdIdx, bool INTS){ +static void IRAM_ATTR fillCmdQueue(i2c_t * i2c, bool INTS){ /* this function is call on initial i2cProcQueue() or when a I2C_END_DETECT_INT occures */ + uint16_t cmdIdx = 0; uint16_t qp = i2c->queuePos; bool done; bool needMoreCmds = false; @@ -1005,7 +404,8 @@ while(!done){ // fill the command[] until either 0..14 filled or out of cmds and //CMD START I2C_DATA_QUEUE_t *tdq=&i2c->dq[qp]; // simpler coding - if(!tdq->ctrl.startCmdSent){// has this dq element's START command been added? + if((!tdq->ctrl.startCmdSent) && (cmdIdx < 14)){// has this dq element's START command been added? + // <14 testing if ReSTART END is causeing the Timeout i2cSetCmd(i2c, cmdIdx++, I2C_CMD_RSTART, 0, false, false, false); tdq->ctrl.startCmdSent=1; done = (cmdIdx>14); @@ -1034,6 +434,11 @@ while(!done){ // fill the command[] until either 0..14 filled or out of cmds and next continuation. It is never modified. But, I update it every time because it might actually be the first time! + 23NOV17 START cannot proceed END. if START is in[14], END cannot be in [15]. + so, AND if END is moved to [14], [14] and [15] can nolonger be use for anything other than END. + If a START is found in [14] then a prior READ or WRITE must be expanded so that there is no START element in [14]. + + */ if((!done)&&(tdq->ctrl.addrCmdSent)){ //room in command[] for at least One data (read/Write) cmd uint8_t blkSize=0; // max is 255? does numBytes =0 actually mean 256? haven't tried it. @@ -1076,12 +481,56 @@ while(!done){ // fill the command[] until either 0..14 filled or out of cmds and } } + if((cmdIdx==14)&&(!tdq->ctrl.startCmdSent)){ + // START would have preceded END, causes SM TIMEOUT + // need to stretch out a prior WRITE or READ to two Command[] elements + done = false; // reuse it + uint16_t i = 13; // start working back until a READ/WRITE has >1 numBytes + cmdIdx =15; + // log_e("before Stretch"); + // dumpCmdQueue(i2c); + while(!done){ + i2c->dev->command[i+1].val = i2c->dev->command[i].val; // push it down + if (((i2c->dev->command[i].op_code == 1)||(i2c->dev->command[i].op_code==2))){ + /* just try a num_bytes =0; + &&(i2c->dev->command[i].byte_num>1)){ // found the one to expand + i2c->dev->command[i+1].byte_num =1; +// the -= in the following statment caused unintential consequences. +// The op_code field value changed from 2 to 4, so the manual cludge was needed +// i2c->dev->command[i].byte_num -= 1; +/ + uint32_t temp = i2c->dev->command[i].val; + temp = (temp&0xFFFFFF00) | ((temp & 0xFF)-1); + i2c->dev->command[i].val = temp; +*/ + i2c->dev->command[i].byte_num = 0; + done = true; + + } + else { + if(i > 0) { + i--; + } + else { // unable to stretch, fatal + log_e("invalid CMD[] layout Stretch Failed"); + dumpCmdQueue(i2c); + done = true; + } + } + } + // log_e("after Stretch"); + // dumpCmdQueue(i2c); + + } + + if(cmdIdx==15){ //need continuation, even if STOP is in 14, it will not matter // cmd buffer is almost full, Add END as a continuation feature // log_e("END at %d, left=%d",cmdIdx,neededRead); i2cSetCmd(i2c, (cmdIdx)++,I2C_CMD_END, 0,false,false,false); i2c->dev->int_ena.end_detect=1; //maybe? i2c->dev->int_clr.end_detect=1; //maybe? + done = true; } if(!done){ @@ -1102,7 +551,6 @@ if(INTS){ // don't want to prematurely enable fifo ints until ISR is ready to ha } } - /* Stickbreaker ISR mode debug support */ static void IRAM_ATTR dumpI2c(i2c_t * i2c){ @@ -1128,6 +576,22 @@ while(aqueueCount){ } } +/* Stickbreaker ISR mode debug support +*/ +void IRAM_ATTR dumpCmdQueue(i2c_t *i2c){ +uint8_t i=0; +while(i<16){ + I2C_COMMAND_t c; + c.val=i2c->dev->command[i].val; + log_e("[%2d] %c op[%d] val[%d] exp[%d] en[%d] bytes[%d]",i,(c.done?'Y':'N'), + c.op_code, + c.ack_val, + c.ack_exp, + c.ack_en, + c.byte_num); + i++; + } +} /* Stickbreaker ISR mode support */ @@ -1141,6 +605,9 @@ static void IRAM_ATTR fillTxFifo(i2c_t * i2c){ queued (write addr, write 10) of the Third command? I need to test! */ /*11/15/2017 will assume that I cannot queue tx after a READ until READ completes +11/23/2017 Seems to be a TX fifo problem, the SM sends 0x40 for last rxbyte, I +enable txEmpty, filltx fires, but the SM has already sent a bogus byte out the BUS. +I am going so see if I can overlap Tx/Rx/Tx in the fifo */ bool readEncountered = false; uint16_t a=i2c->queuePos; // currently executing dq, @@ -1192,7 +659,8 @@ while((a < i2c->queueCount)&&!(full || readEncountered)){ } } } - else readEncountered = true; +//11/23/2017 overlap tx/rx/tx +// else readEncountered = true; if(full) readEncountered =false; //tx possibly needs more @@ -1248,7 +716,7 @@ else { //log_e("emptied %d",*index); } -static void i2cIsrExit(i2c_t * i2c,const uint32_t eventCode,bool Fatal){ +static void IRAM_ATTR i2cIsrExit(i2c_t * i2c,const uint32_t eventCode,bool Fatal){ switch(eventCode){ case EVENT_DONE: @@ -1320,6 +788,8 @@ while (activeInt != 0) { // Ordering of 'if(activeInt)' statements is important, intBuff[intPos][1] = 0; } + intBuff[intPos][2] = xTaskGetTickCountFromISR(); // when IRQ fired + uint32_t oldInt =activeInt; if (activeInt & I2C_TRANS_START_INT_ST_M) { @@ -1411,17 +881,11 @@ while (activeInt != 0) { // Ordering of 'if(activeInt)' statements is important, p_i2c->dev->int_ena.end_detect = 0; p_i2c->dev->int_clr.end_detect = 1; p_i2c->dev->ctr.trans_start=0; - fillCmdQueue(p_i2c,0,true); + fillCmdQueue(p_i2c,true); // enable interrupts p_i2c->dev->ctr.trans_start=1; // go for it activeInt&=~I2C_END_DETECT_INT_ST_M; - // What about Tx, RX fifo fill? } - - if (activeInt & I2C_RXFIFO_OVF_INT_ST_M) { //should never happen, I always use Fifo - p_i2c->dev->int_clr.rx_fifo_ovf = 1; - p_i2c->dev->int_ena.rx_fifo_ovf = 0; // disable it - } - + if(activeInt){ // clear unhandled if possible? What about Disabling interrupt? p_i2c->dev->int_clr.val = activeInt; log_e("unknown int=%x",activeInt); @@ -1438,11 +902,11 @@ uint32_t b; log_e("row count INTR TX RX"); 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",b,((intBuff[b][0]>>16)&0xFFFF),(intBuff[b][0]&0xFFFF),((intBuff[b][1]>>16)&0xFFFF),(intBuff[b][1]&0xFFFF)); + 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]); } } -i2c_err_t i2cProcQueue(i2c_t * i2c){ +i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount){ /* do the hard stuff here install ISR if necessary setup EventGroup @@ -1450,7 +914,7 @@ i2c_err_t i2cProcQueue(i2c_t * i2c){ 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; } @@ -1528,7 +992,8 @@ while(i2c->queuePos < i2c->queueCount){ } i2c->queuePos=0; -fillCmdQueue(i2c,0,false); // start adding command[], END irq will keep it full +fillCmdQueue(i2c,false); // don't enable Tx/RX irq's +// start adding command[], END irq will keep it full //Data Fifo will be filled after trans_start is issued i2c->exitCode=0; @@ -1565,8 +1030,6 @@ if(!i2c->intr_handle){ // create ISR I2C_0 only, // how many ticks should it take to transfer totalBytes thru the I2C hardware, // add 50ms just for kicks - - portTickType ticksTimeOut = ((totalBytes /(i2cGetFrequency(i2c)/(10*1000)))+50)/portTICK_PERIOD_MS; portTickType tBefore=xTaskGetTickCount(); @@ -1634,7 +1097,11 @@ else { // GROSS timeout, shutdown ISR , report Timeout // if error, need to trigger all succeeding dataQueue events with the EVENT_ERROR_PREV b = 0; + while(b < i2c->queueCount){ + if(i2c->dq[b].ctrl.mode==1){ + *readCount += i2c->dq[b].position; // number of data bytes received + } if(b < i2c->queuePos){ // before any error if(i2c->dq[b].queueEvent){ // this data queue element has an EventGroup xEventGroupSetBits(i2c->dq[b].queueEvent,EVENT_DONE); @@ -1664,4 +1131,14 @@ if(i2c->intr_handle){ i2c->intr_handle=NULL; } return I2C_ERROR_OK; -} \ No newline at end of file +} + +/* todo + 24Nov17 + Need to think about not usings I2C_MASTER_TRAN_COMP_INT_ST to adjust queuePos. This + INT triggers every byte. The only reason to know which byte is being transfered is + to decide where to store a READ or if an error occured. It may be possible to use + the status_reg.tx_fifo_cnt and a .txQueued to do this in the fillRxFifo(). The + same mechanism could work if an error occured in i2cErrorExit(). +*/ + diff --git a/cores/esp32/esp32-hal-i2c.h b/cores/esp32/esp32-hal-i2c.h index 110f70f8a01..7b83f29f95a 100644 --- a/cores/esp32/esp32-hal-i2c.h +++ b/cores/esp32/esp32-hal-i2c.h @@ -145,12 +145,14 @@ typedef struct { struct i2c_struct_t; typedef struct i2c_struct_t i2c_t; -i2c_t * i2cInit(uint8_t i2c_num, uint16_t slave_addr, bool addr_10bit_en); +i2c_t * i2cInit(uint8_t i2c_num); //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); @@ -159,26 +161,15 @@ i2c_err_t i2cDetachSCL(i2c_t * i2c, int8_t scl); i2c_err_t i2cAttachSDA(i2c_t * i2c, int8_t sda); i2c_err_t i2cDetachSDA(i2c_t * i2c, int8_t sda); -i2c_err_t i2cWrite(i2c_t * i2c, uint16_t address, bool addr_10bit, uint8_t * data, uint8_t len, bool sendStop); -i2c_err_t i2cRead(i2c_t * i2c, uint16_t address, bool addr_10bit, uint8_t * data, uint8_t len, bool sendStop); -//Stickbreakers attempt to read big blocks -i2c_err_t pollI2cRead(i2c_t * i2c, uint16_t address, bool addr_10bit, uint8_t * data, uint16_t len, bool sendStop); -i2c_err_t i2cProcQueue(i2c_t *i2c); +//Stickbreakers ISR Support +i2c_err_t i2cProcQueue(i2c_t *i2c, uint32_t *readCount); 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); -uint16_t i2cQueueReadPendingCount(i2c_t *i2c); -uint16_t i2cQueueReadCount(i2c_t *i2c); -i2c_err_t i2cGetReadQueue(i2c_t *i2c, uint8_t** buffPtr, uint16_t* lenPtr,uint8_t *savePtr); +//stickbreaker debug support void i2cDumpInts(); - -static void IRAM_ATTR i2c_isr_handler_default(void* arg); //ISR - - -void i2cReset(i2c_t* i2c); - #ifdef __cplusplus } #endif diff --git a/libraries/Wire/examples/eeprom_size/eeprom_size.ino b/libraries/Wire/examples/eeprom_size/eeprom_size.ino new file mode 100644 index 00000000000..b6776fc407c --- /dev/null +++ b/libraries/Wire/examples/eeprom_size/eeprom_size.ino @@ -0,0 +1,173 @@ +#include +// Connect 4.7k pullups on SDA, SCL +// for ESP32 SDA(pin 21), SCL(pin 22) +// for AtMega328p SDA(Pin A5), SCL(pin A4) +// for Mega2560 SDA(Pin 20), SCL(pin 21) + +/* This sketch uses the address rollover of the 24LCxx EEPROMS to detect their size. + The 24LC32 (4kByte) 0x0000 .. 0x0FFF, a Write to addres 0x1000 will actually + be stored in 0x0000. This allows us to read the value of 0x0000, compare + it to the value read from 0x1000, if they are different, then this IC is + not a 24LC32. + If the Value is the same, then we have to change the byte at 0x1000 and + see if the change is reflected in 0x0000. If 0x0000 changes, then we know + that the chip is a 24LC32. We have to restore the 'changed' value so that + the data in the EEPROM is not compromized. + + This pattern of read, compare, test, restore is used for each possible size. + All that changes is the test Address, 0x1000, 0x2000, 0x4000, 0x8000. + if the 0x8000 test is does not change, then the chip is a 24LC512. +*/ + +/* after a write, the I2C device requires upto 5ms to actually program + the memory cells. During this programming cycle, the IC does not respond + to I2C requests. This feature 'NAK' polling is used to determine when + the program cycle has completed. +*/ +bool i2cReady(uint8_t ID){ +uint32_t timeout=millis(); +bool ready=false; +while((millis()-timeout<100)&&(!ready)){ + Wire.beginTransmission(ID); + int err=Wire.endTransmission(); + ready=(err==0); + if(!ready){ + if(err!=2)Serial.printf("{%d}:%s",err,Wire.getErrorText(err)); + } + } +return ready; +} + +/* eepromSize() only works on 24LC32 .. 24LC512 eeprom, + the smaller 24LC01 .. 24LC16 use one byte addressings. +*/ +void eepromSize(){ +Serial.println("Discovering eeprom sizes 0x50..0x57"); +uint8_t ID=0x50,i; +uint16_t size; +char buf[256]; +while(ID<0x58){ + i=0; + size = 0x1000; // Start at 4k, 16bit address devices, + i += sprintf_P(&buf[i],PSTR("0x%02X: "),ID); + if(i2cReady(ID)) { // EEPROM answered + uint8_t zeroByte; + Wire.beginTransmission(ID); + Wire.write((uint8_t)0); // set address ptr to 0, two bytes High + Wire.write((uint8_t)0); // set address ptr to 0, two bytes Low + uint8_t err=Wire.endTransmission(); + if(err==0){// worked, device exists at this ID + err=Wire.requestFrom(ID,(uint8_t)1); + if(err==1){// got the value of the byte at address 0 + zeroByte=Wire.read(); + uint8_t saveByte,testByte; + do{ + if(i2cReady(ID)){ + Wire.beginTransmission(ID); + Wire.write(highByte(size)); // set next test address + Wire.write(lowByte(size)); + Wire.endTransmission(); + err=Wire.requestFrom(ID,(uint8_t)1); + if(err==1){ + saveByte=Wire.read(); + if(saveByte == zeroByte) { // have to test it + Wire.beginTransmission(ID); + Wire.write(highByte(size)); // set next test address + Wire.write(lowByte(size)); + Wire.write((uint8_t)~zeroByte); // change it + err=Wire.endTransmission(); + if(err==0){ // changed it + if(!i2cReady(ID)){ + i+=sprintf_P(&buf[i],PSTR(" notReady2.\n")); + Serial.print(buf); + ID++; + break; + } + Wire.beginTransmission(ID); + Wire.write((uint8_t)0); // address 0 byte High + Wire.write((uint8_t)0); // address 0 byte Low + err=Wire.endTransmission(); + if(err==0){ + err=Wire.requestFrom(ID,(uint8_t)1); + if(err==1){ // now compare it + testByte=Wire.read(); + } + else { + testByte=~zeroByte; // error out + } + } + else { + testByte=~zeroByte; + } + } + else { + testByte = ~zeroByte; + } + + //restore byte + if(!i2cReady(ID)){ + i+=sprintf_P(&buf[i],PSTR(" notReady4.\n")); + Serial.print(buf); + ID++; + break; + } + Wire.beginTransmission(ID); + Wire.write(highByte(size)); // set next test address + Wire.write(lowByte(size)); + Wire.write((uint8_t)saveByte); // restore it + Wire.endTransmission(); + } + else testByte = zeroByte; // They were different so the eeprom Is Bigger + } + else testByte=~zeroByte; + } + else testByte=~zeroByte; + if(testByte==zeroByte){ + size = size <<1; + } + }while((testByte==zeroByte)&&(size>0)); + if(size==0) i += sprintf_P(&buf[i],PSTR("64k Bytes")); + else i+=sprintf_P(&buf[i],PSTR("%dk Bytes"),size/1024); + if(!i2cReady(ID)){ + i+=sprintf_P(&buf[i],PSTR(" notReady3.\n")); + Serial.print(buf); + ID++; + continue; + } + Wire.beginTransmission(ID); + Wire.write((uint8_t)0); // set address ptr to 0, two bytes High + Wire.write((uint8_t)0); // set address ptr to 0, two bytes Low + err=Wire.endTransmission(); + if(err==0){ + err= Wire.requestFrom(ID,1); + if (err==1) { + testByte = Wire.read(); + if(testByte != zeroByte){ //fix it + Wire.beginTransmission(ID); + Wire.write((uint8_t)0); // set address ptr to 0, two bytes High + Wire.write((uint8_t)0); // set address ptr to 0, two bytes Low + Wire.write(zeroByte); //Restore + err=Wire.endTransmission(); + } + } + } + } + else i+=sprintf_P(&buf[i],PSTR("Read 0 Failure")); + } + else i+=sprintf_P(&buf[i],PSTR("Write Adr 0 Failure")); + + } + else i+=sprintf_P(&buf[i],PSTR("Not Present")); + Serial.println(buf); + ID++; + } +} + +void setup(){ + Serial.begin(115200); + Wire.begin(); + eepromSize(); +} + +void loop(){ +} \ No newline at end of file diff --git a/libraries/Wire/examples/i2c_scan/i2c_scan.ino b/libraries/Wire/examples/i2c_scan/i2c_scan.ino new file mode 100644 index 00000000000..5fede726cc2 --- /dev/null +++ b/libraries/Wire/examples/i2c_scan/i2c_scan.ino @@ -0,0 +1,30 @@ +#include + +void scan(){ +Serial.println("\n Scanning I2C Addresses"); +uint8_t cnt=0; +for(uint8_t i=0;i<0x7F;i++){ + Wire.beginTransmission(i); + uint8_t ec=Wire.endTransmission(true); // if device exists on bus, it will aCK + if(ec==0){ // Device ACK'd + if(i<16)Serial.print('0'); + Serial.print(i,HEX); + cnt++; + } + else Serial.print(".."); // no one answered + Serial.print(' '); + if ((i&0x0f)==0x0f)Serial.println(); + } +Serial.print("Scan Completed, "); +Serial.print(cnt); +Serial.println(" I2C Devices found."); +} + +void setup(){ +Serial.begin(115200); +Wire.begin(); +scan(); +} + +void loop(){ +} \ No newline at end of file diff --git a/libraries/Wire/keywords.txt b/libraries/Wire/keywords.txt index 3344011d406..27cdda3be37 100644 --- a/libraries/Wire/keywords.txt +++ b/libraries/Wire/keywords.txt @@ -12,14 +12,23 @@ begin KEYWORD2 setClock KEYWORD2 -setClockStretchLimit KEYWORD2 beginTransmission KEYWORD2 endTransmission KEYWORD2 requestFrom KEYWORD2 -send KEYWORD2 -receive KEYWORD2 onReceive KEYWORD2 onRequest KEYWORD2 +requestFrom KEYWORD2 +writeTransaction KEYWORD2 +transact KEYWORD2 +lastError KEYWORD2 +getErrorText KEYWORD2 +write KEYWORD2 +read KEYWORD2 +available KEYWORD2 +peek KEYWORD2 +flush KEYWORD2 +reset KEYWORD2 + ####################################### # Instances (KEYWORD2) diff --git a/libraries/Wire/library.properties b/libraries/Wire/library.properties index ef29154c8e7..b85b1296bb6 100644 --- a/libraries/Wire/library.properties +++ b/libraries/Wire/library.properties @@ -1,9 +1,9 @@ name=Wire -version=1.0 -author=Hristo Gochkov -maintainer=Hristo Gochkov -sentence=Allows the communication between devices or sensors connected via Two Wire Interface Bus. For esp8266 boards. -paragraph= +version=2.0 +author=StickBreaker github.com +maintainer=StickBreaker at GitHub.com +sentence=V2.0 Rewritten to increase stability by using ISR methods. Allows the communication between devices or sensors connected via Two Wire Interface Bus. For esp32 boards. +paragraph=The origional V1.0 was written by Hristo Gochkov . This new version uses Interrupts and a better understanding of the hardware. As a side benifit of using interrupts, local buffers can now be used, allowing upto 64k-1 byte transfers. The ESP32's Hardware does not allow a naked ReStart (sendStop=false). All calls that end with sendStop=false; are Queued and only executed when a STOP is encountered (endTransmission(true),requestFrom(id,len,true)). category=Signal Input/Output -url=http://arduino.cc/en/Reference/Wire +url=https://github.com/stickbreaker/arduino-esp32 architectures=esp32 diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp index 5a9c82f5c73..fba7b23c8a5 100644 --- a/libraries/Wire/src/Wire.cpp +++ b/libraries/Wire/src/Wire.cpp @@ -19,7 +19,7 @@ Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts Modified December 2014 by Ivan Grokhotkov (ivan@esp8266.com) - esp8266 support Modified April 2015 by Hrsto Gochkov (ficeto@ficeto.com) - alternative esp8266 support - Modified Nov 2017 by Chuck Todd (ctodd@cableone.net) - ESP32 support + Modified Nov 2017 by Chuck Todd (ctodd@cableone.net) - ESP32 ISR Support */ extern "C" { @@ -31,10 +31,11 @@ extern "C" { #include "esp32-hal-i2c.h" #include "Wire.h" #include "Arduino.h" -/* Declarations to support Slave Mode */ -#include "esp_attr.h" -#include "soc/i2c_reg.h" -#include "soc/i2c_struct.h" + +/* Declarations to support Slave Mode +// #include "esp_attr.h" +//#include "soc/i2c_reg.h" +// #include "soc/i2c_struct.h" user_onRequest TwoWire::uReq[2]; user_onReceive TwoWire::uRcv[2]; @@ -42,8 +43,6 @@ user_onReceive TwoWire::uRcv[2]; #define DR_REG_I2C_EXT_BASE_FIXED 0x60013000 #define DR_REG_I2C1_EXT_BASE_FIXED 0x60027000 -/* - void IRAM_ATTR slave_isr_handler(void* arg){ // ... uint32_t num = (uint32_t)arg; @@ -71,10 +70,10 @@ TwoWire::TwoWire(uint8_t bus_num) ,txQueued(0) ,rxQueued(0) {} - +/* slave Mode, no yet Stickbreaker void TwoWire::onRequestService(void){} void TwoWire::onReceiveService(uint8_t*buf, int count){} - +*/ void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency) { if(sdaPin < 0) { @@ -94,13 +93,15 @@ void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency) } if(i2c == NULL) { - i2c = i2cInit(num, 0, false); + i2c = i2cInit(num); if(i2c == NULL) { return; } } +/* Slavemode, not yet uReq[num] =NULL; uRcv[num] =NULL; + */ i2cSetFrequency(i2c, frequency); @@ -128,8 +129,7 @@ void TwoWire::setClock(uint32_t frequency) i2cSetFrequency(i2c, frequency); } /* Original requestFrom() 11/2017 before ISR -*/ -size_t TwoWire::oldRequestFrom(uint8_t address, size_t size, bool sendStop) +size_t TwoWire::requestFrom(uint8_t address, size_t size, bool sendStop) { i2cReleaseISR(i2c); @@ -142,23 +142,23 @@ size_t TwoWire::oldRequestFrom(uint8_t address, size_t size, bool sendStop) rxLength = read; return read; } +*/ /*@StickBreaker common handler for processing the queued commands */ -i2c_err_t TwoWire::processQueue(uint16_t * readCount){ - last_error=i2cProcQueue(i2c); +i2c_err_t TwoWire::processQueue(uint32_t * readCount){ + last_error=i2cProcQueue(i2c,readCount); rxIndex = 0; - rxLength = 0; - txQueued = 0; // the SendStop=true will restart all Queueing + rxLength = rxQueued; rxQueued = 0; - *readCount = i2cQueueReadCount(i2c); // do I want to exclude all Local buffers from this? -// currently 17/NOV/2017 this count is of all bytes read from I2C, for this queue of commands. - + txQueued = 0; // the SendStop=true will restart all Queueing +// *readCount = i2cQueueReadCount(i2c); // do I want to exclude all Local buffers from this? +// currently 17/NOV/2017 this count is of all bytes read from I2C, for this queue of commands. //what about mix local buffers and Wire Buffers? //Handled it by verifying location, only contiguous sections of //rxBuffer are available for Wire.read(). Any local buffer requestfrom(id,*char..) is // completed already, only the ones using Wire's buffers need to be considered - +/* rxQueued should handle this uint8_t *savePtr; uint16_t len; uint8_t idx=0; @@ -167,12 +167,11 @@ i2c_err_t TwoWire::processQueue(uint16_t * readCount){ rxLength = rxLength + len; } } +*/ i2cFreeQueue(i2c); return last_error; } - - /* @stickBreaker 11/2017 fix for ReSTART timeout, ISR */ size_t TwoWire::requestFrom(uint8_t address, size_t size, bool sendStop){ @@ -182,6 +181,7 @@ size_t TwoWire::requestFrom(uint8_t address, size_t size, bool sendStop){ if(cnt<(I2C_BUFFER_LENGTH-1)){ // any room left in rxBuffer if((size+cnt)>I2C_BUFFER_LENGTH) size = (I2C_BUFFER_LENGTH-cnt); + rxQueued += size; } else { // no room to receive more! log_e("rxBuff overflow %d",cnt+size); @@ -195,7 +195,7 @@ size_t TwoWire::requestFrom(uint8_t address, size_t size, bool sendStop){ } size_t TwoWire::requestFrom(uint8_t address, uint8_t * readBuff, size_t size, bool sendStop){ - uint16_t cnt=0; + uint32_t cnt=0; last_error =i2cAddQueueRead(i2c,address,readBuff,size,sendStop,NULL); if(last_error==I2C_ERROR_OK){ // successfully queued the read if(sendStop){ //now actually process the queued commands @@ -212,6 +212,8 @@ size_t TwoWire::requestFrom(uint8_t address, uint8_t * readBuff, size_t size, bo return cnt; } +/* stickBreaker Nov 2017 ISR, and bigblock 64k-1 +*/ uint8_t TwoWire::writeTransaction(uint8_t address, uint8_t *buff, size_t size, bool sendStop){ // will destroy any partially created beginTransaction() @@ -219,7 +221,7 @@ last_error=i2cAddQueueWrite(i2c,address,buff,size,sendStop,NULL); if(last_error==I2C_ERROR_OK){ //queued if(sendStop){ //now actually process the queued commands, including READs - uint16_t dummy; + uint32_t dummy; last_error=processQueue(&dummy); } else { // stop not received, so wait for I2C stop, @@ -244,8 +246,8 @@ size_t TwoWire::getClock(){ return i2cGetFrequency(i2c); } -/*stickbreaker using the i2c hardware without an ISR -*/ +/*stickbreaker using the i2c hardware without an ISR testing + size_t TwoWire::polledRequestFrom(uint8_t address, uint8_t* buf, size_t size, bool sendStop){ i2cReleaseISR(i2c); @@ -262,6 +264,7 @@ size_t TwoWire::polledRequestFrom(uint8_t address, uint8_t* buf, size_t size, bo size_t read = (last_error==0)?size:0; return read; } +*/ /*stickbreaker simple ReSTART handling using internal Wire data buffers */ @@ -309,7 +312,7 @@ if(transmitting==1){ if(last_error == I2C_ERROR_OK){ if(sendStop){ - uint16_t dummy; + uint32_t dummy; last_error = processQueue(&dummy); } else { // queued because it had sendStop==false @@ -329,10 +332,43 @@ transmitting = 0; return last_error; } -i2c_err_t TwoWire::lastError(){ - return last_error; +/* stickbreaker Nov2017 better error reporting +*/ +uint8_t TwoWire::lastError(){ + return (uint8_t)last_error; +} + +const char ERRORTEXT[] = + "OK\0" + "DEVICE\0" + "ACK\0" + "TIMEOUT\0" + "BUS\0" + "BUSY\0" + "MEMORY\0" + "CONTINUE\0" + "NO_BEGIN\0" + "\0"; + + +char * TwoWire::getErrorText(uint8_t err){ +uint8_t t = 0; +bool found=false; +char * message=(char*)&ERRORTEXT; + +while((!found)&&(message[0])){ + found = t==err; + if(!found) { + message = message +strlen(message)+1; + t++; + } + } +if(!found) return NULL; +else return message; } +/* Origional 10Nov17 + uint8_t TwoWire::oldEndTransmission(uint8_t sendStop) { //disable ISR @@ -343,6 +379,7 @@ uint8_t TwoWire::oldEndTransmission(uint8_t sendStop) transmitting = 0; return ret; } +*/ uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop) { @@ -370,8 +407,7 @@ void TwoWire::beginTransmission(uint8_t address) txAddress = address; txIndex = txQueued; // allow multiple beginTransmission(),write(),endTransmission(false) until endTransmission(true) txLength = txQueued; - if(txLength!=0) - log_e("multiple beginTransmissions starting at %d",txQueued); +// if(txLength!=0) log_e("multiple beginTransmissions starting at %d",txQueued); } diff --git a/libraries/Wire/src/Wire.h b/libraries/Wire/src/Wire.h index 3347f1d37a5..1564d3a8079 100644 --- a/libraries/Wire/src/Wire.h +++ b/libraries/Wire/src/Wire.h @@ -19,6 +19,7 @@ Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts Modified December 2014 by Ivan Grokhotkov (ivan@esp8266.com) - esp8266 support Modified April 2015 by Hrsto Gochkov (ficeto@ficeto.com) - alternative esp8266 support + Modified November 2017 by Chuck Todd to use ISR and increase stability. */ #ifndef TwoWire_h @@ -53,12 +54,14 @@ class TwoWire: public Stream uint16_t txQueued; //@stickbreaker uint8_t transmitting; +/* slave Mode, not yet Stickbreaker static user_onRequest uReq[2]; static user_onReceive uRcv[2]; void onRequestService(void); void onReceiveService(uint8_t*, int); +*/ i2c_err_t last_error; // @stickBreaker from esp32-hal-i2c.h - i2c_err_t processQueue(uint16_t *readCount); + i2c_err_t processQueue(uint32_t *readCount); public: TwoWire(uint8_t bus_num); @@ -68,16 +71,14 @@ class TwoWire: public Stream void beginTransmission(int); uint8_t endTransmission(void); uint8_t endTransmission(uint8_t); - uint8_t oldEndTransmission(uint8_t); - size_t oldRequestFrom(uint8_t address, size_t size, bool sendStop); + size_t requestFrom(uint8_t address, size_t size, bool sendStop); //@stickBreaker for big blocks and ISR model uint8_t writeTransaction(uint8_t address, uint8_t* buff, size_t size, bool sendStop); - size_t requestFrom(uint8_t address, size_t size, bool sendStop); size_t requestFrom(uint8_t address, uint8_t* buf, size_t size, bool sendStop); - size_t polledRequestFrom(uint8_t address, uint8_t* buf, size_t size, bool sendStop); size_t transact(size_t readLen); size_t transact(uint8_t* readBuff, size_t readLen); - i2c_err_t lastError(); + uint8_t lastError(); + char * getErrorText(uint8_t err); void dumpInts(); size_t getClock(); // From 89ab899012b3cc7479652cc3b7b7921b6fbe4f46 Mon Sep 17 00:00:00 2001 From: chuck todd Date: Fri, 24 Nov 2017 15:38:59 -0700 Subject: [PATCH 16/63] catch up with current version Remove references to culled code. --- README.md | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index a43f69cf905..589ce4010a4 100644 --- a/README.md +++ b/README.md @@ -54,9 +54,9 @@ uint8_t err =Wire.endTransmission(false); // don't send a STOP, just Pause I2C o if(err==7){ // Prior Operation has been queued, it is NOT guaranteed that it will // successfully occur! err=Wire.requestFrom(addr,len); - if(err!=len){ // complete/partial read failure - Serial.print("Bad Stuff!! Read Failed lastError="); - Serial.print(Wire.lastError(),DEC); + if(Wire.lastError()!=0){ // complete/partial read failure + Serial.printf("Bad Stuff!!\nRead of (%d) bytes read %d bytes\nFailed lastError=%d," + " text=%s\n",len,err,Wire.lastError(),Wire.getErrorText(Wire.lastError())); } // some of the read may have executed while(Wire.avaiable()){ @@ -70,13 +70,8 @@ Additionally I have expanded the ability of `Wire()` to handle larger Reads and I have create a few new methods for Wire: ```c++ - uint8_t oldEndTransmission(uint8_t); //released implementation - size_t oldRequestFrom(uint8_t address, size_t size, bool sendStop); //released implementation -//@stickBreaker for big blocks and ISR model uint8_t writeTransaction(uint8_t address, uint8_t* buff, size_t size, bool sendStop);// big block handling - size_t requestFrom(uint8_t address, size_t size, bool sendStop); size_t requestFrom(uint8_t address, uint8_t* buf, size_t size, bool sendStop); - size_t polledRequestFrom(uint8_t address, uint8_t* buf, size_t size, bool sendStop);//a BigBlock test case Not USING ISR size_t transact(size_t readLen); // replacement for endTransmission(false),requestFrom(ID,readLen,true); size_t transact(uint8_t* readBuff, size_t readLen);// bigger Block read i2c_err_t lastError(); // Expose complete error @@ -91,10 +86,9 @@ Wire.beginTransmission(ID); Wire.write(highByte(addr)); Wire.write(lowByte(addr)); -uint8_t err=Wire.transact(len); -if(err!=len){ // complete/partial read failure - Serial.print("Bad Stuff!! Read Failed lastError="); - Serial.print(Wire.lastError(),DEC); +uint8_t err=Wire.transact(len); // transact() does both Wire.endTransmission(false); and Wire.requestFrom(ID,len,true); +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 while(Wire.avaiable()){ @@ -110,5 +104,4 @@ Set the "core debug level" to 'error' There is MINIMAL to NO ERROR detection, BUS, BUSY. because I have not encounter any of them! - Chuck. From 4bd1ba0a8dcd3846e353143ff1ec1add27f2dc05 Mon Sep 17 00:00:00 2001 From: chuck todd Date: Sat, 25 Nov 2017 10:31:54 -0700 Subject: [PATCH 17/63] Properly release resources before Reset Had not included cleanup for dynamic memory/ Interrupt in Reset() operation. Would cause a memory leak, and Possibly a total failure if Reset() was call. I have not verified how FreeRTOS would handle trying to assign a ISR to an interrupt that is already assigned an ISR? --- libraries/Wire/src/Wire.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp index fba7b23c8a5..b90baa91c2b 100644 --- a/libraries/Wire/src/Wire.cpp +++ b/libraries/Wire/src/Wire.cpp @@ -484,6 +484,8 @@ void TwoWire::flush(void) void TwoWire::reset(void) { + i2cFreeQueue(i2c); // release malloc memory + i2cReleaseISR(i2c); // remove ISR from Interrupt chain i2cReset( i2c ); i2c = NULL; begin( sda, scl ); From 20ba765ac880a37cc6d4bace61f52e7c2f36acb7 Mon Sep 17 00:00:00 2001 From: chuck todd Date: Sat, 25 Nov 2017 10:40:42 -0700 Subject: [PATCH 18/63] Memory Leak Release EventGroup, Heap Memory for dq --- cores/esp32/esp32-hal-i2c.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index d310792c0dd..21dab48b817 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -1130,7 +1130,11 @@ if(i2c->intr_handle){ // log_e("released ISR=%d",error); i2c->intr_handle=NULL; } -return I2C_ERROR_OK; +if(i2c->i2c_event){ + xEventGroupDelete(i2c->i2c_event); + i2c->i2c_event = NULL; + } +return i2cFreeQueue(i2c); //release dynamic memory } /* todo From ea9872333f3e547d2196fbb314ce2dcc24e2c30c Mon Sep 17 00:00:00 2001 From: chuck todd Date: Sat, 25 Nov 2017 10:43:58 -0700 Subject: [PATCH 19/63] Update Wire.cpp --- libraries/Wire/src/Wire.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp index b90baa91c2b..8f1bef7b3aa 100644 --- a/libraries/Wire/src/Wire.cpp +++ b/libraries/Wire/src/Wire.cpp @@ -484,8 +484,7 @@ void TwoWire::flush(void) void TwoWire::reset(void) { - i2cFreeQueue(i2c); // release malloc memory - i2cReleaseISR(i2c); // remove ISR from Interrupt chain + i2cReleaseISR(i2c); // remove ISR from Interrupt chain,Delete EventGroup,Free Heap memory i2cReset( i2c ); i2c = NULL; begin( sda, scl ); From 8cdf38746479e1f4682b21c0b2693a6f49e3c81f Mon Sep 17 00:00:00 2001 From: Chuck Todd Date: Sat, 25 Nov 2017 11:39:09 -0700 Subject: [PATCH 20/63] Memory Leak, Reset() cleanup, add UserSelectable Timeout Calling Wire.reset() would cause a heap memory leak, also, possible failure because I did not release the ISR. And not releasing the EventGroup Added a Wire.setTimeout(millis) to allow user selectable Gross Timeout instead of the hardwired 50ms. --- cores/esp32/esp32-hal-i2c.c | 16 ++++++++++------ cores/esp32/esp32-hal-i2c.h | 2 +- libraries/Wire/src/Wire.cpp | 12 +++++++++++- libraries/Wire/src/Wire.h | 3 +++ 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index d310792c0dd..15591ca9c18 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -906,7 +906,7 @@ for(uint32_t a=1;a<=INTBUFFMAX;a++){ } } -i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount){ +i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis){ /* do the hard stuff here install ISR if necessary setup EventGroup @@ -1028,9 +1028,9 @@ if(!i2c->intr_handle){ // create ISR I2C_0 only, //hang until it completes. // how many ticks should it take to transfer totalBytes thru the I2C hardware, -// add 50ms just for kicks +// add user supplied timeOutMillis to Calc Value -portTickType ticksTimeOut = ((totalBytes /(i2cGetFrequency(i2c)/(10*1000)))+50)/portTICK_PERIOD_MS; +portTickType ticksTimeOut = ((totalBytes /(i2cGetFrequency(i2c)/(10*1000)))+timeOutMillis)/portTICK_PERIOD_MS; portTickType tBefore=xTaskGetTickCount(); //log_e("before startup @tick=%d will wait=%d",xTaskGetTickCount(),ticksTimeOut); @@ -1052,7 +1052,7 @@ if(i2c->exitCode!=eBits){ // try to recover from O/S failure eBits=i2c->exitCode; } -if(!(eBits==EVENT_DONE)&&(eBits&~(EVENT_ERROR_NAK|EVENT_ERROR|EVENT_DONE))){ // not only Done, therefore error, exclude ADDR NAK +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 dumpI2c(i2c); i2cDumpInts(); } @@ -1087,7 +1087,7 @@ else { // GROSS timeout, shutdown ISR , report Timeout i2c->dev->int_clr.val = 0x1FFF; reason = I2C_ERROR_TIMEOUT; eBits = eBits | EVENT_ERROR_TIMEOUT|EVENT_ERROR|EVENT_DONE; - log_e(" Gross Timeout Dead st=%d, ed=%d, =%d, max=%d error=%d",tBefore,tAfter,(tAfter-tBefore),ticksTimeOut,i2c->error); + log_e(" Gross Timeout Dead st=0x%x, ed=0x%x, =%d, max=%d error=%d",tBefore,tAfter,(tAfter-tBefore),ticksTimeOut,i2c->error); dumpI2c(i2c); i2cDumpInts(); } @@ -1130,7 +1130,11 @@ if(i2c->intr_handle){ // log_e("released ISR=%d",error); i2c->intr_handle=NULL; } -return I2C_ERROR_OK; +if(i2c->i2c_event){ + vEventGroupDelete(i2c->i2c_event); + i2c->i2c_event = NULL; + } +return i2cFreeQueue(i2c); } /* todo diff --git a/cores/esp32/esp32-hal-i2c.h b/cores/esp32/esp32-hal-i2c.h index 7b83f29f95a..070fc2c74b7 100644 --- a/cores/esp32/esp32-hal-i2c.h +++ b/cores/esp32/esp32-hal-i2c.h @@ -162,7 +162,7 @@ i2c_err_t i2cAttachSDA(i2c_t * i2c, int8_t sda); i2c_err_t i2cDetachSDA(i2c_t * i2c, int8_t sda); //Stickbreakers ISR Support -i2c_err_t i2cProcQueue(i2c_t *i2c, uint32_t *readCount); +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); diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp index fba7b23c8a5..cab2b51ef1d 100644 --- a/libraries/Wire/src/Wire.cpp +++ b/libraries/Wire/src/Wire.cpp @@ -69,6 +69,7 @@ TwoWire::TwoWire(uint8_t bus_num) ,transmitting(0) ,txQueued(0) ,rxQueued(0) + ,_timeOutMillis(50) {} /* slave Mode, no yet Stickbreaker void TwoWire::onRequestService(void){} @@ -124,6 +125,14 @@ void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency) i2cInitFix(i2c); } +void TwoWire::setTimeOut(uint16_t timeOutMillis){ + _timeOutMillis = timeOutMillis; +} + +uint16_t TwoWire::getTimeOut(){ + return _timeOutMillis; +} + void TwoWire::setClock(uint32_t frequency) { i2cSetFrequency(i2c, frequency); @@ -146,7 +155,7 @@ size_t TwoWire::requestFrom(uint8_t address, size_t size, bool sendStop) /*@StickBreaker common handler for processing the queued commands */ i2c_err_t TwoWire::processQueue(uint32_t * readCount){ - last_error=i2cProcQueue(i2c,readCount); + last_error=i2cProcQueue(i2c,readCount,_timeOutMillis); rxIndex = 0; rxLength = rxQueued; rxQueued = 0; @@ -484,6 +493,7 @@ void TwoWire::flush(void) void TwoWire::reset(void) { + i2cReleaseISR(i2c); // remove ISR from Interrupt chain,Delete EventGroup,Free Heap memory i2cReset( i2c ); i2c = NULL; begin( sda, scl ); diff --git a/libraries/Wire/src/Wire.h b/libraries/Wire/src/Wire.h index 1564d3a8079..cfe8d5a2c80 100644 --- a/libraries/Wire/src/Wire.h +++ b/libraries/Wire/src/Wire.h @@ -62,6 +62,7 @@ class TwoWire: public Stream */ i2c_err_t last_error; // @stickBreaker from esp32-hal-i2c.h i2c_err_t processQueue(uint32_t *readCount); + uint16_t _timeOutMillis; public: TwoWire(uint8_t bus_num); @@ -81,6 +82,8 @@ class TwoWire: public Stream char * getErrorText(uint8_t err); void dumpInts(); size_t getClock(); + void setTimeOut(uint16_t timeOutMillis); + uint16_t getTimeOut(); // uint8_t requestFrom(uint8_t, uint8_t); uint8_t requestFrom(uint8_t, uint8_t, uint8_t); From a59e1e515ffc4ece930c3e3e4b17921b975c5423 Mon Sep 17 00:00:00 2001 From: Chuck Todd Date: Sat, 25 Nov 2017 11:45:13 -0700 Subject: [PATCH 21/63] fix merge error Still learning how to commit --- cores/esp32/esp32-hal-i2c.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index 08d8bd04249..15591ca9c18 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -1131,17 +1131,10 @@ if(i2c->intr_handle){ i2c->intr_handle=NULL; } if(i2c->i2c_event){ -<<<<<<< HEAD vEventGroupDelete(i2c->i2c_event); i2c->i2c_event = NULL; } return i2cFreeQueue(i2c); -======= - xEventGroupDelete(i2c->i2c_event); - i2c->i2c_event = NULL; - } -return i2cFreeQueue(i2c); //release dynamic memory ->>>>>>> origin/master } /* todo From fedd9d9fa6723da8fd8f3eb4a25f4ab11d65e890 Mon Sep 17 00:00:00 2001 From: Chuck Todd Date: Sun, 26 Nov 2017 11:48:19 -0700 Subject: [PATCH 22/63] Update ReadMe, minor Optimization My I2C specific README.md to libraries\Wire\docs add Link in main README --- README.md | 137 +++++++++++----------------------- README_OLD.md | 48 ------------ cores/esp32/esp32-hal-i2c.c | 11 +-- libraries/Wire/docs/README.md | 116 ++++++++++++++++++++++++++++ 4 files changed, 161 insertions(+), 151 deletions(-) delete mode 100644 README_OLD.md create mode 100644 libraries/Wire/docs/README.md diff --git a/README.md b/README.md index 589ce4010a4..dc2f9e6e69b 100644 --- a/README.md +++ b/README.md @@ -1,107 +1,56 @@ -# A fork of Espressif/arduino-esp32: -## i2c communications using a ISR +# Arduino core for ESP32 WiFi chip -The existing main fork of arduino-esp32 implement i2c communications using a polled monitoring of the I2C Hardware StateMachine. The implementation of the I2C ReSTART operations leaves the I2C Hardware StateMachine(SM) in an unstable configuration. The I2C protocol arbitrates exclusive ownership of the bus to a single Master Device from a `START` signal to the next `STOP`. A restart operation is just a start signal inserted into this `START` -> `STOP` flow. Schematically like this: - `START` (Read or Write) -> `ReSTART` (Read or Write) -> `STOP`. - - By I2C protocol as [UM10204 I2C-Bus Specification and User Manual](https://Fwww.nxp.com/docs/en/user-guide/UM10204.pdf), A valid I2C transaction must be a `START` at least one Data Byte `STOP`. The Arduino environment is base on the AVR processor series, These Atmel CPU's implementation of the the I2C protocol allows unlimited pauses between protocol elements. The Espressif implement enforces a Strict TimeOut between elements. This has resulted in perceived I2C bus stability issues ([834](https://github.com/espressif/arduino-esp32/issues/834),[811](https://github.com/espressif/arduino-esp32/issues/811),[741](https://github.com/espressif/arduino-esp32/issues/741),[682](https://github.com/espressif/arduino-esp32/issues/682),[659](https://github.com/espressif/arduino-esp32/issues/659) and many more. @lonerzzz created a partial solution [#751](https://github.com/espressif/arduino-esp32/pull/751). - I spend a couple of weeks investigating these problems, and discovered this Hard TimeOut was the basis of all of these problems. Once this Hard TimeOut limit is triggered the SM cannot recover without a complete reinitialization. -The existing Arduino code base is reliant on the AVR's ability to infintely pause a i2c transaction. The standard coding practice of: -```c++ -// set internal address pointer in I2C EEPROM from which to read -Wire.beginTransmission(ID); -Wire.write(highByte(addr)); -Wire.write(lowByte(addr)); -uint8_t err =Wire.endTransmission(false); // don't send a STOP, just Pause I2C operations -if(err==0){ // successfully set internal address pointer - err=Wire.requestFrom(addr,len); - if(err==0){ // read failed - Serial.print("Bad Stuff!! Read Failed\n"); - } - else {// successful read - while(Wire.avaiable()){ - Serial.print((char)Wire.read()); - } - Serial.println(); - } - } -``` -May not function correctly with the ESP32, actually *usually* will not function correctly. The current arduino-esp32 platform is built upon the espressif-idf which is built on FreeRTOS, a multi-process/processor operating system. The Arduino sketch is just one task executing on One of the Two processor cores. The TimeOut is triggered when the FreeRTOS Task Scheduler does a task switch between `Wire.endTransmission(false);` and the i2c activites inside `Wire.requestfrom()`. The maximum TimeOut interval is around 12ms. -My solution is to avoid this TimeOut from every being possible. I have changed how `Wire()` uses the SM. Specifically I have changed how a `ReSTART` operation is implemented. To avoid the TimeOut I have create a i2c queuing system that does not allow an i2c transaction to start unless a `STOP` has been issued. But, alas, this creates some incompatibilities with the pre-exisiting Arduino code base. The changes to the standard Arduino `Wire()` coding are minimal, but, they are necessary: -```c++ -// new, maybe Excessive Error Return codes for @stickbreaker:arduino-esp32 -typedef enum { - I2C_ERROR_OK=0, - I2C_ERROR_DEV, - I2C_ERROR_ACK, - I2C_ERROR_TIMEOUT, - I2C_ERROR_BUS, - I2C_ERROR_BUSY, - I2C_ERROR_MEMORY, - I2C_ERROR_CONTINUE, - I2C_ERROR_NO_BEGIN -} i2c_err_t; +[![Build Status](https://travis-ci.org/espressif/arduino-esp32.svg?branch=master)](https://travis-ci.org/espressif/arduino-esp32) -// set internal address pointer in I2C EEPROM from which to read -Wire.beginTransmission(ID); -Wire.write(highByte(addr)); -Wire.write(lowByte(addr)); -uint8_t err =Wire.endTransmission(false); // don't send a STOP, just Pause I2C operations +### Need help or have a question? Join the chat at [![https://gitter.im/espressif/arduino-esp32](https://badges.gitter.im/espressif/arduino-esp32.svg)](https://gitter.im/espressif/arduino-esp32?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -//err will be I2C_ERROR_CONTINUE (7), an indication that the preceding -//transaction has been Queued, NOT EXECUTED +## Contents +- [Development Status](#development-status) +- [Installation Instructions](#installation-instructions) +- [Decoding Exceptions](#decoding-exceptions) +- [Issue/Bug report template](#issuebug-report-template) +- [ESP32Dev Board PINMAP](#esp32dev-board-pinmap) -if(err==7){ // Prior Operation has been queued, it is NOT guaranteed that it will -// successfully occur! - err=Wire.requestFrom(addr,len); - if(Wire.lastError()!=0){ // complete/partial read failure - Serial.printf("Bad Stuff!!\nRead of (%d) bytes read %d bytes\nFailed lastError=%d," - " text=%s\n",len,err,Wire.lastError(),Wire.getErrorText(Wire.lastError())); - } - // some of the read may have executed - while(Wire.avaiable()){ - Serial.print((char)Wire.read()); - } - Serial.println(); - } -``` +## Development Status +- Most of the framework is implemented. +- Differences: + - `Wire()` for deeper explaination [README.md](libraries/Wire/docs/README.md) + - 64k-1 data transfers + - Special handling for sendStop=false +- Missing: + - `analogWrite()` While analogWrite is on it's way, there are a few other options that you can use: + - 16 channels [LEDC](cores/esp32/esp32-hal-ledc.h) which is PWM + - 8 channels [SigmaDelta](cores/esp32/esp32-hal-sigmadelta.h) which uses SigmaDelta modulation + - 2 channels [DAC](cores/esp32/esp32-hal-dac.h) which gives real analog output + - `Wire.onReceive()` + - `Wire.onRequest()` -Additionally I have expanded the ability of `Wire()` to handle larger Reads and Writes: up to 64k-1, I have tested READs of these sizes, but the largest single WRITE i have tested is 128bytes. I can send more data to a 24LC512, but it will only store the last 128bytes. +## Installation Instructions -I have create a few new methods for Wire: -```c++ - uint8_t writeTransaction(uint8_t address, uint8_t* buff, size_t size, bool sendStop);// big block handling - size_t requestFrom(uint8_t address, uint8_t* buf, size_t size, bool sendStop); - size_t transact(size_t readLen); // replacement for endTransmission(false),requestFrom(ID,readLen,true); - size_t transact(uint8_t* readBuff, size_t readLen);// bigger Block read - i2c_err_t lastError(); // Expose complete error - void dumpInts(); // diagnostic dump for the last 64 different i2c Interrupts - size_t getClock(); // current i2c Clock rate -``` +- Using Arduino IDE + + [Instructions for Windows](docs/arduino-ide/windows.md) + + [Instructions for Mac](docs/arduino-ide/mac.md) + + [Instructions for Debian/Ubuntu Linux](docs/arduino-ide/debian_ubuntu.md) + + [Instructions for Fedora](docs/arduino-ide/fedora.md) + + [Instructions for openSUSE](docs/arduino-ide/opensuse.md) +- [Using PlatformIO](docs/platformio.md) +- [Building with make](docs/make.md) +- [Using as ESP-IDF component](docs/esp-idf_component.md) -`transact()` coding is: -```c++ -// set internal address pointer in I2C EEPROM from which to read -Wire.beginTransmission(ID); -Wire.write(highByte(addr)); -Wire.write(lowByte(addr)); +#### Decoding exceptions -uint8_t err=Wire.transact(len); // transact() does both Wire.endTransmission(false); and Wire.requestFrom(ID,len,true); -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 -while(Wire.avaiable()){ - Serial.print((char)Wire.read()); - } -Serial.println(); +You can use [EspExceptionDecoder](https://github.com/me-no-dev/EspExceptionDecoder) to get meaningful call trace. -``` +#### Issue/Bug report template +Before reporting an issue, make sure you've searched for similar one that was already created. Also make sure to go through all the issues labelled as [for reference](https://github.com/espressif/arduino-esp32/issues?utf8=%E2%9C%93&q=is%3Aissue%20label%3A%22for%20reference%22%20). -This **APLHA** release should be compiled with ESP32 Dev Module as its target, and -Set the "core debug level" to 'error' +Finally, if you're sure no one else had the issue, follow the [ISSUE_TEMPLATE](docs/ISSUE_TEMPLATE.md) while reporting any issue. -There is MINIMAL to NO ERROR detection, BUS, BUSY. because I have not encounter any of them! +## ESP32Dev Board PINMAP -Chuck. +![Pin Functions](docs/esp32_pinmap.png) + +## Hint + +Sometimes to program ESP32 via serial you must keep GPIO0 LOW during the programming process diff --git a/README_OLD.md b/README_OLD.md deleted file mode 100644 index 4c0c046e5a8..00000000000 --- a/README_OLD.md +++ /dev/null @@ -1,48 +0,0 @@ -# Arduino core for ESP32 WiFi chip - -[![Build Status](https://travis-ci.org/espressif/arduino-esp32.svg?branch=master)](https://travis-ci.org/espressif/arduino-esp32) - -### Need help or have a question? Join the chat at [![https://gitter.im/espressif/arduino-esp32](https://badges.gitter.im/espressif/arduino-esp32.svg)](https://gitter.im/espressif/arduino-esp32?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - -## Contents -- [Development Status](#development-status) -- [Installation Instructions](#installation-instructions) -- [Decoding Exceptions](#decoding-exceptions) -- [Issue/Bug report template](#issuebug-report-template) -- [ESP32Dev Board PINMAP](#esp32dev-board-pinmap) - -## Development Status -Most of the framework is implemented. Most noticable is the missing analogWrite. While analogWrite is on it's way, there are a few other options that you can use: -- 16 channels [LEDC](cores/esp32/esp32-hal-ledc.h) which is PWM -- 8 channels [SigmaDelta](cores/esp32/esp32-hal-sigmadelta.h) which uses SigmaDelta modulation -- 2 channels [DAC](cores/esp32/esp32-hal-dac.h) which gives real analog output - -## Installation Instructions - -- Using Arduino IDE - + [Instructions for Windows](docs/arduino-ide/windows.md) - + [Instructions for Mac](docs/arduino-ide/mac.md) - + [Instructions for Debian/Ubuntu Linux](docs/arduino-ide/debian_ubuntu.md) - + [Instructions for Fedora](docs/arduino-ide/fedora.md) - + [Instructions for openSUSE](docs/arduino-ide/opensuse.md) -- [Using PlatformIO](docs/platformio.md) -- [Building with make](docs/make.md) -- [Using as ESP-IDF component](docs/esp-idf_component.md) - -#### Decoding exceptions - -You can use [EspExceptionDecoder](https://github.com/me-no-dev/EspExceptionDecoder) to get meaningful call trace. - -#### Issue/Bug report template -Before reporting an issue, make sure you've searched for similar one that was already created. Also make sure to go through all the issues labelled as [for reference](https://github.com/espressif/arduino-esp32/issues?utf8=%E2%9C%93&q=is%3Aissue%20label%3A%22for%20reference%22%20). - -Finally, if you're sure no one else had the issue, follow the [ISSUE_TEMPLATE](docs/ISSUE_TEMPLATE.md) while reporting any issue. - - -## ESP32Dev Board PINMAP - -![Pin Functions](docs/esp32_pinmap.png) - -## Hint - -Sometimes to program ESP32 via serial you must keep GPIO0 LOW during the programming process diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index 15591ca9c18..d31ccf2431e 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -365,13 +365,6 @@ void i2cReset(i2c_t* i2c){ I2C_MUTEX_UNLOCK(); } -//** 11/2017 Stickbreaker attempt at ISR for I2C hardware -// liberally stolen from ESP_IDF /drivers/i2c.c -esp_err_t i2c_isr_free(intr_handle_t handle){ - -return esp_intr_free(handle); -} - /* Stickbreaker ISR mode debug support */ #define INTBUFFMAX 64 @@ -1126,8 +1119,8 @@ return reason; i2c_err_t i2cReleaseISR(i2c_t * i2c){ if(i2c->intr_handle){ - esp_err_t error =i2c_isr_free(i2c->intr_handle); -// log_e("released ISR=%d",error); + esp_err_t error =esp_intr_free(i2c->intr_handle); + // log_e("released ISR=%d",error); i2c->intr_handle=NULL; } if(i2c->i2c_event){ diff --git a/libraries/Wire/docs/README.md b/libraries/Wire/docs/README.md new file mode 100644 index 00000000000..dd16b82f1c0 --- /dev/null +++ b/libraries/Wire/docs/README.md @@ -0,0 +1,116 @@ +# i2c communications on ESP32 + + By Official I2C protocol as [UM10204 I2C-Bus Specification and User Manual](https://Fwww.nxp.com/docs/en/user-guide/UM10204.pdf), A valid I2C transaction must be a `START`, at least one Data Byte, and a `STOP`. The Arduino environment is base on the AVR processor series, These Atmel CPU's implemention the I2C protocol allows unlimited pauses between protocol elements. The Espressif ESP32 implement enforces a Strict TimeOut between elements. This has resulted in perceived I2C bus stability issues ([834](https://github.com/espressif/arduino-esp32/issues/834),[811](https://github.com/espressif/arduino-esp32/issues/811),[741](https://github.com/espressif/arduino-esp32/issues/741),[682](https://github.com/espressif/arduino-esp32/issues/682),[659](https://github.com/espressif/arduino-esp32/issues/659) and many more. @lonerzzz created a partial solution [#751](https://github.com/espressif/arduino-esp32/pull/751). + + The I2C protocol arbitrates exclusive ownership of the bus to a single Master Device from a `START` signal to the next `STOP`. A `ReSTART` operation is just a `START` signal inserted into this `START` -> `STOP` flow. Schematically like this: + + `START` (Read or Write) -> `ReSTART` (Read or Write) -> `STOP`. + + The existing Arduino code base is reliant on the AVR's ability to infintely pause a i2c transaction. The standard coding practice of: +```c++ +// set internal address pointer in I2C EEPROM from which to read +Wire.beginTransmission(ID); +Wire.write(highByte(addr)); +Wire.write(lowByte(addr)); +uint8_t err =Wire.endTransmission(false); // don't send a STOP, just Pause I2C operations +if(err==0){ // successfully set internal address pointer + err=Wire.requestFrom(addr,len); + if(err==0){ // read failed + Serial.print("Bad Stuff!! Read Failed\n"); + } + else {// successful read + while(Wire.avaiable()){ + Serial.print((char)Wire.read()); + } + Serial.println(); + } + } +``` +May not function correctly with the ESP32, actually **usually** will not function correctly. The current arduino-esp32 platform is built upon the [espressif-idf](https://github.com/espressif/esp-idf) which is built on FreeRTOS, a multi-process/processor operating system. The Arduino sketch is just one task executing on One of the Two processor cores. The TimeOut is triggered when the FreeRTOS Task Scheduler does a task switch between `Wire.endTransmission(false);` and the i2c activites inside `Wire.requestfrom()`. The maximum TimeOut interval is around 12ms. + +This rewrite of `Wire()` is designed to avoid this TimeOut from ever being possible. To avoid the TimeOut this library uses a queuing system that does not allow an i2c transaction to start unless a `STOP` has been issued. But, alas, this creates some incompatibilities with the pre-exisiting Arduino code base. The changes to the standard Arduino `Wire()` coding are minimal, but, they are necessary: +```c++ +// new, maybe Excessive Error Return codes for @stickbreaker:arduino-esp32 +typedef enum { + I2C_ERROR_OK=0, + I2C_ERROR_DEV, + I2C_ERROR_ACK, + I2C_ERROR_TIMEOUT, + I2C_ERROR_BUS, + I2C_ERROR_BUSY, + I2C_ERROR_MEMORY, + I2C_ERROR_CONTINUE, + I2C_ERROR_NO_BEGIN +} i2c_err_t; + +// set internal address pointer in I2C EEPROM from which to read +Wire.beginTransmission(ID); +Wire.write(highByte(addr)); +Wire.write(lowByte(addr)); +uint8_t err =Wire.endTransmission(false); +// don't send a STOP, just Pause I2C operations + +//err will be I2C_ERROR_CONTINUE (7), an indication that the preceding +//transaction has been Queued, NOT EXECUTED + +if(err == 7){ // Prior Operation has been queued +// it will be executed when the next STOP is encountered. +// if sendStop had equaled true, then a successful endTransmission() would have +// returned 0. So, if this if() was if(err==0), the Queue operation would be +// considered an error. This is the primary Difference. + + err=Wire.requestFrom(addr,len); + if(Wire.lastError()!=0){ // complete/partial read failure + Serial.printf("Bad Stuff!!\nRead of (%d) bytes read %d bytes\nFailed" + " lastError=%d, text=%s\n", len, err, Wire.lastError(), + Wire.getErrorText(Wire.lastError())); + } + // some of the read may have executed + while(Wire.avaiable()){ + Serial.print((char)Wire.read()); + } + Serial.println(); + } +``` + +Additionally this implementation of `Wire()` includes methods to handle local buffer data blocks. These local buffer can be up to 64k-1 bytes in length. + +### New Methods: +```c++ + i2c_err_t writeTransaction(uint8_t address, uint8_t* buff, size_t size, bool sendStop);// big block handling + size_t requestFrom(uint8_t address, uint8_t* buf, size_t size, bool sendStop); + size_t transact(size_t readLen); // replacement for endTransmission(false),requestFrom(ID,readLen,true); + size_t transact(uint8_t* readBuff, size_t readLen);// bigger Block read + i2c_err_t lastError(); // Expose complete error + void dumpInts(); // diagnostic dump for the last 64 different i2c Interrupts + size_t getClock(); // current i2c Clock rate + void setTimeOut(uint16_t timeOutMillis); // allows users to configure Gross Timeout + uint16_t getTimeOut(); +``` + +`transact()` coding is: +```c++ +// set internal address pointer in I2C EEPROM from which to read +Wire.beginTransmission(ID); +Wire.write(highByte(addr)); +Wire.write(lowByte(addr)); + +uint8_t err=Wire.transact(len); // transact() does both Wire.endTransmission(false); and Wire.requestFrom(ID,len,true); +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 +while(Wire.avaiable()){ + Serial.print((char)Wire.read()); + } +Serial.println(); + +``` + +This **APLHA** release should be compiled with ESP32 Dev Module as its target, and +Set the "core debug level" to 'error' + +There is MINIMAL to NO ERROR detection, BUS, BUSY. because I have not encounter any of them! + + +Chuck. From cf2013074eba44cf554a17ce4311d34a56321179 Mon Sep 17 00:00:00 2001 From: Chuck Todd Date: Mon, 27 Nov 2017 15:41:33 -0700 Subject: [PATCH 23/63] cleanup Add dump control to Wire, --- README.md | 2 +- cores/esp32/esp32-hal-i2c.c | 86 +++++++++++++----- cores/esp32/esp32-hal-i2c.h | 1 + cores/esp32/esp32-hal-log.h | 6 ++ libraries/Wire/src/Wire.cpp | 168 ++++++++++++------------------------ libraries/Wire/src/Wire.h | 31 ++++--- 6 files changed, 149 insertions(+), 145 deletions(-) diff --git a/README.md b/README.md index dc2f9e6e69b..d949dd22c69 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ ## Development Status - Most of the framework is implemented. - Differences: - - `Wire()` for deeper explaination [README.md](libraries/Wire/docs/README.md) + - `Wire()` for deeper explanation [README.md](libraries/Wire/docs/README.md) - 64k-1 data transfers - Special handling for sendStop=false - Missing: diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index d31ccf2431e..cc8d54604d0 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -337,6 +337,7 @@ void i2cInitFix(i2c_t * i2c){ return; } I2C_MUTEX_LOCK(); + i2c->dev->ctr.trans_start = 0; i2cResetFiFo(i2c); i2c->dev->int_clr.val = 0xFFFFFFFF; i2cSetCmd(i2c, 0, I2C_CMD_RSTART, 0, false, false, false); @@ -347,7 +348,6 @@ void i2cInitFix(i2c_t * i2c){ { log_e("Busy at initialization!"); } - i2c->dev->ctr.trans_start = 1; uint16_t count = 50000; while ((!i2c->dev->command[2].done) && (--count > 0)); I2C_MUTEX_UNLOCK(); @@ -365,12 +365,20 @@ void i2cReset(i2c_t* i2c){ I2C_MUTEX_UNLOCK(); } +//** 11/2017 Stickbreaker attempt at ISR for I2C hardware +// liberally stolen from ESP_IDF /drivers/i2c.c +esp_err_t i2c_isr_free(intr_handle_t handle){ + +return esp_intr_free(handle); +} + /* Stickbreaker ISR mode debug support */ +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR #define INTBUFFMAX 64 static uint32_t intBuff[INTBUFFMAX][3]; static uint32_t intPos=0; - +#endif /* Stickbreaker ISR mode support */ static void IRAM_ATTR fillCmdQueue(i2c_t * i2c, bool INTS){ @@ -546,7 +554,40 @@ if(INTS){ // don't want to prematurely enable fifo ints until ISR is ready to ha /* Stickbreaker ISR mode debug support */ -static void IRAM_ATTR dumpI2c(i2c_t * i2c){ +void i2cDumpDqData(i2c_t * i2c){ +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR +uint16_t a=0; +char buff[140]; +I2C_DATA_QUEUE_t *tdq; +while(aqueueCount){ + tdq=&i2c->dq[a]; + log_e("[%d] %x %c %s buf@=%p, len=%d, pos=%d, eventH=%p bits=%x",a,tdq->ctrl.addr,(tdq->ctrl.mode)?'R':'W',(tdq->ctrl.stop)?"STOP":"",tdq->data,tdq->length,tdq->position,tdq->queueEvent,(tdq->queueEvent)?xEventGroupGetBits(tdq->queueEvent):0); + uint16_t offset = 0; + while(offsetlength){ + memset(buff,' ',140); + buff[139]='\0'; + uint16_t i = 0,j; + j=sprintf(buff,"0x%04x: ",offset); + while((i<32)&&(offset < tdq->length)){ + char ch = tdq->data[offset]; + sprintf((char*)&buff[(i*3)+41],"%02x ",ch); + if((ch<32)||(ch>126)) ch='.'; + j+=sprintf((char*)&buff[j],"%c",ch); + buff[j]=' '; + i++; + offset++; + } + log_e("%s",buff); + } + a++; + } +#else +log_n("Enable Core Debug Level \"Error\""); +#endif +} + + +void i2cDumpI2c(i2c_t * i2c){ log_e("i2c=%p",i2c); log_e("dev=%p",i2c->dev); log_e("lock=%p",i2c->lock); @@ -560,13 +601,7 @@ log_e("dq=%p",i2c->dq); log_e("queueCount=%d",i2c->queueCount); log_e("queuePos=%d",i2c->queuePos); log_e("byteCnt=%d",i2c->byteCnt); -uint16_t a=0; -I2C_DATA_QUEUE_t *tdq; -while(aqueueCount){ - tdq=&i2c->dq[a]; - log_e("[%d] %x %c %s buf@=%p, len=%d, pos=%d, eventH=%p bits=%x",a,tdq->ctrl.addr,(tdq->ctrl.mode)?'R':'W',(tdq->ctrl.stop)?"STOP":"",tdq->data,tdq->length,tdq->position,tdq->queueEvent,(tdq->queueEvent)?xEventGroupGetBits(tdq->queueEvent):0); - a++; - } +if(i2c->dq) i2cDumpDqData(i2c); } /* Stickbreaker ISR mode debug support @@ -657,9 +692,12 @@ while((a < i2c->queueCount)&&!(full || readEncountered)){ if(full) readEncountered =false; //tx possibly needs more +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR + // update debug buffer tx counts cnt += intBuff[intPos][1]>>16; intBuff[intPos][1] = (intBuff[intPos][1]&0xFFFF)|(cnt<<16); +#endif if(!(full||readEncountered)) a++; // check next buffer for tx } @@ -698,9 +736,11 @@ if(tdq->ctrl.mode==1) { // read moveCnt = (tdq->length - tdq->position); } } +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR // update Debug rxCount cnt += (intBuff[intPos][1])&&0xffFF; intBuff[intPos][1] = (intBuff[intPos][1]&0xFFFF0000)|cnt; +#endif } else { log_e("RxEmpty(%d) call on TxBuffer? dq=%d",moveCnt,i2c->queuePos); @@ -766,11 +806,12 @@ 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; - dumpI2c(p_i2c); - i2cDumpInts(); +// 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; } @@ -782,7 +823,7 @@ while (activeInt != 0) { // Ordering of 'if(activeInt)' statements is important, } intBuff[intPos][2] = xTaskGetTickCountFromISR(); // when IRQ fired - +#endif uint32_t oldInt =activeInt; if (activeInt & I2C_TRANS_START_INT_ST_M) { @@ -891,12 +932,16 @@ while (activeInt != 0) { // Ordering of 'if(activeInt)' statements is important, } void i2cDumpInts(){ +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR uint32_t b; log_e("row count INTR TX RX"); 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]); } +#else +log_n("enable Core Debug Level \"Error\""); +#endif } i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis){ @@ -920,11 +965,10 @@ I2C_MUTEX_LOCK(); */ i2c->stage = I2C_DONE; // until ready -for(intPos=0;intPos= ARDUHAL_LOG_LEVEL_ERROR +memset(intBuff,0,sizeof(intBuff)); intPos=0; +#endif if(!i2c->i2c_event){ i2c->i2c_event = xEventGroupCreate(); @@ -1046,7 +1090,7 @@ 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 - dumpI2c(i2c); + i2cDumpI2c(i2c); i2cDumpInts(); } @@ -1081,7 +1125,7 @@ else { // GROSS timeout, shutdown ISR , report Timeout reason = I2C_ERROR_TIMEOUT; eBits = eBits | EVENT_ERROR_TIMEOUT|EVENT_ERROR|EVENT_DONE; log_e(" Gross Timeout Dead st=0x%x, ed=0x%x, =%d, max=%d error=%d",tBefore,tAfter,(tAfter-tBefore),ticksTimeOut,i2c->error); - dumpI2c(i2c); + i2cDumpI2c(i2c); i2cDumpInts(); } @@ -1119,8 +1163,8 @@ return reason; i2c_err_t i2cReleaseISR(i2c_t * i2c){ if(i2c->intr_handle){ - esp_err_t error =esp_intr_free(i2c->intr_handle); - // log_e("released ISR=%d",error); + esp_err_t error =i2c_isr_free(i2c->intr_handle); +// log_e("released ISR=%d",error); i2c->intr_handle=NULL; } if(i2c->i2c_event){ diff --git a/cores/esp32/esp32-hal-i2c.h b/cores/esp32/esp32-hal-i2c.h index 070fc2c74b7..ce120c02906 100644 --- a/cores/esp32/esp32-hal-i2c.h +++ b/cores/esp32/esp32-hal-i2c.h @@ -169,6 +169,7 @@ i2c_err_t i2cFreeQueue(i2c_t *i2c); i2c_err_t i2cReleaseISR(i2c_t *i2c); //stickbreaker debug support void i2cDumpInts(); +void i2cDumpI2c(i2c_t *i2c); #ifdef __cplusplus } diff --git a/cores/esp32/esp32-hal-log.h b/cores/esp32/esp32-hal-log.h index 015cd67aa21..1509557a7ec 100644 --- a/cores/esp32/esp32-hal-log.h +++ b/cores/esp32/esp32-hal-log.h @@ -107,6 +107,12 @@ int log_printf(const char *fmt, ...); #define log_e(format, ...) #endif +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_NONE +#define log_n(format, ...) log_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__) +#else +#define log_n(format, ...) +#endif + #ifdef CONFIG_ARDUHAL_ESP_LOG #include "esp_log.h" diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp index cab2b51ef1d..109d46c5a78 100644 --- a/libraries/Wire/src/Wire.cpp +++ b/libraries/Wire/src/Wire.cpp @@ -32,30 +32,6 @@ extern "C" { #include "Wire.h" #include "Arduino.h" -/* Declarations to support Slave Mode -// #include "esp_attr.h" -//#include "soc/i2c_reg.h" -// #include "soc/i2c_struct.h" - -user_onRequest TwoWire::uReq[2]; -user_onReceive TwoWire::uRcv[2]; - -#define DR_REG_I2C_EXT_BASE_FIXED 0x60013000 -#define DR_REG_I2C1_EXT_BASE_FIXED 0x60027000 - -void IRAM_ATTR slave_isr_handler(void* arg){ - // ... -uint32_t num = (uint32_t)arg; -i2c_dev_t * dev; -if(num==0) dev=(volatile i2c_dev_t*)DR_REG_I2C_EXT_BASE_FIXED; -else dev=(volatile i2c_dev_t*)DR_REG_I2C1_EXT_BASE_FIXED; - -uint32_t stat = dev->int_status.val; - -} -*/ - - TwoWire::TwoWire(uint8_t bus_num) :num(bus_num & 1) ,sda(-1) @@ -70,11 +46,10 @@ TwoWire::TwoWire(uint8_t bus_num) ,txQueued(0) ,rxQueued(0) ,_timeOutMillis(50) + ,last_error(I2C_ERROR_OK) + ,_dump(false) {} -/* slave Mode, no yet Stickbreaker -void TwoWire::onRequestService(void){} -void TwoWire::onReceiveService(uint8_t*buf, int count){} -*/ + void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency) { if(sdaPin < 0) { @@ -99,21 +74,17 @@ void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency) return; } } -/* Slavemode, not yet - uReq[num] =NULL; - uRcv[num] =NULL; - */ i2cSetFrequency(i2c, frequency); - if(sda >= 0 && sda != sdaPin) { + if(sda >= 0 && sda != sdaPin ) { i2cDetachSDA(i2c, sda); } - if(scl >= 0 && scl != sclPin) { + if(scl >= 0 && scl != sclPin ) { i2cDetachSCL(i2c, scl); } - + sda = sdaPin; scl = sclPin; @@ -137,21 +108,6 @@ void TwoWire::setClock(uint32_t frequency) { i2cSetFrequency(i2c, frequency); } -/* Original requestFrom() 11/2017 before ISR -size_t TwoWire::requestFrom(uint8_t address, size_t size, bool sendStop) -{ - i2cReleaseISR(i2c); - - if(size > I2C_BUFFER_LENGTH) { - size = I2C_BUFFER_LENGTH; - } - last_error = i2cRead(i2c, address, false, rxBuffer, size, sendStop); - size_t read = (last_error == 0)?size:0; - rxIndex = 0; - rxLength = read; - return read; -} -*/ /*@StickBreaker common handler for processing the queued commands */ i2c_err_t TwoWire::processQueue(uint32_t * readCount){ @@ -160,30 +116,17 @@ i2c_err_t TwoWire::processQueue(uint32_t * readCount){ rxLength = rxQueued; rxQueued = 0; txQueued = 0; // the SendStop=true will restart all Queueing -// *readCount = i2cQueueReadCount(i2c); // do I want to exclude all Local buffers from this? -// currently 17/NOV/2017 this count is of all bytes read from I2C, for this queue of commands. - - //what about mix local buffers and Wire Buffers? - //Handled it by verifying location, only contiguous sections of - //rxBuffer are available for Wire.read(). Any local buffer requestfrom(id,*char..) is - // completed already, only the ones using Wire's buffers need to be considered -/* rxQueued should handle this - uint8_t *savePtr; - uint16_t len; - uint8_t idx=0; - while(I2C_ERROR_OK==i2cGetReadQueue(i2c,&savePtr,&len,&idx)){ - if(savePtr==(rxBuffer+rxLength)){ - rxLength = rxLength + len; - } + if(_dump){ + i2cDumpI2c(i2c); + i2cDumpInts(); } -*/ i2cFreeQueue(i2c); return last_error; } /* @stickBreaker 11/2017 fix for ReSTART timeout, ISR */ -size_t TwoWire::requestFrom(uint8_t address, size_t size, bool sendStop){ +uint8_t TwoWire::requestFrom(uint16_t address, uint8_t size, bool sendStop){ //use internal Wire rxBuffer, multiple requestFrom()'s may be pending, try to share rxBuffer uint16_t cnt = rxQueued; // currently queued reads, next available position in rxBuffer @@ -203,7 +146,7 @@ size_t TwoWire::requestFrom(uint8_t address, size_t size, bool sendStop){ return requestFrom(address, &rxBuffer[cnt],size,sendStop); } -size_t TwoWire::requestFrom(uint8_t address, uint8_t * readBuff, size_t size, bool sendStop){ +uint16_t TwoWire::requestFrom(uint16_t address, uint8_t * readBuff, uint16_t size, bool sendStop){ uint32_t cnt=0; last_error =i2cAddQueueRead(i2c,address,readBuff,size,sendStop,NULL); if(last_error==I2C_ERROR_OK){ // successfully queued the read @@ -223,7 +166,7 @@ size_t TwoWire::requestFrom(uint8_t address, uint8_t * readBuff, size_t size, bo /* stickBreaker Nov 2017 ISR, and bigblock 64k-1 */ -uint8_t TwoWire::writeTransaction(uint8_t address, uint8_t *buff, size_t size, bool sendStop){ +i2c_err_t TwoWire::writeTransmission(uint16_t address, uint8_t *buff, uint16_t size, bool sendStop){ // will destroy any partially created beginTransaction() last_error=i2cAddQueueWrite(i2c,address,buff,size,sendStop,NULL); @@ -240,7 +183,23 @@ if(last_error==I2C_ERROR_OK){ //queued txIndex=0; txLength=0; transmitting = 0; -return static_cast(last_error); +return last_error; +} + +i2c_err_t TwoWire::readTransmission(uint16_t address, uint8_t *buff, uint16_t size, bool sendStop){ + +last_error=i2cAddQueueRead(i2c,address,buff,size,sendStop,NULL); + +if(last_error==I2C_ERROR_OK){ //queued + if(sendStop){ //now actually process the queued commands, including READs + uint32_t dummy; + last_error=processQueue(&dummy); + } + else { // stop not received, so wait for I2C stop, + last_error=I2C_ERROR_CONTINUE; + } + } +return last_error; } /*stickbreaker Dump i2c Interrupt buffer, i2c isr Debugging @@ -255,36 +214,16 @@ size_t TwoWire::getClock(){ return i2cGetFrequency(i2c); } -/*stickbreaker using the i2c hardware without an ISR testing - -size_t TwoWire::polledRequestFrom(uint8_t address, uint8_t* buf, size_t size, bool sendStop){ - - i2cReleaseISR(i2c); - - rxIndex = 0; - rxLength = 0; - if(size==0){ - return 0; - } - last_error =pollI2cRead(i2c, address, false, buf, size, sendStop); - if(last_error!=0){ - log_e("err=%d",last_error); - } - size_t read = (last_error==0)?size:0; - return read; -} -*/ - /*stickbreaker simple ReSTART handling using internal Wire data buffers */ -size_t TwoWire::transact(size_t readLen){ // Assumes Wire.beginTransaction(),Wire.write() +uint8_t TwoWire::transact(uint8_t readLen){ // Assumes Wire.beginTransaction(),Wire.write() // this command replaces Wire.endTransmission(false) and Wire.requestFrom(readLen,true); if(transmitting){ last_error = static_cast(endTransmission(false)); } if(last_error==I2C_ERROR_CONTINUE){ // must have queued the Write - size_t cnt = requestFrom(txAddress,readLen,true); + uint8_t cnt = requestFrom(txAddress,readLen,true); return cnt; } else { @@ -295,7 +234,7 @@ else { /*stickbreaker isr ReSTART with external read Buffer */ -size_t TwoWire::transact(uint8_t * readBuff, size_t readLen){ // Assumes Wire.beginTransaction(),Wire.write() +uint16_t TwoWire::transact(uint8_t * readBuff, uint16_t readLen){ // Assumes Wire.beginTransaction(),Wire.write() // this command replaces Wire.endTransmission(false) and Wire.requestFrom(readLen,true); if(transmitting){ last_error = static_cast(endTransmission(false)); @@ -313,10 +252,11 @@ else { /*stickbreaker isr */ -uint8_t TwoWire::endTransmission(uint8_t sendStop){ // Assumes Wire.beginTransaction(), Wire.write() +uint8_t TwoWire::endTransmission(bool sendStop){ // Assumes Wire.beginTransaction(), Wire.write() // this command replaces Wire.endTransmission(true) if(transmitting==1){ +// log_e("txQueued=%d txLength=%d stop %d",txQueued,txLength,sendStop); last_error =i2cAddQueueWrite(i2c,txAddress,&txBuffer[txQueued],txLength-txQueued,sendStop,NULL); //queue tx element if(last_error == I2C_ERROR_OK){ @@ -376,53 +316,52 @@ if(!found) return NULL; else return message; } -/* Origional 10Nov17 - -uint8_t TwoWire::oldEndTransmission(uint8_t sendStop) +uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop) { - //disable ISR - i2cReleaseISR(i2c); - int8_t ret = i2cWrite(i2c, txAddress, false, txBuffer, txLength, sendStop); - txIndex = 0; - txLength = 0; - transmitting = 0; - return ret; + return requestFrom(static_cast(address), static_cast(quantity), static_cast(sendStop)); } -*/ -uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop) +uint8_t TwoWire::requestFrom(uint16_t address, uint8_t quantity, uint8_t sendStop) { return requestFrom(address, static_cast(quantity), static_cast(sendStop)); } uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity) +{ + return requestFrom(static_cast(address), static_cast(quantity), true); +} + +uint8_t TwoWire::requestFrom(uint16_t address, uint8_t quantity) { return requestFrom(address, static_cast(quantity), true); } uint8_t TwoWire::requestFrom(int address, int quantity) { - return requestFrom(static_cast(address), static_cast(quantity), true); + return requestFrom(static_cast(address), static_cast(quantity), true); } uint8_t TwoWire::requestFrom(int address, int quantity, int sendStop) { - return static_cast(requestFrom(static_cast(address), static_cast(quantity), static_cast(sendStop))); + return static_cast(requestFrom(static_cast(address), static_cast(quantity), static_cast(sendStop))); } -void TwoWire::beginTransmission(uint8_t address) +void TwoWire::beginTransmission(uint16_t address) { transmitting = 1; txAddress = address; txIndex = txQueued; // allow multiple beginTransmission(),write(),endTransmission(false) until endTransmission(true) txLength = txQueued; -// if(txLength!=0) log_e("multiple beginTransmissions starting at %d",txQueued); - } void TwoWire::beginTransmission(int address) { - beginTransmission((uint8_t)address); + beginTransmission(static_cast(address)); +} + +void TwoWire::beginTransmission(uint8_t address) +{ + beginTransmission(static_cast(address)); } uint8_t TwoWire::endTransmission(void) @@ -430,6 +369,11 @@ uint8_t TwoWire::endTransmission(void) return endTransmission(true); } +uint8_t TwoWire::endTransmission(uint8_t sendStop) +{ + return endTransmission(static_cast(sendStop)); +} + size_t TwoWire::write(uint8_t data) { if(transmitting) { diff --git a/libraries/Wire/src/Wire.h b/libraries/Wire/src/Wire.h index cfe8d5a2c80..cfec58fa386 100644 --- a/libraries/Wire/src/Wire.h +++ b/libraries/Wire/src/Wire.h @@ -50,7 +50,7 @@ class TwoWire: public Stream uint8_t txBuffer[I2C_BUFFER_LENGTH]; uint16_t txIndex; uint16_t txLength; - uint8_t txAddress; + uint16_t txAddress; uint16_t txQueued; //@stickbreaker uint8_t transmitting; @@ -63,33 +63,42 @@ class TwoWire: public Stream i2c_err_t last_error; // @stickBreaker from esp32-hal-i2c.h i2c_err_t processQueue(uint32_t *readCount); uint16_t _timeOutMillis; + bool _dump; public: TwoWire(uint8_t bus_num); void begin(int sda=-1, int scl=-1, uint32_t frequency=100000); void setClock(uint32_t); - void beginTransmission(uint8_t); - void beginTransmission(int); - uint8_t endTransmission(void); - uint8_t endTransmission(uint8_t); - size_t requestFrom(uint8_t address, size_t size, bool sendStop); + void beginTransmission(uint16_t); + uint8_t endTransmission(bool); + uint8_t requestFrom(uint16_t address, uint8_t size, bool sendStop); //@stickBreaker for big blocks and ISR model - uint8_t writeTransaction(uint8_t address, uint8_t* buff, size_t size, bool sendStop); - size_t requestFrom(uint8_t address, uint8_t* buf, size_t size, bool sendStop); - size_t transact(size_t readLen); - size_t transact(uint8_t* readBuff, size_t readLen); + i2c_err_t writeTransmission(uint16_t address, uint8_t* buff, uint16_t size, bool sendStop=true); + i2c_err_t readTransmission(uint16_t address, uint8_t* buff, uint16_t size, bool sendStop=true); + uint16_t requestFrom(uint16_t address, uint8_t* buf, uint16_t size, bool sendStop); + uint8_t transact(uint8_t readLen); + uint16_t transact(uint8_t* readBuff, uint16_t readLen); uint8_t lastError(); char * getErrorText(uint8_t err); + void dumpOn(){_dump=true;} + void dumpOff(){_dump=false;} void dumpInts(); + void dumpI2C(){i2cDumpI2c(i2c);} size_t getClock(); void setTimeOut(uint16_t timeOutMillis); uint16_t getTimeOut(); // + void beginTransmission(uint8_t); + void beginTransmission(int); + uint8_t endTransmission(void); + uint8_t endTransmission(uint8_t); uint8_t requestFrom(uint8_t, uint8_t); uint8_t requestFrom(uint8_t, uint8_t, uint8_t); + uint8_t requestFrom(uint16_t, uint8_t, uint8_t); uint8_t requestFrom(int, int); uint8_t requestFrom(int, int, int); - + uint8_t requestFrom(uint16_t, uint8_t); + void onReceive( void (*)(int) ); void onRequest( void (*)(void) ); From 278d53ce828302593db2dd2881d9f7e370d33383 Mon Sep 17 00:00:00 2001 From: Chuck Todd Date: Mon, 27 Nov 2017 15:45:46 -0700 Subject: [PATCH 24/63] missed too many deletions --- cores/esp32/esp32-hal-i2c.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index cc8d54604d0..9155eb81d2a 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -348,6 +348,7 @@ void i2cInitFix(i2c_t * i2c){ { log_e("Busy at initialization!"); } + i2c->dev->ctr.trans_start = 1; uint16_t count = 50000; while ((!i2c->dev->command[2].done) && (--count > 0)); I2C_MUTEX_UNLOCK(); @@ -365,13 +366,6 @@ void i2cReset(i2c_t* i2c){ I2C_MUTEX_UNLOCK(); } -//** 11/2017 Stickbreaker attempt at ISR for I2C hardware -// liberally stolen from ESP_IDF /drivers/i2c.c -esp_err_t i2c_isr_free(intr_handle_t handle){ - -return esp_intr_free(handle); -} - /* Stickbreaker ISR mode debug support */ #if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR @@ -1163,7 +1157,7 @@ return reason; i2c_err_t i2cReleaseISR(i2c_t * i2c){ if(i2c->intr_handle){ - esp_err_t error =i2c_isr_free(i2c->intr_handle); + esp_err_t error =esp_intr_free(i2c->intr_handle); // log_e("released ISR=%d",error); i2c->intr_handle=NULL; } From a5b33aff075940b8dc031db9d2c5fd74b8726f89 Mon Sep 17 00:00:00 2001 From: Chuck Todd Date: Mon, 27 Nov 2017 17:35:34 -0700 Subject: [PATCH 25/63] fixing spaces Try to fix source layout of Wire Example eeprom_size.ino --- .../Wire/examples/eeprom_size/eeprom_size.ino | 146 +++++++++--------- 1 file changed, 73 insertions(+), 73 deletions(-) diff --git a/libraries/Wire/examples/eeprom_size/eeprom_size.ino b/libraries/Wire/examples/eeprom_size/eeprom_size.ino index b6776fc407c..8b1ece98476 100644 --- a/libraries/Wire/examples/eeprom_size/eeprom_size.ino +++ b/libraries/Wire/examples/eeprom_size/eeprom_size.ino @@ -23,18 +23,18 @@ the memory cells. During this programming cycle, the IC does not respond to I2C requests. This feature 'NAK' polling is used to determine when the program cycle has completed. -*/ +*/ bool i2cReady(uint8_t ID){ uint32_t timeout=millis(); bool ready=false; while((millis()-timeout<100)&&(!ready)){ - Wire.beginTransmission(ID); - int err=Wire.endTransmission(); - ready=(err==0); - if(!ready){ - if(err!=2)Serial.printf("{%d}:%s",err,Wire.getErrorText(err)); - } - } + Wire.beginTransmission(ID); + int err=Wire.endTransmission(); + ready=(err==0); + if(!ready){ + if(err!=2)Serial.printf("{%d}:%s",err,Wire.getErrorText(err)); + } + } return ready; } @@ -47,29 +47,29 @@ uint8_t ID=0x50,i; uint16_t size; char buf[256]; while(ID<0x58){ - i=0; - size = 0x1000; // Start at 4k, 16bit address devices, - i += sprintf_P(&buf[i],PSTR("0x%02X: "),ID); - if(i2cReady(ID)) { // EEPROM answered - uint8_t zeroByte; - Wire.beginTransmission(ID); - Wire.write((uint8_t)0); // set address ptr to 0, two bytes High - Wire.write((uint8_t)0); // set address ptr to 0, two bytes Low - uint8_t err=Wire.endTransmission(); - if(err==0){// worked, device exists at this ID - err=Wire.requestFrom(ID,(uint8_t)1); - if(err==1){// got the value of the byte at address 0 - zeroByte=Wire.read(); - uint8_t saveByte,testByte; - do{ - if(i2cReady(ID)){ - Wire.beginTransmission(ID); - Wire.write(highByte(size)); // set next test address - Wire.write(lowByte(size)); - Wire.endTransmission(); - err=Wire.requestFrom(ID,(uint8_t)1); - if(err==1){ - saveByte=Wire.read(); + i=0; + size = 0x1000; // Start at 4k, 16bit address devices, + i += sprintf_P(&buf[i],PSTR("0x%02X: "),ID); + if(i2cReady(ID)) { // EEPROM answered + uint8_t zeroByte; + Wire.beginTransmission(ID); + Wire.write((uint8_t)0); // set address ptr to 0, two bytes High + Wire.write((uint8_t)0); // set address ptr to 0, two bytes Low + uint8_t err=Wire.endTransmission(); + if(err==0){// worked, device exists at this ID + err=Wire.requestFrom(ID,(uint8_t)1); + if(err==1){// got the value of the byte at address 0 + zeroByte=Wire.read(); + uint8_t saveByte,testByte; + do{ + if(i2cReady(ID)){ + Wire.beginTransmission(ID); + Wire.write(highByte(size)); // set next test address + Wire.write(lowByte(size)); + Wire.endTransmission(); + err=Wire.requestFrom(ID,(uint8_t)1); + if(err==1){ + saveByte=Wire.read(); if(saveByte == zeroByte) { // have to test it Wire.beginTransmission(ID); Wire.write(highByte(size)); // set next test address @@ -103,41 +103,41 @@ while(ID<0x58){ else { testByte = ~zeroByte; } - - //restore byte - if(!i2cReady(ID)){ - i+=sprintf_P(&buf[i],PSTR(" notReady4.\n")); - Serial.print(buf); - ID++; - break; - } - Wire.beginTransmission(ID); - Wire.write(highByte(size)); // set next test address - Wire.write(lowByte(size)); - Wire.write((uint8_t)saveByte); // restore it - Wire.endTransmission(); - } + + //restore byte + if(!i2cReady(ID)){ + i+=sprintf_P(&buf[i],PSTR(" notReady4.\n")); + Serial.print(buf); + ID++; + break; + } + Wire.beginTransmission(ID); + Wire.write(highByte(size)); // set next test address + Wire.write(lowByte(size)); + Wire.write((uint8_t)saveByte); // restore it + Wire.endTransmission(); + } else testByte = zeroByte; // They were different so the eeprom Is Bigger } - else testByte=~zeroByte; - } - else testByte=~zeroByte; - if(testByte==zeroByte){ - size = size <<1; - } - }while((testByte==zeroByte)&&(size>0)); - if(size==0) i += sprintf_P(&buf[i],PSTR("64k Bytes")); - else i+=sprintf_P(&buf[i],PSTR("%dk Bytes"),size/1024); - if(!i2cReady(ID)){ - i+=sprintf_P(&buf[i],PSTR(" notReady3.\n")); - Serial.print(buf); - ID++; - continue; - } - Wire.beginTransmission(ID); - Wire.write((uint8_t)0); // set address ptr to 0, two bytes High - Wire.write((uint8_t)0); // set address ptr to 0, two bytes Low - err=Wire.endTransmission(); + else testByte=~zeroByte; + } + else testByte=~zeroByte; + if(testByte==zeroByte){ + size = size <<1; + } + }while((testByte==zeroByte)&&(size>0)); + if(size==0) i += sprintf_P(&buf[i],PSTR("64k Bytes")); + else i+=sprintf_P(&buf[i],PSTR("%dk Bytes"),size/1024); + if(!i2cReady(ID)){ + i+=sprintf_P(&buf[i],PSTR(" notReady3.\n")); + Serial.print(buf); + ID++; + continue; + } + Wire.beginTransmission(ID); + Wire.write((uint8_t)0); // set address ptr to 0, two bytes High + Wire.write((uint8_t)0); // set address ptr to 0, two bytes Low + err=Wire.endTransmission(); if(err==0){ err= Wire.requestFrom(ID,1); if (err==1) { @@ -152,15 +152,15 @@ while(ID<0x58){ } } } - else i+=sprintf_P(&buf[i],PSTR("Read 0 Failure")); - } - else i+=sprintf_P(&buf[i],PSTR("Write Adr 0 Failure")); - - } - else i+=sprintf_P(&buf[i],PSTR("Not Present")); - Serial.println(buf); - ID++; - } + else i+=sprintf_P(&buf[i],PSTR("Read 0 Failure")); + } + else i+=sprintf_P(&buf[i],PSTR("Write Adr 0 Failure")); + + } + else i+=sprintf_P(&buf[i],PSTR("Not Present")); + Serial.println(buf); + ID++; + } } void setup(){ From b28fc5f4a81a681874179730357f9c6dac12ebac Mon Sep 17 00:00:00 2001 From: Chuck Todd Date: Fri, 1 Dec 2017 08:32:08 -0700 Subject: [PATCH 26/63] reorder functions, spaceify, clockStretching recovery Move where dumpCmdQueue() exists in .c under IDF as component, it's previous position was causing a 'error: implicit declaration of function' Small change in ISR to try to survive an extended SCL stretching event. @lonerzzz is encountering a timeout during init of a device at 0x30. It is holding SCL low longer than 13.1ms. This change may recover. --- cores/esp32/esp32-hal-i2c.c | 111 +++++++++++++++++++----------------- 1 file changed, 60 insertions(+), 51 deletions(-) diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index 9155eb81d2a..0a053e69f18 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -31,19 +31,19 @@ #define DR_REG_I2C_EXT_BASE_FIXED 0x60013000 #define DR_REG_I2C1_EXT_BASE_FIXED 0x60027000 - + struct i2c_struct_t { i2c_dev_t * dev; #if !CONFIG_DISABLE_HAL_LOCKS xSemaphoreHandle lock; #endif uint8_t num; - I2C_MODE_t mode; + I2C_MODE_t mode; I2C_STAGE_t stage; - I2C_ERROR_t error; - EventGroupHandle_t i2c_event; // a way to monitor ISR process - // maybe use it to trigger callback for OnRequest() - intr_handle_t intr_handle; /*!< I2C interrupt handle*/ + I2C_ERROR_t error; + EventGroupHandle_t i2c_event; // a way to monitor ISR process + // maybe use it to trigger callback for OnRequest() + intr_handle_t intr_handle; /*!< I2C interrupt handle*/ I2C_DATA_QUEUE_t * dq; uint16_t queueCount; uint16_t queuePos; @@ -78,6 +78,7 @@ static i2c_t _i2c_bus_array[2] = { #endif /* Stickbreaker added for ISR 11/2017 +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){ @@ -153,6 +154,11 @@ i2c_err_t i2cAddQueueRead(i2c_t * i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, //10bit read is kind of weird, first you do a 0byte Write with 10bit // address, then a ReSTART then a 7bit Read using the the upper 7bit + // readBit. + + // this might cause an internal register pointer problem with 10bit + // devices, But, Don't have any to test agains. + // this is the Industry Standard specification. + if((i2cDeviceAddr &0xFC00)==0x7800){ // ten bit read i2c_err_t err = i2cAddQueue(i2c,0,i2cDeviceAddr,NULL,0,false,event); if(err==I2C_ERROR_OK){ @@ -218,26 +224,26 @@ i2c_err_t i2cDetachSDA(i2c_t * i2c, int8_t sda) * */ void i2cSetCmd(i2c_t * i2c, uint8_t index, uint8_t op_code, uint8_t byte_num, bool ack_val, bool ack_exp, bool ack_check) { - I2C_COMMAND_t cmd; - cmd.val=0; - cmd.ack_en = ack_check; - cmd.ack_exp = ack_exp; - cmd.ack_val = ack_val; - cmd.byte_num = byte_num; - cmd.op_code = op_code; + I2C_COMMAND_t cmd; + cmd.val=0; + cmd.ack_en = ack_check; + cmd.ack_exp = ack_exp; + cmd.ack_val = ack_val; + cmd.byte_num = byte_num; + cmd.op_code = op_code; i2c->dev->command[index].val = cmd.val; } void i2cResetFiFo(i2c_t * i2c) { - I2C_FIFO_CONF_t f; - f.val = i2c->dev->fifo_conf.val; - f.tx_fifo_rst = 1; - f.rx_fifo_rst = 1; - i2c->dev->fifo_conf.val = f.val; - f.tx_fifo_rst = 0; - f.rx_fifo_rst = 0; - i2c->dev->fifo_conf.val = f.val; + I2C_FIFO_CONF_t f; + f.val = i2c->dev->fifo_conf.val; + f.tx_fifo_rst = 1; + f.rx_fifo_rst = 1; + i2c->dev->fifo_conf.val = f.val; + f.tx_fifo_rst = 0; + f.rx_fifo_rst = 0; + i2c->dev->fifo_conf.val = f.val; } i2c_err_t i2cSetFrequency(i2c_t * i2c, uint32_t clk_speed) @@ -373,6 +379,24 @@ void i2cReset(i2c_t* i2c){ static uint32_t intBuff[INTBUFFMAX][3]; static uint32_t intPos=0; #endif + +/* Stickbreaker ISR mode debug support +*/ +void IRAM_ATTR dumpCmdQueue(i2c_t *i2c){ +uint8_t i=0; +while(i<16){ + I2C_COMMAND_t c; + c.val=i2c->dev->command[i].val; + log_e("[%2d] %c op[%d] val[%d] exp[%d] en[%d] bytes[%d]",i,(c.done?'Y':'N'), + c.op_code, + c.ack_val, + c.ack_exp, + c.ack_en, + c.byte_num); + i++; + } +} + /* Stickbreaker ISR mode support */ static void IRAM_ATTR fillCmdQueue(i2c_t * i2c, bool INTS){ @@ -438,7 +462,7 @@ while(!done){ // fill the command[] until either 0..14 filled or out of cmds and if((!done)&&(tdq->ctrl.addrCmdSent)){ //room in command[] for at least One data (read/Write) cmd uint8_t blkSize=0; // max is 255? does numBytes =0 actually mean 256? haven't tried it. //log_e("needed=%2d index=%d",*neededRead,cmdIdx); - while(( tdq->cmdBytesNeeded > tdq->ctrl.mode )&&(!done )) { // more bytes needed and room in cmd queue, leave room for END + while(( tdq->cmdBytesNeeded > tdq->ctrl.mode )&&(!done )) { // more bytes needed and room in cmd queue, leave room for END blkSize = (tdq->cmdBytesNeeded > 255)?255:(tdq->cmdBytesNeeded - tdq->ctrl.mode); // Last read cmd needs different ACK setting, so leave 1 byte remainer on reads tdq->cmdBytesNeeded -= blkSize; // if(tdq->ctrl.mode==1){ //read mode @@ -521,7 +545,7 @@ while(!done){ // fill the command[] until either 0..14 filled or out of cmds and if(cmdIdx==15){ //need continuation, even if STOP is in 14, it will not matter // cmd buffer is almost full, Add END as a continuation feature - // log_e("END at %d, left=%d",cmdIdx,neededRead); + // log_e("END at %d, left=%d",cmdIdx,neededRead); i2cSetCmd(i2c, (cmdIdx)++,I2C_CMD_END, 0,false,false,false); i2c->dev->int_ena.end_detect=1; //maybe? i2c->dev->int_clr.end_detect=1; //maybe? @@ -583,7 +607,7 @@ log_n("Enable Core Debug Level \"Error\""); void i2cDumpI2c(i2c_t * i2c){ log_e("i2c=%p",i2c); -log_e("dev=%p",i2c->dev); +log_e("dev=%p date=%p",i2c->dev,i2c->dev->date); log_e("lock=%p",i2c->lock); log_e("num=%d",i2c->num); log_e("mode=%d",i2c->mode); @@ -598,23 +622,6 @@ log_e("byteCnt=%d",i2c->byteCnt); if(i2c->dq) i2cDumpDqData(i2c); } -/* Stickbreaker ISR mode debug support -*/ -void IRAM_ATTR dumpCmdQueue(i2c_t *i2c){ -uint8_t i=0; -while(i<16){ - I2C_COMMAND_t c; - c.val=i2c->dev->command[i].val; - log_e("[%2d] %c op[%d] val[%d] exp[%d] en[%d] bytes[%d]",i,(c.done?'Y':'N'), - c.op_code, - c.ack_val, - c.ack_exp, - c.ack_en, - c.byte_num); - i++; - } -} - /* Stickbreaker ISR mode support */ static void IRAM_ATTR fillTxFifo(i2c_t * i2c){ @@ -872,7 +879,7 @@ while (activeInt != 0) { // Ordering of 'if(activeInt)' statements is important, if (p_i2c->mode == I2C_MASTER) { // log_e("AcK Err byteCnt=%d, queuepos=%d",p_i2c->byteCnt,p_i2c->queuePos); if(p_i2c->byteCnt==1) i2cIsrExit(p_i2c,EVENT_ERROR_NAK,true); - else if((p_i2c->byteCnt == 2) && (p_i2c->dq[p_i2c->queuePos].ctrl.addrReq == 2)) + else if((p_i2c->byteCnt == 2) && (p_i2c->dq[p_i2c->queuePos].ctrl.addrReq == 2)) i2cIsrExit(p_i2c,EVENT_ERROR_NAK,true); else i2cIsrExit(p_i2c,EVENT_ERROR_DATA_NAK,true); } @@ -880,18 +887,21 @@ while (activeInt != 0) { // Ordering of 'if(activeInt)' statements is important, } if (activeInt & I2C_TIME_OUT_INT_ST_M) {//fatal death Happens Here - i2cIsrExit(p_i2c,EVENT_ERROR_TIMEOUT,true); - return; + // let Gross timeout occure + p_i2c->dev->int_clr.time_out =1; + activeInt &=~I2C_TIME_OUT_INT_ST; +// i2cIsrExit(p_i2c,EVENT_ERROR_TIMEOUT,true); +// return; } - + if (activeInt & I2C_TRANS_COMPLETE_INT_ST_M) { i2cIsrExit(p_i2c,EVENT_DONE,false); return; // no more work to do /* // how does slave mode act? if (p_i2c->mode == I2C_SLAVE) { // STOP detected - // empty fifo - // dispatch callback + // empty fifo + // dispatch callback */ } @@ -899,7 +909,7 @@ while (activeInt != 0) { // Ordering of 'if(activeInt)' statements is important, i2cIsrExit(p_i2c,EVENT_ERROR_ARBITRATION,true); return; // no more work to do } - + if (activeInt & I2C_SLAVE_TRAN_COMP_INT_ST_M) { p_i2c->dev->int_clr.slave_tran_comp = 1; // need to complete this ! @@ -908,7 +918,7 @@ while (activeInt != 0) { // Ordering of 'if(activeInt)' statements is important, if (activeInt & I2C_END_DETECT_INT_ST_M) { p_i2c->dev->int_ena.end_detect = 0; p_i2c->dev->int_clr.end_detect = 1; - p_i2c->dev->ctr.trans_start=0; + p_i2c->dev->ctr.trans_start=0; fillCmdQueue(p_i2c,true); // enable interrupts p_i2c->dev->ctr.trans_start=1; // go for it activeInt&=~I2C_END_DETECT_INT_ST_M; @@ -948,7 +958,7 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis) //log_e("procQueue i2c=%p",&i2c); *readCount = 0; //total reads accomplished in all queue elements if(i2c == NULL){ - return I2C_ERROR_DEV; + return I2C_ERROR_DEV; } I2C_MUTEX_LOCK(); @@ -1172,7 +1182,6 @@ return i2cFreeQueue(i2c); 24Nov17 Need to think about not usings I2C_MASTER_TRAN_COMP_INT_ST to adjust queuePos. This INT triggers every byte. The only reason to know which byte is being transfered is - to decide where to store a READ or if an error occured. It may be possible to use the status_reg.tx_fifo_cnt and a .txQueued to do this in the fillRxFifo(). The same mechanism could work if an error occured in i2cErrorExit(). */ From 97813821ce3a8a019a2824fba45a07c810a790c0 Mon Sep 17 00:00:00 2001 From: Chuck Todd Date: Mon, 11 Dec 2017 11:16:19 -0700 Subject: [PATCH 27/63] 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) --- cores/esp32/esp32-hal-i2c.c | 60 ++++++-- cores/esp32/esp32-hal-i2c.h | 1 + libraries/Wire/docs/README.md | 24 +++- .../Wire/examples/eeprom_size/eeprom_size.ino | 133 +++++++++++++++++- libraries/Wire/src/Wire.cpp | 4 +- libraries/Wire/src/Wire.h | 1 + 6 files changed, 194 insertions(+), 29 deletions(-) diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index 0a053e69f18..cc866a3e540 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -636,9 +636,14 @@ static void IRAM_ATTR fillTxFifo(i2c_t * i2c){ /*11/15/2017 will assume that I cannot queue tx after a READ until READ completes 11/23/2017 Seems to be a TX fifo problem, the SM sends 0x40 for last rxbyte, I enable txEmpty, filltx fires, but the SM has already sent a bogus byte out the BUS. -I am going so see if I can overlap Tx/Rx/Tx in the fifo + I am going so see if I can overlap Tx/Rx/Tx in the fifo +12/01/2017 The Fifo's are independent, 32 bytes of tx and 32 bytes of Rx. + 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; +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; @@ -795,11 +800,8 @@ if(xResult == pdPASS){ } static void IRAM_ATTR i2c_isr_handler_default(void* arg){ -//log_e("isr Entry=%p",arg); i2c_t* p_i2c = (i2c_t*) arg; // recover data uint32_t activeInt = p_i2c->dev->int_status.val&0x1FFF; -//log_e("int=%x",activeInt); -//dumpI2c(p_i2c); portBASE_TYPE HPTaskAwoken = pdFALSE,xResult; @@ -886,12 +888,11 @@ while (activeInt != 0) { // Ordering of 'if(activeInt)' statements is important, return; } - if (activeInt & I2C_TIME_OUT_INT_ST_M) {//fatal death Happens Here - // let Gross timeout occure + if (activeInt & I2C_TIME_OUT_INT_ST_M) { + // let Gross timeout occur, Slave may release SCL before the configured timeout expires + // the Statemachine only has a 13.1ms max timout, some Devices >500ms p_i2c->dev->int_clr.time_out =1; activeInt &=~I2C_TIME_OUT_INT_ST; -// i2cIsrExit(p_i2c,EVENT_ERROR_TIMEOUT,true); -// return; } if (activeInt & I2C_TRANS_COMPLETE_INT_ST_M) { @@ -973,7 +974,9 @@ i2c->stage = I2C_DONE; // until ready memset(intBuff,0,sizeof(intBuff)); intPos=0; #endif - +// EventGroup is used to signal transmisison 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(); } @@ -1046,7 +1049,7 @@ i2c->stage = I2C_STARTUP; // everything configured, now start the I2C StateMachi i2c->dev->int_ena.val = I2C_ACK_ERR_INT_ENA | // (BIT(10)) Causes Fatal Error Exit I2C_TRANS_START_INT_ENA | // (BIT(9)) Triggered by trans_start=1, initial,END - I2C_TIME_OUT_INT_ENA | //(BIT(8)) causes Fatal error Exit + I2C_TIME_OUT_INT_ENA | //(BIT(8)) Trigger by SLAVE SCL stretching, NOT an ERROR I2C_TRANS_COMPLETE_INT_ENA | // (BIT(7)) triggered by STOP, successful exit I2C_MASTER_TRAN_COMP_INT_ENA | // (BIT(6)) counts each byte xfer'd, inc's queuePos I2C_ARBITRATION_LOST_INT_ENA | // (BIT(5)) cause fatal error exit @@ -1071,7 +1074,7 @@ if(!i2c->intr_handle){ // create ISR I2C_0 only, // how many ticks should it take to transfer totalBytes thru the I2C hardware, // add user supplied timeOutMillis to Calc Value -portTickType ticksTimeOut = ((totalBytes /(i2cGetFrequency(i2c)/(10*1000)))+timeOutMillis)/portTICK_PERIOD_MS; +portTickType ticksTimeOut = ((totalBytes*10*1000)/(i2cGetFrequency(i2c))+timeOutMillis)/portTICK_PERIOD_MS; portTickType tBefore=xTaskGetTickCount(); //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 } 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 i2cDumpI2c(i2c); i2cDumpInts(); +#else + log_n("I2C exitCode=%u",eBits); +#endif } if(eBits&EVENT_DONE){ // no gross timeout +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR + 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; @@ -1126,11 +1142,25 @@ else { // GROSS timeout, shutdown ISR , report Timeout i2c->stage = I2C_DONE; i2c->dev->int_ena.val =0; i2c->dev->int_clr.val = 0x1FFF; - reason = I2C_ERROR_TIMEOUT; - eBits = eBits | EVENT_ERROR_TIMEOUT|EVENT_ERROR|EVENT_DONE; - log_e(" Gross Timeout Dead st=0x%x, ed=0x%x, =%d, max=%d error=%d",tBefore,tAfter,(tAfter-tBefore),ticksTimeOut,i2c->error); + if((i2c->queuePos==0)&&(i2c->byteCnt==0)){ // Bus Busy no bytes Moved + reason = I2C_ERROR_BUSY; + eBits = eBits | EVENT_ERROR_BUS_BUSY|EVENT_ERROR|EVENT_DONE; +#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(); +#endif + } + else { // just a timeout, some data made it out or in. + reason = I2C_ERROR_TIMEOUT; + eBits = eBits | EVENT_ERROR_TIMEOUT|EVENT_ERROR|EVENT_DONE; + +#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(); +#endif + } } // offloading all EventGroups to dispatch, EventGroups in ISR is not always successful diff --git a/cores/esp32/esp32-hal-i2c.h b/cores/esp32/esp32-hal-i2c.h index ce120c02906..bf75f0e6808 100644 --- a/cores/esp32/esp32-hal-i2c.h +++ b/cores/esp32/esp32-hal-i2c.h @@ -104,6 +104,7 @@ typedef enum { // on Exit. Dispatcher will set bits for each dq before/after ISR completion #define EVENT_ERROR_NAK (BIT(0)) #define EVENT_ERROR (BIT(1)) +#define EVENT_ERROR_BUS_BUSY (BIT(2)) #define EVENT_RUNNING (BIT(3)) #define EVENT_DONE (BIT(4)) #define EVENT_IN_END (BIT(5)) diff --git a/libraries/Wire/docs/README.md b/libraries/Wire/docs/README.md index dd16b82f1c0..1633c17ee1f 100644 --- a/libraries/Wire/docs/README.md +++ b/libraries/Wire/docs/README.md @@ -14,8 +14,8 @@ Wire.write(highByte(addr)); Wire.write(lowByte(addr)); uint8_t err =Wire.endTransmission(false); // don't send a STOP, just Pause I2C operations if(err==0){ // successfully set internal address pointer - err=Wire.requestFrom(addr,len); - if(err==0){ // read failed + uint8_t count=Wire.requestFrom(addr,len); + if(count==0){ // read failed Serial.print("Bad Stuff!! Read Failed\n"); } else {// successful read @@ -59,10 +59,10 @@ if(err == 7){ // Prior Operation has been queued // returned 0. So, if this if() was if(err==0), the Queue operation would be // considered an error. This is the primary Difference. - err=Wire.requestFrom(addr,len); + uint8_t count=Wire.requestFrom(addr,len); if(Wire.lastError()!=0){ // complete/partial read failure Serial.printf("Bad Stuff!!\nRead of (%d) bytes read %d bytes\nFailed" - " lastError=%d, text=%s\n", len, err, Wire.lastError(), + " lastError=%d, text=%s\n", len, count, Wire.lastError(), Wire.getErrorText(Wire.lastError())); } // some of the read may have executed @@ -82,7 +82,11 @@ Additionally this implementation of `Wire()` includes methods to handle local bu size_t transact(size_t readLen); // replacement for endTransmission(false),requestFrom(ID,readLen,true); size_t transact(uint8_t* readBuff, size_t readLen);// bigger Block read i2c_err_t lastError(); // Expose complete error + char * getErrorText(uint8_t err); // return char pointer for text of err + void dumpI2C(); // diagnostic dump of I2C control structure and buffers void dumpInts(); // diagnostic dump for the last 64 different i2c Interrupts + void dumpOn(); // Execute dumpI2C() and dumpInts() after every I2C procQueue() + void dumpOff(); // turn off dumpOn() size_t getClock(); // current i2c Clock rate void setTimeOut(uint16_t timeOutMillis); // allows users to configure Gross Timeout uint16_t getTimeOut(); @@ -95,7 +99,7 @@ Wire.beginTransmission(ID); Wire.write(highByte(addr)); Wire.write(lowByte(addr)); -uint8_t err=Wire.transact(len); // transact() does both Wire.endTransmission(false); and Wire.requestFrom(ID,len,true); +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 Serial.printf("Bad Stuff!! Read Failed lastError=%d\n",Wire.lastError()); } @@ -107,6 +111,15 @@ Serial.println(); ``` +### TIMEOUT's +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. + +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. + +The current library signals this occurence by returning I2C_ERROR_OK and a dataLength of 0(zero) back through the `Wire.requestFrom()` call. + +### Alpha + This **APLHA** release should be compiled with ESP32 Dev Module as its target, and Set the "core debug level" to 'error' @@ -114,3 +127,4 @@ There is MINIMAL to NO ERROR detection, BUS, BUSY. because I have not encounter Chuck. + \ No newline at end of file diff --git a/libraries/Wire/examples/eeprom_size/eeprom_size.ino b/libraries/Wire/examples/eeprom_size/eeprom_size.ino index 8b1ece98476..6e70dd2c6a8 100644 --- a/libraries/Wire/examples/eeprom_size/eeprom_size.ino +++ b/libraries/Wire/examples/eeprom_size/eeprom_size.ino @@ -1,18 +1,38 @@ #include // Connect 4.7k pullups on SDA, SCL // for ESP32 SDA(pin 21), SCL(pin 22) -// for AtMega328p SDA(Pin A5), SCL(pin A4) -// for Mega2560 SDA(Pin 20), SCL(pin 21) /* This sketch uses the address rollover of the 24LCxx EEPROMS to detect their size. - The 24LC32 (4kByte) 0x0000 .. 0x0FFF, a Write to addres 0x1000 will actually + This detection sequence will not work with small capacity EEPROMs 24LC01 .. 24LC16. + These small EEPROMS use single byte addressing, To access more than 256 bytes, + the lc04, lc08, and lc16 EEPROMs respond as if they were multiple lc02's with + consective I2CDevice addresses. + device I2Caddr Address Range + 24LC01 0x50 0x00 .. 0x7F + 24LC02 0x50 0x00 .. 0xFF + 24LC04 0x50 0x00 .. 0xFF + 0x51 0x00 .. 0xFF + 24LC08 0x50 0x00 .. 0xFF + 0x51 0x00 .. 0xFF + 0x52 0x00 .. 0xFF + 0x53 0x00 .. 0xFF + 24LC16 0x50 0x00 .. 0xFF + 0x51 0x00 .. 0xFF + 0x52 0x00 .. 0xFF + 0x53 0x00 .. 0xFF + 0x54 0x00 .. 0xFF + 0x55 0x00 .. 0xFF + 0x56 0x00 .. 0xFF + 0x57 0x00 .. 0xFF + The 24LC32 with selectable I2C address 0x50..0x57 are 4kByte devices with an internal + address range of 0x0000 .. 0x0FFF. A Write to address 0x1000 will actually be stored in 0x0000. This allows us to read the value of 0x0000, compare it to the value read from 0x1000, if they are different, then this IC is not a 24LC32. If the Value is the same, then we have to change the byte at 0x1000 and see if the change is reflected in 0x0000. If 0x0000 changes, then we know that the chip is a 24LC32. We have to restore the 'changed' value so that - the data in the EEPROM is not compromized. + the data in the EEPROM is not compromised. This pattern of read, compare, test, restore is used for each possible size. All that changes is the test Address, 0x1000, 0x2000, 0x4000, 0x8000. @@ -27,7 +47,8 @@ bool i2cReady(uint8_t ID){ uint32_t timeout=millis(); bool ready=false; -while((millis()-timeout<100)&&(!ready)){ +while((millis()-timeout<10)&&(!ready)){ // try to evoke a response from the device. +// If the it does not respond within 10ms return as a failure. Wire.beginTransmission(ID); int err=Wire.endTransmission(); ready=(err==0); @@ -38,6 +59,103 @@ while((millis()-timeout<100)&&(!ready)){ return ready; } +void dispBuff(uint8_t *buf, uint16_t len,uint16_t offset){ +char asciibuf[100]; +uint8_t bufPos=0; +uint16_t adr=0; +asciibuf[0] ='\0'; +while(adr127)) ch ='.'; + bufPos+=sprintf(&asciibuf[bufPos],"%c",ch); + adr++; + } + +while(bufPos<32){ + Serial.print(" "); + bufPos++; + } +Serial.printf(" %s\n",asciibuf); +} + +/* detectWritePageSize() attempts to write a 256 byte block to an I2C EEPROM. + This large block will use the side effect of the WritePage to detect it's size. + EEPROM's have to erase a 'page' of data in their memory cell array before they + can change it. To facilitate partial page writes they contain a temporary 'WritePage' + that is used store the contents of the memory cells while their page is erase. + When data is written to the device it is merged into this temporary buffer. If the + amount of data written is longer than the temporary buffer it rolls over to the beginning + of the temporary buffer. In a 24lc32, the standard WritePage is 32 bytes, if 256 + bytes of data is written to the device, only the last 32 bytes are stored. + This side effect allow easy detect of the WritePage size. +*/ + +uint16_t detectWritePageSize(uint16_t i2cID,bool verbose=false){ +if(verbose) Serial.printf("detecting WritePage Size for (0x%02x)\n",i2cID); +uint16_t adr = 0,ps=0; +randomSeed(micros()); +adr = random(256)*256; //address is selected at random for wear leveling purposes. +uint8_t *buf =(uint8_t*)malloc(256); //restore buffer +uint8_t *buf1 =(uint8_t*)malloc(258); // write buffer +i2cReady(i2cID); // device may completing a Write Cycle, wait if necessary +Wire.beginTransmission(i2cID); +Wire.write(highByte(adr)); +Wire.write(lowByte(adr)); +uint16_t count = Wire.transact(buf,256);//save current EEPROM content for Restore +if(Wire.lastError()==0){ // successful read, now we can try the test + for(uint16_t a=0;a<256;a++){ //initialize a detectable pattern in the writeBuffer + buf1[a+2]=a; // leave room for the the address + } + buf1[0] = highByte(adr); + buf1[1] = lowByte(adr); + Wire.writeTransmission(i2cID,buf1,258); + if(Wire.lastError()==0){ // wait for the write to complete + if(i2cReady(i2cID)){ + Wire.beginTransmission(i2cID); + Wire.write(highByte(adr)); + Wire.write(lowByte(adr)); + Wire.transact(&buf1[2],256); + if(Wire.lastError()==0){ + ps = 256-buf1[2]; // if the read succeeded, the byte read from offset 0x0 + // can be used to calculate the WritePage size. On a 24lc32 with a 32byte + // WritePage it will contain 224, therefore 256-224 = 32 byte Writepage. + if(verbose){ + Serial.printf("Origonal data\n",i2cID); + dispBuff(buf,256,adr); + Serial.printf("\n OverWritten with\n"); + dispBuff(&buf1[2],256,adr); + Serial.printf("Calculated Write Page is %d\n",ps); + } + memmove(&buf1[2],buf,256); // copy the savebuffer back into + if(i2cReady(i2cID)){ + Wire.writeTransmission(i2cID,buf1,ps+2); // two address bytes plus WritePage + } + if(Wire.lastError()!=0){ + Serial.printf("unable to Restore prom\n"); + if(i2cReady(i2cID)){ + Wire.beginTransmission(i2cID); + Wire.write(highByte(adr)); + Wire.write(lowByte(adr)); + Wire.transact(&buf1[2],256); + Serial.printf("\n Restored to\n"); + dispBuff(&buf1[2],256,adr); + } + } + } + } + } + } +free(buf1); +free(buf); +return ps; +} + /* eepromSize() only works on 24LC32 .. 24LC512 eeprom, the smaller 24LC01 .. 24LC16 use one byte addressings. */ @@ -127,7 +245,8 @@ while(ID<0x58){ } }while((testByte==zeroByte)&&(size>0)); if(size==0) i += sprintf_P(&buf[i],PSTR("64k Bytes")); - else i+=sprintf_P(&buf[i],PSTR("%dk Bytes"),size/1024); + else i+=sprintf_P(&buf[i],PSTR("%2uk Bytes"),size/1024); + i+=sprintf_P(&buf[i],PSTR(" WritePage=%3u"),detectWritePageSize(ID,false)); if(!i2cReady(ID)){ i+=sprintf_P(&buf[i],PSTR(" notReady3.\n")); Serial.print(buf); @@ -139,7 +258,7 @@ while(ID<0x58){ Wire.write((uint8_t)0); // set address ptr to 0, two bytes Low err=Wire.endTransmission(); if(err==0){ - err= Wire.requestFrom(ID,1); + err= Wire.requestFrom(ID,(uint8_t)1); if (err==1) { testByte = Wire.read(); if(testByte != zeroByte){ //fix it diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp index 109d46c5a78..bccf2bf6038 100644 --- a/libraries/Wire/src/Wire.cpp +++ b/libraries/Wire/src/Wire.cpp @@ -39,12 +39,12 @@ TwoWire::TwoWire(uint8_t bus_num) ,i2c(NULL) ,rxIndex(0) ,rxLength(0) + ,rxQueued(0) ,txIndex(0) ,txLength(0) ,txAddress(0) - ,transmitting(0) ,txQueued(0) - ,rxQueued(0) + ,transmitting(0) ,_timeOutMillis(50) ,last_error(I2C_ERROR_OK) ,_dump(false) diff --git a/libraries/Wire/src/Wire.h b/libraries/Wire/src/Wire.h index cfec58fa386..0b69d7f72e2 100644 --- a/libraries/Wire/src/Wire.h +++ b/libraries/Wire/src/Wire.h @@ -82,6 +82,7 @@ class TwoWire: public Stream char * getErrorText(uint8_t err); void dumpOn(){_dump=true;} void dumpOff(){_dump=false;} + bool getDump(){return _dump;} void dumpInts(); void dumpI2C(){i2cDumpI2c(i2c);} size_t getClock(); From 98cbe102763cb35b2a6b6fb744b015799d8d0a55 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Sat, 13 Jan 2018 11:28:18 -0800 Subject: [PATCH 28/63] Fix compiler warning and an indent. --- libraries/Wire/src/Wire.cpp | 2 +- libraries/Wire/src/Wire.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp index bccf2bf6038..fed35ba71cc 100644 --- a/libraries/Wire/src/Wire.cpp +++ b/libraries/Wire/src/Wire.cpp @@ -45,8 +45,8 @@ TwoWire::TwoWire(uint8_t bus_num) ,txAddress(0) ,txQueued(0) ,transmitting(0) - ,_timeOutMillis(50) ,last_error(I2C_ERROR_OK) + ,_timeOutMillis(50) ,_dump(false) {} diff --git a/libraries/Wire/src/Wire.h b/libraries/Wire/src/Wire.h index 0b69d7f72e2..1466bc03133 100644 --- a/libraries/Wire/src/Wire.h +++ b/libraries/Wire/src/Wire.h @@ -60,7 +60,7 @@ class TwoWire: public Stream void onRequestService(void); void onReceiveService(uint8_t*, int); */ - i2c_err_t last_error; // @stickBreaker from esp32-hal-i2c.h + i2c_err_t last_error; // @stickBreaker from esp32-hal-i2c.h i2c_err_t processQueue(uint32_t *readCount); uint16_t _timeOutMillis; bool _dump; From 323e24046ddbcdc24713ae5c643acf8757511a46 Mon Sep 17 00:00:00 2001 From: "rudi ;-)" Date: Sat, 3 Feb 2018 16:13:55 +0100 Subject: [PATCH 29/63] proposal to i2c see talk: https://github.com/espressif/arduino-esp32/issues/1061 PR on origin master : https://github.com/espressif/arduino-esp32/pull/1073 --- cores/esp32/esp32-hal-i2c.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index cc866a3e540..cfeef612dc9 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -175,7 +175,9 @@ i2c_err_t i2cAttachSCL(i2c_t * i2c, int8_t scl) if(i2c == NULL){ return I2C_ERROR_DEV; } + // digitalWrite(scl, HIGH); // optional before pinMode(scl, OUTPUT_OPEN_DRAIN | PULLUP); + digitalWrite(scl, HIGH); // tested pinMatrixOutAttach(scl, I2C_SCL_IDX(i2c->num), false, false); pinMatrixInAttach(scl, I2C_SCL_IDX(i2c->num), false); return I2C_ERROR_OK; @@ -197,7 +199,9 @@ i2c_err_t i2cAttachSDA(i2c_t * i2c, int8_t sda) if(i2c == NULL){ return I2C_ERROR_DEV; } + // digitalWrite(sda, HIGH); // optional before pinMode(sda, OUTPUT_OPEN_DRAIN | PULLUP); + digitalWrite(sda, HIGH); // tested pinMatrixOutAttach(sda, I2C_SDA_IDX(i2c->num), false, false); pinMatrixInAttach(sda, I2C_SDA_IDX(i2c->num), false); return I2C_ERROR_OK; From 5a8d58d2f7deaf30c935f87eef8cd80ffe540b1f Mon Sep 17 00:00:00 2001 From: chuck todd Date: Mon, 5 Feb 2018 16:32:20 -0700 Subject: [PATCH 30/63] Correctly handle Arduino as Component when disable HAL Locks Add condition compile directive to debug dump code, When used as a component under IDF, and DISABLE_HAL_LOCKS was asserted, my debug code still tried to access the HAL Mutex Lock. my Bad. --- cores/esp32/esp32-hal-i2c.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index cc866a3e540..77e0e19c897 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -608,7 +608,9 @@ 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); +#if !CONFIG_DISABLE_HAL_LOCKS log_e("lock=%p",i2c->lock); +#endif log_e("num=%d",i2c->num); log_e("mode=%d",i2c->mode); log_e("stage=%d",i2c->stage); From dfbc1424d9698f084224cbf69776c3a504e74924 Mon Sep 17 00:00:00 2001 From: chuck todd Date: Sun, 18 Feb 2018 20:59:09 -0700 Subject: [PATCH 31/63] Reduce TimeOut Recover debug output Change when TimeOut Recovery (i.e. SCL stretching is reported). Now, it is only reported if the Global Debug level is set to DEBUG. This message is not an ERROR, it is just debug code. --- cores/esp32/esp32-hal-i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index 77e0e19c897..6ec9ef115fb 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -1108,7 +1108,7 @@ 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_ERROR +#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 From 642d2010a93e48c4756b02fccaced592c8b11840 Mon Sep 17 00:00:00 2001 From: "rudi ;-)" Date: Wed, 21 Feb 2018 11:23:10 +0100 Subject: [PATCH 32/63] Update esp32-hal-i2c.c --- cores/esp32/esp32-hal-i2c.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index cfeef612dc9..c2762d2f34a 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -175,9 +175,8 @@ i2c_err_t i2cAttachSCL(i2c_t * i2c, int8_t scl) if(i2c == NULL){ return I2C_ERROR_DEV; } - // digitalWrite(scl, HIGH); // optional before - pinMode(scl, OUTPUT_OPEN_DRAIN | PULLUP); - digitalWrite(scl, HIGH); // tested + digitalWrite(scl, HIGH); + pinMode(scl, OPEN_DRAIN | PULLUP); pinMatrixOutAttach(scl, I2C_SCL_IDX(i2c->num), false, false); pinMatrixInAttach(scl, I2C_SCL_IDX(i2c->num), false); return I2C_ERROR_OK; @@ -199,9 +198,8 @@ i2c_err_t i2cAttachSDA(i2c_t * i2c, int8_t sda) if(i2c == NULL){ return I2C_ERROR_DEV; } - // digitalWrite(sda, HIGH); // optional before - pinMode(sda, OUTPUT_OPEN_DRAIN | PULLUP); - digitalWrite(sda, HIGH); // tested + digitalWrite(sda, HIGH); + pinMode(sda, OPEN_DRAIN | PULLUP); pinMatrixOutAttach(sda, I2C_SDA_IDX(i2c->num), false, false); pinMatrixInAttach(sda, I2C_SDA_IDX(i2c->num), false); return I2C_ERROR_OK; From 90ff8bffc076166bb2977e36438d1af23f753f73 Mon Sep 17 00:00:00 2001 From: chuck todd Date: Thu, 1 Mar 2018 19:53:20 -0700 Subject: [PATCH 33/63] Create Thread Local Storage for micros() micros() is using thread specific cycle counts. So, when micros() or delayMicroseconds() are called from different tasks, false rollover events are detected. The cycle count is task specific, and micros() was using `if(currentCycleCount < lastCycleCount) processRollover()`. The lastCycleCount variable was updated each time micros() exited with the current Tasks cycle count. So, if the next micros() or delayMicroseconds() was not called from the same Task lastCycleCount had no relation. --- cores/esp32/esp32-hal-misc.c | 48 ++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/cores/esp32/esp32-hal-misc.c b/cores/esp32/esp32-hal-misc.c index bf4ee06ebea..fd46d77e033 100644 --- a/cores/esp32/esp32-hal-misc.c +++ b/cores/esp32/esp32-hal-misc.c @@ -38,20 +38,42 @@ void yield() } portMUX_TYPE microsMux = portMUX_INITIALIZER_UNLOCKED; +static pthread_key_t microsStore=NULL; // Thread Local Storage Handle -unsigned long IRAM_ATTR micros() -{ - static unsigned long lccount = 0; - static unsigned long overflow = 0; - unsigned long ccount; - portENTER_CRITICAL_ISR(µsMux); - __asm__ __volatile__ ( "rsr %0, ccount" : "=a" (ccount) ); - if(ccount < lccount){ - overflow += UINT32_MAX / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ; - } - lccount = ccount; - portEXIT_CRITICAL_ISR(µsMux); - return overflow + (ccount / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ); +void* microsStoreDelete(void * storage){ // release thread local data when task is delete. +if(storage) free(storage); +} + +unsigned long IRAM_ATTR micros(){ + +if (!microsStore){ // first Time Ever thread local not init'd + portENTER_CRITICAL_ISR(µsMux); + pthread_key_create(µsStore,microsStoreDelete); // create initial holder + portEXIT_CRITICAL_ISR(µsMux); + } + +uint32_t *ptr;// [0] is lastCount, [1] is overFlow + +ptr = pthread_getspecific(microsStore); // get address of storage + +if(ptr == NULL){ // first time in this thread, allocate mem, init it. + portENTER_CRITICAL_ISR(µsMux); + ptr = (uint32_t*)malloc(sizeof(uint32_t)*2); + pthread_setspecific(microsStore,ptr); // store the pointer to this thread's values + ptr[0] = 0; // lastCount value + ptr[1] = 0; // overFlow + portEXIT_CRITICAL_ISR(µsMux); + } + +unsigned long ccount; +portENTER_CRITICAL_ISR(µsMux); +__asm__ __volatile__ ( "rsr %0, ccount" : "=a" (ccount) ); +if(ccount < ptr[0]){ + ptr[1] += UINT32_MAX / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ; + } +ptr[0] = ccount; +portEXIT_CRITICAL_ISR(µsMux); +return ptr[1] + (ccount / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ); } unsigned long IRAM_ATTR millis() From 45566aab785b04fe9c5369d21a9bbcb089ff6c9b Mon Sep 17 00:00:00 2001 From: chuck todd Date: Sun, 4 Mar 2018 16:01:12 -0700 Subject: [PATCH 34/63] Wrong function return on microsStoreDelete() Should have been straight Void, not Void * --- cores/esp32/esp32-hal-misc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cores/esp32/esp32-hal-misc.c b/cores/esp32/esp32-hal-misc.c index fd46d77e033..82d61e575d5 100644 --- a/cores/esp32/esp32-hal-misc.c +++ b/cores/esp32/esp32-hal-misc.c @@ -40,7 +40,7 @@ void yield() portMUX_TYPE microsMux = portMUX_INITIALIZER_UNLOCKED; static pthread_key_t microsStore=NULL; // Thread Local Storage Handle -void* microsStoreDelete(void * storage){ // release thread local data when task is delete. +void microsStoreDelete(void * storage){ // release thread local data when task is delete. if(storage) free(storage); } From 053a23ffc5ed18832e739a9765bbc2414164d9ac Mon Sep 17 00:00:00 2001 From: chuck todd Date: Fri, 9 Mar 2018 12:20:52 -0700 Subject: [PATCH 35/63] Delete esp32-hal-misc.c --- cores/esp32/esp32-hal-misc.c | 148 ----------------------------------- 1 file changed, 148 deletions(-) delete mode 100644 cores/esp32/esp32-hal-misc.c diff --git a/cores/esp32/esp32-hal-misc.c b/cores/esp32/esp32-hal-misc.c deleted file mode 100644 index 82d61e575d5..00000000000 --- a/cores/esp32/esp32-hal-misc.c +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "esp32-hal.h" -#include "sdkconfig.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_attr.h" -#include "nvs_flash.h" -#include "nvs.h" -#include "esp_partition.h" -#include "esp_log.h" -#include - -//Undocumented!!! Get chip temperature in Farenheit -//Source: https://github.com/pcbreflux/espressif/blob/master/esp32/arduino/sketchbook/ESP32_int_temp_sensor/ESP32_int_temp_sensor.ino -uint8_t temprature_sens_read(); - -float temperatureRead() -{ - return (temprature_sens_read() - 32) / 1.8; -} - -void yield() -{ - vPortYield(); -} - -portMUX_TYPE microsMux = portMUX_INITIALIZER_UNLOCKED; -static pthread_key_t microsStore=NULL; // Thread Local Storage Handle - -void microsStoreDelete(void * storage){ // release thread local data when task is delete. -if(storage) free(storage); -} - -unsigned long IRAM_ATTR micros(){ - -if (!microsStore){ // first Time Ever thread local not init'd - portENTER_CRITICAL_ISR(µsMux); - pthread_key_create(µsStore,microsStoreDelete); // create initial holder - portEXIT_CRITICAL_ISR(µsMux); - } - -uint32_t *ptr;// [0] is lastCount, [1] is overFlow - -ptr = pthread_getspecific(microsStore); // get address of storage - -if(ptr == NULL){ // first time in this thread, allocate mem, init it. - portENTER_CRITICAL_ISR(µsMux); - ptr = (uint32_t*)malloc(sizeof(uint32_t)*2); - pthread_setspecific(microsStore,ptr); // store the pointer to this thread's values - ptr[0] = 0; // lastCount value - ptr[1] = 0; // overFlow - portEXIT_CRITICAL_ISR(µsMux); - } - -unsigned long ccount; -portENTER_CRITICAL_ISR(µsMux); -__asm__ __volatile__ ( "rsr %0, ccount" : "=a" (ccount) ); -if(ccount < ptr[0]){ - ptr[1] += UINT32_MAX / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ; - } -ptr[0] = ccount; -portEXIT_CRITICAL_ISR(µsMux); -return ptr[1] + (ccount / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ); -} - -unsigned long IRAM_ATTR millis() -{ - return xTaskGetTickCount() * portTICK_PERIOD_MS; -} - -void delay(uint32_t ms) -{ - vTaskDelay(ms / portTICK_PERIOD_MS); -} - -void IRAM_ATTR delayMicroseconds(uint32_t us) -{ - uint32_t m = micros(); - if(us){ - uint32_t e = (m + us); - if(m > e){ //overflow - while(micros() > e){ - NOP(); - } - } - while(micros() < e){ - NOP(); - } - } -} - -void initVariant() __attribute__((weak)); -void initVariant() {} - -void init() __attribute__((weak)); -void init() {} - -void initArduino() -{ - esp_log_level_set("*", CONFIG_LOG_DEFAULT_LEVEL); - esp_err_t err = nvs_flash_init(); - if(err == ESP_ERR_NVS_NO_FREE_PAGES){ - const esp_partition_t* partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, NULL); - if (partition != NULL) { - err = esp_partition_erase_range(partition, 0, partition->size); - if(!err){ - err = nvs_flash_init(); - } else { - log_e("Failed to format the broken NVS partition!"); - } - } - } - if(err) { - log_e("Failed to initialize NVS! Error: %u", err); - } - init(); - initVariant(); -} - -//used by hal log -const char * IRAM_ATTR pathToFileName(const char * path) -{ - size_t i = 0; - size_t pos = 0; - char * p = (char *)path; - while(*p){ - i++; - if(*p == '/' || *p == '\\'){ - pos = i; - } - p++; - } - return path+pos; -} - From 119afb1d9e1a87bd43768af9d75e8cc3e99262e0 Mon Sep 17 00:00:00 2001 From: chuck todd Date: Fri, 9 Mar 2018 12:22:39 -0700 Subject: [PATCH 36/63] solve pr --- cores/esp32/esp32-hal-misc.c | 152 +++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 cores/esp32/esp32-hal-misc.c diff --git a/cores/esp32/esp32-hal-misc.c b/cores/esp32/esp32-hal-misc.c new file mode 100644 index 00000000000..7c87310eac2 --- /dev/null +++ b/cores/esp32/esp32-hal-misc.c @@ -0,0 +1,152 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "esp32-hal.h" +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_attr.h" +#include "nvs_flash.h" +#include "nvs.h" +#include "esp_partition.h" +#include "esp_log.h" +#include "pthread.h" +#include + +//Undocumented!!! Get chip temperature in Farenheit +//Source: https://github.com/pcbreflux/espressif/blob/master/esp32/arduino/sketchbook/ESP32_int_temp_sensor/ESP32_int_temp_sensor.ino +uint8_t temprature_sens_read(); + +float temperatureRead() +{ + return (temprature_sens_read() - 32) / 1.8; +} + +void yield() +{ + vPortYield(); +} + +portMUX_TYPE microsMux = portMUX_INITIALIZER_UNLOCKED; +static pthread_key_t microsStore=NULL; // Thread Local Storage Handle + +void microsStoreDelete(void * storage) { // release thread local data when task is delete. + if(storage) free(storage); +} + +unsigned long IRAM_ATTR micros() +{ + if (!microsStore) { // first Time Ever thread local not init'd + portENTER_CRITICAL_ISR(µsMux); + pthread_key_create(µsStore,microsStoreDelete); // create initial holder + portEXIT_CRITICAL_ISR(µsMux); + } + + uint32_t *ptr;// [0] is lastCount, [1] is overFlow + + ptr = pthread_getspecific(microsStore); // get address of storage + + if(ptr == NULL) { // first time in this thread, allocate mem, init it. + portENTER_CRITICAL_ISR(µsMux); + ptr = (uint32_t*)malloc(sizeof(uint32_t)*2); + pthread_setspecific(microsStore,ptr); // store the pointer to this thread's values + ptr[0] = 0; // lastCount value + ptr[1] = 0; // overFlow + portEXIT_CRITICAL_ISR(µsMux); + } + + unsigned long ccount; + + portENTER_CRITICAL_ISR(µsMux); + __asm__ __volatile__ ( "rsr %0, ccount" : "=a" (ccount) ); //get cycle count + if(ccount < ptr[0]) { // overflow occurred + ptr[1] += UINT32_MAX / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ; + } + + ptr[0] = ccount; + portEXIT_CRITICAL_ISR(µsMux); + + return ptr[1] + (ccount / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ); +} + +unsigned long IRAM_ATTR millis() +{ + return xTaskGetTickCount() * portTICK_PERIOD_MS; +} + +void delay(uint32_t ms) +{ + vTaskDelay(ms / portTICK_PERIOD_MS); +} + +void IRAM_ATTR delayMicroseconds(uint32_t us) +{ + uint32_t m = micros(); + if(us){ + uint32_t e = (m + us); + if(m > e){ //overflow + while(micros() > e){ + NOP(); + } + } + while(micros() < e){ + NOP(); + } + } +} + +void initVariant() __attribute__((weak)); +void initVariant() {} + +void init() __attribute__((weak)); +void init() {} + +void initArduino() +{ + esp_log_level_set("*", CONFIG_LOG_DEFAULT_LEVEL); + esp_err_t err = nvs_flash_init(); + if(err == ESP_ERR_NVS_NO_FREE_PAGES){ + const esp_partition_t* partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, NULL); + if (partition != NULL) { + err = esp_partition_erase_range(partition, 0, partition->size); + if(!err){ + err = nvs_flash_init(); + } else { + log_e("Failed to format the broken NVS partition!"); + } + } + } + if(err) { + log_e("Failed to initialize NVS! Error: %u", err); + } + init(); + initVariant(); +} + +//used by hal log +const char * IRAM_ATTR pathToFileName(const char * path) +{ + size_t i = 0; + size_t pos = 0; + char * p = (char *)path; + while(*p){ + i++; + if(*p == '/' || *p == '\\'){ + pos = i; + } + p++; + } + return path+pos; +} + From 0f79fa0e16971be0b8f4416f9270f3dd0b922814 Mon Sep 17 00:00:00 2001 From: chuck todd Date: Fri, 9 Mar 2018 13:34:28 -0700 Subject: [PATCH 37/63] Update Wire.h Add define for library identification #define STICKBREAKER // issue #17 --- libraries/Wire/src/Wire.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/Wire/src/Wire.h b/libraries/Wire/src/Wire.h index 1466bc03133..89070b6fd0b 100644 --- a/libraries/Wire/src/Wire.h +++ b/libraries/Wire/src/Wire.h @@ -30,6 +30,7 @@ #include "freertos/queue.h" #include "Stream.h" +#define STICKBREAKER #define I2C_BUFFER_LENGTH 128 typedef void(*user_onRequest)(void); typedef void(*user_onReceive)(uint8_t*, int); From 7c35002227a04599a9dc2075ec4498962efd4740 Mon Sep 17 00:00:00 2001 From: chuck todd Date: Sat, 10 Mar 2018 19:36:11 -0700 Subject: [PATCH 38/63] improve Glitch prevention My prior glitch prevention relied on previous pin configuration. This update sets all values. Also, I think I have found a way to reset the I2C hardware to the same state as a Power Cycle or Hardware reset. --- cores/esp32/esp32-hal-i2c.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index 34e8e4d9973..5c4e047bbd5 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -176,7 +176,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 +189,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 +199,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 +212,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; } @@ -297,7 +297,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; @@ -315,9 +315,11 @@ i2c_t * i2cInit(uint8_t i2c_num) #endif 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); } From fb05a5976d631d163e70b0e8bbb66c176e82e873 Mon Sep 17 00:00:00 2001 From: chuck todd Date: Sat, 10 Mar 2018 19:50:08 -0700 Subject: [PATCH 39/63] improved glitch prevention, hardware reset These changes should remove the possibility of a signal glitch when starting or restarting the Wire() object. Also, I think I have found a way to reset the i2c hardware to a Power On or Hardware Reset state. --- libraries/Wire/src/Wire.cpp | 44 +++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp index fed35ba71cc..36165bfd1cd 100644 --- a/libraries/Wire/src/Wire.cpp +++ b/libraries/Wire/src/Wire.cpp @@ -68,32 +68,48 @@ void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency) } } + i2cDetatchSCL(i2c,scl); // detach pins before resetting I2C perpherial + i2cDetatchSDA(i2c,sda); // else a glitch will appear on the i2c bus + i2c = i2cInit(num);// i2cInit() now performs a hardware reset if(i2c == NULL) { - i2c = i2cInit(num); - if(i2c == NULL) { - return; - } - } + return; + } i2cSetFrequency(i2c, frequency); - if(sda >= 0 && sda != sdaPin ) { - i2cDetachSDA(i2c, sda); - } - - if(scl >= 0 && scl != sclPin ) { - i2cDetachSCL(i2c, scl); - } - sda = sdaPin; scl = sclPin; +// 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_d("invalid state sda=%d, scl=%d",digitalRead(sda),digitalRead(scl)); + digitalWrite(sda,HIGH); + digitalWrite(scl,HIGH); + delayMicroseconds(50); + digitalWrite(sda,LOW); + for(uint8_t a=0; a<9;a++){ + delayMicroseconds(50); + digitalWrite(scl,LOW); + delayMicroseconds(50); + digitalWrite(scl,HIGH); + } + delayMicroseconds(50); + digitalWrite(sda,HIGH); + } i2cAttachSDA(i2c, sda); i2cAttachSCL(i2c, scl); flush(); - i2cInitFix(i2c); +/* This function should no longer be necessary, the + i2cInitFix(i2c); +*/ } void TwoWire::setTimeOut(uint16_t timeOutMillis){ From 6b682d23e581f9669cdec11faf2825c34c76e6ba Mon Sep 17 00:00:00 2001 From: chuck todd Date: Sat, 10 Mar 2018 21:00:14 -0700 Subject: [PATCH 40/63] Forgot to reset clockrate after hardware reset After hardware reset, all i2c hardware register are back to zero, so the clockrate needs to be recovered. --- cores/esp32/esp32-hal-i2c.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index 5c4e047bbd5..66ca1e2a211 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -287,8 +287,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; } /* @@ -313,6 +318,8 @@ i2c_t * i2cInit(uint8_t i2c_num) //before this is called, pins should be detache } } #endif + I2C_MUTEX_LOCK(); + 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 @@ -323,8 +330,6 @@ i2c_t * i2cInit(uint8_t i2c_num) //before this is called, pins should be detache 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 ; @@ -338,6 +343,8 @@ i2c_t * i2cInit(uint8_t i2c_num) //before this is called, pins should be detache i2c->dev->slave_addr.val = 0; I2C_MUTEX_UNLOCK(); + + if(old_clock) i2cSetFrequency(i2c,old_clock); // reconfigure return i2c; } @@ -967,7 +974,11 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis) if(i2c == NULL){ return I2C_ERROR_DEV; } - +if (i2c->dev->status_reg.bus_busy){ + log_e("Bus busy, reinit"); + i2cInit(i2c->num); + return I2C_ERROR_BUSY; + } I2C_MUTEX_LOCK(); /* what about co-existance with SLAVE mode? Should I check if a slaveMode xfer is in progress and hang From 8e3928953b0cf98f900c99c8dcc4b153c6f32533 Mon Sep 17 00:00:00 2001 From: chuck todd Date: Sat, 10 Mar 2018 21:35:30 -0700 Subject: [PATCH 41/63] can't Spell! I had manually applied these changes and misspelled two function names! --- libraries/Wire/src/Wire.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp index 36165bfd1cd..c7445e72e3d 100644 --- a/libraries/Wire/src/Wire.cpp +++ b/libraries/Wire/src/Wire.cpp @@ -68,8 +68,8 @@ void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency) } } - i2cDetatchSCL(i2c,scl); // detach pins before resetting I2C perpherial - i2cDetatchSDA(i2c,sda); // else a glitch will appear on the i2c bus + 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; From 6ba9fa123a8711bcba1dc552615ade6ae8495479 Mon Sep 17 00:00:00 2001 From: chuck todd Date: Sun, 11 Mar 2018 13:00:23 -0600 Subject: [PATCH 42/63] change action when bus_busy detected Continue with I2C transaction if bus_busy is cleared by i2cInit() --- cores/esp32/esp32-hal-i2c.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index 66ca1e2a211..6dfdc2a09a6 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -975,9 +975,10 @@ if(i2c == NULL){ return I2C_ERROR_DEV; } if (i2c->dev->status_reg.bus_busy){ - log_e("Bus busy, reinit"); + log_i("Bus busy, reinit"); i2cInit(i2c->num); - return I2C_ERROR_BUSY; + if (i2c->dev->status_reg.bus_busy) return I2C_ERROR_BUSY; + else log_i("recovered"); } I2C_MUTEX_LOCK(); /* what about co-existance with SLAVE mode? From ef95f860c776fa9a54545a0ae170a77f8c8ead47 Mon Sep 17 00:00:00 2001 From: chuck todd Date: Sun, 11 Mar 2018 19:07:40 -0600 Subject: [PATCH 43/63] use begin() to reset after bus busy `SCL` and `SDA` need to be detached from the i2c hardware before the i2c hardware is init'd else a glitch is generated. esp32-hal-i2c.c does not know which pins are assigned to the hardware. Return the error and let `TwoWire()` do the reinit. Also, '~' is not the same as '!' My Bad. --- libraries/Wire/src/Wire.cpp | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp index c7445e72e3d..33f70b54078 100644 --- a/libraries/Wire/src/Wire.cpp +++ b/libraries/Wire/src/Wire.cpp @@ -86,19 +86,19 @@ void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency) pinMode(sda,PULLUP|OPEN_DRAIN|OUTPUT|INPUT); pinMode(scl,PULLUP|OPEN_DRAIN|OUTPUT|INPUT); - if(!digitalRead(sda)||~digitalRead(scl)){ // bus in busy state + if(!digitalRead(sda)||!digitalRead(scl)){ // bus in busy state log_d("invalid state sda=%d, scl=%d",digitalRead(sda),digitalRead(scl)); digitalWrite(sda,HIGH); digitalWrite(scl,HIGH); - delayMicroseconds(50); + delayMicroseconds(5); digitalWrite(sda,LOW); for(uint8_t a=0; a<9;a++){ - delayMicroseconds(50); + delayMicroseconds(5); digitalWrite(scl,LOW); - delayMicroseconds(50); + delayMicroseconds(5); digitalWrite(scl,HIGH); } - delayMicroseconds(50); + delayMicroseconds(5); digitalWrite(sda,HIGH); } @@ -127,17 +127,22 @@ void TwoWire::setClock(uint32_t frequency) /*@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 + begin(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(); + } + i2cFreeQueue(i2c); + return last_error; } /* @stickBreaker 11/2017 fix for ReSTART timeout, ISR From f5b027c88a7062299d99082047b3e7f4ad5b5933 Mon Sep 17 00:00:00 2001 From: chuck todd Date: Sun, 11 Mar 2018 19:12:14 -0600 Subject: [PATCH 44/63] let TwoWire() handle resetting the Hardware The i2c data bus must be disconnected from the i2c peripheral before it is init'd. Else it will generate a glitch on both pins. Since the Hal layer does not remember which pins are assigned, just return the error and let `TwoWire()` handle re-initing. --- cores/esp32/esp32-hal-i2c.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index 6dfdc2a09a6..632b6e0d6ca 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -974,11 +974,9 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis) if(i2c == NULL){ return I2C_ERROR_DEV; } -if (i2c->dev->status_reg.bus_busy){ +if (i2c->dev->status_reg.bus_busy){ // return error, let TwoWire() handle resetting the hardware. log_i("Bus busy, reinit"); - i2cInit(i2c->num); - if (i2c->dev->status_reg.bus_busy) return I2C_ERROR_BUSY; - else log_i("recovered"); + return I2C_ERROR_BUSY; } I2C_MUTEX_LOCK(); /* what about co-existance with SLAVE mode? From c6a44d758a94b58a1f49c98402e0a0b256f591ba Mon Sep 17 00:00:00 2001 From: chuck todd Date: Sun, 11 Mar 2018 20:02:19 -0600 Subject: [PATCH 45/63] fix for reinit had to move the hardware init code out of begin, so that I could call it without destroying queued transactions --- libraries/Wire/src/Wire.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/Wire/src/Wire.h b/libraries/Wire/src/Wire.h index 89070b6fd0b..76293a8cb08 100644 --- a/libraries/Wire/src/Wire.h +++ b/libraries/Wire/src/Wire.h @@ -65,6 +65,7 @@ class TwoWire: public Stream i2c_err_t processQueue(uint32_t *readCount); uint16_t _timeOutMillis; bool _dump; + void initHardware(int sdaPin, int sclPin, uint32_t frequency); public: TwoWire(uint8_t bus_num); From 74710c11112f831ff95773c54ff622c231180d8b Mon Sep 17 00:00:00 2001 From: chuck todd Date: Sun, 11 Mar 2018 20:09:59 -0600 Subject: [PATCH 46/63] separate initHardware() --- libraries/Wire/src/Wire.cpp | 52 ++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp index 33f70b54078..6638b055905 100644 --- a/libraries/Wire/src/Wire.cpp +++ b/libraries/Wire/src/Wire.cpp @@ -67,12 +67,35 @@ void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency) return; } } + + if(!initHardware(sdaPin, sclPin, frequency)) return; + + flush(); + +} + +void TwoWire::setTimeOut(uint16_t timeOutMillis){ + _timeOutMillis = timeOutMillis; +} + +uint16_t TwoWire::getTimeOut(){ + return _timeOutMillis; +} + +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 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; + return false; } i2cSetFrequency(i2c, frequency); @@ -104,33 +127,14 @@ void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency) i2cAttachSDA(i2c, sda); i2cAttachSCL(i2c, scl); + return true; +} - flush(); - -/* This function should no longer be necessary, the - i2cInitFix(i2c); -*/ -} - -void TwoWire::setTimeOut(uint16_t timeOutMillis){ - _timeOutMillis = timeOutMillis; -} - -uint16_t TwoWire::getTimeOut(){ - return _timeOutMillis; -} - -void TwoWire::setClock(uint32_t frequency) -{ - i2cSetFrequency(i2c, frequency); -} -/*@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 - begin(sda,scl,getClock()); - last_error=i2cProcQueue(i2c,readCount,_timeOutMillis); + if(initHardware(sda,scl,getClock())) + last_error=i2cProcQueue(i2c,readCount,_timeOutMillis); } rxIndex = 0; From aac21fed80e37ca4d7bdecd2e9e87d2ccf3a385b Mon Sep 17 00:00:00 2001 From: chuck todd Date: Sun, 11 Mar 2018 20:10:42 -0600 Subject: [PATCH 47/63] match function types --- 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 76293a8cb08..04c53505b4e 100644 --- a/libraries/Wire/src/Wire.h +++ b/libraries/Wire/src/Wire.h @@ -65,7 +65,7 @@ class TwoWire: public Stream i2c_err_t processQueue(uint32_t *readCount); uint16_t _timeOutMillis; bool _dump; - void initHardware(int sdaPin, int sclPin, uint32_t frequency); + bool initHardware(int sdaPin, int sclPin, uint32_t frequency); public: TwoWire(uint8_t bus_num); From c17b4bd222815733eddd325d3127a9c35be3dac5 Mon Sep 17 00:00:00 2001 From: chuck todd Date: Sun, 11 Mar 2018 21:47:30 -0600 Subject: [PATCH 48/63] No glitches, handles Bus Faults Reinit i2c hardware when bus fault (busy) is detected --- libraries/Wire/src/Wire.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp index 6638b055905..c2faa2d5c3d 100644 --- a/libraries/Wire/src/Wire.cpp +++ b/libraries/Wire/src/Wire.cpp @@ -110,7 +110,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 - log_d("invalid state sda=%d, scl=%d",digitalRead(sda),digitalRead(scl)); + // Serial.printf("invalid state sda=%d, scl=%d\n",digitalRead(sda),digitalRead(scl)); digitalWrite(sda,HIGH); digitalWrite(scl,HIGH); delayMicroseconds(5); @@ -124,17 +124,23 @@ bool TwoWire::initHardware(int sdaPin, int sclPin, uint32_t frequency){ delayMicroseconds(5); digitalWrite(sda,HIGH); } - i2cAttachSDA(i2c, sda); i2cAttachSCL(i2c, scl); + + if(!digitalRead(sda)||!digitalRead(scl)){ // bus in busy state +// Serial.println("Bus Invalid State, TwoWire() Can't init"); + return false; // bus is busy + } + return true; } 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 - if(initHardware(sda,scl,getClock())) + if(initHardware(sda,scl,getClock())){ last_error=i2cProcQueue(i2c,readCount,_timeOutMillis); + } } rxIndex = 0; @@ -143,7 +149,7 @@ i2c_err_t TwoWire::processQueue(uint32_t * readCount){ txQueued = 0; // the SendStop=true will restart all Queueing if(_dump){ i2cDumpI2c(i2c); - i2cDumpInts(); + i2cDumpInts(num); } i2cFreeQueue(i2c); return last_error; @@ -230,7 +236,7 @@ return last_error; /*stickbreaker Dump i2c Interrupt buffer, i2c isr Debugging */ void TwoWire::dumpInts(){ - i2cDumpInts(); + i2cDumpInts(num); } /*stickbreaker i2c isr Debugging From 3027a03b7a5e3930991403fe852b4e82f696ff2c Mon Sep 17 00:00:00 2001 From: chuck todd Date: Sun, 11 Mar 2018 21:48:36 -0600 Subject: [PATCH 49/63] no Glitches, ReInit hardware --- cores/esp32/esp32-hal-i2c.c | 99 ++++++++++++++++++++----------------- cores/esp32/esp32-hal-i2c.h | 2 +- 2 files changed, 54 insertions(+), 47 deletions(-) diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index 632b6e0d6ca..742194d2015 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -385,10 +385,10 @@ void i2cReset(i2c_t* i2c){ /* 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 @@ -654,13 +654,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 @@ -706,22 +703,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; } @@ -755,10 +749,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 { @@ -822,23 +816,22 @@ 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; @@ -949,13 +942,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\""); @@ -986,9 +979,13 @@ I2C_MUTEX_LOCK(); */ 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 // not always reliable. Sometimes, the FreeRTOS scheduler is maxed out and refuses request @@ -1075,9 +1072,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"); + uint32_t ret; + 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(); @@ -1113,11 +1120,11 @@ 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 } @@ -1128,7 +1135,7 @@ if(eBits&EVENT_DONE){ // no gross timeout // expected can be zero due to small packets log_e("TimeoutRecovery: expected=%ums, actual=%ums",expected,(tAfter-tBefore)); i2cDumpI2c(i2c); - i2cDumpInts(); + i2cDumpInts(i2c->num); } #endif switch(i2c->error){ @@ -1164,7 +1171,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. @@ -1174,7 +1181,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 } } diff --git a/cores/esp32/esp32-hal-i2c.h b/cores/esp32/esp32-hal-i2c.h index bf75f0e6808..97230622a4b 100644 --- a/cores/esp32/esp32-hal-i2c.h +++ b/cores/esp32/esp32-hal-i2c.h @@ -169,7 +169,7 @@ i2c_err_t i2cAddQueueRead(i2c_t *i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, i2c_err_t i2cFreeQueue(i2c_t *i2c); i2c_err_t i2cReleaseISR(i2c_t *i2c); //stickbreaker debug support -void i2cDumpInts(); +void i2cDumpInts(uint8_t num); void i2cDumpI2c(i2c_t *i2c); #ifdef __cplusplus From a0db0ccce79922c35529bac54fc503476049b624 Mon Sep 17 00:00:00 2001 From: chuck todd Date: Thu, 15 Mar 2018 16:41:30 -0600 Subject: [PATCH 50/63] 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 51/63] 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 52/63] 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 53/63] 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 54/63] 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 55/63] 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 56/63] 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 57/63] 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 58/63] 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 59/63] 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 60/63] 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 61/63] 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 62/63] 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 63/63] 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;