adafruit / Adafruit_CircuitPython_STMPE610

Adafruit CircuitPython driver for the STMPE610 resistive touchscreen controller
MIT License
1 stars 10 forks source link

Device reset during initialization causes trouble #16

Open FoamyGuy opened 3 years ago

FoamyGuy commented 3 years ago

It seems that there is an "unlucky" period during and shortly after the initialization of this driver. If the device resets during this period the touch chip seems to get "locked up" into a state where it will always cause this error:

Traceback (most recent call last):
  File "code.py", line 32, in <module>
  File "adafruit_stmpe610.py", line 281, in __init__
RuntimeError: Failed to find STMPE610! Chip Version 0x8

I am using:

Adafruit CircuitPython 6.2.0-beta.4 on 2021-03-18; Adafruit Feather RP2040 with rp2040

With a 3.5" FeatherWing. and the following test code:

import board
import time
import digitalio
import displayio
from adafruit_stmpe610 import Adafruit_STMPE610_SPI
from adafruit_hx8357 import HX8357

COUNTDOWN = 5
for i in range(COUNTDOWN):
    print("waiting {}".format(COUNTDOWN-i))
    time.sleep(0.7)

displayio.release_displays()
spi = board.SPI()
cs = board.D9
dc = board.D10

ts_cs = board.D6
sd_cs = board.D5

ts_cs = digitalio.DigitalInOut(ts_cs)

_display_bus = displayio.FourWire(spi, command=dc, chip_select=cs)

display = HX8357(_display_bus, width=480, height=320)

st = Adafruit_STMPE610_SPI(spi, ts_cs)

print("Go Ahead - Touch the Screen - Make My Day!")
while True:
    if not st.buffer_empty:
        print(st.read_data())

I am able to enter the state reliably with this set up. Here are the series of events I see:

  1. Plug in device to PC
  2. Device immediately boots and begins running code.py
  3. If I am quick I can see "detected new device" in Mu and open the serial console
  4. I can see the last few numbers from the countdown get printed
  5. The screen gets initialized and I see "Go Ahead - Touch the Screen - Make My Day!" printed briefly.
  6. CIRCUITPY drive appears to my OS
  7. The device resets again. I suspect the OS may have written something automatically to cause it. But I am not certain.
  8. Device reconnects. I see "detected new device" in Mu again and can open the serial console.
  9. Countdown finishes normally. Display turns on and shows the "Failed to find" Runtime error.
  10. at this point I can CTRL-C then CTRL-D over and over and reliably get the same error every time. To get back to a working state I can use the reset button on the device, or unplug / replug. But if nothing else changes it will end up right back in this state again.

I could record a video of this sequence if it would be helpful.

The Chip Version {} number that it that it prints is not always the same.

If the time of the countdown is increased it makes it more likely that the device wont hit his "unlucky" period during the initialization. With a high enough countdown it will reliably boot and work correctly every time because it's never having a chance to try setting up the touch screen before the "extra" reset happens.

If my hunch about the OS writing something to trigger the "extra" reset is correct then I suppose that is really at fault here. However I am left wondering if there is something we could within the driver to try to be more resilient to badly timed resets. Or if there would be some way to more safely catch this error and recover back to a working state without needing the reset or unplug/replug.

Another way I see it get triggered sometimes is with scripts that don't have a wait time built in, when pasting multiple files onto the device (i.e. a module or several image assets) the device resets several times as the different files get finished writing.

Adding enough wait time before doing anything seems to be a reliable solution, but means that you have to wait several seconds before being able to do anything each time it runs.

jerryneedell commented 3 years ago

As I recall, the STMPE610 has a funky way of initializing SPI and sometimes comes up on the wrong polarity -- see https://github.com/adafruit/Adafruit_CircuitPython_STMPE610/blob/master/adafruit_stmpe610.py#L275 -- I wonder if this is related. Just a guess.

FoamyGuy commented 3 years ago

Thank you Jerry. I did notice that as well and am also suspicious that it may be related to the issue somehow.

I did a few very quick tests in the driver:

Neither of them seem to have any noticeable effect on the issue.

Once I get back into it I intend to do some testing to figure out if there is any consistency in which SPI mode tends to work successfully more frequently or not.

jerryneedell commented 3 years ago

@FoamyGuy FYI -- I have not been able to reproduce this issue with a 2.4 inch TFT https://www.adafruit.com/product/3315 connecting to the feather RP2040 from a Raspberry Pi 400. I tried with the countdown at 5, 2 and with it removed.

