Closed daveshome closed 1 year ago
Please use the pre-release version 7. 489163b74ed (#4530) is only in the main branch, not in any 6.x branch.
This issue persist in v7.0.0 for me. Any ideas what it could be?
we (@mjs513 and myself) are taking a look at this now and there may be several issues to look at. Some may be related to some other issues.
First I believe one limitation here include not all pins will work with this. And the ones that may work with this are a subset of the ones that the Teensy Arduino builds support. If you look at my[ Teensy 4.1 xls document
https://github.com/KurtE/TeensyDocuments/blob/master/Teensy4x%20Pins.xlsx
The PWM column shows some additional data in it that is not on the T4.1 card that ships with the board
It appears only the ones like: 4A2 will work (
So if you run a sketch like:
import board
import pwmio
import time
import supervisor
#pwm0 = pwmio.PWMOut(board.D0, frequency=5000, variable_frequency=True, duty_cycle=32767) #1x1
pwm2 = pwmio.PWMOut(board.D2, frequency=5000, variable_frequency=False, duty_cycle=16384) #4A2
pwm4 = pwmio.PWMOut(board.D4, frequency=5000, variable_frequency=False, duty_cycle=16384) #4A2
#pwm10 = pwmio.PWMOut(board.D10, frequency=5000, variable_frequency=True, duty_cycle=32767) #Q10
while True:
if supervisor.runtime.serial_bytes_available:
foo # cause it to break
#time.sleep(0.25)
If I look at Logic analyzer it looks reasonable.
Notice the sleep is commented out!
Now suppose I un-comment it.
The timings are all screwed up...
So looks like several things to investigate.
Minor update to the previous: D0 and D1 don't appear to work either, they don't error out or anything, but nothing shows up on Logic analyzer
Edit: Thought it might be main uart... so added: board.UART().deinit() And it did not appear to make any differences
@dhalbert @jepler and all,
Wondering if helps for you to see some of the information from the debug steps. Or if some of us continue discuss/work on it in background?
For example updated test sketch:
import board
import pwmio
import time
import supervisor
print("uart deinit")
board.UART().deinit()
print("setupPWM pin 1")
pwm1 = pwmio.PWMOut(board.D1, frequency=5000, variable_frequency=True, duty_cycle=32767) #1x1
print("setupPWM pin 2")
pwm2 = pwmio.PWMOut(board.D2, frequency=5000, variable_frequency=False, duty_cycle=16384) #4A2
print("setupPWM pin 4")
pwm4 = pwmio.PWMOut(board.D4, frequency=5000, variable_frequency=False, duty_cycle=16384) #4A2
#pwm10 = pwmio.PWMOut(board.D10, frequency=5000, variable_frequency=True, duty_cycle=32767) #Q10
print("setup done")
while True:
if supervisor.runtime.serial_bytes_available:
foo # cause it to break
#time.sleep(0.25)
I still have some of my debug prints in at the lower levels to see things like what the IOMUXC values are set to... So startup run of below shows:
code.py output:
uart deinit
>>> common_hal_reset_pin called: 0x401b8000 3 401f80c8=5 401f82b8=10b0>>> common_hal_reset_pin called: 0x401b8000 2 401f80c4=5 401f82b4=10b0setupPWM pin 1
>>> IOMUXC_SetPinMux called: mr:401f80c4 mm:4 prev mm:5 ir:0 id:0 cr:401f82b4 iof:0
>>> IOMUXC_SetPinConfig called: mr:0 mm:0 ir:0 id:0 cr:401f82b4 cv:7070 Prev mp:10b0
setupPWM pin 2
>>> IOMUXC_SetPinMux called: mr:401f8024 mm:1 prev mm:5 ir:401f849c id:0 cr:401f8214 iof:0
>>> IOMUXC_SetPinConfig called: mr:0 mm:0 ir:0 id:0 cr:401f8214 cv:7070 Prev mp:10b0
setupPWM pin 4
>>> IOMUXC_SetPinMux called: mr:401f802c mm:1 prev mm:5 ir:401f8478 id:0 cr:401f821c iof:0
>>> IOMUXC_SetPinConfig called: mr:0 mm:0 ir:0 id:0 cr:401f821c cv:7070 Prev mp:10b0
setup done
So it is setting pin 1 to IOMUXC pin to 4 and the others two to 1, which is correct for FlexPWM mode...
Next up to check to see if I can print out the values of the timers... What is confusing by the using sleep screws it up, is first thought is maybe sleep() is spending a lot of time with interrupts disabled, which might help explain why ctrl-c may not work to break out...
But I would expect that the PWM code would setup the timer to automatically update the pin, so that should not matter. But...
So sleep may be shutting down the peripherals as a power-saving measure, and they should not be shut down. This is usually settable on a peripheral, at least on other chips I have seen.
So sleep may be shutting down the peripherals as a power-saving measure, and they should not be shut down. This is usually settable on a peripheral, at least on other chips I have seen.
Good clue and yes, the current code for doing time.sleep() is totally messing this up along with others that I know of and probably lots I have not tried playing with.
That is in port.c we have:
void port_idle_until_interrupt(void) {
// App note here: https://www.nxp.com/docs/en/application-note/AN12085.pdf
// Clear the FPU interrupt because it can prevent us from sleeping.
if (__get_FPSCR() & ~(0x9f)) {
__set_FPSCR(__get_FPSCR() & ~(0x9f));
(void)__get_FPSCR();
}
common_hal_mcu_disable_interrupts();
if (!background_callback_pending()) {
NVIC_ClearPendingIRQ(SNVS_HP_WRAPPER_IRQn);
// Don't down clock on debug builds because it prevents the DAP from
// reading memory
#if CIRCUITPY_DEBUG == 0
CLOCK_SetMode(kCLOCK_ModeWait);
#endif
__WFI();
CLOCK_SetMode(kCLOCK_ModeRun);
}
common_hal_mcu_enable_interrupts();
}
With the code like this we get the totally messes up PWM, as I showed in the posting this morning.
But if you comment out that line in DEBUG... Or set debug... then the PWM output looks correct on many of the pins.
Another issue that this I believe is causing, which is probably #4444 is that in several of the scripts, when running ctrl-C would not break in... So I put hacks in, to detect if any data available on serial and if so do a bogus line...
I have never mucked much with the CCM_CLPCR register (Low Power Control Register), but I have seen many issues associated with them. That for the Teensy boards a user created a library Snooze (https://github.com/duff2013/Snooze) for setting things up in low power mode. And I know that there are lots of issues that one has to decide on when they use this, as for example it may turn off most all of the clocks on the chip, which is probably what is happening here. So many things may not work depending on which clock is configured to be used. For example will LPUARTS work? Not sure.
Not sure yet what to do here. I personally don't think this is something you would want to do for the simple short period of time sleep calls. As I am guessing that in majority of cases when a call like this is made the user expects other things to continue to work.
I would guess that to properly do low power mode, you should probably have some other methods or modules for this, to control things like, do you want to turn off all of the clocks, do you want all of the IO pins to go to low power mode, ... What things can bring you out of low power mode...
Suggestions on what to do for now?
@dhalbert @tannewt @ladyada @jepler
Quick update/question -
a) Main problem getting any pin to work is because of you going into low power mode when you do something like sleep, which does things like turn off the clocks that PWM is using...
b) Current code base for PWM will not support all of the pins that Teensy Cards that ship with T4.x show as PWM. That is the Arduino code base supports both FlexPWMTimers as this code does as well as Quad Timers, which this one does not
c) Currently the FlexPWM code is only working with Channels A and B, not the X channel per each of the Timers/Sub Timers have. It allows you to specify the pins, but they don't work... (What I am working on now)
d) Usage Caveat - If you choose to have PWM running on multiple pins, and they have the same Channel/SubChannel than they will both have the same frequency. Example Pins 2 and 3 are: FLEXPWM4_PWMA02, FLEXPWM4_PWMB02
Question - Can fixes go into SDK files or do we pull in and fix SDK code into the higher level files. That is the: sdk file - fsl_pwm.c does not handle the X channel for PWM output. I put one fix in it so far that at least I get spike output...
But then I need to update the setDuty code, and notice now in PWMout.c that: @jepler changed some code in the set duty to try to support the X channels, https://github.com/adafruit/circuitpython/blame/main/ports/mimxrt10xx/common-hal/pwmio/PWMOut.c#L185 It has the comment:
// we do not use PWM_UpdatePwmDutycycle because ... // it works in integer percents // it can't set the "X" duty cycle
So from this I am assuming that I will need to pull the code in from
if (PWM_Init(self->pwm->pwm, self->pwm->submodule, &pwmConfig) == kStatus_Fail) {
return PWMOUT_INVALID_PIN;
}
As it does not properly handle the X channel?
Edit: I meant not PWM_Init, but: status_t status = PWM_SetupPwm(self->pwm->pwm, self->pwm->submodule, &pwmSignal, 1, kPWM_EdgeAligned, frequency, PWM_SRC_CLK_FREQ);
a) Main problem getting any pin to work is because of you going into low power mode when you do something like sleep, which does things like turn off the clocks that PWM is using...
Are there bits to set on low-power mode that leave the peripheral power on? That would be the right thing to do here.
Question - Can fixes go into SDK files or do we pull in and fix SDK code into the higher level files. That is the: sdk file - fsl_pwm.c does not handle the X channel for PWM output. I put one fix in it so far that at least I get spike output...
Are the SDK files copied from ST upstream? Are there fixes upstream, or this is a deficiency in the SDK HAL files? Certainly if necessary, the SDK files can be fixed. I think that at least some of those files are in submodules?
Are there bits to set on low-power mode that leave the peripheral power on? That would be the right thing to do here.
Sorry, wondering if it is really worth trying to start out that way of setting this global mode, without seeing if it actually does anything for you. And there are lots of pitfalls.
But yes maybe in this case, One can enable the PWM to continue by setting the WAITEN bit in FCTRL2 for each of the Timers/Sub-timers.
But then not sure for example what happens if you have UART4 active. If data comes in, does it wake up? Does it buffer it? Or does it lose it...
Now back to working on getting all 3 channels to work. It won't with the current configuration, with edge... But I know can work with center...
You can stop using the low-power mode for time.sleep()
if you think it is too complicated for now. The person who was an expert on this is no longer working on it. On SAMD, we just do a WFI
to wait for the next interrupt while sleeping, and that lowers the power substantially.
@dhalbert @KurtE @tannewt First to me this is just a discussion on low power mode. So lets talk about low-power mode on the mimxrt chips. To really put the processor in a low power mode you have to turn off the peripherials and unless you know what is resources are being used you can't selectively disable clocks. The snooze library that @KurtE referenced has options for that and is controlled by the user. For a real low power you should lower the system clock as well.
Just to clarify in my own mind, since I am not a Python programmer (just did a little bit with OpenMV), I looked up some definitions:
from https://learn.adafruit.com/arduino-to-circuitpython/time
The equivalent function to delay in CircuitPython is the time.sleep function.
but delay in arduino world does not throw the processor into low power mode.
from https://learn.adafruit.com/deep-sleep-with-circuitpython/overview
- If a program does a deep sleep, it first exits, and then the microcontroller goes to sleep, turning off as much as possible while still being able to wake up later. When the microcontroller wakes up, it will start your program (code.py) from the beginning.
- If a program does a light sleep, it still goes to sleep but continues running the program, resuming after the statement that did the light sleep. Power consumption will be minimized.
However, on some boards, such as the ESP32-S2, light sleep does not save power compared with just using time.sleep().
Then it goes into a discussion on alarms to wake up the processor. So a bit confused.
I understand using WFI to go into I guess would be a light sleep but am not sure why we are looking at putting the processor into low power mode for time.sleep function. Definitely understand the need for deep sleep as described in the last reference. Guess most of my programming has been using delay in milliseconds or think max was a second or two.
To really put the processor in a low power mode you have to turn off the peripherials
I think we should be a bit more specific and differentiate between the processor core and the system on a chip (soc) which includes peripherals.
Light sleep and time.sleep()
should stop the clock to the core to save power. It shouldn't have power disabled because that would reset the core. It should also maintain any active peripherals. In other words, the core is in a low power state but the soc isn't. The peripherals can then wake the core via interrupts. Light sleep works similarly, that's why time.sleep()
shares the idle code.
unless you know what is resources are being used you can't selectively disable clocks.
In CircuitPython you do know what peripherals you are using and the code should ideally enable and disable the appropriate clocks as needed. Unfortunately, vendor libraries usually assume these decisions are made at compile time and therefore CircuitPython has to enable everything. Instead, CircuitPython should enable clocks dynamically outside of the vendor library if necessary. This is a lot of detail work so most ports don't do this well. IIRC SAMD will enable things as needed but not disable them.
I cannot get PWM to work in CircuitPython 6.2 or 6.3. Almost all pins do PWM when using C, C++. I have tried many frequencies and duty cycles.
Have tried several older hex loads of CP and the latest.
Code/REPL
import pwmio import board import time
p = pwmio.PWMOut(board.D0, frequency=5000, variable_frequency=True, duty_cycle=2**15) time.sleep(120)
Behavior
Script runs and the board outputs on the REPL (for example: "pwm: 0x403e0000, sum 2, chan 0, mux 2" while trying PWM on pin D9. Or 0x403dc000, sum 1, chan 2, mux 4 if trying pin D0
Description
No detectable output on selected pin. (Tried most all pins.)