From 2b165f99c4ad21a32c1d2013c03a4d6afc61431b Mon Sep 17 00:00:00 2001 From: morris Date: Thu, 18 Oct 2018 11:25:22 +0800 Subject: [PATCH 1/5] i2c_tool: add i2c_tool example test 1. add example test for i2c-tools 2. make command line arguments number configurable --- .gitlab-ci.yml | 6 +++ components/driver/i2c.c | 8 ++-- examples/peripherals/i2c/i2c_tools/README.md | 23 +++++----- .../peripherals/i2c/i2c_tools/example_test.py | 42 +++++++++++++++++++ .../i2c/i2c_tools/main/Kconfig.projbuild | 14 +++++++ .../i2c/i2c_tools/main/cmd_i2ctools.c | 4 +- .../i2c_tools/main/i2ctools_example_main.c | 4 +- 7 files changed, 80 insertions(+), 21 deletions(-) create mode 100644 examples/peripherals/i2c/i2c_tools/example_test.py diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d6d66fd50d..2e40d3b54f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1009,6 +1009,12 @@ example_test_006_01: - ESP32 - Example_ShieldBox +example_test_007_01: + <<: *example_test_template + tags: + - ESP32 + - Example_I2C_CCS811_SENSOR + UT_001_01: <<: *unit_test_template tags: diff --git a/components/driver/i2c.c b/components/driver/i2c.c index 1fec115896..c69fbe697c 100644 --- a/components/driver/i2c.c +++ b/components/driver/i2c.c @@ -1087,7 +1087,7 @@ esp_err_t i2c_master_read(i2c_cmd_handle_t cmd_handle, uint8_t* data, size_t dat return i2c_master_read_static(cmd_handle, data, data_len, ack); } else { if(data_len == 1) { - return i2c_master_read_byte(cmd_handle, data, I2C_MASTER_NACK); + return i2c_master_read_byte(cmd_handle, data, I2C_MASTER_NACK); } else { esp_err_t ret; if((ret = i2c_master_read_static(cmd_handle, data, data_len - 1, I2C_MASTER_ACK)) != ESP_OK) { @@ -1095,7 +1095,7 @@ esp_err_t i2c_master_read(i2c_cmd_handle_t cmd_handle, uint8_t* data, size_t dat } return i2c_master_read_byte(cmd_handle, data + data_len - 1, I2C_MASTER_NACK); } - } + } } static void IRAM_ATTR i2c_master_cmd_begin_static(i2c_port_t i2c_num) @@ -1306,7 +1306,7 @@ esp_err_t i2c_master_cmd_begin(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle, clear_bus_cnt++; if(clear_bus_cnt >= I2C_ACKERR_CNT_MAX) { i2c_master_clear_bus(i2c_num); - clear_bus_cnt = 0; + clear_bus_cnt = 0; } ret = ESP_FAIL; } else { @@ -1400,4 +1400,4 @@ int i2c_slave_read_buffer(i2c_port_t i2c_num, uint8_t* data, size_t max_size, Ti } xSemaphoreGive(p_i2c->slv_rx_mux); return cnt; -} \ No newline at end of file +} diff --git a/examples/peripherals/i2c/i2c_tools/README.md b/examples/peripherals/i2c/i2c_tools/README.md index 0f8a7b5efa..ca150d838b 100644 --- a/examples/peripherals/i2c/i2c_tools/README.md +++ b/examples/peripherals/i2c/i2c_tools/README.md @@ -24,17 +24,10 @@ To run this example, you should have one ESP32 dev board (e.g. ESP32-WROVER Kit) **Note:** The following pin assignments are used by default, you can change them with `i2cconfig` command at any time. -| | SDA | SCL | -| ---------------- | ------ | ------ | -| ESP32 I2C Master | GPIO18 | GPIO19 | -| Sensor | SDA | SCL | - -- master: - - GPIO18 is assigned as the data signal of I2C master port - - GPIO19 is assigned as the clock signal of I2C master port - -- connection: - - connect SDA/SCL of CCS811 sensor with GPIO18/GPIO19 +| | SDA | SCL | GND | Other | VCC | +| ---------------- | ------ | ------ | ---- | ----- | ---- | +| ESP32 I2C Master | GPIO18 | GPIO19 | GND | GND | 3.3V | +| Sensor | SDA | SCL | GND | WAK | VCC | **Note: ** There’s no need to add an external pull-up resistors for SDA/SCL pin, because the driver will enable the internal pull-up resistors itself. @@ -43,6 +36,8 @@ To run this example, you should have one ESP32 dev board (e.g. ESP32-WROVER Kit) Enter `make menuconfig` if you are using GNU Make based build system or enter `idf.py menuconfig` if you are using CMake based build system. Then go into `Example Configuration` menu. - You can choose whether or not to save command history into flash in `Store command history in flash` option. +- You can set the maximum number of command line arguments under `Maximum number of command line arguments` option. +- You can set the command line buffer length under `Command line buffer length` option. ### Build and Flash @@ -199,9 +194,11 @@ esp32> i2cget -c 0x5b -r 0x02 -l 8 * I don’t find any available address when running `i2cdetect` command. * Make sure your wiring connection is right. * Some sensor will have a “wake up” pin, via which user can put the sensor into a sleep mode. So make sure your sensor in **not** in the sleep state. - * Have a try resetting you I2C device, and then re-run `i2cdetect`. + * Reset you I2C device, and then run `i2cdetect` again. * I can’t get the right content when running `i2cdump` command. - * Currently the `i2cdump` only support those who have the same content length of registers inside the I2C device. For example, if a device have three legal addresses, and the content length at these address are 1 byte, 2 bytes and 4 bytes. Then you should not expect this command to dump the register correctly. + * Currently the `i2cdump` only support those who have the same content length of registers inside the I2C device. For example, if a device have three register addresses, and the content length at these address are 1 byte, 2 bytes and 4 bytes. In this case you should not expect this command to dump the register correctly. +* I really input argument correctly, but the command line “discard” the last few arguements from time to time. + * Enlarge the maximum number of arguments in the menuconfig. diff --git a/examples/peripherals/i2c/i2c_tools/example_test.py b/examples/peripherals/i2c/i2c_tools/example_test.py new file mode 100644 index 0000000000..2e8a109f74 --- /dev/null +++ b/examples/peripherals/i2c/i2c_tools/example_test.py @@ -0,0 +1,42 @@ +from __future__ import print_function +import os +import sys +test_fw_path = os.getenv("TEST_FW_PATH") +if test_fw_path and test_fw_path not in sys.path: + sys.path.insert(0, test_fw_path) +import TinyFW +import IDF + +EXPECT_TIMEOUT = 20 + + +@IDF.idf_example_test(env_tag='Example_I2C_CCS811_SENSOR') +def test_i2ctools_example(env, extra_data): + # Get device under test, flash and start example. "i2ctool" must be defined in EnvConfig + dut = env.get_dut('i2ctools', 'examples/peripherals/i2c/i2c_tools') + dut.start_app() + dut.expect("esp32>", timeout=EXPECT_TIMEOUT) + # Get i2c address + dut.write("i2cdetect") + dut.expect("5b", timeout=EXPECT_TIMEOUT) + # Get chip ID + dut.write("i2cget -c 0x5b -r 0x20 -l 1") + dut.expect("0x81", timeout=EXPECT_TIMEOUT) + # Reset sensor + dut.write("i2cset -c 0x5b -r 0xFF 0x11 0xE5 0x72 0x8A") + dut.expect("OK", timeout=EXPECT_TIMEOUT) + # Get status + dut.write("i2cget -c 0x5b -r 0x00 -l 1") + dut.expect_any("0x10", timeout=EXPECT_TIMEOUT) + # Change work mode + dut.write("i2cset -c 0x5b -r 0xF4") + dut.expect("OK", timeout=EXPECT_TIMEOUT) + dut.write("i2cset -c 0x5b -r 0x01 0x10") + dut.expect("OK", timeout=EXPECT_TIMEOUT) + # Get new status + dut.write("i2cget -c 0x5b -r 0x00 -l 1") + dut.expect_any("0x98", "0x90", timeout=EXPECT_TIMEOUT) + + +if __name__ == '__main__': + test_i2ctools_example() diff --git a/examples/peripherals/i2c/i2c_tools/main/Kconfig.projbuild b/examples/peripherals/i2c/i2c_tools/main/Kconfig.projbuild index 1b1b9632dc..3ff2662a78 100644 --- a/examples/peripherals/i2c/i2c_tools/main/Kconfig.projbuild +++ b/examples/peripherals/i2c/i2c_tools/main/Kconfig.projbuild @@ -8,4 +8,18 @@ config STORE_HISTORY command history. If this option is enabled, initalizes a FAT filesystem and uses it to store command history. +config MAX_CMD_ARGUMENTS + int "Maximum number of command line arguments" + default 16 + range 8 256 + help + maximum number of command line arguments to parse + +config MAX_CMD_LENGTH + int "Command line buffer length" + default 256 + range 256 512 + help + length of command line buffer, in bytes + endmenu diff --git a/examples/peripherals/i2c/i2c_tools/main/cmd_i2ctools.c b/examples/peripherals/i2c/i2c_tools/main/cmd_i2ctools.c index eae93a9d1d..b9c87369c8 100644 --- a/examples/peripherals/i2c/i2c_tools/main/cmd_i2ctools.c +++ b/examples/peripherals/i2c/i2c_tools/main/cmd_i2ctools.c @@ -255,7 +255,7 @@ static int do_i2cset_cmd(int argc, char **argv) /* Check chip address: "-c" option */ int chip_addr = i2cset_args.chip_address->ival[0]; /* Check register address: "-r" option */ - int data_addr = -1; + int data_addr = 0; if (i2cset_args.register_address->count) { data_addr = i2cset_args.register_address->ival[0]; } @@ -267,7 +267,7 @@ static int do_i2cset_cmd(int argc, char **argv) i2c_cmd_handle_t cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, chip_addr << 1 | WRITE_BIT, ACK_CHECK_EN); - if (data_addr != -1) { + if (i2cset_args.register_address->count) { i2c_master_write_byte(cmd, data_addr, ACK_CHECK_EN); } for (int i = 0; i < len; i++) { diff --git a/examples/peripherals/i2c/i2c_tools/main/i2ctools_example_main.c b/examples/peripherals/i2c/i2c_tools/main/i2ctools_example_main.c index 302a1d3879..eacf80c38b 100644 --- a/examples/peripherals/i2c/i2c_tools/main/i2ctools_example_main.c +++ b/examples/peripherals/i2c/i2c_tools/main/i2ctools_example_main.c @@ -86,8 +86,8 @@ static void initialize_console() /* Initialize the console */ esp_console_config_t console_config = { - .max_cmdline_args = 8, - .max_cmdline_length = 256, + .max_cmdline_args = CONFIG_MAX_CMD_ARGUMENTS, + .max_cmdline_length = CONFIG_MAX_CMD_LENGTH, #if CONFIG_LOG_COLORS .hint_color = atoi(LOG_COLOR_CYAN) #endif From 34c1d25c7ff2a5d5c9acc933e3a07c2fdba06ac4 Mon Sep 17 00:00:00 2001 From: jeremy Date: Sun, 30 Sep 2018 19:26:09 +1000 Subject: [PATCH 2/5] reduce speed of i2c master bus reset routine and release sda --- components/driver/i2c.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/components/driver/i2c.c b/components/driver/i2c.c index c69fbe697c..b329a50080 100644 --- a/components/driver/i2c.c +++ b/components/driver/i2c.c @@ -541,14 +541,23 @@ static esp_err_t i2c_master_clear_bus(i2c_port_t i2c_num) // because after some serious interference, the bus may keep high all the time and the i2c bus is out of service. gpio_set_direction(scl_io, GPIO_MODE_OUTPUT_OD); gpio_set_direction(sda_io, GPIO_MODE_OUTPUT_OD); - gpio_set_level(scl_io, 1); + + int scl_half_period = 5; // use standard 100kHz data rate gpio_set_level(sda_io, 1); - gpio_set_level(sda_io, 0); + ets_delay_us(scl_half_period); + gpio_set_level(scl_io, 1); + ets_delay_us(scl_half_period); for (int i = 0; i < 9; i++) { gpio_set_level(scl_io, 0); + ets_delay_us(scl_half_period); gpio_set_level(scl_io, 1); + ets_delay_us(scl_half_period); } - gpio_set_level(sda_io, 1); + gpio_set_level(sda_io, 0); // setup stop condition (this is an implicit start condition) + ets_delay_us(scl_half_period); + gpio_set_level(sda_io, 1); // generate stop condition + ets_delay_us(scl_half_period); + i2c_set_pin(i2c_num, sda_io, scl_io, 1, 1, I2C_MODE_MASTER); return ESP_OK; } From e55f9c1e98c293fed95d740b1db3735021639cab Mon Sep 17 00:00:00 2001 From: chuck todd Date: Sun, 30 Sep 2018 14:47:05 -0600 Subject: [PATCH 3/5] 1. Slow down I2C to 100khz During Reset I am stealing this delay coding from @jeremyherbert #2493 pr. 2. Change Bus Reset to handle interrupted READ sequences. The current code does not handle interrupted READ cycles. If a SLAVE device was in a read operation when the bus was interrupted, the SLAVE device is controlling SDA. The only bit during the 9 clock cycles of a byte READ the MASTER(ESP32) is guaranteed control over, is during the ACK bit period. If the SLAVE is sending a stream of ZERO bytes, it will only release SDA during the ACK bit period. The master(ESP32) cannot generate a STOP unless SDA is HIGH. So, this reset code synchronizes the bit stream with, Either, the ACK bit, Or a 1 bit. 3. fix typo correct `sda_id` to `sda_io` in `i2c_master_clear_bus()` @ryan-ma found it. This typo was generated when I manually edited this patch on GitHub, I should have done a Copy/Paste operation! --- components/driver/i2c.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/components/driver/i2c.c b/components/driver/i2c.c index b329a50080..d268eaab27 100644 --- a/components/driver/i2c.c +++ b/components/driver/i2c.c @@ -537,27 +537,29 @@ static esp_err_t i2c_master_clear_bus(i2c_port_t i2c_num) int sda_io = GPIO.func_in_sel_cfg[sda_in_sig].func_sel; I2C_CHECK((GPIO_IS_VALID_OUTPUT_GPIO(scl_io)), I2C_SCL_IO_ERR_STR, ESP_ERR_INVALID_ARG); I2C_CHECK((GPIO_IS_VALID_GPIO(sda_io)), I2C_SDA_IO_ERR_STR, ESP_ERR_INVALID_ARG); - // We do not check whether the SDA line is low - // because after some serious interference, the bus may keep high all the time and the i2c bus is out of service. gpio_set_direction(scl_io, GPIO_MODE_OUTPUT_OD); gpio_set_direction(sda_io, GPIO_MODE_OUTPUT_OD); - + // If a SLAVE device was in a read operation when the bus was interrupted, the SLAVE device is controlling SDA. + // The only bit during the 9 clock cycles of a READ byte the MASTER(ESP32) is guaranteed control over is during the ACK bit + // period. If the slave is sending a stream of ZERO bytes, it will only release SDA during the ACK bit period. + // So, this reset code needs to synchronize the bit stream with, Either, the ACK bit, Or a 1 bit to correctly generate + // a STOP condition. int scl_half_period = 5; // use standard 100kHz data rate + gpio_set_level(scl_io, 0); gpio_set_level(sda_io, 1); ets_delay_us(scl_half_period); - gpio_set_level(scl_io, 1); - ets_delay_us(scl_half_period); - for (int i = 0; i < 9; i++) { - gpio_set_level(scl_io, 0); - ets_delay_us(scl_half_period); + int i=0; + while( !gpio_get_level(sda_io) && (i<9)){ // cycle SCL until SDA is HIGH gpio_set_level(scl_io, 1); ets_delay_us(scl_half_period); + gpio_set_level(scl_io, 0); + ets_delay_us(scl_half_period); + i++; } - gpio_set_level(sda_io, 0); // setup stop condition (this is an implicit start condition) + gpio_set_level(sda_io,0); // setup for STOP + gpio_set_level(scl_io,1); ets_delay_us(scl_half_period); - gpio_set_level(sda_io, 1); // generate stop condition - ets_delay_us(scl_half_period); - + gpio_set_level(sda_io, 1); // STOP, SDA low -> high while SCL is HIGH i2c_set_pin(i2c_num, sda_io, scl_io, 1, 1, I2C_MODE_MASTER); return ESP_OK; } From 924daf7b8463c0201d81f5d9604d5d7a8be94564 Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Wed, 28 Nov 2018 15:41:46 +0800 Subject: [PATCH 4/5] bugfix(i2c): reduce speed of I2C master bus reset routine and release SDA closes https://github.com/espressif/esp-idf/issues/2494 closes https://github.com/espressif/esp-idf/pull/2493 closes https://github.com/espressif/esp-idf/pull/2496 1. Change bus reset to handle interrupted READ sequences. 2. Slow down I2C to 100khz during reset 3. If a SLAVE device was in a read operation when the bus was interrupted, the SLAVE device is controlling SDA.The only bit during the 9 clock cycles of a byte READ the MASTER(ESP32) is guaranteed control over, is during the ACK bit period. If the SLAVE is sending a stream of ZERO bytes, it will only release SDA during the ACK bit period. The master(ESP32) cannot generate a STOP unless SDA is HIGH. So, this reset code synchronizes the bit stream with, Either, the ACK bit, Or a 1 bit. --- components/driver/i2c.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/components/driver/i2c.c b/components/driver/i2c.c index d268eaab27..ad67b16395 100644 --- a/components/driver/i2c.c +++ b/components/driver/i2c.c @@ -80,6 +80,8 @@ static DRAM_ATTR i2c_dev_t* const I2C[I2C_NUM_MAX] = { &I2C0, &I2C1 }; #define I2C_MASTER_TOUT_CNUM_DEFAULT (8) /* I2C master timeout cycle number of I2C clock, after which the timeout interrupt will be triggered */ #define I2C_ACKERR_CNT_MAX (10) #define I2C_FILTER_CYC_NUM_DEF (7) /* The number of apb cycles filtered by default*/ +#define I2C_CLR_BUS_SCL_NUM (9) +#define I2C_CLR_BUS_HALF_PERIOD_US (5) typedef struct { uint8_t byte_num; /*!< cmd byte number */ @@ -525,7 +527,9 @@ esp_err_t i2c_get_data_mode(i2c_port_t i2c_num, i2c_trans_mode_t *tx_trans_mode, static esp_err_t i2c_master_clear_bus(i2c_port_t i2c_num) { I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + const int scl_half_period = I2C_CLR_BUS_HALF_PERIOD_US; // use standard 100kHz data rate int sda_in_sig = 0, scl_in_sig = 0; + int i = 0; if (i2c_num == I2C_NUM_0) { sda_in_sig = I2CEXT0_SDA_IN_IDX; scl_in_sig = I2CEXT0_SCL_IN_IDX; @@ -536,25 +540,22 @@ static esp_err_t i2c_master_clear_bus(i2c_port_t i2c_num) int scl_io = GPIO.func_in_sel_cfg[scl_in_sig].func_sel; int sda_io = GPIO.func_in_sel_cfg[sda_in_sig].func_sel; I2C_CHECK((GPIO_IS_VALID_OUTPUT_GPIO(scl_io)), I2C_SCL_IO_ERR_STR, ESP_ERR_INVALID_ARG); - I2C_CHECK((GPIO_IS_VALID_GPIO(sda_io)), I2C_SDA_IO_ERR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK((GPIO_IS_VALID_OUTPUT_GPIO(sda_io)), I2C_SDA_IO_ERR_STR, ESP_ERR_INVALID_ARG); gpio_set_direction(scl_io, GPIO_MODE_OUTPUT_OD); - gpio_set_direction(sda_io, GPIO_MODE_OUTPUT_OD); + gpio_set_direction(sda_io, GPIO_MODE_INPUT_OUTPUT_OD); // If a SLAVE device was in a read operation when the bus was interrupted, the SLAVE device is controlling SDA. // The only bit during the 9 clock cycles of a READ byte the MASTER(ESP32) is guaranteed control over is during the ACK bit // period. If the slave is sending a stream of ZERO bytes, it will only release SDA during the ACK bit period. // So, this reset code needs to synchronize the bit stream with, Either, the ACK bit, Or a 1 bit to correctly generate // a STOP condition. - int scl_half_period = 5; // use standard 100kHz data rate gpio_set_level(scl_io, 0); gpio_set_level(sda_io, 1); ets_delay_us(scl_half_period); - int i=0; - while( !gpio_get_level(sda_io) && (i<9)){ // cycle SCL until SDA is HIGH + while(!gpio_get_level(sda_io) && (i++ < I2C_CLR_BUS_SCL_NUM)) { gpio_set_level(scl_io, 1); ets_delay_us(scl_half_period); gpio_set_level(scl_io, 0); ets_delay_us(scl_half_period); - i++; } gpio_set_level(sda_io,0); // setup for STOP gpio_set_level(scl_io,1); From 9163e454f153974d608bf5bf4bf35d0ce0a82410 Mon Sep 17 00:00:00 2001 From: morris Date: Thu, 29 Nov 2018 20:34:57 +0800 Subject: [PATCH 5/5] example_test: using try block to pass the python style check --- examples/peripherals/i2c/i2c_tools/example_test.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/examples/peripherals/i2c/i2c_tools/example_test.py b/examples/peripherals/i2c/i2c_tools/example_test.py index 2e8a109f74..a42a0f0740 100644 --- a/examples/peripherals/i2c/i2c_tools/example_test.py +++ b/examples/peripherals/i2c/i2c_tools/example_test.py @@ -1,14 +1,17 @@ from __future__ import print_function import os import sys -test_fw_path = os.getenv("TEST_FW_PATH") -if test_fw_path and test_fw_path not in sys.path: - sys.path.insert(0, test_fw_path) -import TinyFW -import IDF EXPECT_TIMEOUT = 20 +try: + import IDF +except ImportError: + test_fw_path = os.getenv("TEST_FW_PATH") + if test_fw_path and test_fw_path not in sys.path: + sys.path.insert(0, test_fw_path) + import IDF + @IDF.idf_example_test(env_tag='Example_I2C_CCS811_SENSOR') def test_i2ctools_example(env, extra_data):