0


基于S32K144平台实现两种软件定时器

文章目录

在开发嵌入式软件时,定时器是必不可少的,或用于实时系统的心跳、延时,或用于裸机系统中的周期性任务等。因为硬件定时器资源有限,并且还有可能用于PWM,输入捕获等功能,所以更多的人会基于一个硬件定时器实现多个软件定时器,用于对时间精度要求没那么高的场合,如发送周期性CAN报文,定时采集传感器信号等。本文基于S32K144平台介绍笔者常用的两种软件定时器,希望对读者有用。

1.网红软件定时器MultiTimer

1.1 MultiTimer 简介

MultiTimer是最近比较火的一个开源项目,作者为0x1abin,github地址为:GitHub - 0x1abin/MultiTimer at master,遵循MIT开源许可协议,目前已收获502 stars。

MultiTimer是一个软件定时器扩展模块,可无限扩展你所需的定时器任务,取代传统的标志位判断方式, 更优雅更便捷地管理程序的时间触发时序。

1.2 准备工作

  • S32K144EVB-Q100
  • S32DS For ARM 2.2
  • PC端的串口调试部助手

1.3 MultiTimer 使用

MultiTimer的README文档对于如何使用MultiTimer介绍的非常详细。本文基于S32K144平台并参考README文档介绍如何使用MultiTimer。

1.3.1 新建工程

  1. 选择从例程中新建工程,如下图:
  2. 选择lpit_periodic_interrupt_s32k144的例程,并重命名为lpit_multitimer_s32k144,如下图,点击Finish:
  3. 配置lpit组件,设置定时器周期为1ms,并使能中断,如下图:
  4. multi_timer.cmulti_timer.h复制到工程所在的文件夹,并按F5刷新一下,就能在工程树看到了,如下图所示(串口打印相关的文件后面再介绍):
  5. 确认multi_timer.h中设置的tick值,如下图:
/*
It means 1 tick for 1ms. 
Your can configurate for your tick time such as 5ms/10ms and so on.
*/#defineCFG_TIMER_1_TICK_N_MS1

源代码此处的注释有误,如果想要tick为5ms,宏定义改为5是不行的,需要将硬件定时器的中断周期改为5ms。如果此处改为5,会导致软件定时器的超时时间变为原来的1/5。

1.3.2 修改主函数

  1. 首先在main.c中包含multi_timer.h,并创建两个软件定时器,如下:
#include"multi_timer.h"/* 创建两个软件定时器 */structTimer timer1;structTimer timer2;
  1. 然后对两个软件定时器进行初始化并启动,如下:
/* 软件定时器初始化*/timer_init(&timer1, timer1_callback,1000,2000,NULL);//1s looptimer_start(&timer1);timer_init(&timer2, timer2_callback,50,0,NULL);//50ms delaytimer_start(&timer2);printf("timer start!\r\n");

timer_init函数的定义如下,其中第三个函数表示第一次的超时时间,第四个参数表示第2次到第N次的超时时间。第4个参数如果为0,那么该软件定时器只发生一次超时,对应的回调函数也只执行一次。

voidtimer_init(structTimer* handle,void(*timeout_cb)(void*arg),uint32_t timeout,uint32_t repeat,void*arg)
  1. 接着在1ms的硬件定时器中断里调用timer_ticks(),如下:
/*!
 * @brief: LPIT interrupt handler.
 *         When an interrupt occurs clear channel flag and toggle LED0
 */voidLPIT_ISR(void){/* Clear LPIT channel flag */LPIT_DRV_ClearInterruptFlagTimerChannels(INST_LPIT1,(1<< LPIT_CHANNEL));/* 软件定时器的ticks,此处为1ms*/timer_ticks();}
  1. 最后实现两个软件定时器的回调函数,注意不能有耗时过长的操作,如下:
/* 每个软件定时器的回调函数,不能在回调函数做耗时操作,否则会导致其他定时器无法正常超时 */voidtimer1_callback(void*arg){/* Toggle LED0 */PINS_DRV_TogglePins(LED_GPIO_PORT,(1<< LED0_PIN_INDEX));printf("timer1 timeout! arg: %p\r\n", arg);}voidtimer2_callback(void*arg){printf("timer2 timeout! arg: %p\r\n", arg);}

1.3.3 增加串口打印功能

  1. 首先在工程中增加lpuart组件,在组件库中找到lpuart,双击即可添加,如下图:
  2. 然后点击添加到工程的lpuart组件,选择lpuart1,波特率设为115200,如下:
  3. 配置下lpuart对应的pin脚,RX选择PTC6,TX选择PTC7,如下:
  4. 工程中需要增加四个文件,如下所示: 其中,printf.c,uart_console_io.c,uart.h在S32DS For ARM 2.2安装目录下,uart.c需要自己编写。
  • printf.c地址如下:
  • uart_console_io.c地址如下:
  • uart.h地址如下:
  • uart.c需要自己实现如下四个函数
