Skip to content
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

i2c: set hold time of SDA during transmit to an appropriate value #273

Merged
merged 7 commits into from
Apr 6, 2021
Merged

i2c: set hold time of SDA during transmit to an appropriate value #273

merged 7 commits into from
Apr 6, 2021

Conversation

fivdi
Copy link
Contributor

@fivdi fivdi commented Mar 20, 2021

Fixes adafruit/circuitpython#4082

Increases the set hold time of SDA during transmit from its default value of 1 to 2 enabling pico-sdk to function correctly with the TCS34725 color sensor.

On page 48 of the I2C-bus specification and user manual Table 10 lists the "Characteristics of the SDA and SCL bus lines for Standard, Fast, and Fast-mode Plus I2C-bus devices". Below this table note [3] mentions the following:

[3] A device must internally provide a hold time of at least 300 ns for the SDA signal (with respect to the V IH(min) of the SCL signal) to bridge the undefined region of the falling edge of SCL.

This PR sets the IC_SDA_TX_HOLD field in the IC_SDA_HOLD register to value required for a hold time of 300 ns in Standard-mode and Fast-mode and 120 ns in Fast-mode Plus.

The following tests were performed with this PR at 10000, 100000 and 400000 baud with two devices (MCP9808 and TCS34725) on the I2C bus and all tests ran successfully.

  • Scan the I2C bus with bus_scan from pico-examples. Without this this PR the TCS34725 is not detected. With this PR the TCS34725 is detected.
  • Read the Device ID register form the TCS34725. Without this PR there is an error. With this PR the Device ID register can be successfully read and is 0x44.
  • Read the temperature from the MCP9808 temperature sensor in an infinite loop (works with or without this PR.)

Tests were also successfully performed with a BME280 and TCS34725 at 1000000 baud.

Unfortunately, I can't really explain why the modification made by this PR is needed to to get pico-sdk to function correctly with the TCS34725. According to figure 11 on page 10 of the TCS34725 datasheet the minimum data hold time, t(HDDAT), is 0μs and the maximum is 0.9μs. However, the notes below figure 11 do say the following:

Note(s):
1. Specified by design and characterization; not production tested.

@dhalbert
Copy link
Contributor

There is a thorough implementation for the Synopsys DW_apb_i2c in the Linux kernel:
https://github.com/torvalds/linux/blob/master/drivers/i2c/busses/, files i2c-designware-*.c.

@kilograham
Copy link
Contributor

cc @Wren6991

@kilograham kilograham requested a review from Wren6991 March 22, 2021 00:06
@dhalbert
Copy link
Contributor

I have verified this fix works for the TC34725 and does not seem to interfere with the other I2C sensors I tried. I took Saleae traces and cannot see any timing differences, but without the fix the TCS34725 does not ACK several writes sent to it, and it works fine with the fix. We plan to incorporate this fix into CircuitPython's fork of pico-sdk for now.

dhalbert added a commit to adafruit/pico-sdk that referenced this pull request Mar 26, 2021
@dhalbert
Copy link
Contributor

dhalbert commented Mar 26, 2021

We (CircuitPython) have also seen some flakiness with SH1107-based I2C OLED displays. This fix also seems to fix the SH1107 problems.

EDIT: A problem with SSD1306 OLED displays is also fixed by this.

dhalbert added a commit to adafruit/pico-sdk that referenced this pull request Mar 26, 2021
dhalbert added a commit to adafruit/pico-sdk that referenced this pull request Mar 26, 2021
@dhalbert
Copy link
Contributor

