-
Notifications
You must be signed in to change notification settings - Fork 7.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ReSTART fix, with others #1717
ReSTART fix, with others #1717
Conversation
pr espressif#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 *
pr espressif#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 *
Hi @stickbreaker,
Decoded:
Honestly I'm not sure if your changes cause it, but I never had this before using your code from this PR. Christoph Edit: I reverted the changes and don't see the error coming up. Another point is that I dont have the display connected to the board (which might be a bad idea) |
@emptynick The stack dump is showing a buffer overwrite inside the ISR, inside esp32-hal-esp32.c uncomment line 45: to enable debug
in SSD1306Wire::display() before sendCommand() add Wire.setDebugFlags(1,0); after sendCommand() Wire.setDebugFlags(0,1); Then capture the output. I've got a SSD1306 (thanks @cyberman54) so I might be able to use your failing code. If you want to send it. Chuck. |
One line 360 of Wire.cpp you forgot to add return. I've pulled this PR and integrate to my environment, so far not found any issues. I'll keep testing. |
I've tried to read accelerometer data from LSM6DSL sensor, once in a while I got this:
Please take a look. Also in line 409 of
You have a space between |
@andriyadi found this, total brain fade on my part.
@andriyadi does that LSM6DSL sensor, SDA low busy signaling? Error 5 is an Arbitration error:
What that is saying is the ESP32 released SDA from a LOW, waited a specified number cpu clockcycles, then re-read SDA. SDA was still LOW when it want it to be high, so this generated an arbitration error. The last interrupt in the dumpInts is 0x0020 with is the arbitration failure interrupt. This error could also be created if the pullups on the i2c bus are weak. if SDA does not rise fast enough if condition will be be generated. The errorByte and errorQueue values tells that the First byte of the I see you are using the second I2C bus. Do you use both at the same time? Chuck. p.s that "% 4X" is correct I want the sprintf to generate a 4 character wide HEX field with space (' ') as the fill character instead of the standard '0' fill. |
Hi @stickbreaker , Loaded up the latest esp32 ardunio core yesterday and my MPU9250 invensense library is failing to read the device. I found this thread and I can see the changes you have done. Many thanks! Looking through the driver code for wire.endtransmission(false) and wire.requestfrom(addr,size,false) I see two methods that need changed. What's the best way with the proposed changes here Chuck? cheers iain
|
@ifrew If your sensor has to have ReSTART for its data sampling protocol, you have two choices;
You can try changing the sampling code to not have ReSTART, It may not actually be necessary for the proper operation of the sensor. The comment is kind of questionable, I don't know about the use of 'connection' in i2c parlance. Try this simple change, It's quick, and may work. Wire.endTransmission(); // Send the Tx buffer, but send a restart to keep connection alive I introduced this ReSTART failure with PR#1665 when I changed the ISR to no longer use the Chuck. |
Ok..i wasn’t too sure if I needed to change the way the data is read after issuing endtransmission(…false). I want to use your new code as I was seeing spurious interrupts happening leading to crashes in the isr routine due to accessing invalid memory location when it went to issue a debug print statement. Changing core debug level from debug to error worked around that issue.
I'll try your quick fix when I get back to the house later. If I need restart I’m assuming I can't leave the code as is as it will fail due to the restart being queued if I've followed this thread correctly.
Cheers
Iain
…Sent from my Windows 10 phone
________________________________
From: chuck todd <notifications@github.com>
Sent: Thursday, August 9, 2018 3:57:42 PM
To: espressif/arduino-esp32
Cc: ifrew; Mention
Subject: Re: [espressif/arduino-esp32] ReSTART fix, with others (#1717)
@ifrew<https://nam04.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fifrew&data=02%7C01%7C%7C087e2002a72d406167b408d5fe4b83fd%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C636694522641114852&sdata=U4KpbgsDur7IGV00X6nVZkYTzWPjV136FzUDte9pi2s%3D&reserved=0> If your sensor has to have ReSTART for its data sampling protocol, you have two choices;
* install this PR
* roll back to rc3
You can try changing the sampling code to not have ReSTART, It may not actually be necessary for the proper operation of the sensor. The comment is kind of questionable, I don't know about the use of 'connection' in i2c parlance. Try this simple change, It's quick, and may work.
Wire.endTransmission(); // Send the Tx buffer, but send a restart to keep connection alive
I introduced this ReSTART failure with PR#1665 when I changed the ISR to no longer use the I2C_MASTER_TRAN_COMP interrupt. PR 1665 solved a interrupt saturation problem. But caused this ReSTART problem. This new PR seem to function correctly, I have ran it through more testing.
Chuck.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub<https://nam04.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fespressif%2Farduino-esp32%2Fpull%2F1717%23issuecomment-411924039&data=02%7C01%7C%7C087e2002a72d406167b408d5fe4b83fd%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C636694522641114852&sdata=l88%2BxbT%2F1ogqEgyhHsp7PgjOV%2FFXpc8m%2FzcoImKSer4%3D&reserved=0>, or mute the thread<https://nam04.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FAL-x4ATamFJ5EGYOQDttYh8dW_NpCB1dks5uPL5mgaJpZM4Vs-VS&data=02%7C01%7C%7C087e2002a72d406167b408d5fe4b83fd%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C636694522641114852&sdata=%2F9rEJbeJdXRQ%2Fp5G7XYjGvSgtYMhZxzgYDlc5uexLtM%3D&reserved=0>.
|
@ifrew ReSTART doesn't work in RC4 or Release 1.0.0, There is an internal position index that decides where to store incoming data, pr1665 did not increment it correctly between the Write(ReSTART) element and the Read element. The ISR rxFifoEmpty() detects this error and fails. So, no data is stored into the Read buffer. The code you published does not check the return status of
Then, you would have to change the if to:
Chuck. |
Ah…many thanks Chuck..makes sense now.
…Sent from my Windows 10 phone
________________________________
From: chuck todd <notifications@github.com>
Sent: Thursday, August 9, 2018 4:48:03 PM
To: espressif/arduino-esp32
Cc: ifrew; Mention
Subject: Re: [espressif/arduino-esp32] ReSTART fix, with others (#1717)
@ifrew<https://nam03.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fifrew&data=02%7C01%7C%7Cb87cc622af824009189a08d5fe528cc6%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C636694552853449567&sdata=ixjiC3DgBGV5coegHzjsjazfmQPpHHO0rFvCYBufCo8%3D&reserved=0> ReSTART doesn't work in RC4 or Release 1.0.0, There is an internal position index that decides where to store incoming data, pr1665 did not increment it correctly between the Write(ReSTART) element and the Read element. The ISR rxFifoEmpty() detects this error and fails. So, no data is stored into the Read buffer.
The code you published does not check the return status of endTransmission() so it never sees the different return code. The endTransmission(false) will always return 7 (I2C_ERROR_CONTINUE) to indicate the Write transaction is pending. If your code was:
if( !Wire.endTransmission(false)){ // do read
}else { // die horribly
}
Then, you would have to change the if to:
if( Wire.endTransmission(false) == 7 ){ // do read
}else { // die horribly
}
Chuck.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub<https://nam03.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fespressif%2Farduino-esp32%2Fpull%2F1717%23issuecomment-411932596&data=02%7C01%7C%7Cb87cc622af824009189a08d5fe528cc6%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C636694552853449567&sdata=LbrP8xNqJ%2BY33%2BsoaxNH0jmdkir1K1Zkd%2FCqigG08R4%3D&reserved=0>, or mute the thread<https://nam03.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FAL-x4LzmXttM8JXagfGuBoRsurqXdtCQks5uPMozgaJpZM4Vs-VS&data=02%7C01%7C%7Cb87cc622af824009189a08d5fe528cc6%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C636694552853449567&sdata=Q563xjtUqCKkp4UyPJA1c1V9VCXZOM7IZXMz1SzOis8%3D&reserved=0>.
|
* ReSTART fix, Sequencing fix pr espressif#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 espressif#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.
@ifrew Those MPU9250 routines are from @kriswiner and based on using a Teensy board. Similar routines are used for the EM7180 Sensor Hub based Ultimate Sensor Fusion Solution - MPU9250 and the newer more accurate Ultimate Sensor Fusion Solution - LSM6DSM + LIS2MD. I struggled for a long time to get reliable ESP32 i2c reads of my original USFS board until @stickbreaker merged his improved I2C routines up to v1.0.0-rc3. After that, I2C reads broke again! It was the comment from @stickbreaker: You can try changing the sampling code to not have ReSTART, It may not actually be necessary for the proper operation of the sensor. Correct! The ESP32 is not like the Teensy ARM Cortex boards, so we do not need this for i2c reads:
My code originally looked like this:
So I changed my define at program start:
So now I can again communicate with my USFS board! All is not perfect though, my @kriswiner based code is interrupt driven and over time, some interrupts are missed. There may need to be more ongoing tweaks to the @stickbreaker i2c routines . . . |
If you are having trouble with interrupts (not sure why though, this should
work on all MCUs) then you can easily change to simplypolling the status
rgister for data ready.
…On Sat, Sep 15, 2018 at 3:29 PM Ron M. Battle ***@***.***> wrote:
@ifrew <https://github.com/ifrew> Those MPU9250 routines are from
@kriswiner <https://github.com/kriswiner> and based on using a Teensy
board. Similar routines are used for the EM7180 Sensor Hub based Ultimate
Sensor Fusion Solution - MPU9250
<https://www.tindie.com/products/onehorse/ultimate-sensor-fusion-solution-mpu9250/>
and the newer more accurate Ultimate Sensor Fusion Solution - LSM6DSM +
LIS2MD
<https://www.tindie.com/products/onehorse/ultimate-sensor-fusion-solution-lsm6dsm--lis2md/>.
I struggled for a long time to get reliable ESP32 i2c *reads* of my
original USFS board until @stickbreaker <https://github.com/stickbreaker>
merged his improved I2C routines up to v1.0.0-rc3. After that, I2C reads
*broke* again! It was the comment from @stickbreaker
<https://github.com/stickbreaker>: *You can try changing the sampling
code to not have ReSTART, It may not actually be necessary for the proper
operation of the sensor.* Correct! The ESP32 is not like the Teensy ARM
Cortex boards, so we do not need this for i2c *reads*:
Wire.endTransmission(false); // Send the Tx buffer, but send a restart to keep connection alive
My code originally looked like this:
Wire.endTransmission(I2C_NOSTOP); // Send the Tx buffer, but send a restart to keep connection alive
So I changed my define at program start:
// #define I2C_NOSTOP false // for compatibility with Teensy i2c to keep connection Alive for reads but BREAKS ESP32 i2c system! Not needed.
#define I2C_NOSTOP true // This works for improved i2c routines from @stickbreaker RMB ***
So now I can again communicate with my USFS board! All is not perfect
though, my @kriswiner <https://github.com/kriswiner> based code is
interrupt driven and over time, some interrupts are missed. There may need
to be more ongoing tweaks to the @stickbreaker
<https://github.com/stickbreaker> i2c routines . . .
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1717 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AGY1qj2HyXQX3i1AhSd-MhafTJFP10Qwks5ubX83gaJpZM4Vs-VS>
.
|
@CaptIgmu Release 1.0.0 does not work with ReSTART operations (Wire.endTransmission(false);) So you can recode your I2C operations not to use ReSTART operations, or update to the current GitHub Code. You can just grab the 4 GitHub i2c files and overwrite the ones in Release 1.0.0
Chuck. |
espressif#1869 exposed a resource exhaustion issue. The current HAL layer for I2C support is designed to use a shared interrupt, But, during debugging to solve the interrupt overloading condition identified in espressif#1588, and the generation of pr espressif#1717, the interrupt allocation parameters were changed. This change was unnecessary, the code will work successfully with shared interrupts. So, there is no need to assign a private interrupt for each I2C peripheral.
#1869 exposed a resource exhaustion issue. The current HAL layer for I2C support is designed to use a shared interrupt, But, during debugging to solve the interrupt overloading condition identified in #1588, and the generation of pr #1717, the interrupt allocation parameters were changed. This change was unnecessary, the code will work successfully with shared interrupts. So, there is no need to assign a private interrupt for each I2C peripheral.
@stickbreaker: Even with latest code, cannot use ReStart in Wire.endTransmission(false) in I2C read routines from @kriswiner EM7180 based Ultimate Sensor Fusion Solution - MPU9250; won't run. Works fine with Wire.endTransmission(true). With his example code, I also had to use polling in my main loop instead of interrupts, to get the I2C reads to be reliable at 400 KHz clock with no hangs. This might be improved with #1877, I will try interrupts again! Seems the ESP32 I2C and USFS board have some relationship troubles . . . |
Hello, I have the same problem as described here. I use platformio framework with
I tried to set up SCL and SDA pins to INPUT_PULLUP
I didn't try to add extra pullup resistors as suggested as both devices (MPU6050 and INA219) have these already on boards (2k2 on MPU6050, see https://protosupplies.com/wp-content/uploads/2019/03/MPU-6050-GY-521-Accelerometer-Schematic.jpg, and 10k on INA219, see https://e2e.ti.com/resized-image/__size/1230x0/__key/communityserver-discussions-components-files/14/4314.7585.ina219.png) When I detect I have no data from MPU6050 or INA219 I try to reset I2C. I tried many call, none helped
The only way to make it working again is to power cycle (off & on) the device. Can someone help? What should I do or try? The only thing I can think of is to add some relay controlled by the ESP32 to cut the power off and thus power cycle the device. But it's not what I want. |
@edlman please open an issue with a reproducible case and all debug logs that you can provide. Posting on an old merged PR is not the right approach to seek support. |
@edlman This patch is ancient. It is for an old version 1.0.0. Please open an issue, fill out the issue template. The current release 1.0.2 has been stable. My personal branch is out of date. I would recommend you use this repo's version. either 1.0.2 or the pre-release 1.0.3rc1. Both of them use the same i2c code. Chuck. |
@stickbreaker I'd suggest cleanup/archive your fork to prevent people from intentionally using it going forward. |
Ok, where shall I open new bug report? At the original espressif:master? Or at stickbreaker:patch-3? |
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 avoid
to abool
this will allow the reset functionality ofWire.begin()
to be reported. CurrentlyWire.begin()
attempts to reset the i2c Peripheral, but cannot report success/failure.Wire.busy()
be added. thisbool
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 toWire.begin()
this allows app level recover of a hung bus.Wire.lastError()
value updated for all errors, previously when interleavingWire.endTransmission(false)
andWire.readTransmission(false)
, the 128 byteWire.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 bytewrite()
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.i2cGetStatus()
which returns the i2c peripheral status word, used to detect bus_busy currentlyi2cDebug()
programmatic control of debug buffer outputi2cAddQueue()
to allow data_only queue element this will allow a i2c transaction to use multiple data buffer pointers.i2cDumpInts()
,i2cDumpI2c()
from app, instead, usei2cDebug()
to set trigger points