FB pixel

Application timers in Azure ThreadX | Renesas RA - 26

Published


Hello there! This is the last tutorial devoted to the introduction into Azure RTOS ThreadX services. In the previous four tutorials we have learnt the services for threads interconnection and communication. Here we will discover a totally different service which is used to generate the periodic interrupts. It is called an “application timer”.

Introduction into Application Timers

Application timers are used to implement some code after a specific interval of time using something like a software interrupt when this time expires. Application timers can produce this interrupt once and then stop, in this case they are called one-shot timers. If they automatically restart after expiration and generate interrupts continuously with a given period, they are called periodic timers.

The tick of the application times corresponds to the system tick, which we usually set as 10 ms.

Application timers are executed inside a hidden thread with priority 0. So when their callback function is implemented, other threads with a lower priority become suspended. That’s why the callback functions should be as short as possible. Also, it’s prohibited to use the function calls with the waiting option other than TX_NO_WAIT inside the callback functions or suspend the thread in any other way. And it's strongly not recommended to set the timer period of one tick as the callback function will be called too often and prevent normal operation of the whole system.

If several timers have been created with the same period and started one by one, it’s guaranteed that they will expire in the same order as they have been started.

And that’s all the theoretical background about application timers. It’s better to discover how they work based on a practical example.

Test Setup

To demonstrate the operation of application timers we will use the same setup as the previous three times (Figure 1).

Schematic diagram of the test setup
Figure 1 - Schematic diagram of the test setup

Let’s create three application timers with different settings and tasks.

  1. One shot timer which will start when button S1 is pressed. At the same time, LED1 will turn on, and when the timer expires in 1 second, it will turn off.
  2. Periodic timer which will auto-start at creation and blink LED2 with the frequency of 2 Hz.
  3. Periodic timer which will be started when button S2 is pressed and will be paused when button S3 is pressed. This timer will put the semaphore every 500 ms which will resume the thread, inside which LED3 will be toggled.

Test Program Description

This time let’s also not create the new project the same as we did in the previous tutorial, but import & rename it from the “threadx_message_queue” project in the way I have described several times in previous three tutorials (Figure 2).

Renaming & importing the existing project
Figure 2 - Renaming & importing the existing project

Let’s call the new project “threadx_application_timer” as follows from Figure 2.

Now we need to open the “configuration.xml” file and switch to the “Stacks” tab. There we need to select the existing “Queue” object and click the “Remove” button as we will not use message queues in this tutorial (Figure 3).

Removing of the existing message queue
Figure 3 - Removing of the existing message queue

Also, we need to remove the LED1 and LED2 Threads as they are not needed in this program (Figure 4).

Removing of LED1 Thread and LED2 Thread
Figure 4 - Removing of LED1 Thread and LED2 Thread

Now we need to click the “New Object >” button and select the “Semaphore” in the drop-down list (Figure 5).

Adding new semaphore
Figure 5 - Adding new semaphore

Now we need to change the properties of the Semaphore object according to Figure 6.

Properties of the semaphore
Figure 6 - Properties of the semaphore

If you need more information about semaphores, please refer to tutorial 22, otherwise we will consider that Figure 6 requires no further explanation.

After all, the “Stack” tab should look as follows (Figure 7).

Complete stack configuration of the “threadx_application_timer” project
Figure 7 - Complete stack configuration of the “threadx_application_timer” project

Now we can press the “Generate Project Content” button to let the FSP configurator create the required files, and switch to the “src” folder in the Project Explorer window.

Here we can see that files “led1_thread_entry.c” and “led2_thread_entry.c” are still present in the folder but are excluded from the build (Figure 8).

Content of the “src” folder of the “threadx_application_timer” project
Figure 8 - Content of the “src” folder of the “threadx_application_timer” project

If they annoy you, you can delete them, otherwise just ignore them, as anyway they will not be included in the build.

Let’s open the “buttons_thread_entry.c” file, which will have the most code in this project, and write the following lines in it.

#include "buttons_thread.h"

TX_TIMER led1_timer, led2_timer, led3_timer; //Timers that we will create

bsp_io_level_t led2_level; //Output level of the LED2 pin

void led1_timer_callback (ULONG input);

void led2_timer_callback (ULONG input);

void led3_timer_callback (ULONG input);

/* Buttons Thread entry function */

void buttons_thread_entry(void)

