forked from espressif/arduino-esp32
-
Notifications
You must be signed in to change notification settings - Fork 23
BackGround ESP32 Errata and Discoveries
chuck todd edited this page Mar 11, 2018
·
19 revisions
- A hardware reset function is available, Now I can recover from a hardware failure on the bus!! My library can now recover from SCL or SDA being shorted to Ground. As soon as the short is removed, the next call(endTransmission(), or requestFrom()) will re-init the peripheral and return a
I2C_ERROR_BUSY
error. After this, i2c operations can resume. Prior to this discovery, this condition required a power cycle to guarantee recovery. See\cores\esp32\esp32-hal-i2c.c:i2cInit()
for details. - the GPIO pins must be preconfigured before they are attached to the i2c peripheral, else low going glitches will be generated, these glitches are large enough to be confused the i2c peripheral. It sees these glitches as another master acquiring the bus. This leads to the Dreaded "Bus Busy at initialization" explosion.
Through trial and error some truths have been discovered about the ESP32 v1 chip:
- The I2C StateMachine(SM) is unforgiving.
- The SM enforces the I2C protocol rules.
- All transactions must begin with a START and end with STOP. No exceptions.
- SCL clock stretching must not exceed approximately 2**20 CPU clock cycles (about 12ms)
- Ownership of the bus cannot be forced. This means that if the SM thinks it has lost an Arbitration it will not generate SCL pulses until it sees a STOP. If the Arbitration Loss was due to a unserviced END command it will never release BUS Busy error.
- The SM treats SCL clock stretching TimeOuts as errors. If the SCL signal is held low for longer than the programmed timeout, the SM will issue TimeOut IRQ ever 13.1ms (if TimeOut_period is 0xfffff) until the Slave releases the SCL stretching. The SM command during which this stretching timeout occurred will prematurely aborted. The next command in the queue will then be executed. This is an I2C protocol violation. It should continue with the current command. The clock stretching event is just a pause, not an abort.
- The SM monitors the i2c bus, It will generate state interrupts based on this monitoring.
- The TimeOut interrupt will trigger repeatedly if another master mal-forms an I2C transaction. A low going SDA while SCL will be interpreted as a Start, if Timeout clock ticks happen without a SCL pulse, TimeOut is issued.
- the master_trans (0x40) will be generated every 9 clock cycles.
- and ACK mismatches will be reported as ACK interrupts.
- If an ACK error is detected (ACK value not what was expected), the ST will terminate the cmd[] queue and send a STOP on the bus.
- COMMAND[] queue
- an END command can only be the LAST element of the queue. All entries after END are not Executed. AND the END entry must not be changed when the queue is refilled. This means once an END is used, [15] can only be END.
- an END cannot be directly preceded by a START. If END exists in [15], a START cannot be placed in [14]. If this case is encountered, a prior READ or WRITE must be split to consume the unused position [14]. If this exception is not followed an a START is placed in [14], the SM will not execute the ctrl.trans_start=1. A TimeOut IRQ will happen.
- A dummy command can be created using a READ or WRITE command, just set the num_byte = 0.
- Register fields must not be updated with -= +=.
i2c->dev->command[i].byte_num -= 1; // will change other fields of command[]
// safe method to decrement byte_num
uint32_t temp = i2c->dev->command[i].val;
temp = (temp&0xFFFFFF00) | ((temp & 0xFF)-1);
i2c->dev->command[i].val = temp;
- SCL clock stretching TimeOut
- When a slave device stretches(holds low) the SCL signal longer than the programmed timeout (13.1ms) the SM issues a TIMEOUT IRQ. As long as SCL is held low, every 13.1ms the TimeOut IRQ is reissued. Once the Slave release SCL the SM increments to the next command in its Queue. It DOES not continue from where it was in the Queue before the Clock Stretching event. This is an I2C protocol violation. The SCL stretch should have just Delayed the execution of the transaction, it is not an ABORT condition. @lonerzzz discovered this incompatibility, the was using a HTU21D Temperature/Humidity sensor, when he issued a simple
Wire.requestFrom(Sendor,1);
this READ operation failed after the Slave release SCL. But, it should have succeeded. He had increased the default 50ms timeout to 3000ms usingWire.setTimeOut(3000);
.- The actual cmd[] sequence was:
- START
- WRITE 1 CHECK_ACK
- READ 1 send NAK
- STOP
- The SM processed the START and the WRITE, the Slave stretched SCL for 127ms. During the Stretching, the SM generated 9 TimeOUT IRQ's. I believe the SM considered the TimeOut's a failure for the READ command and incremented it's internal cmd pointer to the STOP command. After the Slave Released SCL the SM then Issued the STOP. This is a protocol violation. The SM should not have bypassed the READ command. The SCL stretch should just have held the SM at the READ command.
- When a slave device stretches(holds low) the SCL signal longer than the programmed timeout (13.1ms) the SM issues a TIMEOUT IRQ. As long as SCL is held low, every 13.1ms the TimeOut IRQ is reissued. Once the Slave release SCL the SM increments to the next command in its Queue. It DOES not continue from where it was in the Queue before the Clock Stretching event. This is an I2C protocol violation. The SCL stretch should have just Delayed the execution of the transaction, it is not an ABORT condition. @lonerzzz discovered this incompatibility, the was using a HTU21D Temperature/Humidity sensor, when he issued a simple
- The SM is semi-promiscuous, It monitors the I2C bus and triggers its interrupts as if the transactions were directed at it.
- When configured as master ctrl.mode=1
- Specifically, the 0x80 Interrupt (int_raw.trans_complete) what I call STOP interrupt
- Specifically, the 0x40 interrupt (master_tran_comp) is issued for every byte that traverses the bus.
- The status_reg.rx_fifo_cnt is increment for each of these bytes, up to 0x20
- The fifo_st.rx_fifo_end_addr increment for each byte(Address and Data) and loops
- When configured as Not Master (slave) ctr.ms_mode=0, interrupts not related to this slave will fire
- any ACK error will be reflected 0x400 (int_raw.ack_error) event
- the 0x80 Interrupt (int_raw.trans_complete) what I call STOP interrupt
- When configured as not Master (slave) with slave.addr = 0 will respond to all data on bus