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.
This commit is contained in:
chuck todd 2018-08-14 03:51:15 -06:00 committed by Me No Dev
parent e346f20aa9
commit b05430cfd9
4 changed files with 413 additions and 228 deletions

View File

@ -36,17 +36,21 @@
/* Stickbreaker ISR mode debug support /* Stickbreaker ISR mode debug support
ENABLE_I2C_DEBUG_BUFFER ENABLE_I2C_DEBUG_BUFFER
Enable debug interrupt history buffer, setting this define will result in 1544 bytes of RAM Enable debug interrupt history buffer, fifoTx history buffer.
being used whenever CORE_DEBUG_LEVEL is higher than WARNING. Unless you are debugging Setting this define will result in 2570 bytes of RAM being used whenever CORE_DEBUG_LEVEL
a problem in the I2C subsystem I would recommend you leave it commented out. 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 //#define ENABLE_I2C_DEBUG_BUFFER
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER) #if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER)
#define INTBUFFMAX 64 #define INTBUFFMAX 64
#define FIFOMAX 512
static uint32_t intBuff[INTBUFFMAX][3][2]; static uint32_t intBuff[INTBUFFMAX][3][2];
static uint32_t intPos[2]= {0,0}; static uint32_t intPos[2]= {0,0};
static uint16_t fifoBuffer[FIFOMAX];
static uint16_t fifoPos = 0;
#endif #endif
// start from tools/sdk/include/soc/soc/i2c_struct.h // start from tools/sdk/include/soc/soc/i2c_struct.h
@ -154,7 +158,7 @@ typedef union {
// individual dq element // individual dq element
typedef struct { 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 length; // size of data buffer
uint16_t position; // current position for next char in buffer (<length) uint16_t position; // current position for next char in buffer (<length)
uint16_t cmdBytesNeeded; // used to control number of I2C_COMMAND_t blocks added to queue uint16_t cmdBytesNeeded; // used to control number of I2C_COMMAND_t blocks added to queue
@ -179,9 +183,10 @@ struct i2c_struct_t {
I2C_DATA_QUEUE_t * dq; I2C_DATA_QUEUE_t * dq;
uint16_t queueCount; // number of dq entries in queue. uint16_t queueCount; // number of dq entries in queue.
uint16_t queuePos; // current queue that still has or needs data (out/in) uint16_t queuePos; // current queue that still has or needs data (out/in)
uint16_t errorByteCnt; // count of bytes moved (both in and out) int16_t errorByteCnt; // byte pos where error happened, -1 devId, 0..(length-1) data
uint16_t errorQueue; // errorByteCnt is in this queue,(for error locus) uint16_t errorQueue; // errorByteCnt is in this queue,(for error locus)
uint32_t exitCode; uint32_t exitCode;
uint32_t debugFlags;
}; };
enum { enum {
@ -197,16 +202,16 @@ enum {
#define I2C_MUTEX_UNLOCK() #define I2C_MUTEX_UNLOCK()
static i2c_t _i2c_bus_array[2] = { static i2c_t _i2c_bus_array[2] = {
{(volatile i2c_dev_t *)(DR_REG_I2C_EXT_BASE_FIXED), 0, -1, -1,I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0}, {(volatile i2c_dev_t *)(DR_REG_I2C_EXT_BASE_FIXED), 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), 1, -1, -1,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, -1, -1,I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0,0}
}; };
#else #else
#define I2C_MUTEX_LOCK() do {} while (xSemaphoreTake(i2c->lock, portMAX_DELAY) != pdPASS) #define I2C_MUTEX_LOCK() do {} while (xSemaphoreTake(i2c->lock, portMAX_DELAY) != pdPASS)
#define I2C_MUTEX_UNLOCK() xSemaphoreGive(i2c->lock) #define I2C_MUTEX_UNLOCK() xSemaphoreGive(i2c->lock)
static i2c_t _i2c_bus_array[2] = { 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_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} {(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 #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_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. * 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(a<i2c->queueCount) { // 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(a<i2c->queueCount) {
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(offset<tdq->length) {
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 1master 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) 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; I2C_COMMAND_t cmd;
@ -231,41 +452,22 @@ static void IRAM_ATTR i2cSetCmd(i2c_t * i2c, uint8_t index, uint8_t op_code, uin
} }
/* 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) 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 /* this function is called on initial i2cProcQueue() or when a I2C_END_DETECT_INT occurs
*/ */
uint16_t cmdIdx = 0; 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 done;
bool needMoreCmds = false; bool needMoreCmds = false;
bool ena_rx=false; // if we add a read op, better enable Rx_Fifo IRQ 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 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 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++; qp++;
} else { } else {
needMoreCmds=true; 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 // (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); i2cSetCmd(i2c, cmdIdx++, I2C_CMD_RSTART, 0, false, false, false);
tdq->ctrl.startCmdSent=1; tdq->ctrl.startCmdSent=1;
done = (cmdIdx>14);
} }
//CMD WRITE ADDRESS //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 tdq->ctrl.dataCmdSent=(tdq->cmdBytesNeeded==0); // enough command[] to send all data
if((!done)&&(tdq->ctrl.dataCmdSent)) { // possibly add stop if((!done)&&(tdq->ctrl.dataCmdSent)) { // possibly add stop
if(!tdq->ctrl.stopCmdSent){
if(tdq->ctrl.stop) { //send a stop if(tdq->ctrl.stop) { //send a stop
i2cSetCmd(i2c, (cmdIdx)++,I2C_CMD_STOP,0,false,false,false); i2cSetCmd(i2c, (cmdIdx)++,I2C_CMD_STOP,0,false,false,false);
done = cmdIdx > 14; done = cmdIdx > 14;
tdq->ctrl.stopCmdSent = 1; }
} else { // dummy a stop because this is a restart
tdq->ctrl.stopCmdSent = 1; tdq->ctrl.stopCmdSent = 1;
} }
} }
@ -370,7 +571,7 @@ static void IRAM_ATTR fillCmdQueue(i2c_t * i2c, bool INTS)
i--; i--;
} else { // unable to stretch, fatal } else { // unable to stretch, fatal
log_e("invalid CMD[] layout Stretch Failed"); log_e("invalid CMD[] layout Stretch Failed");
dumpCmdQueue(i2c); i2cDumpCmdQueue(i2c);
done = true; 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) 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, uint16_t a=i2c->queuePos; // currently executing dq,
bool full=!(i2c->dev->status_reg.tx_fifo_cnt<31); bool full=!(i2c->dev->status_reg.tx_fifo_cnt<31);
uint8_t cnt; uint8_t cnt;
bool rxQueueEncountered = false;
while((a < i2c->queueCount) && !full) { while((a < i2c->queueCount) && !full) {
I2C_DATA_QUEUE_t *tdq = &i2c->dq[a]; I2C_DATA_QUEUE_t *tdq = &i2c->dq[a];
cnt=0; cnt=0;
@ -449,24 +649,38 @@ static void IRAM_ATTR fillTxFifo(i2c_t * i2c)
i2c->dev->fifo_data.val = tdq->ctrl.addr&0xFF; i2c->dev->fifo_data.val = tdq->ctrl.addr&0xFF;
cnt++; cnt++;
tdq->ctrl.addrSent=1; // 7bit lowbyte sent 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 // 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.mode==0) { // write
if(tdq->ctrl.addrSent == tdq->ctrl.addrReq) { //address has been sent, is Write Mode! 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++]; i2c->dev->fifo_data.val = tdq->data[tdq->position++];
cnt++; 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) #if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER)
// update debug buffer tx counts // update debug buffer tx counts
@ -474,28 +688,46 @@ 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); intBuff[intPos[i2c->num]][1][i2c->num] = (intBuff[intPos[i2c->num]][1][i2c->num]&0xFFFF)|(cnt<<16);
#endif #endif
full=!(i2c->dev->status_reg.tx_fifo_cnt<31);
if(!full) { 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 if(a >= i2c->queueCount ) { // disable TX IRQ, all tx Data has been queued
// (a >= i2c->queueCount) means no more data is available
i2c->dev->int_ena.tx_fifo_empty= 0; i2c->dev->int_ena.tx_fifo_empty= 0;
} }
i2c->dev->int_clr.tx_fifo_empty=1; i2c->dev->int_clr.tx_fifo_empty=1;
} }
/* Stickbreaker ISR mode support
*/
static void IRAM_ATTR emptyRxFifo(i2c_t * i2c) static void IRAM_ATTR emptyRxFifo(i2c_t * i2c)
{ {
uint32_t d, cnt=0, moveCnt; 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 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 while((moveCnt > 0)&&(i2c->queuePos < i2c->queueCount)){ // data to move
I2C_DATA_QUEUE_t *tdq =&i2c->dq[i2c->queuePos]; //short cut 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(tdq->ctrl.mode == 1){ // read command
if(moveCnt > (tdq->length - tdq->position)) { //make sure they go in this dq 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
@ -503,36 +735,31 @@ static void IRAM_ATTR emptyRxFifo(i2c_t * i2c)
} }
} else {// error } else {// error
log_e("RxEmpty(%d) call on TxBuffer? dq=%d",moveCnt,i2c->queuePos); log_e("RxEmpty(%d) call on TxBuffer? dq=%d",moveCnt,i2c->queuePos);
return; // discard
while(moveCnt>0){
d = i2c->dev->fifo_data.val;
moveCnt--;
cnt++;
} }
while(moveCnt > 0) { break;
}
while(moveCnt > 0) { // store data
d = i2c->dev->fifo_data.val; d = i2c->dev->fifo_data.val;
moveCnt--; moveCnt--;
cnt++; cnt++;
tdq->data[tdq->position++] = (d&0xFF); tdq->data[tdq->position++] = (d&0xFF);
} }
if(tdq->position >= tdq->length ){ // inc queuePos until next READ command or end of queue
i2c->queuePos++; moveCnt = i2c->dev->status_reg.rx_fifo_cnt; //any more out there?
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;
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);
}
}
}
} }
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&& (defined ENABLE_I2C_DEBUG_BUFFER) #if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&& (defined ENABLE_I2C_DEBUG_BUFFER)
// update Debug rxCount // 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; intBuff[intPos[i2c->num]][1][i2c->num] = (intBuff[intPos[i2c->num]][1][i2c->num]&0xFFFF0000)|cnt;
#endif #endif
} }
static void IRAM_ATTR i2cIsrExit(i2c_t * i2c,const uint32_t eventCode,bool Fatal) 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 /* 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. 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 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. 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 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_DATA_QUEUE_t *tdq;
i2c->errorByteCnt = 0; i2c->errorByteCnt = 0;
while( a < i2c->queueCount){ // add up all bytes loaded into fifo's 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; i2c->errorByteCnt += tdq->position;
a++; a++;
} }
// log_v("errorByteCnt=%d",i2c->errorByteCnt);
// now errorByteCnt contains total bytes moved into and out of FIFO's // now errorByteCnt contains total bytes moved into and out of FIFO's
// but, there may still be bytes waiting in 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.tx_fifo_cnt; // waiting to go out;
i2c->errorByteCnt += i2c->dev->status_reg.rx_fifo_cnt; // already received i2c->errorByteCnt += i2c->dev->status_reg.rx_fifo_cnt; // already received
// now walk thru DQ again, find which byte is 'current' // now walk thru DQ again, find which byte is 'current'
bool done = false;
bc = i2c->errorByteCnt; bc = i2c->errorByteCnt;
i2c->errorQueue = 0; i2c->errorQueue = 0;
while(( i2c->errorQueue < i2c->queueCount)&&( !done )){ while( i2c->errorQueue < i2c->queueCount ){
tdq = &i2c->dq[i2c->errorQueue]; tdq = &i2c->dq[i2c->errorQueue];
if(bc>0){ // not found yet if(bc>0){ // not found yet
if( tdq->ctrl.addrSent >= bc){ // in address if( tdq->ctrl.addrSent >= bc){ // in address
done = true; bc = -1; // in address
continue; break;
} else { } else {
bc -= tdq->ctrl.addrSent; bc -= tdq->ctrl.addrSent;
if( tdq->position >= bc) { // data nak if( tdq->length > bc) { // data nak
done = true; break;
continue;
} else { // count down } else { // count down
bc -= tdq->position; bc -= tdq->length;
} }
} }
} else { } else break;
done= true;
continue;
}
i2c->errorQueue++; i2c->errorQueue++;
} }
// log_v("errorByteCnt=%d errorQueue=%d",i2c->errorByteCnt,i2c->errorQueue);
i2c->errorByteCnt = bc; i2c->errorByteCnt = bc;
} }
static void IRAM_ATTR i2c_isr_handler_default(void* arg) 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; uint32_t activeInt = p_i2c->dev->int_status.val&0x7FF;
if(p_i2c->stage==I2C_DONE) { //get Out, can't service, not configured 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_ena.val = 0;
p_i2c->dev->int_clr.val = 0x1FFF; 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; 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 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) #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 (activeInt & I2C_ACK_ERR_INT_ST_M) {//fatal error, abort i2c service
if (p_i2c->mode == I2C_MASTER) { if (p_i2c->mode == I2C_MASTER) {
i2c_update_error_byte_cnt(p_i2c); // calc which byte caused ack Error, check if address or data 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 < 0 ) { // address
if(p_i2c->errorByteCnt <= p_i2c->dq[p_i2c->errorQueue].ctrl.addrReq) { // address
i2cIsrExit(p_i2c,EVENT_ERROR_NAK,true); i2cIsrExit(p_i2c,EVENT_ERROR_NAK,true);
} else { } else {
i2cIsrExit(p_i2c,EVENT_ERROR_DATA_NAK,true); //data 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); 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 /* Stickbreaker added for ISR 11/2017
functional with Silicon date=0x16042000 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, // need to grab a MUTEX for exclusive Queue,
// what about if ISR is running? // 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.position = 0;
dqx.cmdBytesNeeded = dataLen; dqx.cmdBytesNeeded = dataLen;
dqx.ctrl.val = 0; 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.addr = i2cDeviceAddr;
dqx.ctrl.mode = mode; dqx.ctrl.mode = mode;
dqx.ctrl.stop= sendStop; dqx.ctrl.stop= sendStop;
dqx.ctrl.addrReq = ((i2cDeviceAddr&0xFC00)==0x7800)?2:1; // 10bit or 7bit address
dqx.queueEvent = event; dqx.queueEvent = event;
if(event) { // an eventGroup exist, so, initialize it 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) 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) 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. // this is the Industry Standard specification.
if((i2cDeviceAddr &0xFC00)==0x7800) { // ten bit read 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) { 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 { } else {
return err; 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) 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) { if(i2c == NULL) {
return I2C_ERROR_DEV; 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 (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 multi master then this if should be changed to this 03/12/2018
if(multiMaster){// try to let the bus clear by its self 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; intBuff[i][2][i2c->num] = 0;
} }
intPos[i2c->num] = 0; intPos[i2c->num] = 0;
fifoPos = 0;
memset(fifoBuffer,0,FIFOMAX);
#endif #endif
// EventGroup is used to signal transmission 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 // not always reliable. Sometimes, the FreeRTOS scheduler is maxed out and refuses request
@ -950,13 +1183,11 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis)
// receives the TRANS_START // receives the TRANS_START
uint32_t interruptsEnabled =
i2c->dev->int_ena.val =
I2C_ACK_ERR_INT_ENA | // (BIT(10)) Causes Fatal Error Exit 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_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_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_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_ARBITRATION_LOST_INT_ENA | // (BIT(5)) cause fatal error exit
I2C_SLAVE_TRAN_COMP_INT_ENA | // (BIT(4)) unhandled I2C_SLAVE_TRAN_COMP_INT_ENA | // (BIT(4)) unhandled
I2C_END_DETECT_INT_ENA | // (BIT(3)) refills cmd[] list I2C_END_DETECT_INT_ENA | // (BIT(3)) refills cmd[] list
@ -964,6 +1195,8 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis)
I2C_TXFIFO_EMPTY_INT_ENA | // (BIT(1)) triggers fillTxFifo() I2C_TXFIFO_EMPTY_INT_ENA | // (BIT(1)) triggers fillTxFifo()
I2C_RXFIFO_FULL_INT_ENA; // (BIT(0)) trigger emptyRxFifo() I2C_RXFIFO_FULL_INT_ENA; // (BIT(0)) trigger emptyRxFifo()
i2c->dev->int_ena.val = interruptsEnabled;
if(!i2c->intr_handle) { // create ISR for either peripheral if(!i2c->intr_handle) { // create ISR for either peripheral
// log_i("create ISR %d",i2c->num); // log_i("create ISR %d",i2c->num);
uint32_t ret = 0; 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. ESP_INTR_FLAG_LOWMED; //< Low and medium prio interrupts. These can be handled in C.
if(i2c->num) { 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 { } 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) { 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; return I2C_ERROR_MEMORY;
} }
} }
//hang until it completes. //hang until it completes.
// how many ticks should it take to transfer totalBytes through the I2C hardware, // 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_ena.val =0;
i2c->dev->int_clr.val = 0x1FFF; i2c->dev->int_clr.val = 0x1FFF;
i2c_update_error_byte_cnt(i2c); 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; reason = I2C_ERROR_BUSY;
eBits = eBits | EVENT_ERROR_BUS_BUSY|EVENT_ERROR|EVENT_DONE; eBits = eBits | EVENT_ERROR_BUS_BUSY|EVENT_ERROR|EVENT_DONE;
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG #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++; b++;
} }
if(i2c->debugFlags & 0x00ff0000) i2cTriggerDumps(i2c,(i2c->debugFlags>>16),"after ProcQueue");
I2C_MUTEX_UNLOCK(); I2C_MUTEX_UNLOCK();
return reason; return reason;
@ -1311,6 +1544,8 @@ i2c_err_t i2cFlush(i2c_t * i2c)
if(i2c==NULL) { if(i2c==NULL) {
return I2C_ERROR_DEV; return I2C_ERROR_DEV;
} }
i2cTriggerDumps(i2c,i2c->debugFlags & 0xff, "FLUSH");
// need to grab a MUTEX for exclusive Queue, // need to grab a MUTEX for exclusive Queue,
// what out if ISR is running? // what out if ISR is running?
i2c_err_t rc=I2C_ERROR_OK; i2c_err_t rc=I2C_ERROR_OK;
@ -1425,91 +1660,27 @@ uint32_t i2cGetFrequency(i2c_t * i2c)
} }
/* Stickbreaker ISR mode debug support uint32_t i2cDebug(i2c_t * i2c, uint32_t setBits, uint32_t resetBits){
*/ if(i2c != NULL) {
void i2cDumpDqData(i2c_t * i2c) i2c->debugFlags = ((i2c->debugFlags | setBits) & ~resetBits);
{ return i2c->debugFlags;
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR
uint16_t a=0;
char buff[140];
I2C_DATA_QUEUE_t *tdq;
while(a<i2c->queueCount) {
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(offset<tdq->length) {
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); return 0;
buff[j]=' ';
i++;
offset++;
} }
log_e("%s",buff);
uint32_t i2cGetStatus(i2c_t * i2c){
if(i2c != NULL){
return i2c->dev->status_reg.val;
} }
a++; else return 0;
}
#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);
}
}
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 /* todo
22JUL18 22JUL18
need to add multi-thread capability, use dq.queueEvent as the group marker. When multiple threads 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 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 with the same dq.queueEvent value. Succeeding unserviced transactions with different dq.queueEvent values
can be re-queued and processed independently. can be re-queued and processed independently.
30JUL18 complete data only queue elements, this will allow transfers to use multiple data blocks,
*/ */

