Page 1 of 3

How to make PWM rising edge trigger an interrupt?

Posted: Fri Jul 15, 2022 11:41 am
by bittware
Is it possible to have a pin output PWM meanwhile let the output's rising or falling edge trigger an interrupt?

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

Posted: Sat Jul 16, 2022 10:28 am
by tepalia02
Which particular board do you want to use? ESP32?

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

Posted: Sat Jul 16, 2022 11:24 am
by Roberthh
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.

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

Posted: Sun Jul 17, 2022 1:21 am
by bittware
tepalia02 wrote:
Sat Jul 16, 2022 10:28 am
Which particular board do you want to use? ESP32?
The pyboard in my case

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

Posted: Sun Jul 17, 2022 1:27 am
by bittware
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.

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

Posted: Sun Jul 17, 2022 6:19 am
by Roberthh
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.

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

Posted: Sun Jul 17, 2022 3:49 pm
by rkompass

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?

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

Posted: Sun Jul 17, 2022 4:14 pm
by Roberthh
so the behaviour is port-specific. Your code shows that it works for STM32. For other ports it has to be tested.

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

Posted: Sun Jul 17, 2022 9:45 pm
by rkompass
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.

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

Posted: Mon Jul 18, 2022 2:36 am
by bittware
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.