Software timer based on hardware timer

Generalize

The hardware timer is very accurate, and the software timer has a delay anyway. It is mainly used where precise timing is not required, and the software timing wastes the resources of the single-chip microcomputer.
Carding
mentioned timer, we are exposed to more or less hardware timer, but sometimes due to resource constraints, and there will inevitably be using a software timer appears, but stresses the need to start the timer From a hardware timer, software timer The device is extended from its foundation.

Hardware timer

1. General hardware timers are integrated inside the CPU, and some can use external hardware timer chips, which can be programmed to set the operating frequency of the hardware timer. Once the hardware timer has set the operating frequency, just upload The hardware timer will periodically output an interrupt signal to the CPU, which is called a clock interrupt. The linux kernel has implemented the service program corresponding to the clock interrupt. This service program is also called the clock interrupt service function. Since the hardware timer periodically generates clock interrupts for the CPU, the corresponding interrupt service routine will be called periodically by the kernel;

2. The hardware delay uses the timer/counter chip, or the timer/counter inside the microcontroller. In fact, it is the frequency division of the crystal oscillator (the frequency division coefficient can be set programmable) to obtain a precise low-frequency cycle Signal, use this periodic signal (such as 10ms) to trigger the interrupt, and call the timer interrupt service routine every 10ms. Add count variables to the timer interrupt service program, and you can get any timing. When the 10ms is not up, the microcontroller can run other programs, and then automatically enter the interrupt service routine to process the timing tasks when the 10ms is up, and it will not be blocked like a software delay.

Software timer

The software timer is a timer based on the hardware timer. Thousands of software timers can be simulated by a hardware timer, so that the program will not be limited when it needs to use more timers. The lack of hardware resources is an advantage of software timers, that is, the number is not limited. However, because the software timer is implemented by a program, its operation and maintenance need to consume a certain amount of CPU resources, and the accuracy is also worse than that of the hardware timer.
The
typical implementation method is to generate a fixed clock beat through a hardware timer. Each time the hardware timer is interrupted, a global time stamp is incremented. Each software timer saves the expiration time. All running software timers need to be scanned regularly, and each expiration time is compared with the global clock mark to determine whether the corresponding software timer has expired, and when the corresponding software timer expires, the corresponding callback function is executed and the timer is closed.
Note
Because the interrupt service is performed in the kernel mode, we need to use volatile to define global variables. The previous blog also talked about its usage. There are clearly three situations, otherwise the timer will fail. Such as:

volatile uint32_t whiole_cnt;
void tickCnt_Update(void)
{
    
    
    whiole_cnt++;
}

Set a callback function we need: argv, argc is its parameter:

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

Once it starts running, whiole_cnt will keep increasing by one, and each software timer records an expiration time. As long as whiole_cnt is greater than the expiration time, it means that the timer has expired.
The above code can be understood. At this time, if we need a timing, what needs to be determined?

typedef struct time
{
    
    
	unsigned char state;//此软件定时的状态
	unsigned char mode;//单次计时还是循环计时
	uint32_t timer_cnt;//此软件计时值与whiole_cnt比较
	uint32_t period;//循环计时时时间间隔
	callback *cb;//回调函数指针
	void *argv;//回调函数参数,根据实际情况可增可减
	uint16_t argc;//回调函数参数,根据实际情况可增可减
}_timer;

This is the basic situation of a single software timer. If you need more than one, you can do this:
define two timer arrays :

#define NUM 2
_timer TIME[NUM];

The above is just a framework, we need to initialize it first, so as not to affect the later use, the
initialization function:

void TIME_NUM_INIT(void)
{
    
    
	int i=0; 
	for( i ;i<NUM;i++)//循环初始化结构体
	{
    
    
		TIME[i].timer_cnt=0;
		TIME[i].period=0;
		TIME[i].state=stop;
		TIME[i].mode=once;
		TIME[i].cb=NULL;
		TIME[i].argv=NULL;
		TIME[i].argc=0;
	}
}

TIME[i].state has three states:

typedef enum state
{
    
    
	stop=1,
	runing,
	time_out
}timer_status;

TIME[i].mode has two states:

typedef enum mode
{
    
    
	once=1,
	on_once
}timer_mode;

After initialization, no real timer has actually appeared yet. The next step is to encapsulate the structure and construct a separate timer:

void TIME_START(uint8_t id,uint32_t timer,uint32_t delay,unsigned char mode,void *cb,void *argv,uint16_t argc)
{
    
    
	TIME[id].timer_cnt=timer;
	TIME[id].period=delay;
	TIME[id].mode=mode;
	TIME[id].cb=cb;
	TIME[id].argv=argv;
	TIME[id].argc=argc;
	TIME[id].state=runing;
}

According to the defined NUM value, initialize them separately, and you can run after initialization. For example, if you want to time a 500ms software timer, and the timer interrupt time is 10ms, you need 50 interrupts:

TIME_START(0,50,0,once,Time ,"12345",15);

Time is the callback function pointer:
argv, argc is its parameter

int *Time(void *argv,uint16_t argc)
{
    
    
	printf("运行状态:%d",time_state(0));
	printf("%s,%d\r\n",argv,argc);
	
}

After the initialization is complete, we need to scan the software timer all the time. The main reason is to compare with the variable of the timer interrupt to determine whether the time is up! The scanned function looks like this:

void check_time(void)
{
    
    
	uint16_t i=0;
	  for(i;i<NUM;i++)
			{
    
    
				switch(TIME[i].state)
				{
    
    
					case stop:
						break;
						
					case runing:	
						if(TIME[i].timer_cnt<=whiole_cnt)
						{
    
    
							TIME[i].state=time_out;
							TIME[i].cb(TIME[i].argv,TIME[i].argc);
						}
						break;
						
					case time_out:
						if(TIME[i].mode==once)
						{
    
    
							TIME[i].state=stop;
						}
						else
						{
    
    
							TIME[i].timer_cnt=whiole_cnt+TIME[i].period;
							TIME[i].state=runing;
						}
						break;
					default:break;		
				}
			}
}

We define a while(1) in the main function, and scan all the time to achieve the effect.
Main function main.c

int main(void)
{
    
    
	
	LED_Init();
	delay_init();
	timer_init();
	usart_init(115200);
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
   
	TIME_NUM_INIT();//初始化

	TIME_START(0,2,2,on_once,Time ,"12345",15);//定时器1
	TIME_START(1,4,2,once,Time ,"6789",30);//定时器2初始化
	
	 while(1)
	 {
    
     
		 check_time();
		 if(TIME[1].state==stop)//如果软件定时器1定时时间到,LED0反转
		 {
    
    
			  LED0=!LED0;
		 }
	 }
}

Don't forget the callback function!

to sum up

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 of void pointer type, which is convenient for passing in different types of parameters.

Guess you like

Origin blog.csdn.net/weixin_42271802/article/details/106062854
Recommended