esp-rs / esp32-hal

A hardware abstraction layer for the esp32 written in Rust.
Apache License 2.0
192 stars 28 forks source link

[WIP] Blocking I2C #26

Closed fmckeogh closed 3 years ago

fmckeogh commented 4 years ago

A very rough initial suggestion for how I2C might look. There are several things that need to be added, including proper configuration during construction and possible an nrf-hal style GPIO Pin implementation to make peripherals that can work on any pins slightly more ergonomic.

Currently this PR adds a new example of using the I2C interface with a display (as found on several esp32 dev boards). The rest of the implementation attempts to mimic exactly what the esp32-idf-hal I2C implementation does, and is commented with the respective esp-idf functions.

Currently there is a bug which is that it doesn't work. I don't have a working logic analyser, my multimeter's leads introduce too much capacitance to reliably measure if the clock signal is even enabled (tested by running an Arduino example which successfully draws on the display). I really wish I could give more details, but the only esp32 board I have uses the JTAG pins for the SSD1306 display which makes debugging a program that writes to the display very tricky.

Here is the serial output I see when I run the program.

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3ffb0018,len:176
load:0x3ffb00d0,len:7828
ho 0 tail 12 room 4
load:0x40080400,len:29384
entry 0x400849����x��

serial initialized
display built
addr: 60, bytes: [0, 174]
txfifo_cnt: 3
start
write
stop
addr: 60, bytes: [0, 213, 128]
txfifo_cnt: 4
start
write
stop
addr: 60, bytes: [0, 168, 63]
txfifo_cnt: 4
start
write
stop
addr: 60, bytes: [0, 211, 0]
txfifo_cnt: 4
start
write
stop
addr: 60, bytes: [0, 64]
txfifo_cnt: 3
start
write
stop
addr: 60, bytes: [0, 141, 20]
txfifo_cnt: 4
start
write
stop
addr: 60, bytes: [0, 32, 0]
txfifo_cnt: 4
start
write
stop
addr: 60, bytes: [0, 161]
txfifo_cnt: 3
start
write
stop
Fatal exception (28): LoadProhibited
epc1=0x400826fd, epc2=0x00000000, epc3=0x00000000, excvaddr=0x00000001, depc=0x00000000
MabezDev commented 4 years ago

Thanks! I have a logic analyser, just not sure I have one of those displays! I'll try and dig out an I2C device and give it go :)

fmckeogh commented 4 years ago

I've been using https://github.com/reitermarkus/esp32-hello to try and help debug my implementation by swapping out parts with esp-idf functions, no luck so far but I'll keep trying.

fmckeogh commented 4 years ago

Just moved the latest i2c.rs, this was working on reitermarkus's template but I'm getting the LoadProhibited errors again.

fmckeogh commented 4 years ago

I've just pushed @arjanmels's changes from his branch, but it's failing to send commands on my device :/ I'll test again on the esp32-hello platform.

arjanmels commented 4 years ago

@chocol4te Checked out freshly and for me it still works. Does it still hang in the same loop on your device?

fmckeogh commented 4 years ago

@arjanmels It does, what device are you using? I’ve only got the one esp32-based dev board unfortunately.

arjanmels commented 4 years ago

It is one of these: https://m.nl.aliexpress.com/item/32822105291.html?spm=a2g0n.orderlist-amp.item.32822105291&aff_trace_key=&aff_platform=msite&m_page_id=1463amp-H5OSSPYtzwPGRVxTR0OP5g1592256854558&browser_id=e6f0dbe35873489988d03936066ca05a&is_c=Y

fmckeogh commented 4 years ago

Hmm, I’m using one of these: https://heltec.org/project/wifi-lora-32/

I’ll try clearing everything on my machine and starting fresh I guess 😅

fmckeogh commented 4 years ago

I'm unfortunately still getting the issue where commands don't ever send/sent register never set, but I'm happy to close this and merge @arjanmels I2C branch, if nobody else can replicate my issue then we shouldn't be blocked on it.

arjanmels commented 4 years ago

@chocol4te It looks like the helltec board has no pullup resistors on SCK and SDA, so it counts on the pullups of the esp32. It could be that the problem is due to errata "3.6 GPIO pull-up and pull-down resistors for pads with both GPIO and RTC_GPIO functionality can only be controlled via RTC_GPIO registers. (rev0, rev1 and rev3)". I will try to implement the suggested workaround one of the upcoming days.

fmckeogh commented 4 years ago

@arjanmels Oh thank you so much! I'd never have spotted that, I'll try and confirm on my board :)

arjanmels commented 4 years ago

@arjanmels Oh thank you so much! I'd never have spotted that, I'll try and confirm on my board :)

