adafruit / circuitpython

CircuitPython - a Python implementation for teaching coding with microcontrollers
https://circuitpython.org
Other
4.13k stars 1.22k forks source link

Strange problem with pin in use after power on. #2590

Open ThomasAtBBTF opened 4 years ago

ThomasAtBBTF commented 4 years ago

I am using all pins on the ItsyBitsy M4 and would need more... So I tried to do this: BTSelect = digitalio.DigitalInOut(microcontroller.pin.PA30) And was very happy that I can also use PA30 & PA31 (the SWCLK & SWDIO pins on the "other end" of the ItsyBitsy)

But now when I am using them after powering on a runtime error happens on the first boot. If I the do a reset all works fine!

Is there a way to "Deinit" all pins at startup of a script? Can I somehow deinit a pin even thou I hold no reference to the pin?

Thomas

Note: this happens on CP4 and also the same on CP Beta 5.

hierophect commented 4 years ago

The SWD pins are intentionally protected on startup, as debugging is very difficult without them. There isn't really an interface for releasing them either (I'm not sure why they're even available on your first startup). But you could try removing them from the protected list and rebuilding circuitpython. I'm not sure where the list is for Atmel-SAMD but @tannewt should be able to help you out tomorrow.

ThomasAtBBTF commented 4 years ago

Thank you for the explanation. I have one more observation:

  1. I can catch the error I am getting in a try: except clause.
  2. If I do in the except part: microprocessor.reboot() I get into a "reboot loop"
  3. If I do in the except part: a supervisor.reload() I can see in the serial console the system constantly reloading... So far so good; understood and reproducible. But if I break the supervisor.reload() loop, by pressing the "reset" switch on the board the code will work fine. No error to catch, the pins do what they are intending to do (input/output no problem!) So I think there can be / should be a possibility to actively "release" these pins once python loads a boot.py / code.py module. I don't think this will hinder the well understood and necessary startup debugging necessities.. A simple call into supervisor.xx or microprocessor.xx could easily fix that problem without the need to create a whole new branch for CircuitPython!

Thomas

tannewt commented 4 years ago

Hi Thomas, It looks like you've hit a bug in the pin reset code. For SAMD it is here: https://github.com/adafruit/circuitpython/blob/master/ports/atmel-samd/common-hal/microcontroller/Pin.c#L81

Are you hitting the RuntimeError after your code works once? Is it a "pin in use" error? All pins should be reset before all VM runs using the function in the link above. Since the SWD pins are a special case, it is likely that it needs to be fixed.

ThomasAtBBTF commented 4 years ago

Hi, after a pressing reset and the code starts to work (so: "after my code works once") I am not getting the error ever again until I power on the device once again.

Your answer leads me to another question / request: In my project the ItsyBitsy is a controller for peripherals and does "powermanagement" for an Arm-Board (NanoPi Air). The ItsyBitsy can (among other things) turn the power on and off for the Arm-Board. This (and the behaviour of CP) creates with a problem for updating the CP-Code on the ItsyBitsy. Because when I update files on the CIRCUITPY drive, the VM's seem to reset and the power for the ARM-board id cut off.

It would be helpful if you could add to microcontroller/supervisor a "value" like "don't touch the pins on next VM run.

Thomas

ThomasAtBBTF commented 4 years ago

Hi Scott, I tried to create some simple code, so that you can reproduce the problem. But not so easy!!!!! I took a new (never used) itsybitsy and loaded my code into it. (In order to strip it down to a minimal repro code.) But it turns out when no pins are connected to any peripherals the bug does not occur.

Then I reread the code piece you referenced... Line 81 says: // Configure SWD. SWDIO will be automatically switched on PA31 when a signal is input on SWCLK ! I have connected the status line of a simple bluetooth module (JDY-32). This alone may lead to the problem. Maybe over the weekend, I will investigate this further.

