How to make PWM rising edge trigger an interrupt?

The official pyboard running MicroPython.
This is the reference design and main target board for MicroPython.
You can buy one at the store.
Target audience: Users with a pyboard.
bittware
Posts: 45
Joined: Mon Aug 18, 2014 3:27 am

How to make PWM rising edge trigger an interrupt?

Post by bittware » Fri Jul 15, 2022 11:41 am

Is it possible to have a pin output PWM meanwhile let the output's rising or falling edge trigger an interrupt?

tepalia02
Posts: 99
Joined: Mon Mar 21, 2022 5:13 am

Re: How to make PWM rising edge trigger an interrupt?

Post by tepalia02 » Sat Jul 16, 2022 10:28 am

Which particular board do you want to use? ESP32?

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: How to make PWM rising edge trigger an interrupt?

Post by Roberthh » Sat Jul 16, 2022 11:24 am

Almost all internal counters can be programmed to trigger an interrupt if the count range is reached, which is at every period of PWM. With pyb.Timer you could check, if the callback options of timer.channel is doing that. Alternatively, you can connect the PWM output to an input Pin object and use pin.irq(). Be aware of the callback latency, which is fast for PyBoard (~5 µs), but slow for other boards like ESP32.

bittware
Posts: 45
Joined: Mon Aug 18, 2014 3:27 am

Re: How to make PWM rising edge trigger an interrupt?

Post by bittware » Sun Jul 17, 2022 1:21 am

tepalia02 wrote:
Sat Jul 16, 2022 10:28 am
Which particular board do you want to use? ESP32?
The pyboard in my case

bittware
Posts: 45
Joined: Mon Aug 18, 2014 3:27 am

Re: How to make PWM rising edge trigger an interrupt?

Post by bittware » Sun Jul 17, 2022 1:27 am

Roberthh wrote:
Sat Jul 16, 2022 11:24 am
Almost all internal counters can be programmed to trigger an interrupt if the count range is reached, which is at every period of PWM. With pyb.Timer you could check, if the callback options of timer.channel is doing that. Alternatively, you can connect the PWM output to an input Pin object and use pin.irq(). Be aware of the callback latency, which is fast for PyBoard (~5 µs), but slow for other boards like ESP32.
In fact, I thought the latter approach as the fallback solution, but it seems to be redundant. That's also why I raised this question.
As for the period triggered interrupt, I doubt it triggers the interrupt at the very end of the period. But my intention was to trigger the interrupt at the beginning of the PWM by either the rising or falling edge of the PWM signal.

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: How to make PWM rising edge trigger an interrupt?

Post by Roberthh » Sun Jul 17, 2022 6:19 am

As for the period triggered interrupt, I doubt it triggers the interrupt at the very end of the period.
That's hardware dependent and configurable ni the hardware. If that should be implemented, a common semantics would have to be defined for all ports supporting it. So for the moment, pin IRQ is the way to go.

rkompass
Posts: 66
Joined: Fri Sep 17, 2021 8:25 pm

Re: How to make PWM rising edge trigger an interrupt?

Post by rkompass » Sun Jul 17, 2022 3:49 pm

Code: Select all

from pyb import Pin, Timer
from time import ticks_us, ticks_diff, sleep_ms
from array import array
import micropython
micropython.alloc_emergency_exception_buf(160)


pb3 = Pin('B3')          # -------  B3 has TIM2, CH2 on Stm32F411 Blackpill board ----
tim = Timer(2, freq=1000)
ch = tim.channel(2, Timer.PWM, pin=pb3)
ch.pulse_width_percent(70)

n_td_told = array("i", (0, ticks_us(),
                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0))

@micropython.viper
def _callb(pin):
    p = ptr32(n_td_told)
    n = p[0] + 1
    if n <= 50:
        p[2*n] = n
        p[2*n+1] = int(ticks_us())
    p[0] = n

try:
    pb3.irq(trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING, handler=_callb, hard=True)  # Pin.IRQ_RISING | Pin.IRQ_FALLING
    print('hard interrupt')
except TypeError:
    pb3.irq(trigger=Pin.IRQ_RISING, handler=_callb)
    print('soft interrupt')

