Article count:922 Read by:3074353

Account Entry

Implementation of an embedded software timer!

Latest update time:2024-06-15
    Reads:


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. One hardware timer can simulate thousands of software timers, so that when the program needs to use more timers, it will not be limited by insufficient hardware resources. This is one of the advantages of software timers, that is, the number is unlimited.

However, since the software timer is implemented through a program, its operation and maintenance require a certain amount of CPU resources, and its accuracy is also lower than that of the hardware timer.

2. Implementation principle of software timer

In Linux, uC/OS, FreeRTOS and other operating systems, there are software timers, and the principles are similar. The typical implementation method is: a hardware timer generates a fixed clock beat, and each time the hardware timer interrupts, a global time stamp is increased by one, and each software timer saves the expiration time.

The program needs to periodically scan all running software timers, compare each expiration time with the global clock mark to determine whether the corresponding software timer has expired. If it has expired, the corresponding callback function will be executed and the timer will be closed.

The above is the implementation of a single timer. If you want to implement a periodic timer, that is, to re-time after expiration, you only need to get the value of the current time stamp 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 ticks

The software timer needs a hardware clock source as a benchmark. This clock source has a fixed beat (which can be understood as each tick of the second hand). A 32-bit global variable tickCnt is used to record the changes in this beat:

static volatile uint32_t tickCnt = 0;    //软件定时器时钟节拍

For each beat, tickCnt is incremented by one (recording how many ticks there are):

/* 需在定时器中断内执行 */
void tickCnt_Update(void)
{
 tickCnt++;
}

Once it starts running, tickCnt will continue to increase by one, and each software timer records an expiration time. As long as tickCnt is greater than the expiration time, it means that the timer has expired.

3.2 Data Structure

The data structure of a software timer determines its performance and functionality. It can be generally divided into two types: array structure and linked list structure. What does this mean? This is how (multiple) software timers are stored in memory. They can be stored in an array or in a linked list.

The difference between the two is the characteristics of the two data structures: array-based timers are faster to search, but the number is fixed and cannot change dynamically. If the array is large, it is easy to waste memory, and if the array is small, it may not be enough. It is suitable for systems with clear and fixed timing events; the number of linked list timers can be increased or decreased dynamically, which is easy to cause memory fragmentation (if there is no memory management), and the search time overhead is relatively large. It is suitable for systems with strong versatility. Linux, uC/OS, FreeRTOS and other operating systems all use linked list software timers.

This article uses an array structure:

static softTimer timer[TIMER_NUM];        //软件定时器数组

Arrays and linked lists are the overall data structures of software timers. When it comes to a single timer, it involves the definition of the software timer structure. The functions of the software timer are closely related to its structure definition. The following is the structure definition of the software timer in this article:

typedef struct softTimer {
 uint8_t state;           //状态
 uint8_t mode;            //模式
 uint32_t match;          //到期时间
 uint32_t period;         //定时周期
 callback *cb;            //回调函数指针
 void *argv;              //参数指针
 uint16_t argc;           //参数个数
}softTimer;

There are three states of the timer. The default is stopped, running after startup, and timeout after expiration.

typedef enum tmrState {
 SOFT_TIMER_STOPPED = 0,  //停止
 SOFT_TIMER_RUNNING,      //运行
 SOFT_TIMER_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,       //单次模式
 MODE_PERIODIC,           //周期模式
}tmrMode;

Regardless of the mode, the callback function will be executed after the timer expires. The following is the definition of the function. The parameter pointer argv is a void pointer type to facilitate the passing of different types of parameters.

typedef void callback(void *argv, uint16_t argc);

The mode state and callback function pointer cb in the above structure are optional functions. If the system does not need a timer that executes periodically, or does not need to automatically execute a function after expiration, the definitions of these two can be deleted.

3.3 Timer Operation

3.3.1 Initialization

The first step is to initialize the software timer and assign initial values ​​to each member of the timer structure. Although the initial value of the static variable is 0, I personally think it is necessary to keep 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 Startup

To start a software timer, you not only need to change its state to running state, but also tell the timer when it will expire (the current tickCnt value plus the delay time is the expiration time), whether it is a single timing or a periodic timing, which function to execute after the expiration, and what the function's parameters are. Once these are explained, you 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() in the above function is used for parameter checking, similar to the library function assert().

3.3.3 Update

The software timer in this article has three states: stop, run and timeout. Different states do different things. The stop state is the simplest and does nothing. The run state needs to constantly check whether it has expired. When it expires, the callback function is executed and the timeout state is entered. The timeout state determines the mode of the timer. If it is a periodic mode, the expiration time is updated and the timer continues to run. If it is a single mode, the timer is stopped.

These operations are all 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);       //执行回调函数
     }
     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 you want to stop the timer halfway through, you need a stop function. The operation is very simple. Just change the state of the target timer to stop:

void softTimer_Stop(uint16_t id)
{
 assert_param(id < TIMER_NUM);
 timer[id].state = SOFT_TIMER_STOPPED;
}

3.3.5 Read Status

What if you want to know whether a timer is running or has stopped? It's also simple, just return its status:

uint8_t softTimer_GetState(uint16_t id)
{
 return timer[id].state;
}

Maybe this looks strange, why return instead of reading directly? Don't forget 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 an external file needs to access it, it can only be returned through the function. This is a simple encapsulation to keep the program modular.

3.4 Testing

Finally, of course, we need to verify whether our software timer has achieved the expected function.

Define three timers:

Timer TMR_STRING_PRINT is executed only once, and a string of characters is printed on serial port 1 after 1 second;

Timer TMR_TWINKLING is a periodic timer with a period of 0.5s. Each time it expires, the state of LED0 will be inverted to achieve the flashing of LED0.

Timer TMR_DELAY_ON is executed once, and LED1 is turned on after 3 seconds. Unlike the first timer, the callback function of this timer is an empty function nop(). The operation of turning on LED1 is achieved by judging the state of the timer in the main loop. This method may be used in some occasions.

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, NULL0);
 softTimer_Start(TMR_DELAY_ON, MODE_ONE_SHOT, 3000, nop, NULL0);
 
 while(1) {
  softTimer_Update();
  if(softTimer_GetState(TMR_DELAY_ON) == SOFT_TIMER_TIMEOUT) {
   LED1_On();
  }
 }
}



The spring recruitment has begun. If you are not adequately prepared, it will be difficult to find a good job during the spring recruitment.


Here is a big employment gift package for everyone. You can prepare for the spring recruitment and find a good job!



Latest articles about

 
EEWorld WeChat Subscription

 
EEWorld WeChat Service Number

 
AutoDevelopers

About Us Customer Service Contact Information Datasheet Sitemap LatestNews

Room 1530, Zhongguancun MOOC Times Building,Block B, 18 Zhongguancun Street, Haidian District,Beijing, China Tel:(010)82350740 Postcode:100190

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号