It just comes up to the "Go Ahead..." prompt after plugging it in

the 2.4 inch display is a 320x240 ILI9341 , not an HX8357 if that is relevant. I don't have the 3.5 inch display.

FoamyGuy commented 3 years ago

For experimentation later: Maybe supervisor.disable_auto_reload and supervisor.enable_auto_reload could be used during initialization to avoid resets from file writes during this time. Although that behavior might be confusing to users. Better than this crash / lockout I think though.

jerryneedell commented 3 years ago

very interesting, As noted above, I have beeb unable to reproduce this with a feather rp2040 on a Raspberry Pi 4 BUT -- I tried it when connected to my MAC running Mu and it fails every time!! also fails on the MAC without Mu. there does appear to be an extra RESET on the Mac when plugging int.

jerryneedell commented 3 years ago

Another bit of information. I connected an STMPE610 breakout (no display) to the Feather RP2040. So far I cannot reproduce the failure on either the Raspberry Pi or the Mac. So, there may be some interaction with displayio involved....

jerryneedell commented 3 years ago

Now I am having trouble reproducing the issue reliably on the Mac....so back to just trying to find a reliable trigger.

jerryneedell commented 3 years ago

interesting new bit of information -- On the Feather RP2040 the simpletest runs fine with CP 6.2.0 but it fails as above when I load 7.0.0-alpha.1 ....

jerryneedell commented 3 years ago

Another bit of information -- it works fine with 7.0.0-alpha.1 on a feather_stm32f405 just not with the feather_rp2040.

jerryneedell commented 3 years ago

sigh -- today I am finding that the feather_rp2040 works sometimes with 7.0.0-alpha.1 -- "g6097afdaa" ( tip of main ) But it only works after a hard RESET or power-cycle. It fails on soft reset or if run from the REPL....

jerryneedell commented 3 years ago

ah -- if I do a displayio.release_displays() prior to a soft reboot, then the stmpe610 initialization works OK after reboot and the code runs normally. This is on the feather_rp2040 with CP 7.0.0-alpha.1

FoamyGuy commented 3 years ago

Nice find! I wonder if it would be worthwhile in the core to have it do a release_displays() whenever the file write reboot is triggered. Not sure if that would cause other problems though with the way that the display stays running even in REPL and outside of code.py

jerryneedell commented 3 years ago

Just a brief update. I have been looking into the behavior of the STMPE610 with a logic analyzer and finding some very interesting results. The STMPE610 data sheet https://cdn-shop.adafruit.com/datasheets/STMPE610.pdf has a lot of information about how the chip configures itself for SPI at power on. It looks at the state of a few of its input pins. What I am finding is that it keeps toggling SPI modes with every soft reset but it is not between Mode 0 and 1 as expected, it is toggling between Mode 1 and 2 (flipping the clock polarity!).Some things are making sense and others are still baffling. I hope to have some screen shots to share soon.

jerryneedell commented 3 years ago

The plot thickens... If I connect an STMPE610 breakout to the feather RP2040. It always initializes the stmpe610 to SPI MODE0.... so it appears that the way the stmpe610 is sensing the critical pins on power up is different, causing it to come up in different SPI modes.

I next connected the stmpe610 breakout to the aft featherwing with a different CS pin. When I ru the code not using displayio, the tft featherwing stmpe610 toggles between SPI modes 1 and 2 but works after every soft reboot. The breakout board using the same code (different CS) always works in SPI Mode 0.... If I try the code using displayio, both toggle between working and failing to find the board but the tft uses SPI Mode 2 and the breakout uses SPI mode 0...

This is puzzling but I think informative... back to the datasheet and testing...

jerryneedell commented 3 years ago

This is very confusing .. I have now concluded that all of the above testing was flawed and unreliable. Sorry for all the confusion.

I am going back to the beginning and trying to test more methodically. I'll post new results as I can.

As of now, with the Feather rp2040, if displayio is not used, that is , just run the stmpe610_simpletest.py the tft featherwing works normally on hard reset and soft reset. With displayio enabled, it runs normal after a hard reset and on every other soft reset .... if I keep doing control C followed by control D it alternates between running normally normal or failing to detect the board properly....

It also runs normally every time if I release the display before the soft reset (control-d).

This is essentially where we started.

All of the SPI Mode setting confusion above does not appear to have been reliable.