print('total', n_td_told[0], 't0:', n_td_told[1])
for i in range(1, 51):
    print('n', n_td_told[2*i], 't:', n_td_told[2*i+1], 't-diff', n_td_told[2*i+1]-n_td_told[2*i-1])
gives as output on my Blackpill with uPy 1.19 (should be the same with pyboard):

Code: Select all

hard interrupt
total 1 t0: 1020892502
n 1 t: 1020894079 t-diff 1577
n 2 t: 1020894376 t-diff 297
n 3 t: 1020895083 t-diff 707
n 4 t: 1020895376 t-diff 293
n 5 t: 1020896076 t-diff 700
n 6 t: 1020896376 t-diff 300
n 7 t: 1020897076 t-diff 700
n 8 t: 1020897376 t-diff 300
n 9 t: 1020898076 t-diff 700
n 10 t: 1020898382 t-diff 306
n 11 t: 1020899083 t-diff 701
n 12 t: 1020899376 t-diff 293
n 13 t: 1020900076 t-diff 700
n 14 t: 1020900376 t-diff 300
n 15 t: 1020901076 t-diff 700
n 16 t: 1020901376 t-diff 300
n 17 t: 1020902076 t-diff 700
n 18 t: 1020902376 t-diff 300
n 19 t: 1020903076 t-diff 700
n 20 t: 1020903376 t-diff 300
n 21 t: 1020904076 t-diff 700
n 22 t: 1020904376 t-diff 300
n 23 t: 1020905078 t-diff 702
n 24 t: 1020905376 t-diff 298
n 25 t: 1020906076 t-diff 700
n 26 t: 1020906376 t-diff 300
n 27 t: 1020907076 t-diff 700
n 28 t: 1020907376 t-diff 300
n 29 t: 1020908083 t-diff 707
n 30 t: 1020908376 t-diff 293
n 31 t: 1020909076 t-diff 700
n 32 t: 1020909376 t-diff 300
n 33 t: 1020910076 t-diff 700
n 34 t: 1020910376 t-diff 300
n 35 t: 1020911083 t-diff 707
n 36 t: 1020911376 t-diff 293
n 37 t: 1020912083 t-diff 707
n 38 t: 1020912376 t-diff 293
n 39 t: 1020913076 t-diff 700
n 40 t: 1020913376 t-diff 300
n 41 t: 1020914076 t-diff 700
n 42 t: 1020914376 t-diff 300
n 43 t: 1020915076 t-diff 700
n 44 t: 1020915376 t-diff 300
n 45 t: 1020916076 t-diff 700
n 46 t: 1020916381 t-diff 305
n 47 t: 1020917076 t-diff 695
n 48 t: 1020917376 t-diff 300
n 49 t: 1020918076 t-diff 700
n 50 t: 1020918376 t-diff 300
so it proves, it is possible to have a pin as pwm out and then afterwards put an external interrupt on it.

Riddle: why is total = 1 in the printout?

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: How to make PWM rising edge trigger an interrupt?

Post by Roberthh » Sun Jul 17, 2022 4:14 pm

so the behaviour is port-specific. Your code shows that it works for STM32. For other ports it has to be tested.

rkompass
Posts: 66
Joined: Fri Sep 17, 2021 8:25 pm

Re: How to make PWM rising edge trigger an interrupt?

Post by rkompass » Sun Jul 17, 2022 9:45 pm

Exactly. That was no documented behaviour, even for the pyboard.
With

Code: Select all

