Skip to content

Commit fedd9d9

Browse files
author
Chuck Todd
committed
Update ReadMe, minor Optimization
My I2C specific README.md to libraries\Wire\docs add Link in main README
1 parent a59e1e5 commit fedd9d9

File tree

4 files changed

+161
-151
lines changed

4 files changed

+161
-151
lines changed

Diff for: README.md

+43-94
Original file line numberDiff line numberDiff line change
@@ -1,107 +1,56 @@
1-
# A fork of Espressif/arduino-esp32:
2-
## i2c communications using a ISR
1+
# Arduino core for ESP32 WiFi chip
32

4-
The existing main fork of arduino-esp32 implement i2c communications using a polled monitoring of the I2C Hardware StateMachine. The implementation of the I2C ReSTART operations leaves the I2C Hardware StateMachine(SM) in an unstable configuration. The I2C protocol arbitrates exclusive ownership of the bus to a single Master Device from a `START` signal to the next `STOP`. A restart operation is just a start signal inserted into this `START` -> `STOP` flow. Schematically like this:
5-
`START` (Read or Write) -> `ReSTART` (Read or Write) -> `STOP`.
6-
7-
By I2C protocol as [UM10204 I2C-Bus Specification and User Manual](https://Fwww.nxp.com/docs/en/user-guide/UM10204.pdf), A valid I2C transaction must be a `START` at least one Data Byte `STOP`. The Arduino environment is base on the AVR processor series, These Atmel CPU's implementation of the the I2C protocol allows unlimited pauses between protocol elements. The Espressif implement enforces a Strict TimeOut between elements. This has resulted in perceived I2C bus stability issues ([834](https://github.com/espressif/arduino-esp32/issues/834),[811](https://github.com/espressif/arduino-esp32/issues/811),[741](https://github.com/espressif/arduino-esp32/issues/741),[682](https://github.com/espressif/arduino-esp32/issues/682),[659](https://github.com/espressif/arduino-esp32/issues/659) and many more. @lonerzzz created a partial solution [#751](https://github.com/espressif/arduino-esp32/pull/751).
8-
I spend a couple of weeks investigating these problems, and discovered this Hard TimeOut was the basis of all of these problems. Once this Hard TimeOut limit is triggered the SM cannot recover without a complete reinitialization.
9-
The existing Arduino code base is reliant on the AVR's ability to infintely pause a i2c transaction. The standard coding practice of:
10-
```c++
11-
// set internal address pointer in I2C EEPROM from which to read
12-
Wire.beginTransmission(ID);
13-
Wire.write(highByte(addr));
14-
Wire.write(lowByte(addr));
15-
uint8_t err =Wire.endTransmission(false); // don't send a STOP, just Pause I2C operations
16-
if(err==0){ // successfully set internal address pointer
17-
err=Wire.requestFrom(addr,len);
18-
if(err==0){ // read failed
19-
Serial.print("Bad Stuff!! Read Failed\n");
20-
}
21-
else {// successful read
22-
while(Wire.avaiable()){
23-
Serial.print((char)Wire.read());
24-
}
25-
Serial.println();
26-
}
27-
}
28-
```
29-
May not function correctly with the ESP32, actually *usually* will not function correctly. The current arduino-esp32 platform is built upon the espressif-idf which is built on FreeRTOS, a multi-process/processor operating system. The Arduino sketch is just one task executing on One of the Two processor cores. The TimeOut is triggered when the FreeRTOS Task Scheduler does a task switch between `Wire.endTransmission(false);` and the i2c activites inside `Wire.requestfrom()`. The maximum TimeOut interval is around 12ms.
30-
My solution is to avoid this TimeOut from every being possible. I have changed how `Wire()` uses the SM. Specifically I have changed how a `ReSTART` operation is implemented. To avoid the TimeOut I have create a i2c queuing system that does not allow an i2c transaction to start unless a `STOP` has been issued. But, alas, this creates some incompatibilities with the pre-exisiting Arduino code base. The changes to the standard Arduino `Wire()` coding are minimal, but, they are necessary:
31-
```c++
32-
// new, maybe Excessive Error Return codes for @stickbreaker:arduino-esp32
33-
typedef enum {
34-
I2C_ERROR_OK=0,
35-
I2C_ERROR_DEV,
36-
I2C_ERROR_ACK,
37-
I2C_ERROR_TIMEOUT,
38-
I2C_ERROR_BUS,
39-
I2C_ERROR_BUSY,
40-
I2C_ERROR_MEMORY,
41-
I2C_ERROR_CONTINUE,
42-
I2C_ERROR_NO_BEGIN
43-
} i2c_err_t;
3+
[![Build Status](https://travis-ci.org/espressif/arduino-esp32.svg?branch=master)](https://travis-ci.org/espressif/arduino-esp32)
444

45-
// set internal address pointer in I2C EEPROM from which to read
46-
Wire.beginTransmission(ID);
47-
Wire.write(highByte(addr));
48-
Wire.write(lowByte(addr));
49-
uint8_t err =Wire.endTransmission(false); // don't send a STOP, just Pause I2C operations
5+
### Need help or have a question? Join the chat at [![https://gitter.im/espressif/arduino-esp32](https://badges.gitter.im/espressif/arduino-esp32.svg)](https://gitter.im/espressif/arduino-esp32?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
506

51-
//err will be I2C_ERROR_CONTINUE (7), an indication that the preceding
52-
//transaction has been Queued, NOT EXECUTED
7+
## Contents
8+
- [Development Status](#development-status)
9+
- [Installation Instructions](#installation-instructions)
10+
- [Decoding Exceptions](#decoding-exceptions)
11+
- [Issue/Bug report template](#issuebug-report-template)
12+
- [ESP32Dev Board PINMAP](#esp32dev-board-pinmap)
5313

54-
if(err==7){ // Prior Operation has been queued, it is NOT guaranteed that it will
55-
// successfully occur!
56-
err=Wire.requestFrom(addr,len);
57-
if(Wire.lastError()!=0){ // complete/partial read failure
58-
Serial.printf("Bad Stuff!!\nRead of (%d) bytes read %d bytes\nFailed lastError=%d,"
59-
" text=%s\n",len,err,Wire.lastError(),Wire.getErrorText(Wire.lastError()));
60-
}
61-
// some of the read may have executed
62-
while(Wire.avaiable()){
63-
Serial.print((char)Wire.read());
64-
}
65-
Serial.println();
66-
}
67-
```
14+
## Development Status
15+
- Most of the framework is implemented.
16+
- Differences:
17+
- `Wire()` for deeper explaination [README.md](libraries/Wire/docs/README.md)
18+
- 64k-1 data transfers
19+
- Special handling for sendStop=false
20+
- Missing:
21+
- `analogWrite()` While analogWrite is on it's way, there are a few other options that you can use:
22+
- 16 channels [LEDC](cores/esp32/esp32-hal-ledc.h) which is PWM
23+
- 8 channels [SigmaDelta](cores/esp32/esp32-hal-sigmadelta.h) which uses SigmaDelta modulation
24+
- 2 channels [DAC](cores/esp32/esp32-hal-dac.h) which gives real analog output
25+
- `Wire.onReceive()`
26+
- `Wire.onRequest()`
6827

69-
Additionally I have expanded the ability of `Wire()` to handle larger Reads and Writes: up to 64k-1, I have tested READs of these sizes, but the largest single WRITE i have tested is 128bytes. I can send more data to a 24LC512, but it will only store the last 128bytes.
28+
## Installation Instructions
7029

71-
I have create a few new methods for Wire:
72-
```c++
73-
uint8_t writeTransaction(uint8_t address, uint8_t* buff, size_t size, bool sendStop);// big block handling
74-
size_t requestFrom(uint8_t address, uint8_t* buf, size_t size, bool sendStop);
75-
size_t transact(size_t readLen); // replacement for endTransmission(false),requestFrom(ID,readLen,true);
76-
size_t transact(uint8_t* readBuff, size_t readLen);// bigger Block read
77-
i2c_err_t lastError(); // Expose complete error
78-
void dumpInts(); // diagnostic dump for the last 64 different i2c Interrupts
79-
size_t getClock(); // current i2c Clock rate
80-
```
30+
- Using Arduino IDE
31+
+ [Instructions for Windows](docs/arduino-ide/windows.md)
32+
+ [Instructions for Mac](docs/arduino-ide/mac.md)
33+
+ [Instructions for Debian/Ubuntu Linux](docs/arduino-ide/debian_ubuntu.md)
34+
+ [Instructions for Fedora](docs/arduino-ide/fedora.md)
35+
+ [Instructions for openSUSE](docs/arduino-ide/opensuse.md)
36+
- [Using PlatformIO](docs/platformio.md)
37+
- [Building with make](docs/make.md)
38+
- [Using as ESP-IDF component](docs/esp-idf_component.md)
8139

82-
`transact()` coding is:
83-
```c++
84-
// set internal address pointer in I2C EEPROM from which to read
85-
Wire.beginTransmission(ID);
86-
Wire.write(highByte(addr));
87-
Wire.write(lowByte(addr));
40+
#### Decoding exceptions
8841

89-
uint8_t err=Wire.transact(len); // transact() does both Wire.endTransmission(false); and Wire.requestFrom(ID,len,true);
90-
if(Wire.lastError != 0){ // complete/partial read failure
91-
Serial.printf("Bad Stuff!! Read Failed lastError=%d\n",Wire.lastError());
92-
}
93-
// some of the read may have executed
94-
while(Wire.avaiable()){
95-
Serial.print((char)Wire.read());
96-
}
97-
Serial.println();
42+
You can use [EspExceptionDecoder](https://github.com/me-no-dev/EspExceptionDecoder) to get meaningful call trace.
9843

99-
```
44+
#### Issue/Bug report template
45+
Before reporting an issue, make sure you've searched for similar one that was already created. Also make sure to go through all the issues labelled as [for reference](https://github.com/espressif/arduino-esp32/issues?utf8=%E2%9C%93&q=is%3Aissue%20label%3A%22for%20reference%22%20).
10046

101-
This **APLHA** release should be compiled with ESP32 Dev Module as its target, and
102-
Set the "core debug level" to 'error'
47+
Finally, if you're sure no one else had the issue, follow the [ISSUE_TEMPLATE](docs/ISSUE_TEMPLATE.md) while reporting any issue.
10348

104-
There is MINIMAL to NO ERROR detection, BUS, BUSY. because I have not encounter any of them!
10549

50+
## ESP32Dev Board PINMAP
10651

107-
Chuck.
52+
![Pin Functions](docs/esp32_pinmap.png)
53+
54+
## Hint
55+
56+
Sometimes to program ESP32 via serial you must keep GPIO0 LOW during the programming process

Diff for: README_OLD.md

-48
This file was deleted.

Diff for: cores/esp32/esp32-hal-i2c.c

+2-9
Original file line numberDiff line numberDiff line change
@@ -365,13 +365,6 @@ void i2cReset(i2c_t* i2c){
365365
I2C_MUTEX_UNLOCK();
366366
}
367367

368-
//** 11/2017 Stickbreaker attempt at ISR for I2C hardware
369-
// liberally stolen from ESP_IDF /drivers/i2c.c
370-
esp_err_t i2c_isr_free(intr_handle_t handle){
371-
372-
return esp_intr_free(handle);
373-
}
374-
375368
/* Stickbreaker ISR mode debug support
376369
*/
377370
#define INTBUFFMAX 64
@@ -1126,8 +1119,8 @@ return reason;
11261119

11271120
i2c_err_t i2cReleaseISR(i2c_t * i2c){
11281121
if(i2c->intr_handle){
1129-
esp_err_t error =i2c_isr_free(i2c->intr_handle);
1130-
// log_e("released ISR=%d",error);
1122+
esp_err_t error =esp_intr_free(i2c->intr_handle);
1123+
// log_e("released ISR=%d",error);
11311124
i2c->intr_handle=NULL;
11321125
}
11331126
if(i2c->i2c_event){

Diff for: libraries/Wire/docs/README.md

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# i2c communications on ESP32
2+
3+
By Official I2C protocol as [UM10204 I2C-Bus Specification and User Manual](https://Fwww.nxp.com/docs/en/user-guide/UM10204.pdf), A valid I2C transaction must be a `START`, at least one Data Byte, and a `STOP`. The Arduino environment is base on the AVR processor series, These Atmel CPU's implemention the I2C protocol allows unlimited pauses between protocol elements. The Espressif ESP32 implement enforces a Strict TimeOut between elements. This has resulted in perceived I2C bus stability issues ([834](https://github.com/espressif/arduino-esp32/issues/834),[811](https://github.com/espressif/arduino-esp32/issues/811),[741](https://github.com/espressif/arduino-esp32/issues/741),[682](https://github.com/espressif/arduino-esp32/issues/682),[659](https://github.com/espressif/arduino-esp32/issues/659) and many more. @lonerzzz created a partial solution [#751](https://github.com/espressif/arduino-esp32/pull/751).
4+
5+
The I2C protocol arbitrates exclusive ownership of the bus to a single Master Device from a `START` signal to the next `STOP`. A `ReSTART` operation is just a `START` signal inserted into this `START` -> `STOP` flow. Schematically like this:
6+
7+
`START` (Read or Write) -> `ReSTART` (Read or Write) -> `STOP`.
8+
9+
The existing Arduino code base is reliant on the AVR's ability to infintely pause a i2c transaction. The standard coding practice of:
10+
```c++
11+
// set internal address pointer in I2C EEPROM from which to read
12+
Wire.beginTransmission(ID);
13+
Wire.write(highByte(addr));
14+
Wire.write(lowByte(addr));
15+
uint8_t err =Wire.endTransmission(false); // don't send a STOP, just Pause I2C operations
16+
if(err==0){ // successfully set internal address pointer
17+
err=Wire.requestFrom(addr,len);
18+
if(err==0){ // read failed
19+
Serial.print("Bad Stuff!! Read Failed\n");
20+
}
21+
else {// successful read
22+
while(Wire.avaiable()){
23+
Serial.print((char)Wire.read());
24+
}
25+
Serial.println();
26+
}
27+
}
28+
```
29+
May not function correctly with the ESP32, actually **usually** will not function correctly. The current arduino-esp32 platform is built upon the [espressif-idf](https://github.com/espressif/esp-idf) which is built on FreeRTOS, a multi-process/processor operating system. The Arduino sketch is just one task executing on One of the Two processor cores. The TimeOut is triggered when the FreeRTOS Task Scheduler does a task switch between `Wire.endTransmission(false);` and the i2c activites inside `Wire.requestfrom()`. The maximum TimeOut interval is around 12ms.
30+
31+
This rewrite of `Wire()` is designed to avoid this TimeOut from ever being possible. To avoid the TimeOut this library uses a queuing system that does not allow an i2c transaction to start unless a `STOP` has been issued. But, alas, this creates some incompatibilities with the pre-exisiting Arduino code base. The changes to the standard Arduino `Wire()` coding are minimal, but, they are necessary:
32+
```c++
33+
// new, maybe Excessive Error Return codes for @stickbreaker:arduino-esp32
34+
typedef enum {
35+
I2C_ERROR_OK=0,
36+
I2C_ERROR_DEV,
37+
I2C_ERROR_ACK,
38+
I2C_ERROR_TIMEOUT,
39+
I2C_ERROR_BUS,
40+
I2C_ERROR_BUSY,
41+
I2C_ERROR_MEMORY,
42+
I2C_ERROR_CONTINUE,
43+
I2C_ERROR_NO_BEGIN
44+
} i2c_err_t;
45+
46+
// set internal address pointer in I2C EEPROM from which to read
47+
Wire.beginTransmission(ID);
48+
Wire.write(highByte(addr));
49+
Wire.write(lowByte(addr));
50+
uint8_t err =Wire.endTransmission(false);
51+
// don't send a STOP, just Pause I2C operations
52+
53+
//err will be I2C_ERROR_CONTINUE (7), an indication that the preceding
54+
//transaction has been Queued, NOT EXECUTED
55+
56+
if(err == 7){ // Prior Operation has been queued
57+
// it will be executed when the next STOP is encountered.
58+
// if sendStop had equaled true, then a successful endTransmission() would have
59+
// returned 0. So, if this if() was if(err==0), the Queue operation would be
60+
// considered an error. This is the primary Difference.
61+
62+
err=Wire.requestFrom(addr,len);
63+
if(Wire.lastError()!=0){ // complete/partial read failure
64+
Serial.printf("Bad Stuff!!\nRead of (%d) bytes read %d bytes\nFailed"
65+
" lastError=%d, text=%s\n", len, err, Wire.lastError(),
66+
Wire.getErrorText(Wire.lastError()));
67+
}
68+
// some of the read may have executed
69+
while(Wire.avaiable()){
70+
Serial.print((char)Wire.read());
71+
}
72+
Serial.println();
73+
}
74+
```
75+
76+
Additionally this implementation of `Wire()` includes methods to handle local buffer data blocks. These local buffer can be up to 64k-1 bytes in length.
77+
78+
### New Methods:
79+
```c++
80+
i2c_err_t writeTransaction(uint8_t address, uint8_t* buff, size_t size, bool sendStop);// big block handling
81+
size_t requestFrom(uint8_t address, uint8_t* buf, size_t size, bool sendStop);
82+
size_t transact(size_t readLen); // replacement for endTransmission(false),requestFrom(ID,readLen,true);
83+
size_t transact(uint8_t* readBuff, size_t readLen);// bigger Block read
84+
i2c_err_t lastError(); // Expose complete error
85+
void dumpInts(); // diagnostic dump for the last 64 different i2c Interrupts
86+
size_t getClock(); // current i2c Clock rate
87+
void setTimeOut(uint16_t timeOutMillis); // allows users to configure Gross Timeout
88+
uint16_t getTimeOut();
89+
```
90+
91+
`transact()` coding is:
92+
```c++
93+
// set internal address pointer in I2C EEPROM from which to read
94+
Wire.beginTransmission(ID);
95+
Wire.write(highByte(addr));
96+
Wire.write(lowByte(addr));
97+
98+
uint8_t err=Wire.transact(len); // transact() does both Wire.endTransmission(false); and Wire.requestFrom(ID,len,true);
99+
if(Wire.lastError != 0){ // complete/partial read failure
100+
Serial.printf("Bad Stuff!! Read Failed lastError=%d\n",Wire.lastError());
101+
}
102+
// some of the read may have executed
103+
while(Wire.avaiable()){
104+
Serial.print((char)Wire.read());
105+
}
106+
Serial.println();
107+
108+
```
109+
110+
This **APLHA** release should be compiled with ESP32 Dev Module as its target, and
111+
Set the "core debug level" to 'error'
112+
113+
There is MINIMAL to NO ERROR detection, BUS, BUSY. because I have not encounter any of them!
114+
115+
116+
Chuck.

0 commit comments

Comments
 (0)