@chocol4te Ok, I implemented the workaround. For me it still works (I haven't confirmed the actual pull down/up currents yet). Can you give it a spin? https://github.com/arjanmels/esp32-hal/tree/feature-i2c-am. You will need to use updated esp32 to avoid a version conflict: https://github.com/arjanmels/esp32/tree/update-cargo-xtensa-lx6-rt.

fmckeogh commented 4 years ago

@arjanmels Same behaviour as before. I can't find any resistors I can use for manual pull-ups, and connecting the display to non-RTC pins (22 and 23) doesn't appear to work either, but it does work when using pins 22 and 23 to connect to an MPU6050. Reading still needs to be implemented so I'm gonna start on that today :)

arjanmels commented 4 years ago

@chocol4te The pull up/down were not disabled by default in the RTC. I fixed this now. Can you give it another try?

(I tested the pins with a multimeter now. On my board there are indeed 10kOhm pullups to 3.3V on pins 4 & 15. I also tested the functionality of the library. It now works properly. Pull up/down are capable to give ~ 75uA, which is ~ 45kOhm.)

fmckeogh commented 4 years ago

@arjanmels It's working!! 🥳

fmckeogh commented 4 years ago

Once the GPIO improvements get merged I can rebase and copy some of my changes onto feature-i2c-am, like frequency and filter configuration?

arjanmels commented 4 years ago

Once the GPIO improvements get merged I can rebase and copy some of my changes onto feature-i2c-am, like frequency and filter configuration? Yes, please take ownership in any way you want for the i2c driver again. (I don't plan to drive this forward, I just used it as a vehicle to get the GPIO right.)

fmckeogh commented 4 years ago

I've been trying and failing to implement reading/transfers, I've just pushed my work so far in the last commit. I'm again using the reference manual and esp-idf source to understand how it needs to work and I am, again, stuck. When reading the RXFIFO buffer fills with 255. My guess would be that maybe the pull-up is too strong, but both sensors (MPU6050 and SGP30) work fine in an Arduino environment. I've checked and double-checked the ACK settings, even tried random combinations but no luck. Any advice/help would be greatly appreciated :/

arjanmels commented 4 years ago

According to the errata 3.18, the FIFO's cannot be reliably read via the AHB bus (address 0x600x_xxxx), but need to be read via the DPORT (0x3ffx_xxxx). Hope that helps, otherwise I can try to take a look later this week.

fmckeogh commented 4 years ago

No rush, thank you so much for your help! I read through the errata but totally missed the last one. Just pushed a commit for using DPORT for FIFO reads and AHB for FIFO writes for compatibility across all versions, but still reading 255.

arjanmels commented 4 years ago

@chocol4te I didn't have too many I2C device at hand. I tried on a M5stickC board. I can get the AXP192 going (but not the SH200Q, may be some power gating on the board).

Which board do you use for the MPU6050 and SGP30?

Only thing different I noted between Arduino Lib & your rust code is a slight difference in timings, but for AXP192 both sets of timings work. To reproduce the Arduino timing you can use:

        let period = ((80_000_000 / freq) / 2) as u16;
        let half_period = period / 2;
        let quarter_period = half_period / 2;
        let scl_low = period;
        let scl_high = period;
        let sda_hold = quarter_period;
        let sda_sample = quarter_period;
        let setup = half_period;
        let hold = half_period;
ubamrein commented 3 years ago

Regarding reading from I2C:

I'm currently experimenting with a HT16K33, when I try yo read the display buffer the first byte in the returned buffer seems to be the last byte in the actual buffer (I write the buffer and read it out again with changing data). Every other byte is 255 (default?).

Also I was wondering, if anyone is currently actively working on it? :)

I don't have enough knowledge yet into how exactly I2C works, so I'm a bit confused about the read from the exact same byte for rx_ncount times.

svenstaro commented 3 years ago

I think this is very exciting work and would love to actually see this merged as I'm trying to get a ssd1306 OLED display to work as well. Could somebody shed some light on where this currently sits at? Perhaps in this case, even incomplete work would be useful to get merged so that someone else may pick it up.

MabezDev commented 3 years ago

@chocol4te thanks for your hard work so far! If possible, could you briefly outline where this PR is stands so that someone else could take a stab at finishing it up?

fmckeogh commented 3 years ago

@MabezDev I'm so sorry, university started back up in September and I didn't have much free time.

My general process to implement this was reading the esp-idf source and trying to replicate register writes exactly, and once it was working, build up the layers of abstraction (expose clock and other configuration methods). Essentially the code as it stands should be a basic working I2C implementation, but has a bug where it reads in invalid data (0xFF).

Anyone who wants to work on it should most likely start by running the code as is under a logic analyser to see if there is any explanation there, followed by testing different pull-up configurations to find if that could be the issue.

hanhossain commented 3 years ago

I was able to get this merged with the latest master (commit e8dd426) on my fork. I've verified that I can write to the SSD1306 using the same Heltec lora board mentioned above. Next up, I'm going to try to get reads working on an MPU6050 - I do have a logic analyzer so I'll start there.