The Featherwing and the breakout board do come up in different SPI Modes. The featherwing uses SPI Mode 1 while the breakout uses SPI Mode 0. The library driver correctly makes the chageover. I thought the reason for this was due to the pulldown resistor on the breakout board for the A0/MISO line, but adding a pulldown resistor to the TFT does not change its behavior. It is still not clear why they configure differently for SPI.

The issue regarding displayio and the soft reset does not appear to be related to the SPI Mode...

jerryneedell commented 3 years ago

I am still baffled by this but here is an update.

Here is the test code I am using with the feather RP2040 on a 2.4 inch TFT (and also on a keyboard featherwing)

import board
import time
import digitalio
import displayio
from adafruit_stmpe610 import Adafruit_STMPE610_SPI
from adafruit_ili9341 import ILI9341

displayio.release_displays()
spi = board.SPI()
cs = board.D9
dc = board.D10
sd_cs = board.D5
ts_cs = digitalio.DigitalInOut(board.D6)
st = Adafruit_STMPE610_SPI(spi, ts_cs)

_display_bus = displayio.FourWire(spi, command=dc, chip_select=cs)

display = ILI9341(_display_bus, width=320, height=240)

print("Go Ahead - Touch the Screen - Make My Day!")

while True:
    if not st.buffer_empty:
        print(st.read_data())

Note that the order of initializing the stmpe610 and the display has been swapped compared to the initial example above.

If I run this as code,py starting with a Hard RESET or power on, it works normally. If I then connect to the REPL and stop execution with a Control-C then issue a soft reset - Control-D the code fails to recognize the stmpe610. If I then repeat the Control-D, it runs normally! This pattern continues in that it woill alternately execute normally or fail on eachattempt to soft reboot.

soft reboot

Auto-reload is on. Simply save files over USB to run them or enter REPL to disable.

code.py output:
Traceback (most recent call last):
  File "code.py", line 14, in <module>
  File "adafruit_stmpe610.py", line 283, in __init__
RuntimeError: Failed to find STMPE610! Chip Version 0x804

Code done running.

Press any key to enter the REPL. Use CTRL-D to reload.

Adafruit CircuitPython 7.0.0-alpha.1-120-g0189b80aa on 2021-04-18; Adafruit Feather RP2040 with rp2040
>>> 
soft reboot

Auto-reload is on. Simply save files over USB to run them or enter REPL to disable.

code.py output:
Go Ahead - Touch the Screen - Make My Day!

without the displayio configuration , the following test runs normally every time.

import board
import digitalio
from adafruit_stmpe610 import Adafruit_STMPE610_SPI

cs = digitalio.DigitalInOut(board.D6)
st = Adafruit_STMPE610_SPI(board.SPI(), cs)

print("Go Ahead - Touch the Screen - Make My Day!")

while True:
    if not st.buffer_empty:
        print(st.read_data())

This issue only occurs on the feather rp2040 -- I have also run it with a feather m4 express and a feather stm32f405 with no issues.

Looking at logic analyzer out put has not revealed a problem other than the returned data is incorrect as reported. The driver read the version ID from the STMPE at start up. When it fails, it appears to be receiving unexpected responses.

Another bit of information about displayio: If I exectute

import displayio
displayio.release_displays()

before executing the soft reboot, then the code runs normally every time! This is puzzling because the code example does execute displayio.release_displays() before initializing the STMPE as well, but it fails ever other time.....

The rp2040 does not have an "special" code for the "FourWire" displayio configuration but this issue is unique to the rp2040. My guess at this point is that there is some issue with the "FourWire" code in CP that is leaving the SPI bus in an bad state.

Should this issue be moved to the CP repository?

FoamyGuy commented 3 years ago

Testing mentioning @adafruit/circuitpythonlibrarians

jerryneedell commented 3 years ago

Making progress. this has nothing to do with the STMPE610... I tried replacing the Access of an STMPE610 with accessing and SPI FRAM and reproduced the same behavior.

However, I found that the error is induced only if a Control-C is used to break out of the "while True" loop. If I replace the "while True" loop with a "for x n range(10000)" and let the loop terminated, then it restarts normally after a Conrol-D reset but if I terminated the program with a Control-C before the "for" loop completes, the error condition occurs. This happens with the SPMPE610 or with the FRAM.

I will open a new issue in CP later today -- and reference this but I'll leave this open until the issue is resolved.