{

led2_level = BSP_IO_LEVEL_LOW; //Initial state of the LED2

tx_timer_create(&led1_timer, "LED1 Timer", led1_timer_callback, 1, 100, 0, TX_NO_ACTIVATE); //Creation of LED1 Timer

tx_timer_create(&led2_timer, "LED2 Timer", led2_timer_callback, 2, 25, 25, TX_AUTO_ACTIVATE);//Creation of LED2 Timer

tx_timer_create(&led3_timer, "LED3 Timer", led3_timer_callback, 3, 50, 50, TX_NO_ACTIVATE); //Creation of LED3 Timer

while (1)

{

if (R_BSP_PinRead(S1) == BSP_IO_LEVEL_LOW) //If S1 button is pressed

{

tx_thread_sleep(3); //Delay for 30 ms

if (R_BSP_PinRead(S1) == BSP_IO_LEVEL_LOW) //If S1 button is still pressed

{

while (R_BSP_PinRead(S1) == BSP_IO_LEVEL_LOW); //Wait while it is pressed

tx_thread_sleep(3); //Delay for 30 ms

if (R_BSP_PinRead(S1) == BSP_IO_LEVEL_HIGH) //If S1 button is released

{

tx_timer_change(&led1_timer, 100, 0); //Reset LED1 Timer

tx_timer_activate (&led1_timer); //Start LED1 Timer

R_BSP_PinAccessEnable(); //Enable access to the IO registers

R_BSP_PinWrite(LED1, BSP_IO_LEVEL_HIGH); //Turn on LED1

R_BSP_PinAccessDisable(); //Disable access to the IO registers

}

}

}

if (R_BSP_PinRead(S2) == BSP_IO_LEVEL_LOW) //If S2 button is pressed

{

tx_thread_sleep(3); //Delay for 30 ms

if (R_BSP_PinRead(S2) == BSP_IO_LEVEL_LOW) //If S2 button is still pressed

{

while (R_BSP_PinRead(S2) == BSP_IO_LEVEL_LOW); //Wait while it is pressed

tx_thread_sleep(3); //Delay for 30 ms

if (R_BSP_PinRead(S2) == BSP_IO_LEVEL_HIGH) //If S2 button is released

{

tx_timer_activate (&led3_timer); //Start LED3 Timer

}

}

}

if (R_BSP_PinRead(S3) == BSP_IO_LEVEL_LOW) //If S3 button is pressed

{

tx_thread_sleep(3); //Delay for 30 ms

if (R_BSP_PinRead(S3) == BSP_IO_LEVEL_LOW) //If S3 button is still pressed

{

while (R_BSP_PinRead(S3) == BSP_IO_LEVEL_LOW); //Wait while it is pressed

tx_thread_sleep(3); //Delay for 30 ms

if (R_BSP_PinRead(S3) == BSP_IO_LEVEL_HIGH) //If S3 button is released

{

tx_timer_deactivate (&led3_timer); //Pause LED3 Timer

}

}

}

}

}

void led1_timer_callback (ULONG input)

{

(void)input; //To prevent the "unused parameter" warning

R_BSP_PinAccessEnable(); //Enable access to the IO registers

R_BSP_PinWrite(LED1, BSP_IO_LEVEL_LOW); //Turn off LED1

R_BSP_PinAccessDisable(); //Disable access to the IO registers

}

void led2_timer_callback (ULONG input)

{

(void)input; //To prevent the "unused parameter" warning

R_BSP_PinAccessEnable(); //Enable access to the IO registers

led2_level ^= BSP_IO_LEVEL_HIGH; //Toggle the LED2 level

R_BSP_PinWrite(LED2, led2_level); //Set the new state of the LED2 level

R_BSP_PinAccessDisable(); //Disable access to the IO registers

}

void led3_timer_callback (ULONG input)

{

(void)input; //To prevent the "unused parameter" warning

tx_semaphore_put(&g_led3_semaphore); //Put the semaphore

}

In line 3 we declare three variables: led1_timer, led2_timer, and led3_timer of type TX_TIMER, which represent the timer control blocks. Whenever we need to apply any operation to a timer, we should use the corresponding control block name to address it.

In line 4 we declare the variable led2_level which represents the output level of the pin to which LED2 is connected.

In lines 6-8 there are declarations of the timer callback functions, which are invoked every time the corresponding timer expires. The body of these functions is located in lines 68-89, and we will consider them later.

The main function of Buttons Thread buttons_thread_entry is located in lines 11-66.

First, in line 13, we initialize the led2_level variable with the BSP_IO_LEVEL_LOW value to start the execution of the program with the turned off LED2. Then in lines 14-16 we create three application timers using the tx_timer_create function. Let’s consider it in more detail.

