From b05430cfd9f273d627baa6c4dac6c215d6cde902 Mon Sep 17 00:00:00 2001 From: chuck todd Date: Tue, 14 Aug 2018 03:51:15 -0600 Subject: [PATCH] Wire ReSTART fix, with others (#1717) * ReSTART fix, Sequencing fix pr #1665 introduce a problem with ReSTART, when solving this problem I found an interaction between the TxFifo refill, RxFifo empty and CMD[] fill. during certain sequences a dataqueue command would be skipped, this skipping resulted in a mismatch between the contents of the TxFifo and the i2c command sequence. The problem manifested as an ACK error. In addition to this required bug fix I propose: * `Wire.begin()` be changed from a `void` to a `bool` this will allow the reset functionality of `Wire.begin()` to be reported. Currently `Wire.begin()` attempts to reset the i2c Peripheral, but cannot report success/failure. * `Wire.busy()` be added. this `bool` function returns the hardware status of the bus. This status can be use in multi-master environments for application level interleaving of commands, also in single master environment, it can be used to detect a 'hung' bus. With the functional change to `Wire.begin()` this allows app level recover of a hung bus. * `Wire.lastError()` value updated for all errors, previously when interleaving `Wire.endTransmission(false)` and `Wire.readTransmission(false)`, the 128 byte `Wire.write()` buffer was exhausted without generating and error(very exotic). I discovered this error when I created a sequence of directed reads to a EEPROM. Each directed read used 2 bytes of the 128 byte `write()` buffer, so after 64 consecutive ReSTART writes with ReSTART reads, `Wire()` had no room to record the directed address bytes. It generated just a NAK check without setting the EEPROMs internal register address. The succeeding ReSTART read succeeded at incorrect address. * Changes to the HAL layer: ** added `i2cGetStatus()` which returns the i2c peripheral status word, used to detect bus_busy currently ** added `i2cDebug()` programmatic control of debug buffer output ** changed `i2cAddQueue()` to allow data_only queue element this will allow a i2c transaction to use multiple data pointers. ** removed direct access to DumpInts(), DumpI2c() from app, use i2cDebug() to set trigger points * * Update esp32-hal-i2c.c * Update Wire.cpp * ReSTART, Sequencing pr #1665 introduce a problem with ReSTART, when solving this problem I found an interaction between the TxFifo refill, RxFifo empty and CMD[] fill. during certain sequences a dataqueue command would be skipped, this skipping resulted in a mismatch between the contents of the TxFifo and the i2c command sequence. The problem manifested as an ACK error. In addition to this required bug fix I propose: * `Wire.begin()` be changed from a `void` to a `bool` this will allow the reset functionality of `Wire.begin()` to be reported. Currently `Wire.begin()` attempts to reset the i2c Peripheral, but cannot report success/failure. * `Wire.busy()` be added. this `bool` function returns the hardware status of the bus. This status can be use in multi-master environments for application level interleaving of commands, also in single master environment, it can be used to detect a 'hung' bus. With the functional change to `Wire.begin()` this allows app level recover of a hung bus. * `Wire.lastError()` value updated for all errors, previously when interleaving `Wire.endTransmission(false)` and `Wire.readTransmission(false)`, the 128 byte `Wire.write()` buffer was exhausted without generating and error(very exotic). I discovered this error when I created a sequence of directed reads to a EEPROM. Each directed read used 2 bytes of the 128 byte `write()` buffer, so after 64 consecutive ReSTART writes with ReSTART reads, `Wire()` had no room to record the directed address bytes. It generated just a NAK check without setting the EEPROMs internal register address. The succeeding ReSTART read succeeded at incorrect address. * Changes to the HAL layer: ** added `i2cGetStatus()` which returns the i2c peripheral status word, used to detect bus_busy currently ** added `i2cDebug()` programmatic control of debug buffer output ** changed `i2cAddQueue()` to allow data_only queue element this will allow a i2c transaction to use multiple data pointers. ** removed direct access to DumpInts(), DumpI2c() from app, use i2cDebug() to set trigger points * * Forgot DebugFlags Return @andriyadi found this, total brain fade on my part. --- cores/esp32/esp32-hal-i2c.c | 581 +++++++++++++++++++++++------------- cores/esp32/esp32-hal-i2c.h | 14 +- libraries/Wire/src/Wire.cpp | 35 +-- libraries/Wire/src/Wire.h | 11 +- 4 files changed, 413 insertions(+), 228 deletions(-) diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index 2757c385..c864419b 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -36,17 +36,21 @@ /* Stickbreaker ISR mode debug support ENABLE_I2C_DEBUG_BUFFER - Enable debug interrupt history buffer, setting this define will result in 1544 bytes of RAM - being used whenever CORE_DEBUG_LEVEL is higher than WARNING. Unless you are debugging - a problem in the I2C subsystem I would recommend you leave it commented out. + Enable debug interrupt history buffer, fifoTx history buffer. + Setting this define will result in 2570 bytes of RAM being used whenever CORE_DEBUG_LEVEL + is higher than WARNING. Unless you are debugging a problem in the I2C subsystem, + I would recommend you leave it commented out. */ //#define ENABLE_I2C_DEBUG_BUFFER #if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER) #define INTBUFFMAX 64 +#define FIFOMAX 512 static uint32_t intBuff[INTBUFFMAX][3][2]; static uint32_t intPos[2]= {0,0}; +static uint16_t fifoBuffer[FIFOMAX]; +static uint16_t fifoPos = 0; #endif // start from tools/sdk/include/soc/soc/i2c_struct.h @@ -154,7 +158,7 @@ typedef union { // individual dq element typedef struct { - uint8_t *data; // datapointer for read/write buffer + uint8_t *data; // data pointer for read/write buffer uint16_t length; // size of data buffer uint16_t position; // current position for next char in buffer (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, -1, -1, I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0,0}, - {(volatile i2c_dev_t *)(DR_REG_I2C1_EXT_BASE_FIXED), NULL, 1, -1, -1,I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0,0} + {(volatile i2c_dev_t *)(DR_REG_I2C_EXT_BASE_FIXED), NULL, 0, -1, -1, I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0,0,0}, + {(volatile i2c_dev_t *)(DR_REG_I2C1_EXT_BASE_FIXED), NULL, 1, -1, -1,I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0,0,0} }; #endif @@ -218,6 +223,222 @@ static i2c_t _i2c_bus_array[2] = { * ack_exp - This bit is to set an expected ACK value for the transmitter. * ack_check - This bit is to decide whether the transmitter checks ACK bit. 1 means yes and 0 means no. * */ + + +/* Stickbreaker ISR mode debug support + */ +static void IRAM_ATTR i2cDumpCmdQueue(i2c_t *i2c) +{ +#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR)&&(defined ENABLE_I2C_DEBUG_BUFFER) + static const char * const cmdName[] ={"RSTART","WRITE","READ","STOP","END"}; + uint8_t i=0; + while(i<16) { + I2C_COMMAND_t c; + c.val=i2c->dev->command[i].val; + log_e("[%2d]\t%c\t%s\tval[%d]\texp[%d]\ten[%d]\tbytes[%d]",i,(c.done?'Y':'N'), + cmdName[c.op_code], + c.ack_val, + c.ack_exp, + c.ack_en, + c.byte_num); + i++; + } +#endif +} + +/* Stickbreaker ISR mode debug support + */ +static void i2cDumpDqData(i2c_t * i2c) +{ +#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&&(defined ENABLE_I2C_DEBUG_BUFFER) + uint16_t a=0; + char buff[140]; + I2C_DATA_QUEUE_t *tdq; + int digits=0,lenDigits=0; + a = i2c->queueCount; + while(a>0) { + digits++; + a /= 10; + } + while(aqueueCount) { // find maximum number of len decimal digits for formatting + if (i2c->dq[a].length > lenDigits ) lenDigits = i2c->dq[a].length; + a++; + } + a=0; + while(lenDigits>0){ + a++; + lenDigits /= 10; + } + lenDigits = a; + a = 0; + while(aqueueCount) { + tdq=&i2c->dq[a]; + char buf1[10],buf2[10]; + sprintf(buf1,"%0*d",lenDigits,tdq->length); + sprintf(buf2,"%0*d",lenDigits,tdq->position); + log_i("[%0*d] %sbit %x %c %s buf@=%p, len=%s, pos=%s, ctrl=%d%d%d%d%d",digits,a, + (tdq->ctrl.addr>0x100)?"10":"7", + (tdq->ctrl.addr>0x100)?(((tdq->ctrl.addr&0x600)>>1)|(tdq->ctrl.addr&0xff)):(tdq->ctrl.addr>>1), + (tdq->ctrl.mode)?'R':'W', + (tdq->ctrl.stop)?"STOP":"", + tdq->data, + buf1,buf2, + tdq->ctrl.startCmdSent,tdq->ctrl.addrCmdSent,tdq->ctrl.dataCmdSent,(tdq->ctrl.stop)?tdq->ctrl.stopCmdSent:0,tdq->ctrl.addrSent + ); + 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_i("%s",buff); + } + a++; + } +#endif +} + +static void i2cDumpI2c(i2c_t * i2c) +{ + log_e("i2c=%p",i2c); + log_i("dev=%p date=%p",i2c->dev,i2c->dev->date); +#if !CONFIG_DISABLE_HAL_LOCKS + log_i("lock=%p",i2c->lock); +#endif + log_i("num=%d",i2c->num); + log_i("mode=%d",i2c->mode); + log_i("stage=%d",i2c->stage); + log_i("error=%d",i2c->error); + log_i("event=%p bits=%x",i2c->i2c_event,(i2c->i2c_event)?xEventGroupGetBits(i2c->i2c_event):0); + log_i("intr_handle=%p",i2c->intr_handle); + log_i("dq=%p",i2c->dq); + log_i("queueCount=%d",i2c->queueCount); + log_i("queuePos=%d",i2c->queuePos); + log_i("errorByteCnt=%d",i2c->errorByteCnt); + log_i("errorQueue=%d",i2c->errorQueue); + log_i("debugFlags=0x%08X",i2c->debugFlags); + if(i2c->dq) { + i2cDumpDqData(i2c); + } +} + +static void i2cDumpInts(uint8_t num) +{ +#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER) + + uint32_t b; + log_i("%u row\tcount\tINTR\tTX\tRX\tTick ",num); + for(uint32_t a=1; a<=INTBUFFMAX; a++) { + b=(a+intPos[num])%INTBUFFMAX; + if(intBuff[b][0][num]!=0) { + log_i("[%02d]\t0x%04x\t0x%04x\t0x%04x\t0x%04x\t0x%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_i("Debug Buffer not Enabled"); +#endif +} + +static void IRAM_ATTR i2cDumpStatus(i2c_t * i2c){ +#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&&(defined ENABLE_I2C_DEBUG_BUFFER) + typedef union { + struct { + uint32_t ack_rec: 1; /*This register stores the value of ACK bit.*/ + uint32_t slave_rw: 1; /*when in slave mode 1:master read slave 0: master write 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.*/ + 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.*/ + uint32_t reserved14: 4; + uint32_t tx_fifo_cnt: 6; /*This register stores the amount of received data in ram.*/ + uint32_t scl_main_state_last: 3; /*This register stores the value of state machine for i2c module. 3'h0: SCL_MAIN_IDLE 3'h1: SCL_ADDRESS_SHIFT 3'h2: SCL_ACK_ADDRESS 3'h3: SCL_RX_DATA 3'h4 SCL_TX_DATA 3'h5:SCL_SEND_ACK 3'h6:SCL_WAIT_ACK*/ + uint32_t reserved27: 1; + uint32_t scl_state_last: 3; /*This register stores the value of state machine to produce SCL. 3'h0: SCL_IDLE 3'h1:SCL_START 3'h2:SCL_LOW_EDGE 3'h3: SCL_LOW 3'h4:SCL_HIGH_EDGE 3'h5:SCL_HIGH 3'h6:SCL_STOP*/ + uint32_t reserved31: 1; + }; + uint32_t val; + } status_reg; + + status_reg sr; + sr.val= i2c->dev->status_reg.val; + + log_i("ack(%d) sl_rw(%d) to(%d) arb(%d) busy(%d) sl(%d) trans(%d) rx(%d) tx(%d) sclMain(%d) scl(%d)",sr.ack_rec,sr.slave_rw,sr.time_out,sr.arb_lost,sr.bus_busy,sr.slave_addressed,sr.byte_trans, sr.rx_fifo_cnt, sr.tx_fifo_cnt,sr.scl_main_state_last, sr.scl_state_last); +#endif +} + +static void i2cDumpFifo(i2c_t * i2c){ +#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&&(defined ENABLE_I2C_DEBUG_BUFFER) +char buf[64]; +uint16_t k = 0; +uint16_t i = fifoPos+1; + i %=FIFOMAX; +while((fifoBuffer[i]==0)&&(i!=fifoPos)){ + i++; + i %=FIFOMAX; +} +if(i != fifoPos){// actual data + do{ + if(fifoBuffer[i] & 0x8000){ // address byte + if(fifoBuffer[i] & 0x100) { // read + if(fifoBuffer[i] & 0x1) { // valid read dev id + k+= sprintf(&buf[k],"READ 0x%02X",(fifoBuffer[i]&0xff)>>1); + } else { // invalid read dev id + k+= sprintf(&buf[k],"Bad READ 0x%02X",(fifoBuffer[i]&0xff)); + } + } else { // write + if(fifoBuffer[i] & 0x1) { // bad write dev id + k+= sprintf(&buf[k],"bad WRITE 0x%02X",(fifoBuffer[i]&0xff)); + } else { // good Write + k+= sprintf(&buf[k],"WRITE 0x%02X",(fifoBuffer[i]&0xff)>>1); + } + } + } else k += sprintf(&buf[k],"% 4X ",fifoBuffer[i]); + + i++; + i %= FIFOMAX; + bool outBuffer=false; + if( fifoBuffer[i] & 0x8000){ + outBuffer=true; + k=0; + } + if((outBuffer)||(k>50)||(i==fifoPos)) log_i("%s",buf); + outBuffer = false; + if(k>50) { + k=sprintf(buf,"-> "); + } + }while( i!= fifoPos); +} +#endif +} + +static void IRAM_ATTR i2cTriggerDumps(i2c_t * i2c, uint8_t trigger, const char locus[]){ +#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&&(defined ENABLE_I2C_DEBUG_BUFFER) + if( trigger ){ + log_i("%s",locus); + if(trigger & 1) i2cDumpI2c(i2c); + if(trigger & 2) i2cDumpInts(i2c->num); + if(trigger & 4) i2cDumpCmdQueue(i2c); + if(trigger & 8) i2cDumpStatus(i2c); + if(trigger & 16) i2cDumpFifo(i2c); + } +#endif +} + // end of debug support routines + static void IRAM_ATTR i2cSetCmd(i2c_t * i2c, uint8_t index, uint8_t op_code, uint8_t byte_num, bool ack_val, bool ack_exp, bool ack_check) { I2C_COMMAND_t cmd; @@ -229,43 +450,24 @@ static void IRAM_ATTR i2cSetCmd(i2c_t * i2c, uint8_t index, uint8_t op_code, uin cmd.op_code = op_code; i2c->dev->command[index].val = cmd.val; } + - -/* Stickbreaker ISR mode debug support - */ -void IRAM_ATTR dumpCmdQueue(i2c_t *i2c) -{ -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR - 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++; - } -#endif -} - -/* Stickbreaker ISR mode support - */ static void IRAM_ATTR fillCmdQueue(i2c_t * i2c, bool INTS) { /* this function is called on initial i2cProcQueue() or when a I2C_END_DETECT_INT occurs */ uint16_t cmdIdx = 0; - uint16_t qp = i2c->queuePos; + uint16_t qp = i2c->queuePos; // all queues before queuePos have been completely processed, + // so look start by checking the 'current queue' so see if it needs commands added to + // hardware peripheral command list. walk through each queue entry until all queues have been + // checked bool done; bool needMoreCmds = false; 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) { + if(i2c->dq[qp].ctrl.stopCmdSent) {// marks that all required cmds[] have been added to peripheral qp++; } else { needMoreCmds=true; @@ -282,7 +484,6 @@ static void IRAM_ATTR fillCmdQueue(i2c_t * i2c, bool INTS) // (cmdIdx<14) because a START op cannot directly precede an END op, else a time out cascade occurs i2cSetCmd(i2c, cmdIdx++, I2C_CMD_RSTART, 0, false, false, false); tdq->ctrl.startCmdSent=1; - done = (cmdIdx>14); } //CMD WRITE ADDRESS @@ -341,11 +542,11 @@ static void IRAM_ATTR fillCmdQueue(i2c_t * i2c, bool INTS) 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 + if(!tdq->ctrl.stopCmdSent){ + if(tdq->ctrl.stop) { //send a stop + i2cSetCmd(i2c, (cmdIdx)++,I2C_CMD_STOP,0,false,false,false); + done = cmdIdx > 14; + } tdq->ctrl.stopCmdSent = 1; } } @@ -370,7 +571,7 @@ static void IRAM_ATTR fillCmdQueue(i2c_t * i2c, bool INTS) i--; } else { // unable to stretch, fatal log_e("invalid CMD[] layout Stretch Failed"); - dumpCmdQueue(i2c); + i2cDumpCmdQueue(i2c); done = true; } } @@ -409,8 +610,6 @@ static void IRAM_ATTR fillCmdQueue(i2c_t * i2c, bool INTS) } } -/* Stickbreaker ISR mode support - */ static void IRAM_ATTR fillTxFifo(i2c_t * i2c) { /* @@ -421,6 +620,7 @@ static void IRAM_ATTR fillTxFifo(i2c_t * i2c) uint16_t a=i2c->queuePos; // currently executing dq, bool full=!(i2c->dev->status_reg.tx_fifo_cnt<31); uint8_t cnt; + bool rxQueueEncountered = false; while((a < i2c->queueCount) && !full) { I2C_DATA_QUEUE_t *tdq = &i2c->dq[a]; cnt=0; @@ -449,24 +649,38 @@ static void IRAM_ATTR fillTxFifo(i2c_t * i2c) i2c->dev->fifo_data.val = tdq->ctrl.addr&0xFF; cnt++; tdq->ctrl.addrSent=1; // 7bit lowbyte sent +#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER) + uint16_t a = 0x8000 | tdq->ctrl.addr | (tdq->ctrl.mode<<8); + fifoBuffer[fifoPos++] = a; + fifoPos %= FIFOMAX; +#endif } } } } - 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)) { + uint32_t moveCnt = 32 - i2c->dev->status_reg.tx_fifo_cnt; // how much room in txFifo? + while(( moveCnt>0)&&(tdq->position < tdq->length)) { +#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER) + fifoBuffer[fifoPos++] = tdq->data[tdq->position]; + fifoPos %= FIFOMAX; +#endif i2c->dev->fifo_data.val = tdq->data[tdq->position++]; cnt++; - full=!(i2c->dev->status_reg.tx_fifo_cnt<31); + moveCnt--; + } } + } else { // read Queue Encountered, can't update QueuePos past this point, emptyRxfifo will do it + if( ! rxQueueEncountered ) { + rxQueueEncountered = true; + if(a > i2c->queuePos){ + i2c->queuePos = a; + } + } } - #if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER) // update debug buffer tx counts @@ -474,65 +688,78 @@ static void IRAM_ATTR fillTxFifo(i2c_t * i2c) intBuff[intPos[i2c->num]][1][i2c->num] = (intBuff[intPos[i2c->num]][1][i2c->num]&0xFFFF)|(cnt<<16); #endif - + full=!(i2c->dev->status_reg.tx_fifo_cnt<31); if(!full) { - a++; // check next buffer for tx + a++; // check next buffer for address tx, or write data } } - if(a >= i2c->queueCount ) { // disable IRQ, the next dq will re-enable it - // (a >= i2c->queueCount) means no more data is available + if(a >= i2c->queueCount ) { // disable TX IRQ, all tx Data has been queued 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, moveCnt; + moveCnt = i2c->dev->status_reg.rx_fifo_cnt;//no need to check the reg until this many are read + while((moveCnt > 0)&&(i2c->queuePos < i2c->queueCount)){ // data to move - I2C_DATA_QUEUE_t *tdq =&i2c->dq[i2c->queuePos]; //short cut - if(tdq->ctrl.mode == 1){ // read command - if(moveCnt > (tdq->length - tdq->position)) { //make sure they go in this dq - // part of these reads go into the next dq - moveCnt = (tdq->length - tdq->position); - } - } else {// error - log_e("RxEmpty(%d) call on TxBuffer? dq=%d",moveCnt,i2c->queuePos); - return; - } - while(moveCnt > 0) { - d = i2c->dev->fifo_data.val; - moveCnt--; - cnt++; - tdq->data[tdq->position++] = (d&0xFF); - } - if(tdq->position >= tdq->length ){ // inc queuePos until next READ command or end of queue - i2c->queuePos++; - while((i2c->queuePos < i2c->queueCount)&&(i2c->dq[i2c->queuePos].ctrl.mode !=1)){ - i2c->queuePos++; - } - if(i2c->queuePos < i2c->queueCount){ // found new place to store rx data - tdq = &i2c->dq[i2c->queuePos]; // update shortcut - // see if any more chars showed up while empting Fifo. - moveCnt = i2c->dev->status_reg.rx_fifo_cnt; + + I2C_DATA_QUEUE_t *tdq =&i2c->dq[i2c->queuePos]; //short cut + + while((tdq->position >= tdq->length)&&( i2c->queuePos < i2c->queueCount)){ // find were to store + i2c->queuePos++; + tdq = &i2c->dq[i2c->queuePos]; + } + + if(i2c->queuePos >= i2c->queueCount){ // bad stuff, rx data but no place to put it! + log_e("no Storage location for %d",moveCnt); + // discard + while(moveCnt>0){ + d = i2c->dev->fifo_data.val; + moveCnt--; + cnt++; + } + break; + } + + if(tdq->ctrl.mode == 1){ // read command if(moveCnt > (tdq->length - tdq->position)) { //make sure they go in this dq - // part of these reads go into the next dq + // part of these reads go into the next dq moveCnt = (tdq->length - tdq->position); } + } else {// error + log_e("RxEmpty(%d) call on TxBuffer? dq=%d",moveCnt,i2c->queuePos); + // discard + while(moveCnt>0){ + d = i2c->dev->fifo_data.val; + moveCnt--; + cnt++; + } + break; } - } + + while(moveCnt > 0) { // store data + d = i2c->dev->fifo_data.val; + moveCnt--; + cnt++; + tdq->data[tdq->position++] = (d&0xFF); + } + + moveCnt = i2c->dev->status_reg.rx_fifo_cnt; //any more out there? } + #if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&& (defined ENABLE_I2C_DEBUG_BUFFER) // update Debug rxCount - cnt += (intBuff[intPos[i2c->num]][1][i2c->num])&&0xffFF; + 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 - } +} static void IRAM_ATTR i2cIsrExit(i2c_t * i2c,const uint32_t eventCode,bool Fatal) { @@ -578,16 +805,17 @@ static void IRAM_ATTR i2cIsrExit(i2c_t * i2c,const uint32_t eventCode,bool Fatal } } + +static void IRAM_ATTR i2c_update_error_byte_cnt(i2c_t * i2c) +{ /* i2c_update_error_byte_cnt 07/18/2018 Only called after an error has occurred, so, most of the time this function is never used. This function obliterates the need to interrupt monitor each byte transferred, at high bitrates the byte interrupts were overwhelming the OS. Spurious Interrupts were being generated. - it update errorByteCnt, errorQueue. + it updates errorByteCnt, errorQueue. */ -static void IRAM_ATTR i2c_update_error_byte_cnt(i2c_t * i2c) -{ uint16_t a=0; // start at top of DQ, count how many bytes added to tx fifo, and received from rx_fifo. - uint16_t bc = 0; + int16_t bc = 0; I2C_DATA_QUEUE_t *tdq; i2c->errorByteCnt = 0; while( a < i2c->queueCount){ // add up all bytes loaded into fifo's @@ -596,40 +824,34 @@ static void IRAM_ATTR i2c_update_error_byte_cnt(i2c_t * i2c) i2c->errorByteCnt += tdq->position; a++; } -// log_v("errorByteCnt=%d",i2c->errorByteCnt); // now errorByteCnt contains total bytes moved into and out of FIFO's // but, there may still be bytes waiting in Fifo's i2c->errorByteCnt -= i2c->dev->status_reg.tx_fifo_cnt; // waiting to go out; i2c->errorByteCnt += i2c->dev->status_reg.rx_fifo_cnt; // already received // now walk thru DQ again, find which byte is 'current' - bool done = false; bc = i2c->errorByteCnt; i2c->errorQueue = 0; - while(( i2c->errorQueue < i2c->queueCount)&&( !done )){ + while( i2c->errorQueue < i2c->queueCount ){ tdq = &i2c->dq[i2c->errorQueue]; if(bc>0){ // not found yet if( tdq->ctrl.addrSent >= bc){ // in address - done = true; - continue; + bc = -1; // in address + break; } else { bc -= tdq->ctrl.addrSent; - if( tdq->position >= bc) { // data nak - done = true; - continue; + if( tdq->length > bc) { // data nak + break; } else { // count down - bc -= tdq->position; + bc -= tdq->length; } } - } else { - done= true; - continue; - } + } else break; + i2c->errorQueue++; } -// log_v("errorByteCnt=%d errorQueue=%d",i2c->errorByteCnt,i2c->errorQueue); i2c->errorByteCnt = bc; -} + } static void IRAM_ATTR i2c_isr_handler_default(void* arg) { @@ -637,17 +859,14 @@ static void IRAM_ATTR i2c_isr_handler_default(void* arg) uint32_t activeInt = p_i2c->dev->int_status.val&0x7FF; if(p_i2c->stage==I2C_DONE) { //get Out, can't service, not configured -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE - uint32_t raw = p_i2c->dev->int_raw.val; -#endif p_i2c->dev->int_ena.val = 0; p_i2c->dev->int_clr.val = 0x1FFF; - log_v("eject raw=%p, int=%p",raw,activeInt); +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE + uint32_t raw = p_i2c->dev->int_raw.val; + log_w("eject raw=%p, int=%p",raw,activeInt); +#endif return; - }else if(!activeInt){ //spurious interrupt, possibly bus relate 20180711 - log_v("r=0x%x s=0x%x %d",p_i2c->dev->int_raw.val,p_i2c->dev->int_status.val,p_i2c->stage); } - while (activeInt != 0) { // Ordering of 'if(activeInt)' statements is important, don't change #if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER) @@ -690,8 +909,7 @@ static void IRAM_ATTR i2c_isr_handler_default(void* arg) if (activeInt & I2C_ACK_ERR_INT_ST_M) {//fatal error, abort i2c service if (p_i2c->mode == I2C_MASTER) { i2c_update_error_byte_cnt(p_i2c); // calc which byte caused ack Error, check if address or data - log_v("AcK Err errorByteCnt=%d, errorQueue=%d queuepos=%d",p_i2c->errorByteCnt,p_i2c->errorQueue, p_i2c->queuePos); - if(p_i2c->errorByteCnt <= p_i2c->dq[p_i2c->errorQueue].ctrl.addrReq) { // address + if(p_i2c->errorByteCnt < 0 ) { // address i2cIsrExit(p_i2c,EVENT_ERROR_NAK,true); } else { i2cIsrExit(p_i2c,EVENT_ERROR_DATA_NAK,true); //data @@ -750,14 +968,16 @@ static void IRAM_ATTR i2c_isr_handler_default(void* arg) 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 +// activeInt = p_i2c->dev->int_status.val; // start all over if another interrupt happened +// 01AUG2018 if another interrupt happened, the OS will schedule another interrupt +// this is the source of spurious interrupts } } /* 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) +static i2c_err_t i2cAddQueue(i2c_t * i2c,uint8_t mode, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen,bool sendStop, bool dataOnly, EventGroupHandle_t event) { // need to grab a MUTEX for exclusive Queue, // what about if ISR is running? @@ -772,10 +992,21 @@ static i2c_err_t i2cAddQueue(i2c_t * i2c,uint8_t mode, uint16_t i2cDeviceAddr, u dqx.position = 0; dqx.cmdBytesNeeded = dataLen; dqx.ctrl.val = 0; + if( dataOnly) { + /* special case to add a queue data only element. + START and devAddr will not be sent, this dq element can have a STOP. + allows multiple buffers to be used for one transaction. + sequence: normal transaction(sendStop==false), [dataonly(sendStop==false)],dataOnly(sendStop==true) + *** Currently only works with WRITE, final byte NAK an READ will cause a fail between dq buffer elements. (in progress 30JUL2018) + */ + dqx.ctrl.startCmdSent = 1; // mark as already sent + dqx.ctrl.addrCmdSent = 1; + } else { + dqx.ctrl.addrReq = ((i2cDeviceAddr&0xFC00)==0x7800)?2:1; // 10bit or 7bit address + } dqx.ctrl.addr = i2cDeviceAddr; dqx.ctrl.mode = mode; dqx.ctrl.stop= sendStop; - dqx.ctrl.addrReq = ((i2cDeviceAddr&0xFC00)==0x7800)?2:1; // 10bit or 7bit address dqx.queueEvent = event; if(event) { // an eventGroup exist, so, initialize it @@ -808,7 +1039,7 @@ static i2c_err_t i2cAddQueue(i2c_t * i2c,uint8_t mode, uint16_t i2cDeviceAddr, u 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,event); + return i2cAddQueue(i2c,0,i2cDeviceAddr,dataPtr,dataLen,sendStop,false,event); } i2c_err_t i2cAddQueueRead(i2c_t * i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen,bool sendStop,EventGroupHandle_t event) @@ -822,16 +1053,15 @@ i2c_err_t i2cAddQueueRead(i2c_t * i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, // 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); + i2c_err_t err = i2cAddQueue(i2c,0,i2cDeviceAddr,NULL,0,false,false,event); if(err==I2C_ERROR_OK) { - return i2cAddQueue(i2c,1,(i2cDeviceAddr>>8),dataPtr,dataLen,sendStop,event); + return i2cAddQueue(i2c,1,(i2cDeviceAddr>>8),dataPtr,dataLen,sendStop,false,event); } else { return err; } } - return i2cAddQueue(i2c,1,i2cDeviceAddr,dataPtr,dataLen,sendStop,event); + return i2cAddQueue(i2c,1,i2cDeviceAddr,dataPtr,dataLen,sendStop,false,event); } -// Stickbreaker i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis) { @@ -847,6 +1077,7 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis) if(i2c == NULL) { return I2C_ERROR_DEV; } + if(i2c->debugFlags & 0xff000000) i2cTriggerDumps(i2c,(i2c->debugFlags>>24),"before ProcQueue"); 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 @@ -876,6 +1107,8 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis) intBuff[i][2][i2c->num] = 0; } intPos[i2c->num] = 0; + fifoPos = 0; + memset(fifoBuffer,0,FIFOMAX); #endif // EventGroup is used to signal transmission completion from ISR // not always reliable. Sometimes, the FreeRTOS scheduler is maxed out and refuses request @@ -950,20 +1183,20 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis) // receives the TRANS_START - - i2c->dev->int_ena.val = + uint32_t interruptsEnabled = 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)) 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 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() - + + i2c->dev->int_ena.val = interruptsEnabled; + if(!i2c->intr_handle) { // create ISR for either peripheral // log_i("create ISR %d",i2c->num); uint32_t ret = 0; @@ -972,9 +1205,9 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis) ESP_INTR_FLAG_LOWMED; //< Low and medium prio interrupts. These can be handled in C. if(i2c->num) { - ret = esp_intr_alloc_intrstatus(ETS_I2C_EXT1_INTR_SOURCE, flags, (uint32_t)&i2c->dev->int_status.val, 0x7FF, &i2c_isr_handler_default,i2c, &i2c->intr_handle); + ret = esp_intr_alloc_intrstatus(ETS_I2C_EXT1_INTR_SOURCE, flags, (uint32_t)&i2c->dev->int_status.val, interruptsEnabled, &i2c_isr_handler_default,i2c, &i2c->intr_handle); } else { - ret = esp_intr_alloc_intrstatus(ETS_I2C_EXT0_INTR_SOURCE, flags, (uint32_t)&i2c->dev->int_status.val, 0x7FF, &i2c_isr_handler_default,i2c, &i2c->intr_handle); + ret = esp_intr_alloc_intrstatus(ETS_I2C_EXT0_INTR_SOURCE, flags, (uint32_t)&i2c->dev->int_status.val, interruptsEnabled, &i2c_isr_handler_default,i2c, &i2c->intr_handle); } if(ret!=ESP_OK) { @@ -983,7 +1216,6 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis) return I2C_ERROR_MEMORY; } } - //hang until it completes. // how many ticks should it take to transfer totalBytes through the I2C hardware, @@ -1052,7 +1284,7 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis) i2c->dev->int_ena.val =0; i2c->dev->int_clr.val = 0x1FFF; i2c_update_error_byte_cnt(i2c); - if(i2c->errorByteCnt == 0) { // Bus Busy no bytes Moved + if((i2c->errorByteCnt == 0)&&(i2c->errorQueue==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_DEBUG @@ -1103,6 +1335,7 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis) } b++; } + if(i2c->debugFlags & 0x00ff0000) i2cTriggerDumps(i2c,(i2c->debugFlags>>16),"after ProcQueue"); I2C_MUTEX_UNLOCK(); return reason; @@ -1311,6 +1544,8 @@ i2c_err_t i2cFlush(i2c_t * i2c) if(i2c==NULL) { return I2C_ERROR_DEV; } + i2cTriggerDumps(i2c,i2c->debugFlags & 0xff, "FLUSH"); + // need to grab a MUTEX for exclusive Queue, // what out if ISR is running? i2c_err_t rc=I2C_ERROR_OK; @@ -1425,91 +1660,27 @@ uint32_t i2cGetFrequency(i2c_t * i2c) } -/* Stickbreaker ISR mode debug support - */ -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] %sbit %x %c %s buf@=%p, len=%d, pos=%d, eventH=%p bits=%x",a, - (tdq->ctrl.addr>0x100)?"10":"7", - (tdq->ctrl.addr>0x100)?(((tdq->ctrl.addr&0x600)>>1)|(tdq->ctrl.addr&0xff)):(tdq->ctrl.addr>>1), - (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++; +uint32_t i2cDebug(i2c_t * i2c, uint32_t setBits, uint32_t resetBits){ + if(i2c != NULL) { + i2c->debugFlags = ((i2c->debugFlags | setBits) & ~resetBits); + return i2c->debugFlags; } -#endif -} - -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); - log_e("error=%d",i2c->error); - 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); - log_e("queuePos=%d",i2c->queuePos); - log_e("errorByteCnt=%d",i2c->errorByteCnt); - log_e("errorQueue=%d",i2c->errorQueue); - if(i2c->dq) { - i2cDumpDqData(i2c); + return 0; + + } + +uint32_t i2cGetStatus(i2c_t * i2c){ + if(i2c != NULL){ + return i2c->dev->status_reg.val; } + else return 0; } - -void i2cDumpInts(uint8_t num) -{ -#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER) - - uint32_t b; - log_e("%u row count INTR TX RX",num); - for(uint32_t a=1; a<=INTBUFFMAX; a++) { - 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_i("Debug Buffer not Enabled"); -#endif -} - /* todo 22JUL18 need to add multi-thread capability, use dq.queueEvent as the group marker. When multiple threads transactions are present in the same queue, and an error occurs, abort all succeeding unserviced transactions with the same dq.queueEvent value. Succeeding unserviced transactions with different dq.queueEvent values can be re-queued and processed independently. + 30JUL18 complete data only queue elements, this will allow transfers to use multiple data blocks, */ diff --git a/cores/esp32/esp32-hal-i2c.h b/cores/esp32/esp32-hal-i2c.h index 580986e2..1a696aca 100644 --- a/cores/esp32/esp32-hal-i2c.h +++ b/cores/esp32/esp32-hal-i2c.h @@ -48,6 +48,7 @@ i2c_err_t i2cRead(i2c_t * i2c, uint16_t address, uint8_t* buff, uint16_t size, b i2c_err_t i2cFlush(i2c_t *i2c); i2c_err_t i2cSetFrequency(i2c_t * i2c, uint32_t clk_speed); uint32_t i2cGetFrequency(i2c_t * i2c); +uint32_t i2cGetStatus(i2c_t * i2c); // Status register of peripheral //Functions below should be used only if well understood //Might be deprecated and removed in future @@ -62,8 +63,17 @@ i2c_err_t i2cAddQueueWrite(i2c_t *i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, i2c_err_t i2cAddQueueRead(i2c_t *i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen, bool SendStop, EventGroupHandle_t event); //stickbreaker debug support -void i2cDumpInts(uint8_t num); -void i2cDumpI2c(i2c_t *i2c); +uint32_t i2cDebug(i2c_t *, uint32_t setBits, uint32_t resetBits); +// Debug actions have 3 currently defined locus +// 0xXX------ : at entry of ProcQueue +// 0x--XX---- : at exit of ProcQueue +// 0x------XX : at entry of Flush +// +// bit 0 causes DumpI2c to execute +// bit 1 causes DumpInts to execute +// bit 2 causes DumpCmdqueue to execute +// bit 3 causes DumpStatus to execute +// bit 4 causes DumpFifo to execute #ifdef __cplusplus } diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp index 7109835d..cebdbde5 100644 --- a/libraries/Wire/src/Wire.cpp +++ b/libraries/Wire/src/Wire.cpp @@ -58,7 +58,7 @@ TwoWire::~TwoWire() } } -void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency) +bool TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency) { if(sdaPin < 0) { // default param passed if(num == 0) { @@ -70,7 +70,7 @@ void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency) } else { if(sda==-1) { log_e("no Default SDA Pin for Second Peripheral"); - return; //no Default pin for Second Peripheral + return false; //no Default pin for Second Peripheral } else { sdaPin = sda; // reuse prior pin } @@ -87,7 +87,7 @@ void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency) } else { if(scl == -1) { log_e("no Default SCL Pin for Second Peripheral"); - return; //no Default pin for Second Peripheral + return false; //no Default pin for Second Peripheral } else { sclPin = scl; // reuse prior pin } @@ -98,10 +98,11 @@ void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency) scl = sclPin; i2c = i2cInit(num, sdaPin, sclPin, frequency); if(!i2c) { - return; + return false; } flush(); + return true; } @@ -145,6 +146,7 @@ void TwoWire::beginTransmission(uint16_t address) txAddress = address; txIndex = txQueued; // allow multiple beginTransmission(),write(),endTransmission(false) until endTransmission(true) txLength = txQueued; + last_error = I2C_ERROR_OK; } /*stickbreaker isr @@ -202,6 +204,7 @@ size_t TwoWire::write(uint8_t data) { if(transmitting) { if(txLength >= I2C_BUFFER_LENGTH) { + last_error = I2C_ERROR_MEMORY; return 0; } txBuffer[txIndex] = data; @@ -209,20 +212,19 @@ size_t TwoWire::write(uint8_t data) txLength = txIndex; return 1; } + last_error = I2C_ERROR_NO_BEGIN; // no begin, not transmitting return 0; } size_t TwoWire::write(const uint8_t *data, size_t quantity) { - if(transmitting) { - for(size_t i = 0; i < quantity; ++i) { - if(!write(data[i])) { - return i; - } + for(size_t i = 0; i < quantity; ++i) { + if(!write(data[i])) { + return i; } - return quantity; } - return 0; + return quantity; + } int TwoWire::available(void) @@ -353,14 +355,13 @@ char * TwoWire::getErrorText(uint8_t err) /*stickbreaker Dump i2c Interrupt buffer, i2c isr Debugging */ -void TwoWire::dumpInts() -{ - i2cDumpInts(num); + +uint32_t TwoWire::setDebugFlags( uint32_t setBits, uint32_t resetBits){ + return i2cDebug(i2c,setBits,resetBits); } -void TwoWire::dumpI2C() -{ - i2cDumpI2c(i2c); +bool TwoWire::busy(void){ + return ((i2cGetStatus(i2c) & 16 )==16); } TwoWire Wire = TwoWire(0); diff --git a/libraries/Wire/src/Wire.h b/libraries/Wire/src/Wire.h index 755c8150..e9172005 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 V1.0.1 #define I2C_BUFFER_LENGTH 128 typedef void(*user_onRequest)(void); typedef void(*user_onReceive)(uint8_t*, int); @@ -67,7 +67,7 @@ protected: public: TwoWire(uint8_t bus_num); ~TwoWire(); - void begin(int sda=-1, int scl=-1, uint32_t frequency=0); + bool begin(int sda=-1, int scl=-1, uint32_t frequency=0); void setClock(uint32_t frequency); // change bus clock without initing hardware size_t getClock(); // current bus clock rate in hz @@ -129,8 +129,8 @@ public: void onReceive( void (*)(int) ); void onRequest( void (*)(void) ); - void dumpInts(); - void dumpI2C(); + uint32_t setDebugFlags( uint32_t setBits, uint32_t resetBits); + bool busy(); }; extern TwoWire Wire; @@ -138,6 +138,9 @@ extern TwoWire Wire1; /* +V1.0.1 02AUG2018 First Fix after release, Correct ReSTART handling, change Debug control, change begin() + to a function, this allow reporting if bus cannot be initialized, Wire.begin() can be used to recover + a hung bus busy condition. 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 */