Things are slowly starting to improve in the I2C department.
While trying to figure out how to make long (>32 bytes) transfers work, I found there's a nicer way to send long frames.
I2_CMD_WRITE will actually take a byte_num paramater larger than 32 in FIFO mode.
All you have to do is keep the FIFO filled asynchronously until the transmission is done.
This is a bit simpler than segmented transfers, and faster too.
While trying to get i2c_send with sendStop=0 to work I found one of the problems that kept the original code from working.
The condition for "transmission done" must exactly match the condition you used to decide if to send STOP or END.
CMD_STOP : Wait for the CMD done bit
CMD_END: Wait until bus not busy
To get rid of the random lockups that occasionally happened I replaced 2 unbounded while loops with 1ms delays,
going by the theory that waiting any longer probably won't make the I2C bus work again.
This is looking good so far, I've seen no more lockups.
I'm rebooting my ESP via software when I2C errors pop up. This hasn't worked reliably before, but now it seems to do.
Sometimes more than 1 reboot happens before the I2C goes error free again.
After making this last change, I haven't seen any unprovoked I2C errors anymore!
These used to happen every 2-3 hours. It's a bit early to say if they're really gone, but things are looking good so far.
I don't know how this last change would relate to random I2C errors, but I'll take the improvement anyway.
New Version of i2cWrite:
Code: Select all
i2c_err_t i2cWrite(i2c_t * i2c, uint16_t address, bool addr_10bit, uint8_t * data, uint8_t len, bool sendStop)
{
int i;
uint8_t index = 0;
uint8_t dataLen = len + (addr_10bit?2:1);
address = (address << 1);
if(i2c == NULL){
return I2C_ERROR_DEV;
}
// I2C_MUTEX_LOCK();
i2cResetFiFo(i2c);
i2cResetCmd(i2c);
//Clear Interrupts
i2c->dev->int_clr.val = 0xFFFFFFFF;
//CMD START
i2cSetCmd(i2c, 0, I2C_CMD_RSTART, 0, false, false, false);
//CMD WRITE(ADDRESS + DATA)
i2cSetCmd(i2c, 1, I2C_CMD_WRITE, dataLen, false, false, true);
i2c->dev->fifo_data.data = address & 0xFF;
dataLen--;
if(addr_10bit) {
i2c->dev->fifo_data.data = (address >> 8) & 0xFF;
dataLen--;
}
//CMD STOP or CMD END
if(!sendStop) {
i2cSetCmd(i2c, 2, I2C_CMD_END, 0, false, false, false);
} else {
i2cSetCmd(i2c, 2, I2C_CMD_STOP, 0, false, false, false);
}
//START Transmission
i2c->dev->ctr.trans_start = 1;
//WAIT Transmission
uint32_t startAt = millis();
while(1) {
// Keep FIFO stocked
if ( (i2c->dev->status_reg.tx_fifo_cnt < 30) && (dataLen) ){
i2c->dev->fifo_data.val = data[index++];
dataLen--;
}
//have been looping for too long
if((millis() - startAt)>50){
//log_e("Timeout! Addr: %x", address >> 1);
// I2C_MUTEX_UNLOCK();
return I2C_ERROR_BUS;
}
//Bus failed (maybe check for this while waiting?
if(i2c->dev->int_raw.arbitration_lost) {
//log_e("Bus Fail! Addr: %x", address >> 1);
// I2C_MUTEX_UNLOCK();
return I2C_ERROR_BUS;
}
//Bus timeout
if(i2c->dev->int_raw.time_out) {
//log_e("Bus Timeout! Addr: %x", address >> 1);
// I2C_MUTEX_UNLOCK();
return I2C_ERROR_TIMEOUT;
}
//Transmission did not finish and ACK_ERR is set
if(i2c->dev->int_raw.ack_err) {
//log_w("Ack Error! Addr: %x", address >> 1);
// while(i2c->dev->status_reg.bus_busy);
delayMicroseconds(1000);
// I2C_MUTEX_UNLOCK();
return I2C_ERROR_ACK;
}
if((sendStop && i2c->dev->command[2].done) || (!sendStop && !i2c->dev->status_reg.bus_busy)){
break;
}
}
// I2C_MUTEX_UNLOCK();
return I2C_ERROR_OK;
}
New version of i2cInitFix: (this contained another unbounded while loop)
Code: Select all
void i2cInitFix(i2c_t * i2c){
if(i2c == NULL){
return;
}
I2C_MUTEX_LOCK();
i2cResetFiFo(i2c);
i2cResetCmd(i2c);
i2c->dev->int_clr.val = 0xFFFFFFFF;
i2cSetCmd(i2c, 0, I2C_CMD_RSTART, 0, false, false, false);
i2c->dev->fifo_data.data = 0;
i2cSetCmd(i2c, 1, I2C_CMD_WRITE, 1, false, false, false);
i2cSetCmd(i2c, 2, I2C_CMD_STOP, 0, false, false, false);
i2c->dev->ctr.trans_start = 1;
// while(!i2c->dev->command[2].done);
delayMicroseconds(1000);
I2C_MUTEX_UNLOCK();
}
Cheers
P.S:
I commented out the I2C_MUTEX in i2cWrite because it's really slow
You might or might not want to do this