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.
rkompass
Posts: 66
Joined: Fri Sep 17, 2021 8:25 pm

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

Post by rkompass » Mon Jul 18, 2022 7:36 am

Thanks, its all copied together from roberthh, pythoncoder and others and the uPy docs.:-)
I have fun and learn a lot by following this forum. Meanwhile I dare to give something back and this is an interesting question.

The riddle: Printing started immediately. When you inserted sleep_ms(200) before the print('total',....) then you got a higher number
and the slight irregularities in the t-diffs vanished (only 300 and 700 remain).

What do you want to use the pwm rising edge interrupts for, btw. ?

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 8:50 am

rkompass wrote:
Mon Jul 18, 2022 7:36 am
The riddle: Printing started immediately. When you inserted sleep_ms(200) before the print('total',....) then you got a higher number
and the slight irregularities in the t-diffs vanished (only 300 and 700 remain).
Oh, yes, I get it. I was thinking print total at the very end, then the total number probably won't stop just at 50 or 51, because the interrupt keeps coming when running 50ish print statements, assuming each print takes longer than 1ms, i.e. one interrupt interval.
rkompass wrote:
Mon Jul 18, 2022 7:36 am
What do you want to use the pwm rising edge interrupts for, btw. ?
My PWM signal triggers a sample-hold circuit. But it won't hold the sample for too long so I have to inform the ADC to get the sampling data back ASAP through interrupt service routine.

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

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

Post by rkompass » Mon Jul 18, 2022 10:07 am

Nice. Regarding the riddle: I thought exactly the same and supposed an error first.

New observation:
GPIO hard interrupts seem to be completely independent from the direction (Input/Output) of driving the pins, at least on Stm32.
If I change my script to:

Code: Select all

pb3 = Pin('B3', Pin.OUT)
before defining the interrupts and

Code: Select all

# @micropython.viper
# def flipflop():
#     for _ in range(25):
#         pb3(1)
#         pb3(0)
# 
# flipflop()

for _ in range(25):
    pb3(1)
    pb3(0)
afterwards I get similar output, with of course shorter time differences.

Perhaps this is established knowledge.
Back in my youth studying maths I found something new by myself, but my professor said it was 'folklore'. It wasn't in the textbooks but apparently known by all aquainted with the subject.
So perhaps the above is folklore too ???, but apparently not ??? I will try to find out how it behaves with different ports.

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

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

Post by rkompass » Mon Jul 18, 2022 2:03 pm

Update:

PWM does not generate interrupts at all (with the above script) on RPi2040.

PWM on the esp8266 generates 0, 1 or 2 interrupts in different runs of the script. So in principle it is able to generate them.
There is only soft interrupt on the 8266, which is slow, so perhaps the interrupt-pipeline is congested and this prevents more interrupts to get through. This figure does not get better with reducing PWM frequency down to 10 Hz though. Which I don't understand.
I even observed in a few runs blocking of PWM altogether and hanging of the system. Then all printouts after a few, say 15 seconds and afterwards starting of PWM.

TheSilverBullet
Posts: 50
Joined: Thu Jul 07, 2022 7:40 am

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

Post by TheSilverBullet » Mon Jul 18, 2022 3:45 pm

rkompass wrote:
Mon Jul 18, 2022 2:03 pm
Update:
PWM does not generate interrupts at all (with the above script) on RPi2040.
Using a Pin and the PWM, the rp2040 is quite happy to provide interrupts above the 20kHz range.

Code: Select all

:~/tmp$ picorun IRQ.py 
expected:25.0 µs and 25.0 µs
n: 1   t:73153325   t-diff:  25
n: 2   t:73153350   t-diff:  25
n: 3   t:73153375   t-diff:  25
n: 4   t:73153401   t-diff:  26
n: 5   t:73153425   t-diff:  24
n: 6   t:73153450   t-diff:  25
n: 7   t:73153475   t-diff:  25
n: 8   t:73153500   t-diff:  25
n: 9   t:73153525   t-diff:  25
n:10   t:73153550   t-diff:  25
total time:225 µs   average:25.0 µs/irq