#include"uart.h"#include"lpuart1.h"
UARTError InitializeUART(UARTBaudRate baudRate){//lpuart1_InitConfig0.baudRate = baudRate;/* Initialize LPUART instance */LPUART_DRV_Init(INST_LPUART1,&lpuart1_State,&lpuart1_InitConfig0);return kUARTNoError;}

UARTError ReadUARTPoll(char* c){
    UARTError err = kUARTNoError;
    err =LPUART_DRV_ReceiveDataBlocking(INST_LPUART1,(uint8_t*)c,1,1000);return err;}

UARTError ReadUARTN(void* bytes,unsignedlong length){
    UARTError err = kUARTNoError;
    err =LPUART_DRV_ReceiveDataBlocking(INST_LPUART1,(uint8_t*)bytes, length,1000);return err;}

UARTError WriteUARTN(constvoid* bytes,unsignedlong length){
    UARTError err = kUARTNoError;uint32_t bytesRemaining;
    err =LPUART_DRV_SendData(INST_LPUART1,(constuint8_t*const)bytes, length);/* Wait for transfer to be completed */while(LPUART_DRV_GetTransmitStatus(INST_LPUART1,&bytesRemaining)!= STATUS_SUCCESS);return err;}
  1. 工程的属性中的库需要更改为ewl_c no I/O
  2. 工程属性中的linker的库需要全部删除,否则编译时会发生错误
  3. 在main.c中包含stdio.h之后,就可以调用printf函数在串口打印信息了。

1.4 MultiTimer功能测试

将程序下载到S32K144EVB中,在串口助手打印的信息如下:

从打印信号可以看出,各个定时器的运行符合预期。

1.5 MultiTimer借鉴

MultiTimer使用了单链表操作,且代码量不大,可以借鉴该项目学习单链表。网络上对该项目的解读文章非常多,笔者推荐阅读如下这个:

MultiTimer | 一款可无限扩展的软件定时器

2.个人常用软件定时器

除了网红定时器MultiTimer,笔者再推荐一个自己经常使用的软件定时器。

2.1 个人常用软件定时器介绍

  1. 需要将上一章节的工程中的multi_timer.cmulti_timer.h换成自己编写的soft_timer.csoft_timer.h,工程树如下:
  2. soft_timer.h主要定义定时器相关的枚举和结构体,如下所示:
typedefenum{
    TIMER1,
    TIMER2,
    VTIMER_NUM,} VtimerName_t;typedefstruct{unsignedchar     enable;unsignedint     msec;} Vtimer_t,*PVtimer;
  1. soft_timer.c主要实现定时器的初始化,启动,判断以及心跳函数等,如下所示:
Vtimer_t sVtimer[VTIMER_NUM];voidvtimer_Init(){unsignedchar i;for(i=0; i<VTIMER_NUM; i++){
        sVtimer[i].msec =0;
        sVtimer[i].enable = false;}vtimer_SetTimer(TIMER1, TIMER1_PERIOD);vtimer_SetTimer(TIMER2, TIMER2_PERIOD);}voidvtimer_SetTimer(VtimerName_t name,unsignedint msec){if(false == sVtimer[name].enable){
        sVtimer[name].msec = msec;
        sVtimer[name].enable = true;}}
bool vtimer_TimerElapsed(VtimerName_t name){if((sVtimer[name].msec ==0)&&(sVtimer[name].enable == true)){
        sVtimer[name].enable = false;return true;}else{return false;}}voidvtimer_UpdateHandler(void){/*Enter each 1ms*/unsignedchar i;for(i=0; i<VTIMER_NUM; i++){if(sVtimer[i].msec &&(sVtimer[i].enable == true)){
            sVtimer[i].msec--;}else{
            sVtimer[i].msec =0;}}}
  1. main.c中在硬件定时器中断中调用心跳函数,如下所示:
/*!
 * @brief: LPIT interrupt handler.
 *         When an interrupt occurs clear channel flag and toggle LED0
 */voidLPIT_ISR(void){/* Clear LPIT channel flag */LPIT_DRV_ClearInterruptFlagTimerChannels(INST_LPIT1,(1<< LPIT_CHANNEL));/* 软件定时器的ticks,此处为1ms*/vtimer_UpdateHandler();}
  1. 在main函数中调用初始化函数和判断函数,如下所示:
/* 软件定时器初始化*/vtimer_Init();printf("timer start!\r\n");while(1){if(vtimer_TimerElapsed(TIMER1)== true){vtimer_SetTimer(TIMER1, TIMER1_PERIOD);/* Toggle LED0 */PINS_DRV_TogglePins(LED_GPIO_PORT,(1<< LED0_PIN_INDEX));printf("timer1 timeout!\r\n");}if(vtimer_TimerElapsed(TIMER2)== true){vtimer_SetTimer(TIMER2, TIMER2_PERIOD);printf("timer2 timeout!\r\n");}}

2.2 功能测试

将程序下载到S32K144EVB,串口助手打印信息如下:

打印信息符合预期。

3.例程分享

本文使用的两个例程已上传百度网盘,分享如下:


如果觉得本文对你有用,不妨给个一键三连!!!


本文转载自: https://blog.csdn.net/bjxdbz/article/details/127166162
版权归原作者 Auto FAE进阶之路 所有, 如有侵权,请联系我们删除。

“基于S32K144平台实现两种软件定时器”的评论:

还没有评论