-
-
Notifications
You must be signed in to change notification settings - Fork 8.2k
tests/extmod_hardware: Add a test for machine.PWM freq and duty. #16147
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
tests/extmod_hardware: Add a test for machine.PWM freq and duty. #16147
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #16147 +/- ##
=======================================
Coverage 98.57% 98.57%
=======================================
Files 164 164
Lines 21349 21349
=======================================
Hits 21045 21045
Misses 304 304 ☔ View full report in Codecov by Sentry. |
@dpgeorge What is the problem with MIMXRT and constant high setting. At least PWM is creating a constant high signal? Edit: I see that the test for 100% calculates the duty_u16 as 100 * 65535 // 100, resulting in 65535. That will indeed still create a PWM pulse, not only at the MIMXRT devices. duty_u16(65536) is constant high. |
tests/extmod_hardware/machine_pwm.py
Outdated
for freq in (50, 100, 500, 1_000, 2_000, 5_000, 10_000): | ||
pwm.freq(freq) | ||
for duty in (0, 10, 25, 50, 75, 90, 100): | ||
duty_u16 = duty * 65535 // 100 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Must be duty_u16 = duty * 65536 // 100
to get a high pulse at all devices.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But that fails with the actual ESP32 firmware. With PR #10854 it works properly. That PR is major improvement for PWM. Even with the current implementation of PWM for ESP32, the output is NOT constant high at duty_u16=65535, but time_pulse_us() may be too slow at ESP32 to sample it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's not true according to the docs, and the rp2 implementation. Both of those have 65535 as the upper limit.
This definitely needs to be clarified!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This definitely needs to be clarified!
The docs have to be updated. Besides ESP32 all ports accept 65536 as setting the output constant high and 0 for setting the output constant low. Depending on the dividers and frequencies, there may be no output pulse at e.g. 65535 (or 1).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A duty of 0 should definitely mean a duty of 0%, ie constant low output.
Then if the duty range is 16-bits, the maximum you can specify is 65535. And so that must be a constant high output. (For example, what if you are storing duty values in 2 bytes of data, you need to be able to specify the whole range in 2 bytes.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But doing it that way there is no 50% or 25% duty. Just numbers close to it. We can of course define 65535 as the value of "constant on", but keep the ratio as duty_u16/65536.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's also no perfectly accurate 10%, whether the division is /65535 or /65536.
With /65535 there is an accurate 20%, 40%, 60% and 80%.
But if the resolution of the underlying hardware PWM counter is <= 65535 (eg on rp2), then it should always be possible to select all of the available hardware duty cycles, including an accurate 50% (with /65535). Eg on rp2 a duty_u16 of 32768 should give you exactly 50%, due to integer rounding when calculating the hardware counter limit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made PR #16153 to set back the upper range limit to 65535, at which value the output is at constant high level.
time_pulse_us() is not good at timing repeating pulses. If the level at start is identical to the requested level, then it will return the remaining duration of that pulse. Changing the code to wait for the opposite level first improves it.
|
I don't think we should change The test here uses a dummy call to |
I was not suggesting changing time_pulse_us(). And the test script seems to implement that behavior. |
Would it makes sense to extract the pins configuration to a seperate file/input? I dont want to over-engineer, but having a common, perhaps shared, config will make running these HIL against different MCU more efficient |
That would be nice. But I think it's quite valuable to have a self contained test that's easy to run, and also all the requirements/settings for the test are all within that test. Eg even if the pin config was in a special Well, maybe we can improve this in the future. For now I want to encode the behaviour of hardware peripherals in the tests to make it easier to test ports and get them all behaving the same. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Limitations notwithstanding, this looks good. It's a lot more thorough than the test I wrote!
tests/extmod_hardware/machine_pwm.py
Outdated
# Test machine.PWM, frequncy and duty cycle (using machine.time_pulse_us). | ||
# | ||
# IMPORTANT: This test requires hardware connections: the PWM-output and pulse-input | ||
# pins must be wired together. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# pins must be wired together. | |
# pins must be wired together (see variables "pwm_pin" and "pulse_pin"). |
Small, and this is evident after you read and understanding the code, but if someone is running the whole test suite and doesn't overly care about PWM then it's all the information they need.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
FWIW I'd also like to see this eventually too (split out the hardware config and the test). I don't think it has to be a complex architecture, but I'd appreciate a way to add a new test case without either appending to an existing file or copy-pasting the config into a new file. However, as damien says, the test environment is currently simple because very few tests rely on additional files. It's a non-trivial amount of work to change this. |
ESP32, ESP32C3 passed this test in #10854. |
Please see some of the improvements of |
@dpgeorge and all interested persons. I want to express my opinion about the range of duty PWM. First, many ports already have a range of 0-65356. Second, ports that do not have the physical ability to accept the value 65356 can easily replace it with 65355 internally.
At the same time, they will return the actual achieved duty value 65355. pwm.duty_u16(65356)
pwm.duty_u16()
>>>65355 This is not a problem because Third, at high frequencies the resolution decreases and most duty values cannot be achieved. So, they are replaced by the closest possible ones. For example, when freq=20MHz the resolution decreases to 5 available duty value of all range 0-65356.
output is
So replacing 65356 with 65355 in the second paragraph works the same like here. Fourthly, 65356 is a power of two and is more beautiful than 65355 purely aesthetically. 😄 Sorry for the many words. |
@IhorNehrutsa the method is named For example you may want to store duty values in 2 bytes (eg in |
Once #16216 is merged, this test will be refactored to use |
Teensy pins to be updated to test 3 output types, per #16153 (comment) :
|
6a4185e
to
cebe8e9
Compare
This has been refactored to use I also added support for more than one set of pins, and when running on a Teensy board it uses the three sets suggested by @robert-hh . |
Now that it's using unittest it could print out the pins being used? Perhaps just on failure? |
cebe8e9
to
bbbfc9a
Compare
Updated to skip higher frequencies on SAMD21 (which is too slow to measure the short duty duration). Retested on all supported ports. |
I have made a test class for each set of supported pins, which is automatically generated based on the list of pins provided by the port. Eg on rp2 the test class is called And at the start of the test it now prints out the pins that are configured for that run, eg:
|
This adds a hardware test for `machine.PWM`. It requires a jumper wire between two pins, uses `machine.PWM` to output on one of them, and `machine.time_pulse_us()` to time the PWM on the other pin (some boards test more than one pair of pins). It times both the high and low duty cycle (and hence the frequency) for a range of PWM frequencies and duty cycles (including full on and full off). Currently supported on: - esp32 (needs a minor hack for initialisation, and some tests still fail) - esp8266 (passes for frequencies 1kHz and less) - mimxrt / Teensy 4.0 (passes) - rp2 (passes) - samd21 (passes for frequencies 2kHz and less) Signed-off-by: Damien George <damien@micropython.org>
bbbfc9a
to
bdda91f
Compare
Summary
This adds a hardware test for
machine.PWM
. It requires a jumper wire between two pins, usesmachine.PWM
to output on one of them, andmachine.time_pulse_us()
to time the PWM on the other pin.It times both the high and low duty cycle (and hence the frequency) for a range of PWM frequencies and duty cycles (including full on and full off).
Testing
Currently supported on:
Trade-offs and Alternatives
Need a way to exclude higher frequencies on certain platforms (eg above 1kHz on esp8266):
assert
instead of printing, but that misses a lot of useful info.unittest
which solves all the above problems.