tx_timer_create function, as follows from its name, creates the application timer with the given parameters. It has seven arguments:

  • pointer to the timer control block of type TX_TIMER;
  • pointer to the name of the timer which is the regular string, so the name can be any;
  • callback function which is called automatically when the timer expires. This function should have a single argument of the ULONG type;
  • value of the ULONG type that is transmitted to the callback function when it’s called (the argument of this function, described in the previous bullet);
  • initial number of ticks for timer expiration. The legal values are 1 to 0xFFFFFFFF;
  • the number of ticks for all timer expirations after the first. A zero for this parameter makes the timer a one-shot timer. Otherwise, for periodic timers, legal values range from 1 through 0xFFFFFFFF.
  • auto-activation option. If this argument is TX_AUTO_ACTIVATE then the timer starts right after creation, and if it is TX_NO_ACTIVATE then the timer is created but not started. To start it we need to call the tx_timer_activate function which we will consider later.

As you see, the application timer can have a different number of ticks before the first and all other expirations. If you want to create a one-shot timer, you should set the latter value as 0, and if you want to create the periodic timer, these two values should (but not necessarily) be the same.

In line 14 we create the timer led1_timer which has the name “LED1 Timer” and the callback function led1_timer_callback to which value “1” is passed. This value doesn’t affect anything, as we don’t use it. The initial number of ticks for timer expiration is 100, which gives the time of 1 second. The next number of ticks is 0 which makes the timer one-shot. The activation parameter is TX_NO_ACTIVATE, so the timer is not started instantly but requires the tx_timer_activate function invocation to start.

In line 15 we create the timer led2_timer which has the name “LED2 Timer” and the callback function led2_timer_callback to which value “2” is passed. The initial number of ticks for timer expiration is 25 which gives the time of 250 ms (If LED2 will change its state every 250 ms, this will give the blinking frequency 2 Hz). The next number of ticks is also 25 which makes the timer periodic. The activation parameter is TX_AUTO_ACTIVATE, so the timer starts immediately after creation.

In line 16 we create the timer led3_timer which has the name “LED3 Timer” and the callback function led3_timer_callback to which value “3” is passed. The initial number of ticks for timer expiration is 50 which gives the time of 500 ms. The next number of ticks is also 50 which makes the timer periodic. The activation parameter is TX_NO_ACTIVATE, so we need to activate it manually later, the same as LED1 Timer.

In the main loop of the thread (lines 20-64) we process pressing of S1 (lines 20-36), S2 (lines 38-50), and S3 (lines 52-64).

According to the given task, when S1 is pressed LED1 Timer should be started, and LED1 should be turned on. This is what we do in this part (lines 29-33). In line 29 we call the tx_timer_change function which resets the timer. This function should be invoked after the one-shot timer has expired before its restart. It has three arguments:

  • <pointer to the timer control block of type TX_TIMER;
  • initial number of ticks for timer expiration.
  • the number of ticks for all timer expirations after the first.

The meaning and valid values of these arguments are the same as for the tx_timer_create function. In line 29 we set the initial ticks number as 100 and the next ticks number as 0, the same as in line 14.

After resetting the timer we need to start it using the function tx_timer_activate (line 30). This function has a single argument - the pointer to the timer control block and starts the countdown of the specified timer. If the timer already has been activated, this function has no effect.

In line 31, we enable access to the IO registers, then we turn on LED1 (line 32), and finally disable the access to the IO registers (line 33).

Let’s now switch to lines 68-74 where the led1_timer_callback function is located. As I said before, it’s called automatically when the LED1 Timer expires.

In line 70 we cast the input argument to the void type. This action makes no sense but it prevents the compiler warning about the unused variable. The value of the input parameter is 1, as we passed this value in line 14 into the callback function. In lines 71-73 we turn off LED1. This is the only action that is implemented with LED1 Timer.

Now let’s consider the callback of the LED2 Timer which is called led2_timer_callback and is located in lines 76-83. As you remember, LED2 Timer starts automatically and can’t be paused by the buttons.

The content of this callback is very similar to the previous one. In line 78, we cast the input argument to the void type. Then in line 79 we enable access to the IO registers. In line 80 we toggle the value of the led2_level using the XOR function between the old led2_level value and the BSP_IO_LEVEL_HIGH macro definition. Then in line 81 we write the new output value into the LED2 pin and finally disable the access to the IO registers (line 82).

Now let’s consider the last timer - LED3 Timer. It is started by pressing the S2 button and paused by pressing the S3 button.