Increasing IC_SDA_TX_HOLD to 5 instead of 2 will support the PA1010D GPS as well (#252 (comment)).

@fivdi
Copy link
Contributor Author

fivdi commented Mar 28, 2021

On page 48 of the I2C-bus specification and user manual Table 10 lists the "Characteristics of the SDA and SCL bus lines for Standard, Fast, and Fast-mode Plus I2C-bus devices". Below this table note [3] mentions the following:

[3] A device must internally provide a hold time of at least 300 ns for the SDA signal (with respect to the V IH(min) of the SCL signal) to bridge the undefined region of the falling edge of SCL.

On page 511 of the RP2040 Datasheet the documentation for the IC_SDA_HOLD register mentions the following:

The bits [15:0] of this register are used to control the hold time of SDA during transmit in both slave and master mode
(after SCL goes from HIGH to LOW).

It would also be interesting to know what the DW_apb_i2c documentation has to say, but I don't have access to the documentation.

Should the value programmed in IC_SDA_TX_HOLD be what every is necessary to result in a 300 ns hold time?

@dhalbert
Copy link
Contributor

It would also be interesting to know what the DW_apb_i2c documentation has to say, but I don't have access to the documentation.

I think what is in the RP2040 datasheet is just copied from the DW_apb_i2c documentation; it says so at the top.

Should the value programmed in IC_SDA_TX_HOLD be what every is necessary to result in a 300 ns hold time?

That makes perfect sense; the value can be calculated instead of this empirical testing. Would you like to revise the PR to do that?

The Linux driver mentions 300ns:
https://github.com/torvalds/linux/blob/a5e13c6df0e41702d2b2c77c8ad41677ebb065b3/drivers/i2c/busses/i2c-designware-master.c#L54-L56:

	/* Set standard and fast speed dividers for high/low periods */
	sda_falling_time = t->sda_fall_ns ?: 300; /* ns */
	scl_falling_time = t->scl_fall_ns ?: 300; /* ns */

@dhalbert
Copy link
Contributor

Would you like to revise the PR to do that?

I made a PR for the Adafruit fork of pico-sdk: adafruit#3. It calculates the SDA hold time. There's a Saleae screenshot included showing the difference.

@fivdi
Copy link
Contributor Author

fivdi commented Mar 29, 2021

Would you like to revise the PR to do that?

I'll revise it today.

@fivdi
Copy link
Contributor Author

fivdi commented Mar 29, 2021

The PR has been revised.

@fivdi fivdi changed the title i2c: set hold time of SDA during transmit to 2 for TCS34725 color sensor i2c: set hold time of SDA during transmit to 300 nanoseconds Mar 29, 2021
@dhalbert
Copy link
Contributor

@tannewt pointed out in our own PR that for "Fast Mode +", which is a 1MHz SCL clock rate, the specified fall time is only 120ns, so IC_SDA_TX_HOLD need only be 120ns or just over rather than 300ns. We did not hold up our own PR for this, but it could be added to this PR. The way the pico-sdk driver is written, it uses the "fast mode" setting of the hardware for all possible clock speeds. However, I don't know that the other calculations are applicable to 1MHz either.

I have never actually seen anyone use a 1MHz clock with CircuitPython, so if this is a limitation, we can live with it for now.

@fivdi
Copy link
Contributor Author

fivdi commented Mar 30, 2021

@tannewt pointed out in our own PR that for "Fast Mode +", which is a 1MHz SCL clock rate, the specified fall time is only 120ns, so IC_SDA_TX_HOLD need only be 120ns or just over rather than 300ns. We did not hold up our own PR for this, but it could be added to this PR. The way the pico-sdk driver is written, it uses the "fast mode" setting of the hardware for all possible clock speeds. However, I don't know that the other calculations are applicable to 1MHz either.

I have never actually seen anyone use a 1MHz clock with CircuitPython, so if this is a limitation, we can live with it for now.

@dhalbert Thank you for the information. I'll look into it.

@fivdi
Copy link
Contributor Author

fivdi commented Mar 30, 2021

I did some tests with a TCS34725 and a BME280 using the 300 ns SDA TX hold time in Fast-mode Plus and there were no issues. The wording in the specification doesn't explicitly mention what the SDA TX hold time should be in Fast-mode Plus. However, given that the fall time of both SDA and SCL signals in Fast-mode Plus is 120 ns a hold time of 120ns is more appropriate. This application note is also good. The PR has been updated to set the SDA TX hold time to 120 ns in Fast-mode Plus.

@fivdi fivdi changed the title i2c: set hold time of SDA during transmit to 300 nanoseconds i2c: set hold time of SDA during transmit to an appropriate value Mar 30, 2021
src/rp2_common/hardware_i2c/i2c.c Outdated Show resolved Hide resolved
@fivdi fivdi requested review from lurch and kilograham March 31, 2021 13:00
@lurch
Copy link
Contributor

lurch commented Mar 31, 2021

This looks reasonable to me, and it's encouraging to see that it fixes some of the reported problems with I2C, but I don't know enough about I2C to be able to give it a "formal approval" 🙂

Oh, but could you squash the commits please, as conceptually this is only a single change.

Copy link
Contributor

@Wren6991 Wren6991 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, this is a good change as per spec, and calculations look correct to me. I did to a double take on 25e6 vs 2.5e6, but seems correct.

@Wren6991 Wren6991 merged commit 2e777d4 into raspberrypi:develop Apr 6, 2021
@dhalbert
Copy link
Contributor

dhalbert commented Apr 6, 2021

@fivdi Thank you for your initial idea about this and your detailed spec reading to find the 300ns/120ns values.

@fivdi
Copy link
Contributor Author

fivdi commented Apr 6, 2021

@FiViDi Thank you for your initial idea about this and your detailed spec reading to find the 300ns/120ns values.

@dhalbert You're welcome 😄

@fivdi fivdi deleted the sda-tx-hold-time branch April 6, 2021 18:24
@kilograham kilograham added this to the 1.2.0 milestone Apr 9, 2021
kilograham added a commit that referenced this pull request Jun 3, 2021
See release notes for more descriptive details

* Delete some redundant CMake parts (#240)

* pio: Add 'pragma once' to generated header files (#237)

* pio: allow programs with 32 instructions (#236)

* Fixup incorrect doxygen for multicore_fifo_wready

* Add param-validation to spin_lock_instance

* Fix back-to-front description of IRQ priority in doxygen (#245)

* Fix ROSC typo (#259)

* Typo (#251)

* Add gpio_get_out_level() accessor, and correct SIO GPIO_OUT struct ty… (#247)

* Add gpio_get_out_level() accessor, and correct SIO GPIO_OUT struct type from WO to RW

* Add pico_get_unique_board_id_string API (#281)

* Clean up -Wconversion=error issues

* move PLL reset code from clocks driver to pll driver (#110)

* Don't clear PLL PWR/FBDIV after reset as unnecessary. Call out in runtime.c why USB/syscfg aren't reset.

* i2c: set hold time of SDA during transmit to an appropriate value (#273)

* i2c: set hold time of SDA during transmit to 2 for TCS34725 color sensor

* i2c: fix issues in i2c_write_blocking_internal

* i2c: rename sda_hold_count to sda_tx_hold_count

* use assert rather than invalid_params_if for internal consistency checks

* i2c: use a more appropriate sda tx hold time at higher baudrates

* i2c: reduce 120/1e9 to the smallest possible integer numerator and denominator

* Update NULL GPIO function to 0x1f (#320)

* i2c: set high and low times to values that conform to the i2c specification (#314)

* Make flash_do_cmd public (#269)

* Fix implementation config listing in structs/i2c.h (#324)

* Clarify that cache is flushed, but that function is intended for low-level metadata access during startup (#322)

* Fix implementation config listing in structs/i2c.h (#325)

* Fix param-validation for PIO sideset encoding (#311)

* Remove MASTER_ON_HOLD bit from I2C status registers. Fix typos. (#326)

* Fixing arithmetic underflow in SPI I/O loops #337 (#338)

* Source code licence clarification (#340)

* Updated existing Pimoroni board headers to match latest style, and added a new board (#343)

* Added new pimoroni board headers

* SPI Definitions for SparkFun boards (#344)

* SPI Definitions for SparkFun boards


* Clarify multicore_fifo doxygen (#323)

Based on my observations in #284

* correct adafruit flash size for itsybitsy and qt rp2040 (#348)

from 4 MB to 8 MB

* Small typos (#366)

* make spi_init return baud rate set (#296)

* Fix path + typo in README.md (#347)

* Fix path + typo in README.md

* Remove incorrect path change

* Remove typo

* disable core 0 SIO FIFO IRQ handler during core 1 launch in case someone has already installed one (#375)

* add PICO_DIVIDER_DISABLE_INTERRUPTS flag which makes PICO_DIVIDER disable interrupts around division rather than using co-operative guards to protect nested use (i.e. within IRQ/exception). Use of this flag can simplify porting of RTOSes but with a different performance profile (#372)

* make all non hardware_ libraries foo add C preprocessor definition LIB_FOO=1, and remove bespoke definitions which were all undocumented anyway (#374)

* Change various (confusing to user) message to be DEBUG only (#365)

* add small delay to stdio_get_until to prevent starvation of USB IRQ handler due to in use mutex. build was non deterministic due to missing link wrapping of getchar (#364)

* Some cmake build improvements (#376)

* Change some cmake output to DEBUG level
Make SDK build more consistent with other libraries (use an INTERFACE marker library for inclusion tests)
Add PICO_SDK_PRE_LIST_FILES, PICO_SDK_POST_LIST_FILES build vars

* fix typo

* remove leftover debugging message

* i2c: improve communication with i2c devices in i2c_write_blocking

* Definitions for IC_TX_BUFFER_DEPTH inconsistent (fixes #335) (#381)

* Add hardware_exception for setting exception handlers at runtime (#380)

* add __always_inline to trivial super low level inline functions (#379)

* Rework lock_core / timers (#378)

* remove spurious sys/select.h include (#377)

* Fixup IRQ_PRIORITY #define values (#393)

* Correct doxygen for mutex_try_enter (#392)

* Fix a bunch of doxygen typos (#391)

* Rework ordering of cmake, so that libraries in subdirectories can add to internal lists as PICO_SDK_POST_LIST_FILES, PICO_CONFIG_HEADER_FILES etc. (#382)

* Fix some hardware_library dependencies (#383)

* make host pico_platform.h and binary_info.h CMakeLists.txt safe for inclusion in non SDK build (#388)

* Add basic CMSIS core headers (#384)

* Fix the PICO_CONFIG default value for PICO_CMSIS_RENAME_EXCEPTIONS (#399)

* add timeout_us/until to mutex/sem blocking methods (#402)

* Fixup divider save_restore for floating point too; improve tests (#405)

* fix pico_promote_common_scope_vars (#397)

* add comment about using clk_gpout0 enable bit (Fixes #413)

* pioasm: prevent double inclusion for C SDK generated headers (#417)

* Add missing cast to uint32_t in hw_divider_u32_quotient for host (#436)

* Optional feature to get the max level that has ever been held by a queue (#444)

* Fix wrong format string in alarm_pool_dump_key (#437)

* Add support for Arduino Nano RP2040 Connect (#425)

* Add support for Arduino Nano RP2040 Connect

* Add support for at25sf128a flash

* Fix function-name misspelling (#443)

* Update host multicore.h to match multicore.h in rp2_common (#439)

* Implement `uart_write_blocking` and `uart_read_blocking` for host (#438)

* Define `__STRING` for other compilers than MSVC in the host platform.h file (#434)

* Prevent warnings about some unused parameters in pico_stdio_usb when building with -Wextra (#431)

* Fix warnings about some unused parameters in pico_stdio_usb

* Use `__unused` for the unused parameter in tud_descriptor_configuration_cb

* Remove redundant inclusions of `pico/platform.h`

* Define `void operator delete[](void *p, std::size_t n)` in new_delete.cpp (#430)

* queue: make data pointers const in queue_try_add and queue_add_blocking (#423)

* misc interp_ fixes (#428)

* some typo fixes (#408)

* Prevent the literal string DEBUG from being appended to some messages in CMake < 3.15 (#433)

* Add function to get the currently selected channel (#451)

* Add missing board detection macros (#448)

* add board detection macros for Sparkfun & RPi Pico / VGA Board

* dma_channel_transfer_[from/to]_buffer_now: added const volatile to read_addr and volatile to write_addr (#449)

* Change the quick-start instructions to include installation of the (#92)

* added spi_get_baudrate() + some consistency changes (#395)

* Allow lengthening xosc startup delay with a compile option (#457)

* Add hardware_gpio accessors for Schmitt, slew rate, drive strength (fixes #290) (#464)

* Add some spin lock related doxygen

* Move to Tinyusb 0.10.0 (#462)

* Add usb device dpram to svd file. Fixes #351 (#465)

* Add PICO_PANIC_FUNCTION define to allow replacement of the default panic function (#463)

* Add missing DREQ_ definitions

* store actual clock frequency in clock_configure (fixes #368)

* Fix hw_is_claimed, and add xxx_is_claimed APIs

* Add some PIO irq helper methods

* Add DMA channel IRQ status getter and clear methods

* Implement the correct PIO IRQ status/clear methods (good to have methods here as the h/w interrupt registers are super confusing)

* fix pico_multicore dependencies

* add missing wrapper func __aeabi_f2d

* Further DMA/PIO IRQ API cleanup (and review fixes)

* add PICO_INT64_OPS_IN_RAM flag

* fix qtpy rp2040 uart rx rev B (#466)

* Move to tinyusb 0.10.1 (upstream tinyusb repo) ($467)

* Add gpio_set_irqover to match inover/outover/oeover (fixes #265) (#470)

Co-authored-by: Andrew Scheller <andrew.scheller@raspberrypi.com>
Co-authored-by: Christian Flach <cmfcmf.flach@gmail.com>
Co-authored-by: Luke Wren <wren6991@gmail.com>
Co-authored-by: Earle F. Philhower, III <earlephilhower@yahoo.com>
Co-authored-by: majbthrd <majbthrd@gmail.com>
Co-authored-by: Peter Lawrence <12226419+majbthrd@users.noreply.github.com>
Co-authored-by: Brian Cooke <bdscooke@gmail.com>
Co-authored-by: Scott Shawcroft <scott@tannewt.org>
Co-authored-by: Michael Stoops <spam@michaelstoops.com>
Co-authored-by: ZodiusInfuser <christopher.parrott2@gmail.com>
Co-authored-by: Kirk Benell <36707344+kirk-sfe@users.noreply.github.com>
Co-authored-by: Ha Thach <thach@tinyusb.org>
Co-authored-by: Exr0n <spotyie@gmail.com>
Co-authored-by: Joni Kähärä <joni.kahara@async.fi>
Co-authored-by: Rafael G. Martins <rafael@rafaelmartins.eng.br>
Co-authored-by: Jonathan Reichelt Gjertsen <jonath.re@gmail.com>
Co-authored-by: Martino Facchin <m.facchin@arduino.cc>
Co-authored-by: Rene <reneg973@gmail.com>
Co-authored-by: Brendan <2bndy5@gmail.com>
Co-authored-by: geurtv <48989893+geurtv@users.noreply.github.com>
Co-authored-by: ewpa <34030942+ewpa@users.noreply.github.com>
Co-authored-by: Dan Halbert <halbert@halwitz.org>
Co-authored-by: Liam Fraser <liam@raspberrypi.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants