Question about Timer.callback(), lambdas and functions

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
Post Reply
willcharlton
Posts: 2
Joined: Fri Sep 05, 2014 9:19 pm

Question about Timer.callback(), lambdas and functions

Post by willcharlton » Sun Sep 14, 2014 2:28 pm

If I do as the docs point out:

Code: Select all

import pyb
t_4 = pyb.Timer(4)
t_4.init(freq=1)
t_4.callback(lambda t: pyb.LED(4).toggle())
I get a blinking blue light.

It even works if I wrap it in a simple toggle function (I believe the restraint is callback() has to be given a function with 1 arg):

Code: Select all

def toggleLed(n_led):
    pyb.LED(n_led).toggle()
But if I wrap the toggle in a slightly more complicated function:

Code: Select all

def ledHeartbeat(n_led):
    for i in [1,2]:
        pyb.LED(n_led).toggle()
        pyb.delay(100)
        pyb.LED(n_led).toggle()
        pyb.delay(100)
The callback appears to run it once and only once.

I can verify it gives the 'heartbeat' I'm looking for by running:

Code: Select all

for m in [1,2,3,4,5,6,7,8]:
    ledHeartbeat(4)
    pyb.delay(1000)
I have also tried removing the

Code: Select all

lambda t:
from the callback() and it works for the simple case, but not the more complex one...

I was thinking that, perhaps, if the callback() doesn't return before the function is through, it could create problems, but I'm pretty sure the cumulative 400ms of delay + toggle code will complete in 1 second. Any ideas on why I'm seeing this behavior? Thanks in advance for your replies.

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: Question about Timer.callback(), lambdas and functions

Post by dhylands » Sun Sep 14, 2014 5:38 pm

You shouldn't be calling delay from within the irq handler.

pyb.delay calls HAL_Delay: https://github.com/micropython/micropyt ... #L332-L339
which basically enters an infinite loop and waits for the tick counter to be incremted an appropriate number of times.
The tick counter is itself incremented by another interrupt handler, which the timer interrupt is preventing from running.

It turns out pyb.udelay is currently written as a busy loop, so you could use it, but I wouldn't be surprised to see this rewritten in the future, so I wouldn't rely on it.

You also have to be careful not to do anything in an interrupt handler which might allocate memory. So you can't use floating point operations either.

Here's a couple of interrupt examples:
https://github.com/dhylands/upy-example ... eat_irq.py
https://github.com/dhylands/upy-example ... ade_irq.py

When writing interrupt handlers, you should probably also do:

Code: Select all

import micropython

micropython.alloc_emergency_exception_buf(100)
This will allow you to see exceptions thrown from the interrupt handler.

LarryTru
Posts: 26
Joined: Wed Aug 06, 2014 5:46 am

Re: Question about Timer.callback(), lambdas and functions

Post by LarryTru » Sun Sep 14, 2014 7:00 pm

Thanks especially for that last tip about exceptions from interrupt handlers. Lot's of time lost debugging with too little info. ;)

Larry

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: Question about Timer.callback(), lambdas and functions

Post by dhylands » Sun Sep 14, 2014 7:52 pm

There was an example posted over here:
http://forum.micropython.org/viewtopic. ... upts#p1009

The top of the thread shows what I originally encountered (with a simple typo in my irq handler), and after adding appropriate patches, the post mentioned above shows what you see with the emergency buffer allocated.

User avatar
Tage
Posts: 5
Joined: Wed Sep 24, 2014 2:29 pm
Location: Fremont California

Re: Question about Timer.callback(), lambdas and functions

Post by Tage » Wed Sep 24, 2014 2:43 pm

I am trying to figure out how to write simple program that can handle this task:
-generate pulses at a certain frequency from a digital output
-read an analog input (eg. the voltage from a potentiometer)
-adjust the frequency of the output continuously according to the voltage reading (potentiometer setting)
-the frequency change should happen with minimal disturbance of the pulse train. some jitter is allowed but it is not acceptable to drop pulses.
-the frequency range can be from near zero to 20kHz

I have tried to use Timer.callback() but only get the frequency change to happen once.

The experiment where I need this function is an indexing table driven by a NEMA 23 stepping motor. the motor driver takes pulse and direction. if I drop pulses the position of the table becomes unpredictable. I need to change the frequency smoothly so that I can control acceleration and avoid stalling.

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Question about Timer.callback(), lambdas and functions

Post by pythoncoder » Thu Sep 25, 2014 8:14 am

There is no fundamental problem in dynamically changing the period of a timer. The following example illustrates this:

Code: Select all

import pyb
l = pyb.LED(1)

val = 100
def cbfunc(tt):
    l.toggle()
    tt.period(val)

t = pyb.Timer(2)
t.init(prescaler=16800, period =val)
t.callback(cbfunc)

for n in range(10):
    pyb.delay(1000)
    val *=2
Things to note: your callback function should use integers only. It's also worth looking at the chip datasheet as the hardware timers have different characteristics so check your prescalar and perios values are in range. Keep the callback function simple and do your scaling, rate limitation, and frequency to period conversion in your main program. Good luck.
Peter Hinch
Index to my micropython libraries.

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: Question about Timer.callback(), lambdas and functions

Post by dhylands » Thu Sep 25, 2014 2:54 pm

With the newly landed timer code, you can also use the Output Compare functionality or PWM, and you can generate interrupts along the way as well.

The documentation has been updated: http://micropython.org/doc/module/pyb/Timer (although I'm in the process of changing pulse_width_ratio to pulse_width_percent).

There are some good references out there about how to generate pulse trains for driving steppers.
http://www.atmel.com/images/doc8017.pdf

There was also a really good article published in the November 2006 edition of Circuit Cellar titled "Electronic Gear Control" which describes the algorithm he uses (which is quite similar to the one used in the Mach 3 CNC Controller).

User avatar
Tage
Posts: 5
Joined: Wed Sep 24, 2014 2:29 pm
Location: Fremont California

Re: Question about Timer.callback(), lambdas and functions

Post by Tage » Sat Sep 27, 2014 12:37 am

thank you pythoncoder and dhylands,
now the project is moving forward again. I got the frequency is smoothly controlled by turning the potentiometer. no dropped pulses as far as I can see, and relatively low jitter, the jitter is mostly due noise on the ADC I think. I will spend some time during the weekend working on acceleration profiles and checking if the table position is accurate. :)

Post Reply