micropython
micropython copied to clipboard
Has write_analog changed since firmware 1.7.9
I had some code for controlling a servo, the code used PMW to send a micro second timed pulse. I was using firmware 1.7.9, but recently have upgraded Mu and uflash to use the latest version 1.9.2-34 v1.0.0-rc.2
def write_us(self, period):
duty=int(period * 1024 * self.frequency / 1000000)
self.pin.write_analog(duty)
self.pin.write_digital(0)
which worked and the servo would move. Now there's nothing until a sleep is added between write_analog and write_digital.
Yes the PWM code has changed, see for example 508bb402f1eafab0530f22d19550f4f41a2856be
If you do a write_analog
followed directly by a write_digital
then it's not guaranteed that the output will have a chance to do the write_analog
part. Having a short delay between the two (of the length of one duty cycle) would be the right thing to do here.
@dpgeorge is this is a breaking change for user code? Is there something in this area we should include in as part of the release notes for a the final 1.0.0 release?
is this is a breaking change for user code?
Technically, yes. I'm not sure how the existing PWM code worked, but the current code works as follows:
- PWM period is set to a specific value (can be changed by user, defaults to 20ms)
- PWM driver cycles at the period, turning pins on and off at the right time
- active PWM pins turn on when they are ready to turn on, given their duty cycle; bigger duty cycles turn on earlier in the full PWM period
- all active PWM pins turn off together at the end of the PWM period, then the period starts again
In a diagram (pin0 has the largest duty, pin2 the smallest):
o------------- PWM PERIOD -----------o
pin0 0000011111111111111111111111111111111100...
pin1 0000000000001111111111111111111111111100...
pin2 0000000000000000000000000000001111111100...
If there are no pins active as PWM then the PWM period is 20ms and it just cycles doing nothing.
So if you call pin.write_analog(duty)
when no PWM is currently active, then the pin might take up to 40ms to turn on in the worst case (if the 20ms idle PWM period just started and duty is very small), and 0ms to turn on in the best case (if the 20ms idle PWM period just finished and the duty is very large).
Is there something in this area we should include in as part of the release notes for a the final 1.0.0 release?
I guess the best we can say is that you must wait at least 2x PWM period before the pin goes high after the first write_analog()
.
We could possible fix this for the case of a single PWM pin being enabled/disabled (as in the code example at the top of this issue). For example if the PWM driver is idle and write_analog()
is called, then it jumps ahead straightaway to the start of the duty for the new pin. But that would require a bit of effort and a fair bit of testing (because it'll be a very low level change).