View File

@ -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 i2cFlush(i2c_t *i2c);
i2c_err_t i2cSetFrequency(i2c_t * i2c, uint32_t clk_speed); i2c_err_t i2cSetFrequency(i2c_t * i2c, uint32_t clk_speed);
uint32_t i2cGetFrequency(i2c_t * i2c); 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 //Functions below should be used only if well understood
//Might be deprecated and removed in future //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); i2c_err_t i2cAddQueueRead(i2c_t *i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen, bool SendStop, EventGroupHandle_t event);
//stickbreaker debug support //stickbreaker debug support
void i2cDumpInts(uint8_t num); uint32_t i2cDebug(i2c_t *, uint32_t setBits, uint32_t resetBits);
void i2cDumpI2c(i2c_t *i2c); // 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 #ifdef __cplusplus
} }

View File

@ -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(sdaPin < 0) { // default param passed
if(num == 0) { if(num == 0) {
@ -70,7 +70,7 @@ void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency)
} else { } else {
if(sda==-1) { if(sda==-1) {
log_e("no Default SDA Pin for Second Peripheral"); 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 { } else {
sdaPin = sda; // reuse prior pin sdaPin = sda; // reuse prior pin
} }
@ -87,7 +87,7 @@ void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency)
} else { } else {
if(scl == -1) { if(scl == -1) {
log_e("no Default SCL Pin for Second Peripheral"); 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 { } else {
sclPin = scl; // reuse prior pin sclPin = scl; // reuse prior pin
} }
@ -98,10 +98,11 @@ void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency)
scl = sclPin; scl = sclPin;
i2c = i2cInit(num, sdaPin, sclPin, frequency); i2c = i2cInit(num, sdaPin, sclPin, frequency);
if(!i2c) { if(!i2c) {
return; return false;
} }
flush(); flush();
return true;
} }
@ -145,6 +146,7 @@ void TwoWire::beginTransmission(uint16_t address)
txAddress = address; txAddress = address;
txIndex = txQueued; // allow multiple beginTransmission(),write(),endTransmission(false) until endTransmission(true) txIndex = txQueued; // allow multiple beginTransmission(),write(),endTransmission(false) until endTransmission(true)
txLength = txQueued; txLength = txQueued;
last_error = I2C_ERROR_OK;
} }
/*stickbreaker isr /*stickbreaker isr
@ -202,6 +204,7 @@ size_t TwoWire::write(uint8_t data)
{ {
if(transmitting) { if(transmitting) {
if(txLength >= I2C_BUFFER_LENGTH) { if(txLength >= I2C_BUFFER_LENGTH) {
last_error = I2C_ERROR_MEMORY;
return 0; return 0;
} }
txBuffer[txIndex] = data; txBuffer[txIndex] = data;
@ -209,20 +212,19 @@ size_t TwoWire::write(uint8_t data)
txLength = txIndex; txLength = txIndex;
return 1; return 1;
} }
last_error = I2C_ERROR_NO_BEGIN; // no begin, not transmitting
return 0; return 0;
} }
size_t TwoWire::write(const uint8_t *data, size_t quantity) size_t TwoWire::write(const uint8_t *data, size_t quantity)
{ {
if(transmitting) {
for(size_t i = 0; i < quantity; ++i) { for(size_t i = 0; i < quantity; ++i) {
if(!write(data[i])) { if(!write(data[i])) {
return i; return i;
} }
} }
return quantity; return quantity;
}
return 0;
} }
int TwoWire::available(void) int TwoWire::available(void)
@ -353,14 +355,13 @@ char * TwoWire::getErrorText(uint8_t err)
/*stickbreaker Dump i2c Interrupt buffer, i2c isr Debugging /*stickbreaker Dump i2c Interrupt buffer, i2c isr Debugging
*/ */
void TwoWire::dumpInts()
{ uint32_t TwoWire::setDebugFlags( uint32_t setBits, uint32_t resetBits){
i2cDumpInts(num); return i2cDebug(i2c,setBits,resetBits);
} }
void TwoWire::dumpI2C() bool TwoWire::busy(void){
{ return ((i2cGetStatus(i2c) & 16 )==16);
i2cDumpI2c(i2c);
} }
TwoWire Wire = TwoWire(0); TwoWire Wire = TwoWire(0);