Code: Select all

#!/usr/bin/python3
from machine import PWM, Pin
from time import ticks_us, ticks_diff, sleep_ms
from array import array
from micropython import alloc_emergency_exception_buf

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

alloc_emergency_exception_buf(160)
SIZE = 10
n_td_told = array("i", (0 for _ in range(2 + 2*SIZE)))

pin = Pin(25, Pin.OUT) # LED on Pi pico board
pwm = PWM(pin)

def test(frequency=1000, duty_percent=50):
    pwm.freq(frequency)
    pwm.duty_u16(round(duty_percent / 100.0 * 2**16))

    n_td_told[0] = -10
    _ = pin.irq(trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING, handler=_callb, hard=True)

    while not n_td_told[0] > SIZE:
        pass
    _ = pin.irq(handler=None)

    print(f'expected:{1e6*duty_percent/100/frequency} µs and {1e6*(100-duty_percent)/100/frequency} µs')
    for i in range(1, SIZE+1):
        print(f'n:{n_td_told[2*i]:2d}   t:{n_td_told[2*i+1]}   t-diff:{n_td_told[2*i+1]-n_td_told[2*i-1]:4d}')

    td = n_td_told[2*SIZE+1]  - n_td_told[2+1]
    print(f'total time:{td} µs   average:{td/(SIZE-1)} µs/irq')

test(20000,50)

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

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

Post by rkompass » Mon Jul 18, 2022 6:37 pm

You are right. Thank you for correcting. I again had forgotten that some sleep is necessary before evaluating the array (trapped in the riddle).
I repeated the test with esp8266 and the output also is nice:

Code: Select all

n 3 t: 91919756 t-diff 725
n 4 t: 91920042 t-diff 286
n 5 t: 91920724 t-diff 682
n 6 t: 91921046 t-diff 322
n 7 t: 91921733 t-diff 687
n 8 t: 91922055 t-diff 322
n 9 t: 91922741 t-diff 686
n 10 t: 91923063 t-diff 322
n 11 t: 91923750 t-diff 687
n 12 t: 91924072 t-diff 322
n 13 t: 91924759 t-diff 687
with 70 % at 1 kHz.

So at least for stm, esp8266 and RPi2040 we have the same behaviour: Interrupts may be triggered by software-switching a pin in output mode and by PWM.

TheSilverBullet
Posts: 50
Joined: Thu Jul 07, 2022 7:40 am

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

Post by TheSilverBullet » Mon Jul 18, 2022 7:34 pm

I did some tests with PIO assembler code in order to figure out what latency to expect.
PWM switches LED on, triggers hard_irq, callback switches LED off, meanwhile PIO measures…
At standard (125MHz) frequency it looks like roughly 20µs…30µs.

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

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

Post by rkompass » Mon Jul 18, 2022 9:52 pm

Very good Idea,
you have the precise moment of triggering the interrupt this way.
My estimation was about 20-25 us for dispatching the interrupt and 4-5 us just for the jump into (any) function, regardless of assembler, viper or native. It was written somewhere here, that the dispatching takes much less time on the stm32. We could measure that also with your approach, just need an external logic analyzer or a second RPi2040.

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

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

Post by rkompass » Mon Jul 18, 2022 9:55 pm

It's too late at night - I'm thinking nonsense. You have the precise moment anyway, if you trigger externally. Of course on the RPi2040 its very elegant as you do not need any wiring.

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 » Tue Jul 19, 2022 1:13 pm

So I added a Pin pulse to the irq and measured the latency. The yellow trace is the PWM signal, the green the created pulse of about 5 µs duration. The average latency is ~17.4 µs at 125MHz.
Attachments
pwm_irq_latency.png
pwm_irq_latency.png (59.03 KiB) Viewed 18278 times

Post Reply