I still think you should "offer" a force "deinit" and leave your boot and debug code as it is. This forced deinit will almost never be called. And certainly not hinder your boot code debug experience. As any forced deinit will only happen after code.py (or maybe boot.by) started to execute.

Also I want to reiterate my request from my post above to have an additional runmode for microcontroller.on_next_reset(run_mode) which leaves the pins as they are on autoloading a new code.py !!!!!! Maybe it is not "next reset" ... So maybe something similar in supervisor like: supervisor.on_next_reload(xxx)

Thomas

tannewt commented 4 years ago

The pin reset is deliberate so that the state of the hardware is the same every time the code runs. Why do you want to control the power pin from CircuitPython but not have it reset and start the same as the first run?

ThomasAtBBTF commented 4 years ago

Because the ARM-Linux should run continuously as long a possible. And changes to the code of the itsybitsy should not shut the down the Linux system.

I can very well understand your argument, that "the hardware" should be the same every time the code runs. And this is very positive for most, if not all of the users. But: you introduced exceptions already by offering "microcontroller.on_next_reset(run_mode)" I think such an obscure and really not "dangerous" / rarely used (and not many bytes consuming) function would do no harm for the normal users.

tannewt commented 4 years ago

@ThomasAtBBTF Why not just disconnect the power control pin when you are iterating on the circuitpython code? In "normal" operating mode the CircuitPython vm won't reset and therefore won't reset the pin.

If you are designing a custom board you can exempt pins from reset but I'd rather not allow that from Python. The "never_reset" concept is only for use under-the-hood.

ThomasAtBBTF commented 4 years ago

Ok, I can live with that. But what about the two "unusable" pins? If you don't want to change the code there, how about to get a reference to the allocated pin objects in microcontroller. So they become usable.

ThomasAtBBTF commented 4 years ago

As I am not an native english speaker, can you please elaborate what you mean with: "concept is only for use under-the-hood"

tannewt commented 4 years ago

The two SWD pins should be usable. If they aren't it is because there is a bug. It is complex because when reset, the pins should be in SWD configuration. I'm not sure what you mean by a pin reference.

I meant that the concept of never_reset is meant for use in C code ("under the hood of Python") only. Thanks for requesting clarification. I'm happy to elaborate.

hierophect commented 4 years ago

@tannewt I'm guessing you mean that the two SWD pins should be usable by SWD, right? As in, under normal operation, they should not be accessible as GPIO pins, ever. Or should they be SWD pins only on reset, and then become accessible as GPIO pins for code.py? Asking because I want to make sure my STM32 port is aligning to this as well!

tannewt commented 4 years ago

@hierophect the default "reset" state of the pins should be SWD. If user code uses them then they won't be until they are deinit or the VM finishes and resets them.

ThomasAtBBTF commented 4 years ago

Thank you too! So again my problem:

  1. I would like to use the two SWD pins.
  2. When the itsybitsy starts because of getting power and I then try to use one of these pins I get an error.
  3. When I manually press the reset key and the itsybitsy restarts, if I then try to use one of these pins, I can use them with no problems.

Neither microprocessor.reboot() nor supervisor.reload() is chaingeing this.

I understand, that this is a complex case!

But when these pin are in use, someone / some part of the underlying code uses them and does not set them free when booting after power on. If they are in use somewhere there is a reference(/instance?) to the pin object either in python or in the c code or in the SAMD. Could this instance to the object be shared? like microprocessor.cpu which is the sole object of microprocessor.Processor.

tannewt commented 4 years ago

@ThomasAtBBTF thank you for the clear recap of the issue. I don't believe the issue is any user code. My guess is that the state of the pins on start up varies with reset as you are seeing. It'll take some debugging to sort out what state the pins are in when you are seeing them as in use.

deshipu commented 1 year ago

I don't think this is a good first issue. It looks like a complex thing that needs a lot of knowledge to debug properly. Should we remove the label?