View File

@ -30,7 +30,7 @@
#include "freertos/queue.h" #include "freertos/queue.h"
#include "Stream.h" #include "Stream.h"
#define STICKBREAKER V0.2.2 #define STICKBREAKER V1.0.1
#define I2C_BUFFER_LENGTH 128 #define I2C_BUFFER_LENGTH 128
typedef void(*user_onRequest)(void); typedef void(*user_onRequest)(void);
typedef void(*user_onReceive)(uint8_t*, int); typedef void(*user_onReceive)(uint8_t*, int);
@ -67,7 +67,7 @@ protected:
public: public:
TwoWire(uint8_t bus_num); TwoWire(uint8_t bus_num);
~TwoWire(); ~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 void setClock(uint32_t frequency); // change bus clock without initing hardware
size_t getClock(); // current bus clock rate in hz size_t getClock(); // current bus clock rate in hz
@ -129,8 +129,8 @@ public:
void onReceive( void (*)(int) ); void onReceive( void (*)(int) );
void onRequest( void (*)(void) ); void onRequest( void (*)(void) );
void dumpInts(); uint32_t setDebugFlags( uint32_t setBits, uint32_t resetBits);
void dumpI2C(); bool busy();
}; };
extern TwoWire Wire; 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.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 V0.2.1 15MAR2018 Hardware reset, Glitch prevention, adding destructor for second i2c testing
*/ */