打印
[uCOS/RTOS]

每周分享】FreeRTOS定时器讲解

[复制链接]
101|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
#申请原创#
@21小跑堂 @21ic小喇叭
一、定时器
定时器是计算机系统中的一种常用工具,用于在指定的时间间隔或延迟后执行特定的任务或操作。定时器可以用于各种应用场景,例如任务调度、超时处理、事件触发等。

在计算机系统中,定时器通常分为软件定时器和硬件定时器两种类型:

1. 软件定时器。

软件定时器是通过软件实现的定时机制。它依赖于系统的时钟源和计时功能,通过不断地检查当前时间与预设的时间间隔来判断是否触发定时事件。软件定时器的精度和准确性受到系统时钟的影响,通常适用于对时间要求不太严格的应用场景。软件定时器一般由操作系统或应用程序提供,并通过任务调度器或中断服务例程来触发相应的任务或回调函数。

2. 硬件定时器。

硬件定时器是由计算机系统的硬件部分实现的定时机制。它通常基于硬件的计时器或定时器模块,具有更高的精度和准确性。硬件定时器可以通过设置定时器的计数器、预分频器、中断等参数来实现特定的定时功能。硬件定时器常用于实时操作系统、嵌入式系统和实时应用中,能够提供可靠的定时功能和高精度的时间触发。

FreeRTOS中也具有定时器功能,属于软件定时器。

二、开发流程
Timer的创建
TimerHandle_t xTimerCreate(        const char * const pcTimerName, 
                                   const TickType_t xTimerPeriodInTicks,
                                                        const BaseType_t xAutoReload,
                                    void * const pvTimerID,
                                TimerCallbackFunction_t pxCallbackFunction );
函数参数说明:
const char * const pcTimerName:定时器名称。
const TickType_t xTimerPeriodInTicks:定时器的周期
const BaseType_t xAutoReload:是否自动重载。
void * const pvTimerID:定时器标识ID
TimerCallbackFunction_t pxCallbackFunction:回调函数
返回值说明
TimerHandle_t:创建的定时器句柄。创建失败为NULL。
定时器周期
直译过来,就是表示多少次Tick运行一个周期。

稍微直白点说,多少次Tick执行一次回调函数。

如果需要再直白点描述,就需要把Tick概念了解清楚。在 FreeRTOS 中,Tick(时钟节拍)是指 FreeRTOS 内核使用的基本时间单位。它代表了内核中的时间流逝。在FreeRTOSConfig.h中默认配置有这个Tick的频率,configTICK_RATE_HZ默认值为1000,表示1秒钟有1000个Tick。Tick可以理解为数数,1秒钟数1000下。

回到定时器周期描述上来说,简单来说,操作系统数多少下,执行1次回调函数。

人的认知,可能更好的理解多长时间执行一次这个函数,这里有可以将时间转换为tick计数的方法:pdMS_TO_TICKS.

毫秒转换为Tick计数
pdMS_TO_TICKS(1000);//参数为毫秒值
自动重载
自动重载,取值有两种:pdFALSE和pdTrue

1. pdFALSE:非自动重载模式。

在非自动重载模式下,定时器只会触发一次,在到期时停止计时,不会自动重新启动。这意味着在定时器到期后,需要手动调用 xTimerStart() 函数重新启动定时器,才能触发下一次定时。

2. pdTRUE:自动重载模式。

在自动重载模式下,定时器在每次到期后会自动重新启动,以便周期性地触发任务或回调函数的执行。定时器到期后,会重新开始计时,等待下一个到期时刻。

回调函数
// typedef void (* TimerCallbackFunction_t)( TimerHandle_t xTimer );
void callback(TimerHandle_t xTimer);
定时器标识

通常多个定时器公用一个回调时,可以通过标识来判断是哪个个timer触发的。

// 定时器回调函数
void timerCallback(TimerHandle_t xTimer)
{
    // 获取定时器的标识符
    BaseType_t timerID = pvTimerGetTimerID(xTimer);

    // 根据标识符处理相应的逻辑
    if (timerID == 1)
    {
        // 处理定时器1的逻辑
    }
    else if (timerID == 2)
    {
        // 处理定时器2的逻辑
    }
    // ...
}
......
// 创建定时器1,并传递标识符1
TimerHandle_t timer1 = xTimerCreate("Timer1", pdMS_TO_TICKS(1000), pdTRUE, (void *)1, timerCallback);

// 创建定时器2,并传递标识符2
TimerHandle_t timer2 = xTimerCreate("Timer2", pdMS_TO_TICKS(2000), pdTRUE, (void *)2, timerCallback);
Timer开启和停止

Timer开启API

xTimerStart(xTimer, xTicksToWait);
第一个参数为timer的句柄
第二个参数表示,多少个tick后启动timer,0表示立刻启动

Timer停止API

xTimerStop(xTimer, xTicksToWait);
第一个参数为timer的句柄
第二个参数表示,多少个tick后停止timer,0表示立刻停止

Timer的开启和启动可以重复调用。

Timer删除

Timer删除API

xTimerDelete(xTimer, xTickToWait);
第一个参数为timer的句柄
第二个参数表示,多少个tick后删除timer,0表示立刻删除

Timer一旦删除,就不可以再通过timer的句柄进行启动和停止等操作。

三、开发案例重复Timer和单次Timer

timer间隔固定时间执行逻辑

main.c

#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
#include "usart0.h"

