Setting up a timer to behave as a periodic task is exactly the same as setting up a timer in MicroPython for any other purpose. We can create an application very similar to our round-robin scheduler using timers by initializing a timer for each task in the application. The first timer will control the blue LED, while the second will control the green LED. Each timer will use a callback function to the task code that will be executed when the timer expires.
We can use the exact same format for our code that we used previously. We will initialize the blue LED as on, and the green LED as off. This allows us to let the timers free-run and generate the railroad pattern that we saw earlier. It's important to note that if we let the timer free-run, even if we stop the application in the REPL, the timers will continue to execute! The reason for this is that the timers are hardware peripherals that will run until the peripheral is disabled, even if we exit our application and return to the REPL. I mention this because any print statements you add to your callback functions will continue to populate the REPL, even after you halt the program, which can make it difficult to work or determine the state of the application.
When using timers to set up tasks, there is no need for an infinite while loop like we saw with the round-robin applications. The timers will just free-run. If the infinite loop is not added to main.py, background processing will fall back to the system REPL and sit there instead. I personally still like to include the while loop and some status information so that I know whether the MicroPython interpreter is executing code. In this example, we will put a sleep delay in the main loop and then calculate how long the application has been running.
The Python code for our tasks is identical to the round-robin example, except for the addition of the emergency exception buffer, as shown here:
import micropython # For emergency exception buffer
import pyb # For uPython MCU
import time
micropython.alloc_emergency_exception_buf(100)
LED_RED = 1
LED_GREEN = 2
LED_BLUE = 3
LED_YELLOW = 4
def task1(timer):
pyb.LED(LED_BLUE).toggle()
return
def task2(timer):
pyb.LED(LED_GREEN).toggle()
return
Instead of calling the task code directly, we set up two timers – time 1, and timer 2 – with a frequency of 5 Hz (period of 200 milliseconds) and set up the callback function to call the tasks. The code to accomplish this is as follows:
pyb.LED(LED_BLUE).on()
pyb.LED(LED_GREEN).off()
# Create task timer for Blue LED
TimerBlueLed = pyb.Timer(1)
TimerBlueLed.init(freq=5)
TimerBlueLed.callback(task1)
print("Task 1 - Blue LED Toggle initialized ...")
# Create task timer for Green LED
TimerGreenLed = pyb.Timer(2)
TimerGreenLed.init(freq=5)
TimerGreenLed.callback(task2)
print("Task 2 - Green LED Toggle initialized ...")
The only code that's necessary for this example is the code for the main loop, which will do nothing more than print out how long our application has been running. To accomplish this, we need to sample the application start time using the time module's ticks_ms method and store it in TimeStart. We can then use time.ticks_diff to calculate the elapsed time between the current tick and the application start tick. The final piece of code is as follows:
TimeStart = time.ticks_ms()
while True:
time.sleep_ms(5000)
SecondsLive = time.ticks_diff(time.ticks_ms(), TimeStart) / 1000
print("Executing for ", SecondsLive, " seconds")
Once the code is on the pyboard and executing, the REPL should display the information shown in the following screenshot. It shows timer-based task scheduling, which prints the current execution time in the REPL and toggles between the blue and green LEDs at 5 Hz. At this point, you know how to use timers to schedule periodic tasks:
At this point, we are ready to examine some additional scheduling paradigms that are not completely mainstream within MicroPython, such as thread support.