Click the "Linux Tech Enthusiast" above and select "Add to Favorites"
Quality articles delivered promptly
☞【Resource】ChatGPT 4.0 is unlocked, no limit on questions!!!
☞【Resource】Tsinghua University senior's self-study Linux notes, top-level!
☞【Resource】Comprehensive guide to commonly used Linux commands, all in one article
☞【Resource】Collection! Linux basic to advanced learning roadmap
Link: https://blog.csdn.net/qq_26904271/article/details/83833168
1. What is a Software Timer
A software timer is a timer simulated by a program, which can be simulated by a hardware timer to create thousands of software timers. This way, when a program needs to use many timers, it is not limited by insufficient hardware resources. This is one of the advantages of software timers, as their quantity is not limited.
However, since software timers are implemented through programs, their operation and maintenance consume a certain amount of CPU resources, and their precision is relatively inferior to that of hardware timers.
2. Implementation Principle of Software Timers
In operating systems like Linux, uC/OS, and FreeRTOS, software timers are included, and the principles are similar. A typical implementation method is to generate a fixed clock tick using a hardware timer. Each time the hardware timer interrupts, a global time marker is incremented, and each software timer keeps track of its expiration time.
The program needs to periodically scan all running software timers, comparing each expiration time with the global clock marker to determine if the corresponding software timer has expired. If it has expired, the corresponding callback function is executed, and the timer is closed.
The above describes the implementation of a one-shot timer. To implement a periodic timer, which resets after expiration, simply retrieve the current time marker value after executing the callback function, add the delay time as the next expiration time, and continue running the software timer.
3. Software Timer Based on STM32
3.1 Clock Tick
A software timer requires a hardware clock source as a reference. This clock source has a fixed tick (which can be understood as the tick of a second hand), and a 32-bit global variable tickCnt is used to record the changes in this tick:
static volatile uint32_t tickCnt = 0; // Software timer clock tick
Each tick increments tickCnt (recording how many ticks have occurred):
/* Must be executed within the timer interrupt */
void tickCnt_Update(void)
{
tickCnt++;
}
Once it starts running, tickCnt will keep incrementing, and each software timer records an expiration time. As long as tickCnt is greater than the expiration time, it indicates that the timer has expired.
3.2 Data Structure
The data structure of the software timer determines its execution performance and functionality. Generally, it can be divided into two types: array structure and linked list structure. What does this mean? It refers to how (multiple) software timers are stored in memory; they can be stored in an array or a linked list.
The advantages and disadvantages of both are based on the characteristics of these two data structures: the array-based timer has faster lookup times but a fixed quantity, which cannot change dynamically. A large array may waste memory, while a small array may not be sufficient, making it suitable for systems with clearly defined and fixed timing events; the linked list-based timer can dynamically increase or decrease in quantity, but it may cause memory fragmentation (if there is no memory management), and the lookup time overhead is relatively larger than that of an array. Operating systems like Linux, uC/OS, and FreeRTOS use linked list-based software timers.
This article uses an array structure:
static softTimer timer[TIMER_NUM]; // Software timer array
The array and linked list are the overall data structures of the software timer. When it comes to a single timer, it involves the definition of the software timer structure. The functionalities of the software timer are closely related to its structure definition. Below is the structure definition of the software timer used in this article:
typedef struct softTimer {
uint8_t state; // State
uint8_t mode; // Mode
uint32_t match; // Expiration time
uint32_t period; // Timing period
callback *cb; // Callback function pointer
void *argv; // Argument pointer
uint16_t argc; // Number of arguments
}softTimer;
There are three states for the timer: the default is stopped, running after starting, and timeout after expiration.
typedef enum tmrState {
SOFT_TIMER_STOPPED = 0, // Stopped
SOFT_TIMER_RUNNING, // Running
SOFT_TIMER_TIMEOUT // Timeout
}tmrState;
There are two modes: the one-shot mode stops after expiration, and the periodic mode resets after expiration.
typedef enum tmrMode {
MODE_ONE_SHOT = 0, // One-shot mode
MODE_PERIODIC, // Periodic mode
}tmrMode;
Regardless of the mode, after the timer expires, the callback function will be executed. Below is the definition of that function, where the argument pointer argv is of type void pointer, allowing different types of parameters to be passed.
typedef void callback(void *argv, uint16_t argc);
The state mode and callback function pointer cb in the above structure are optional features. If the system does not require a periodically executing timer or does not need to automatically execute a function after expiration, these two definitions can be removed. Search for the public account: Architect Guide, reply: Architect to receive materials.
3.3 Timer Operations
3.3.1 Initialization
First, the software timer must be initialized by assigning initial values to each member of the timer structure. Although static variables are initialized to 0, I believe it is still necessary to maintain the habit of initializing variables to avoid some strange bugs.
void softTimer_Init(void)
{
uint16_t i;
for(i=0; i<TIMER_NUM; i++) {
timer[i].state = SOFT_TIMER_STOPPED;
timer[i].mode = MODE_ONE_SHOT;
timer[i].match = 0;
timer[i].period = 0;
timer[i].cb = NULL;
timer[i].argv = NULL;
timer[i].argc = 0;
}
}
3.3.2 Start
To start a software timer, not only must its state be changed to running, but it must also specify when the timer will expire (the current tickCnt value plus the delay time equals the expiration time), whether it is a one-shot or periodic timer, and which function to execute upon expiration, along with the function’s parameters. Once these are specified, the timer can start running.
void softTimer_Start(uint16_t id, tmrMode mode, uint32_t delay, callback *cb, void *argv, uint16_t argc)
{
assert_param(id < TIMER_NUM);
assert_param(mode == MODE_ONE_SHOT || mode == MODE_PERIODIC);
timer[id].match = tickCnt_Get() + delay;
timer[id].period = delay;
timer[id].state = SOFT_TIMER_RUNNING;
timer[id].mode = mode;
timer[id].cb = cb;
timer[id].argv = argv;
timer[id].argc = argc;
}
The assert_param() function in the above code is used for parameter checking, similar to the library function assert().
3.3.3 Update
This article’s software timer has three states: stopped, running, and timeout, with different actions for each state. The stopped state is the simplest, doing nothing; the running state requires continuous checking for expiration, executing the callback function upon expiration and entering the timeout state; the timeout state checks the timer’s mode. If it is periodic mode, it updates the expiration time and continues running; if it is one-shot mode, it stops the timer.
These operations are implemented by an update function:
void softTimer_Update(void)
{
uint16_t i;
for(i=0; i<TIMER_NUM; i++) {
switch (timer[i].state) {
case SOFT_TIMER_STOPPED:
break;
case SOFT_TIMER_RUNNING:
if(timer[i].match <= tickCnt_Get()) {
timer[i].state = SOFT_TIMER_TIMEOUT;
timer[i].cb(timer[i].argv, timer[i].argc); // Execute callback function
}
break;
case SOFT_TIMER_TIMEOUT:
if(timer[i].mode == MODE_ONE_SHOT) {
timer[i].state = SOFT_TIMER_STOPPED;
} else {
timer[i].match = tickCnt_Get() + timer[i].period;
timer[i].state = SOFT_TIMER_RUNNING;
}
break;
default:
printf("timer[%d] state error!\r\n", i);
break;
}
}
}
3.3.4 Stop
If the timer is running and needs to be stopped, a stop function is required, which is simple: just change the target timer’s state to stopped:
void softTimer_Stop(uint16_t id)
{
assert_param(id < TIMER_NUM);
timer[id].state = SOFT_TIMER_STOPPED;
}
3.3.5 Read State
If you want to know whether a timer is running or has stopped, it is also simple: return its state:
uint8_t softTimer_GetState(uint16_t id)
{
return timer[id].state;
}
This may seem strange; why return the state instead of reading it directly? Remember that the timer array defined in section 3.2 is a static global variable, which can only be accessed by the current source file. When external files need to access it, they can only do so through function returns. This is a simple encapsulation that maintains the modularity of the program.
3.4 Testing
Finally, we need to verify whether our software timer achieves the expected functionality.
Define three timers:
The timer TMR_STRING_PRINT executes only once, printing a string on Serial Port 1 after 1 second;
The timer TMR_TWINKLING is a periodic timer with a period of 0.5 seconds, toggling the state of LED0 each time it expires, achieving LED0 blinking;
The timer TMR_DELAY_ON executes once, turning on LED1 after 3 seconds. Unlike the first timer, this timer’s callback function is a no-operation function nop(), and the operation of turning on LED1 is achieved by checking the timer’s state in the main loop. This method may be used in certain situations.
static uint8_t data[] = {1,2,3,4,5,6,7,8,9,0};
int main(void)
{
USART1_Init(115200);
TIM4_Init(TIME_BASE_MS);
TIM4_NVIC_Config();
LED_Init();
printf("I just grabbed a spoon.\r\n");
softTimer_Start(TMR_STRING_PRINT, MODE_ONE_SHOT, 1000, stringPrint, data, 5);
softTimer_Start(TMR_TWINKLING, MODE_PERIODIC, 500, LED0_Twinkling, NULL, 0);
softTimer_Start(TMR_DELAY_ON, MODE_ONE_SHOT, 3000, nop, NULL, 0);
while(1) {
softTimer_Update();
if(softTimer_GetState(TMR_DELAY_ON) == SOFT_TIMER_TIMEOUT) {
LED1_On();
}
}
}
-End-
Reading this indicates that you enjoy the articles from this public account. Welcome to pin (star) this public account Linux Tech Enthusiast, so you can receive notifications promptly~
In this public account Linux Tech Enthusiast, reply: Linux to receive 2T learning materials!
Recommended reading
1. ChatGPT Chinese version 4.0, everyone can use it, fast and stable!
2. Common Linux commands, a total of 20,000 words, the most comprehensive summary on the internet
3. Linux learning guide (collection edition)
4. No need to translate official ChatGPT and Claude as well as Midjourney, stable with after-sales service