TaskHandle_t            start_handler;
TimerHandle_t                                         timer1;

void Usart0_recv(uint8_t *data, uint32_t len) {
    printf("recv: %s\r\n", data);
}

void timerCallback(TimerHandle_t xTimer) {
    printf("timer\r\n");
}

static void GPIO_config() {
    // 时钟初始化
    rcu_periph_clock_enable(RCU_GPIOA);
    // 配置GPIO模式
    gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO_PIN_0);
}

int main(void)
{
    NVIC_SetPriorityGrouping(NVIC_PRIGROUP_PRE4_SUB0);
    systick_config();
    GPIO_config();
    Usart0_init();

                timer1 = xTimerCreate("Timer1", pdMS_TO_TICKS(1000), pdTRUE, (void *)1, timerCallback);
                xTimerStart(timer1, 0);

                vTaskStartScheduler();

    while(1) {}
}
创建timer时配置是否自动重载
Timer启停控制

通过按键启动和停止Timer

main.c

#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
#include "usart0.h"

TaskHandle_t            start_handler;
TaskHandle_t            task_key_handler;
TimerHandle_t                                         timer1;


void task_key(void *pvParameters) {
    uint32_t flag = 0;
    FlagStatus pre_state = RESET;
    BaseType_t result;
    while(1) {
        FlagStatus state = gpio_input_bit_get(GPIOA, GPIO_PIN_0);
        if(SET == state && pre_state == RESET) {
            // 当前高电平, 上一次为低电平,按下
            pre_state = state;

            if(flag == 1) {
                printf("stop\r\n");
                xTimerStop(timer1, 0);
            } else {
                printf("start\r\n");
                xTimerStart(timer1, 0);
            }
            flag ++;
            if(flag == 2) flag = 0;
        } else if(RESET == state && pre_state == SET) {
            // 当前高电平, 上一次为低电平,抬起
            pre_state = state;
        }
        vTaskDelay(20);
    }
}

void Usart0_recv(uint8_t *data, uint32_t len) {
    printf("recv: %s\r\n", data);
}

void timerCallback(TimerHandle_t xTimer) {
    printf("timer\r\n");
}

void start_task(void *pvParameters) {
    taskENTER_CRITICAL();

    timer1 = xTimerCreate("Timer1", pdMS_TO_TICKS(1000), pdTRUE, (void *)1, timerCallback);
    xTaskCreate(task_key, "task_key", 64, NULL, 2, &task_key_handler);

    vTaskDelete(start_handler);

    taskEXIT_CRITICAL();
}

static void GPIO_config() {
    // 时钟初始化
    rcu_periph_clock_enable(RCU_GPIOA);
    // 配置GPIO模式
    gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO_PIN_0);
}

int main(void)
{
    NVIC_SetPriorityGrouping(NVIC_PRIGROUP_PRE4_SUB0);
    systick_config();
    GPIO_config();
    Usart0_init();

    xTaskCreate(start_task, "start_task", 128, NULL, 1, &start_handler);
    vTaskStartScheduler();

    while(1) {}
}
中断中启停Timer

在中断中启动和停止Timer

main.c

#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
#include "usart0.h"

TaskHandle_t            start_handler;
TaskHandle_t            task_key_handler;
TimerHandle_t                                         timer1;


void task_key(void *pvParameters) {
    uint32_t flag = 0;
    FlagStatus pre_state = RESET;
    BaseType_t result;
    while(1) {
        FlagStatus state = gpio_input_bit_get(GPIOA, GPIO_PIN_0);
        if(SET == state && pre_state == RESET) {
            // 当前高电平, 上一次为低电平,按下
            pre_state = state;

            if(flag == 1) {
                printf("stop\r\n");
                xTimerStop(timer1, 0);
            } else {
                printf("start\r\n");
                xTimerStart(timer1, 0);
            }
            flag ++;
            if(flag == 2) flag = 0;
        } else if(RESET == state && pre_state == SET) {
            // 当前高电平, 上一次为低电平,抬起
            pre_state = state;
        }
        vTaskDelay(20);
    }
}

void Usart0_recv(uint8_t *data, uint32_t len) {
    printf("recv: %s\r\n", data);
    if(data[0] == 0x00) {
        printf("start \r\n");
        xTimerStartFromISR(timer1, NULL);
    } else if(data[0] == 0x01) {
        printf("stop \r\n");
        xTimerStopFromISR(timer1, NULL);
    }
}

void timerCallback(TimerHandle_t xTimer) {
    printf("timer\r\n");
}

void start_task(void *pvParameters) {
    taskENTER_CRITICAL();

    timer1 = xTimerCreate("Timer1", pdMS_TO_TICKS(1000), pdTRUE, (void *)1, timerCallback);
    xTaskCreate(task_key, "task_key", 64, NULL, 2, &task_key_handler);

    vTaskDelete(start_handler);

    taskEXIT_CRITICAL();
}

static void GPIO_config() {
    // 时钟初始化
    rcu_periph_clock_enable(RCU_GPIOA);
    // 配置GPIO模式
    gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO_PIN_0);
}

int main(void)
{
    NVIC_SetPriorityGrouping(NVIC_PRIGROUP_PRE4_SUB0);
    systick_config();
    GPIO_config();
    Usart0_init();

    xTaskCreate(start_task, "start_task", 128, NULL, 1, &start_handler);
    vTaskStartScheduler();

    while(1) {}
}







使用特权

评论回复

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

15

主题

116

帖子

2

粉丝