for loop within interrupt callback
-
- Posts: 33
- Joined: Wed Sep 07, 2016 10:46 am
for loop within interrupt callback
I have a timer interrupt callback at 40Hz. It's a keypad scanner which does the normal row/column scanning.
I had a list of Pin objects, and I was going to iterate over the list to check each column pin. I soon found out that the timer callback was executing in interrupt context, and that python objects can't be created.
What I don't understand is I was iterating over a list of preallocated objects. I was just calling a method to see if the pin value was True or False.
I did a simple 'for x in range(10):' test and even that failed, so I ended up having to expand the loop logic manually
I presumed allocating for loop variable names is the problem, however, I found this page on micropython interrupts and it gives an example using a for loop. Is this no longer valid or am I doing something wrong with my simple for loop ??
Is there a way to something to call a microthread/task (e.g. with asyncio, greenlets, gevent) so that the timer interrupt will trigger/wakeup a a waiting task, so that the full power of python can be used?
Brendan.
I had a list of Pin objects, and I was going to iterate over the list to check each column pin. I soon found out that the timer callback was executing in interrupt context, and that python objects can't be created.
What I don't understand is I was iterating over a list of preallocated objects. I was just calling a method to see if the pin value was True or False.
I did a simple 'for x in range(10):' test and even that failed, so I ended up having to expand the loop logic manually
I presumed allocating for loop variable names is the problem, however, I found this page on micropython interrupts and it gives an example using a for loop. Is this no longer valid or am I doing something wrong with my simple for loop ??
Is there a way to something to call a microthread/task (e.g. with asyncio, greenlets, gevent) so that the timer interrupt will trigger/wakeup a a waiting task, so that the full power of python can be used?
Brendan.
- pythoncoder
- Posts: 5956
- Joined: Fri Jul 18, 2014 8:01 am
- Location: UK
- Contact:
Re: for loop within interrupt callback
You can put a for loop in a timer callback: I've just tried it on a Pyboard running firmware built today. There must be something else in your code causing the allocation failure. I suggest you post a minimal example of code which fails.
As for scheduling, the official way is uasyncio. An alternative is this scheduler based on micro threading https://github.com/peterhinch/Micropython-scheduler.
As for scheduling, the official way is uasyncio. An alternative is this scheduler based on micro threading https://github.com/peterhinch/Micropython-scheduler.
Peter Hinch
Index to my micropython libraries.
Index to my micropython libraries.
-
- Posts: 33
- Joined: Wed Sep 07, 2016 10:46 am
Re: for loop within interrupt callback
OK. Here is a simple test program, which doesn't work for me.
Code: Select all
##============================================================================
##
## test_timer module for MicroPython.
##
##============================================================================
import micropython
from pyb import Pin, Timer, LED, delay
##============================================================================
class Keypad():
def __init__(self):
self.init()
def init(self):
self.led = LED(1)
self.timer = Timer(5, freq=1)
def timer_callback(self, timer):
#print("DEBUG: Keypad.timer_callback()")
self.led.toggle()
## Can't use loop with micropython as memory is allocated => exeception in timer interrupt !!
for i in [1,2,3]:
#print("DEBUG: i = {}".format(i))
pass
def start(self):
self.timer.callback(self.timer_callback)
def stop(self):
self.timer.callback(None)
##============================================================================
def main_test():
"""Main test function."""
print("main_test(): start")
micropython.alloc_emergency_exception_buf(100)
keypad = Keypad()
keypad.start()
for i in range(10000):
delay(1)
keypad.stop()
print("main_test(): end")
##============================================================================
if __name__ == '__main__':
main_test()
Re: for loop within interrupt callback
AFAIK, the callback function must only have one parameter. Your has two. You may use the @staticmethod decorator
to get rid of self.
Code: Select all
@staticmethod
def timer_callback(timer):
Re: for loop within interrupt callback
This code works:
If you need access to the class in the ISR, you have to find a way to retrieve the link to the instance, e.g. by storing it in a global variable.
Code: Select all
##============================================================================
##
## test_timer module for MicroPython.
##
##============================================================================
import micropython
from pyb import Pin, Timer, LED, delay
##============================================================================
myself = None
class Keypad():
def __init__(self):
self.init()
def init(self):
global myself
self.led = LED(1)
self.timer = Timer(5, freq=1)
myself = self
@staticmethod
def timer_callback(timer):
global myself
self = myself
#print("DEBUG: Keypad.timer_callback()")
self.led.toggle()
## Can't use loop with micropython as memory is allocated => exeception in timer interrupt !!
for i in range(3):
#print("DEBUG: i = {}".format(i))
pass
def start(self):
self.timer.callback(self.timer_callback)
def stop(self):
self.timer.callback(None)
##============================================================================
def main_test():
"""Main test function."""
print("main_test(): start")
micropython.alloc_emergency_exception_buf(100)
keypad = Keypad()
keypad.start()
for i in range(10000):
delay(1)
keypad.stop()
print("main_test(): end")
##============================================================================
if __name__ == '__main__':
main_test()
Re: for loop within interrupt callback
You can use class methods as timer callbacks. Here's a working example for the pyboard: https://github.com/dhylands/upy-example ... eat_irq.pyRoberthh wrote:AFAIK, the callback function must only have one parameter. Your has two. You may use the @staticmethod decoratorto get rid of self.Code: Select all
@staticmethod def timer_callback(timer):
Re: for loop within interrupt callback
In your example, you used: It's the [1, 2, 3] that's allocating the memory. If, instead, you use range(3) then it won't allocate memory. This example works on my pyboard:
Code: Select all
for i in [1, 2, 3]:
Code: Select all
import pyb
import micropython
class Heartbeat(object):
def __init__(self):
self.tick = 0
self.led = pyb.LED(4) # 4 = Blue
tim = pyb.Timer(4)
tim.init(freq=10)
tim.callback(self.heartbeat_cb)
self.counter = 0
def heartbeat_cb(self, tim):
if self.tick <= 3:
self.led.toggle()
self.tick = (self.tick + 1) % 10
if self.tick == 0:
self.counter += 1
print(self.counter)
for i in range(3):
print(i)
micropython.alloc_emergency_exception_buf(100)
Heartbeat()
Re: for loop within interrupt callback
@dhylands: Thanks, good to know. Is that limited to timer callbacks or available for all callbacks?
Re: for loop within interrupt callback
When you pass self.heartbeat_cb your're passing what's known as a bound method. So the "self" is bound into the method and the caller doesn't need to pass the self argument. Here's an example: So it works for all callbacks, and the caller of the callback is unaware of the fact (unless they examine the type of the callback).
Here's the output run on the pyboard:
Code: Select all
callback = None
counter = 0
def set_callback(func):
global callback
callback = func
print('func is', func)
def call_callback():
global counter
callback(counter)
counter += 1
class Bar:
def __init__(self, blah):
self.blah = blah
set_callback(self.my_callback)
def my_callback(self, cnt):
print('my_callback({}) cnt = {}'.format(self.blah, cnt))
x = Bar(42)
call_callback()
call_callback()
def another_callback(cnt):
print('another_callback cnt =', cnt)
set_callback(another_callback)
call_callback()
call_callback()
Here's the output run on the pyboard:
Code: Select all
>>> import bound
func is <bound_method>
my_callback(42) cnt = 0
my_callback(42) cnt = 1
func is <function another_callback at 0x20003530>
another_callback cnt = 2
another_callback cnt = 3
Re: for loop within interrupt callback
Thanks. Understood, tried, works!