不管是单片机还是实时操作系统,在应用中都会有写延时的需求,比如:点亮一个LED,然后以1S频率亮灭;不可能亮了之后delay1S,然后灭,delay1S;那这样系统就浪费了CPU资源;
可以做个系统的滴答时钟,使用定时器做个1ms的滴答,举例stm32上面就有专门的滴答定时器,叫systick;
这个滴答的周期越短,对CPU的开销就越大,这个时间根据系统的负荷调整,比如:电力系统要求遥信的分辨率为2ms,为了满足要求,滴答的周期只能设置为1ms的周期。
初始化操作之后,会1ms进入systick中断一次,但是中断里处理操作不可太多,中断会一直占用CPU的资源,直到执行完中断内容后才会释放CPU,去执行用户应用;还有一点就是中断里面执行的操作时间一定要小于中断周期时间,否则CPU会一直在中断里面运行,无法跳出来了。
中心思想就是获取当前的时间,然后通过后续更新的时间减去时标的差值,作为延时的时长;
1、初始化systick定时器
1/***************************************************************************************
2* 函数名称: SysTick_Init
3* 功能描述: RCC模块配置,为系统其它模块提供一个时基源
4* 输入参数: Systick_1us = SysTick周期 单位us
5* 输出参数: 无
6* 返 回 值: OK=成功 ERR=失败
7* 作 者:
8* 日 期:
9* 修改记录:
10* 修改人 :
11* 修改日期:
12* 修改内容:
13***************************************************************************************/
14ERR_TYPE SysTick_Init(u32 Systick_1us)
15{
16 u32 systick = 0;
17 RCC_ClocksTypeDef RCC_Clocks;
18
19/* 输入参数有效判断 */
20if(Systick_1us == 0)
21 {
22return ERR;
23 }
24
25/* 计算配置时间对应的计数次数 */
26 RCC_GetClocksFreq(&RCC_Clocks);
27 systick = RCC_Clocks.SYSCLK_Frequency / 1000000; //单位转为us
28 systick = systick * Systick_1us;
29
30/*SysTick周期配置 */
31if(SysTick_Config(systick))
32 {
33while(1);
34 }
35
36/* SysTick中断优先级设置 */
37 NVIC_SetPriority(SysTick_IRQn, 0x0);
38
39/* SysTick中断设置 */
40 SysTick->CTRL |= 0x00000002; //SysTick_ITConfig(ENABLE);
41
42/* 使能计数器 */
43 SysTick->CTRL |= SysTick_Counter_Enable;
44
45return OK;
46}
2、重定向中断处理函数
1/***************************************************************************************
2* 函数名称: SysTick_Handler
3* 功能描述: SysTick中断函数
4* 输入参数: 无
5* 输出参数: 无
6* 返 回 值: 无
7* 作 者:
8* 日 期:
9* 修改记录:
10* 修改人 :
11* 修改日期:
12* 修改内容:
13***************************************************************************************/
14void SysTick_Handler(void)
15{
16 gSystickT++; //系统心跳ticker计数
17}
3、延时回绕函数
1/***************************************************************************************
2* 函数名称: Get_SysTick
3* 功能描述: 获取SysTick计数器
4* 输入参数: 无
5* 输出参数: 无
6* 返 回 值: 时间
7* 作 者:
8* 日 期:
9* 修改记录:
10* 修改人 :
11* 修改日期:
12* 修改内容:
13***************************************************************************************/
14u32 Get_SysTick(void)
15{
16return gSystickT;
17}
18/***************************************************************************************
19* 函数名称: BSP_TickerOut
20* 功能描述: 时基 1mS 延时周期判定
21* 输入参数: *pTicker 模块定时器 cnt=0 赋初值 cnt!=0 比较延时周期
22* 输出参数: 无
23* 返 回 值: eFalse,eTrue
24* 作 者:
25* 日 期:
26* 修改记录:
27* 修改人 :
28* 修改日期:
29* 修改内容:
30****************************************************************************************/
31E_Bool BSP_TickerOut(u32 *pTicker, u32 cnt)
32{
33 u32 ticks = 0;
34
35/* 输入参数有效判断 */
36if(pTicker == (void *)0)
37 {
38return(eFalse);
39 }
40
41 ticks = Get_SysTick();
42
43if(cnt == 0)
44 {
45 *pTicker = ticks; // 开始计时
46 }
47else
48 {
49if(ticks >= *pTicker)
50 {
51 ticks = ticks - *pTicker;
52 }
53else
54 {
55 ticks = (0XFFFFFFFF - *pTicker) + ticks;
56 }
57
58if(ticks >= cnt) //计时判断
59 {
60return(eTrue);
61 }
62 }
63
64return eFalse;
65}
4、示例
1//LED 1S频率亮灭操作
2void LED_Test(void)
3{
4 static u32 LedDelay = 0;
5
6/* 判断1S是否到 */
7if(!TickerOut(&LedDelay, 1000))
8 {
9return;
10 }
11/* 更新LedDelay时间 */
12 TickerOut(&LedDelay, 0);
13
14/* LED进行翻转 */
15 GPIO_ToggleBits(RUN_LED_GPIO_PORT, RUN_LED_PIN);
16}