Starting the LED3 Timer is performed in line 47 by invoking the tx_timer_activate function which we are met in line 30.

Pausing the LED3 Timer is performed in line 61 by invoking the tx_timer_deactivate function. This function has a single argument - the pointer to the timer control block and stops the specified timer. If the timer already has been deactivated, this function has no effect.

The callback function of LED3 Timer is called led3_timer_callback and is located in lines 85-89. According to the given task this timer puts the semaphore g_led3_semaphore (line 88) using the tx_semaphore_put function (you can read more about it in tutorial 22). This semaphore resumes LED3 Thread which code is located in the “led3_thread_entry.c” file and we will now consider.

#include "led3_thread.h"

/* LED3 Thread entry function */

void led3_thread_entry(void)

{

bsp_io_level_t led3_level = BSP_IO_LEVEL_LOW; //Output level of the LED3 pin

while (1)

{

tx_semaphore_get(&g_led3_semaphore, TX_WAIT_FOREVER); //Get the semaphore

R_BSP_PinAccessEnable(); //Enable access to the IO registers

led3_level ^= BSP_IO_LEVEL_HIGH; //Toggle the LED3 level

R_BSP_PinWrite(LED3, led3_level); //Set the new state of the LED3 level

R_BSP_PinAccessDisable(); //Disable access to the IO registers

}

}

As you can see, it is quite simple. In line 6 we declare the led3_level variable which represents the output state of the LED3 pin and assign it with the BSP_IO_LEVEL_LOW value to turn off the LED3 after reset.

In the main loop of the thread (lines 7-14) we first try to get the semaphore g_led3_semaphore using the tx_semaphore_get function (line 9) which also is described in detail in tutorial 18. As you can see, the waiting option here is TX_WAIT_FOREVER, which means that the thread will be suspended for an infinite period of time while the semaphore will be put, which will be done by LED3 Timer when it expires.

After that, LED3 will be toggled in the same way as LED2 which I already described in detail before, so I will not stop on this part of the code.

As you see, there is nothing complex in using application timers. They can be used to implement some actions by themselves or to synchronize the threads using semaphores, mutexes, event flags or message queues. The second approach is more preferable because it minimizes the code in the callback function.

Now let’s see how the program works in reality. To do this, connect the development board to the PC, build the application and start the debugging.

You will see that LED2 starts to blink with the frequency of 2 Hz immediately, as LED2 Timer starts automatically (Figure 9).

Running of the program after resetting
Figure 9 - Running of the program after resetting

As you can see, in the beginning of the program three timers are created (blocks CR). For some reason the expiration of the timers isn’t shown in TraceX, so you will not see anything here, just running Buttons Thread.

If you now press the S1 button, LED1 will turn on (Figure10) and then turn off in one second.

Activation of LED1 Timer after pressing the S1 button
Figure 10 - Activation of LED1 Timer after pressing the S1 button

In figure 10 there are shown two events related to LED1 Timer: timer change (TC) and timer activate (TA).

Pressing the S2 button (Figure 11) will start blinking LED3 with the frequency of 1 Hz (Figure 12), and pressing the S3 button will stop its blinking (Figure. 13). The LED3 state in this case will remain the same as it was before the stop.

Activation of LED3 Timer after pressing the S2 button
Figure 11 - Activation of LED3 Timer after pressing the S2 button
Putting the semaphore by LED3 Timer and getting it by LED3 Thread
Figure 12 - Putting the semaphore by LED3 Timer and getting it by LED3 Thread
Deactivation of LED3 Timer after pressing the S3 button
Figure 13 - Deactivation of LED3 Timer after pressing the S3 button

As you can see in Figure 12 blinking of LED3 is provided by putting the semaphore in the “Interrupt” line (SP) and the following getting it by LED3 Thread (SG).

And that’s it for application timers. As I already said, there is nothing difficult about using them. Yet they provide a convenient and simple way of implementing periodic events. Obviously you still can use regular timers in the MCU for this purpose, and in some cases this solution is preferable, especially if you need to obtain very small periods. Application timers, on the other hand, can be created in the required amount limited only by the memory size, and are controlled by the ThreadX core, which makes their use the same on different MCU platforms.

As homework, try different cases of using application timers: one-shot and periodic timers, synchronization of several threads with the timers and event flags, sending messages from timers to threads and vice versa etc.

Make Bread with our CircuitBread Toaster!

Get the latest tools and tutorials, fresh from the toaster.

What are you looking for?