Timer interrupt service routine memory issue

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.
Post Reply
artur
Posts: 12
Joined: Thu Dec 08, 2016 8:05 pm

Timer interrupt service routine memory issue

Post by artur » Sun Feb 26, 2017 8:12 pm

Hello,

originally I intended to use a timer ISR to send out periodic CAN messages. The timer callback was a class method. However, this did not work, giving "heap allocation not allowed" error messages.

I understand that no heap can be allocated in an ISR and that it should be as short as possible a function. But is really the only solution then to use flags (e.g. and then to check the flag in main routine and send CAN message if set)?

Also, I wonder how to determine when heap gets allocated. For example, I run the code provided in the docs (http://docs.micropython.org/en/latest/p ... #pyb-timer), namely:

Code: Select all

def tick(timer):               
    print(timer.counter())    
tim = pyb.Timer(4, freq=1)      
tim.callback(tick)


This only provides '0' as output on the screen. So apparently there is a memory issue with the print during ISR, but no heap allocation message is thrown.

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

Re: Timer interrupt service routine memory issue

Post by dhylands » Sun Feb 26, 2017 11:40 pm

You are allowed to have class methods as timer callbacks, as this example shows:
https://github.com/dhylands/upy-example ... eat_irq.py

The way that the timers work is that they are sourced by a clock (frequency can be determined by the source_freq() function). That is then divided by a prescaler and causes the timer to increment until it hits a reload value (period), and then the counter gets reset to zero.

So if I modify your example slightly:

Code: Select all

import pyb

def tick(timer):               
    print(timer.counter())   

tim = pyb.Timer(4, freq=1)     
print("tim.source_freq() =", tim.source_freq());
print("tim.prescaler()  =", tim.prescaler());
print("tim.period()  =", tim.period());
tim.callback(tick)
then I get this output:

Code: Select all

>>> import tick
tim.source_freq() = 84000000
tim.prescaler()  = 3124
tim.period()  = 26879
>>> 0
0
So the 84 MHz clock is getting divided by 3125 which results in 26880 ticks per second. (the period and prescaler are both stored as the actual prescaler and period minus one). So the counter will increment from 0 to 26879 and then it will reset to zero, causing the timer interrupt to fire. This is why the counter is always being read as a zero.

You can read more about writing interrupt handlers here: http://docs.micropython.org/en/latest/p ... rules.html

Basically small ints (signed ints which fit in 31 bits) don't require heap allocation. The statment x = 1 will not require memory allocation. The statement x = 1.0 or x = 0x123456789 will require memory allocation. Lots of other things require memory allocation.

artur
Posts: 12
Joined: Thu Dec 08, 2016 8:05 pm

Re: Timer interrupt service routine memory issue

Post by artur » Mon Feb 27, 2017 9:40 am

Thanks for the explanations.
In your code example, I replaced the LED toggle with a print statement:

Code: Select all

def heartbeat_cb(self, tim):
        if self.tick <= 3:
            #self.led.toggle()
            print(tim.counter)
        self.tick = (self.tick + 1) % 10
This results in "Memory Error: memory allocation failed, heap is locked". So, really I can't do much in an ISR, can I?

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

Re: Timer interrupt service routine memory issue

Post by dhylands » Mon Feb 27, 2017 10:18 am

You asked to print tim.counter which is a bound function pointer. Creating bound function pointers requires a memory allocation.

I suspect that you intended to actually call the tim.counter function, in which case you need to add some parenthesis. I.e. use tim.counter()

Post Reply