ch.callback(_callb)
instead of pb3.irq(trigger=Pin.IRQ... the interrupt is generated every 1000 us btw. A slight modification showed, it is generated about at falling flank of the pin of the program (same interrupt routine used with Pin.IRQ_RISING only and in ch.callback).
For the RPi2040 I found earlier, that outputs generated by the PIO can be used as inputs (by pin definition) and generate interrupts.
This motivated me to try this.

bittware
Posts: 45
Joined: Mon Aug 18, 2014 3:27 am

Re: How to make PWM rising edge trigger an interrupt?

Post by bittware » Mon Jul 18, 2022 2:36 am

rkompass wrote:
Sun Jul 17, 2022 3:49 pm

Code: Select all

from pyb import Pin, Timer
from time import ticks_us, ticks_diff, sleep_ms
from array import array
import micropython
micropython.alloc_emergency_exception_buf(160)


pb3 = Pin('B3')          # -------  B3 has TIM2, CH2 on Stm32F411 Blackpill board ----
tim = Timer(2, freq=1000)
ch = tim.channel(2, Timer.PWM, pin=pb3)
ch.pulse_width_percent(70)

n_td_told = array("i", (0, ticks_us(),
                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0))

@micropython.viper
def _callb(pin):
    p = ptr32(n_td_told)
    n = p[0] + 1
    if n <= 50:
        p[2*n] = n
        p[2*n+1] = int(ticks_us())
    p[0] = n

try:
    pb3.irq(trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING, handler=_callb, hard=True)  # Pin.IRQ_RISING | Pin.IRQ_FALLING
    print('hard interrupt')
except TypeError:
    pb3.irq(trigger=Pin.IRQ_RISING, handler=_callb)
    print('soft interrupt')

print('total', n_td_told[0], 't0:', n_td_told[1])
for i in range(1, 51):
    print('n', n_td_told[2*i], 't:', n_td_told[2*i+1], 't-diff', n_td_told[2*i+1]-n_td_told[2*i-1])
gives as output on my Blackpill with uPy 1.19 (should be the same with pyboard):

Code: Select all

hard interrupt
total 1 t0: 1020892502
n 1 t: 1020894079 t-diff 1577
n 2 t: 1020894376 t-diff 297
n 3 t: 1020895083 t-diff 707
n 4 t: 1020895376 t-diff 293
n 5 t: 1020896076 t-diff 700
n 6 t: 1020896376 t-diff 300
n 7 t: 1020897076 t-diff 700
n 8 t: 1020897376 t-diff 300
n 9 t: 1020898076 t-diff 700
n 10 t: 1020898382 t-diff 306
n 11 t: 1020899083 t-diff 701
n 12 t: 1020899376 t-diff 293
n 13 t: 1020900076 t-diff 700
n 14 t: 1020900376 t-diff 300
n 15 t: 1020901076 t-diff 700
n 16 t: 1020901376 t-diff 300
n 17 t: 1020902076 t-diff 700
n 18 t: 1020902376 t-diff 300
n 19 t: 1020903076 t-diff 700
n 20 t: 1020903376 t-diff 300
n 21 t: 1020904076 t-diff 700
n 22 t: 1020904376 t-diff 300
n 23 t: 1020905078 t-diff 702
n 24 t: 1020905376 t-diff 298
n 25 t: 1020906076 t-diff 700
n 26 t: 1020906376 t-diff 300
n 27 t: 1020907076 t-diff 700
n 28 t: 1020907376 t-diff 300
n 29 t: 1020908083 t-diff 707
n 30 t: 1020908376 t-diff 293
n 31 t: 1020909076 t-diff 700
n 32 t: 1020909376 t-diff 300
n 33 t: 1020910076 t-diff 700
n 34 t: 1020910376 t-diff 300
n 35 t: 1020911083 t-diff 707
n 36 t: 1020911376 t-diff 293
n 37 t: 1020912083 t-diff 707
n 38 t: 1020912376 t-diff 293
n 39 t: 1020913076 t-diff 700
n 40 t: 1020913376 t-diff 300
n 41 t: 1020914076 t-diff 700
n 42 t: 1020914376 t-diff 300
n 43 t: 1020915076 t-diff 700
n 44 t: 1020915376 t-diff 300
n 45 t: 1020916076 t-diff 700
n 46 t: 1020916381 t-diff 305
n 47 t: 1020917076 t-diff 695
n 48 t: 1020917376 t-diff 300
n 49 t: 1020918076 t-diff 700
n 50 t: 1020918376 t-diff 300
so it proves, it is possible to have a pin as pwm out and then afterwards put an external interrupt on it.

Riddle: why is total = 1 in the printout?
Thank you rkompass. This example is very enlightening.
About the last riddle, I guess because you didn't mask out the interrupt source afterwards, this leads to the total element keeps accumulating to overflow to the maximum. But why does it stay at 1, I don't know.

Post Reply