【ESP32 C++教程】Unit6-1 定时器

本节主要讲解Timer类,FreeRTOS定时器的使用。

定时器是指从指定的时刻开始,经过一个指定时间触发超时事件,按类型分为硬件定时器和软件定时器。

硬件定时器是芯片本身提供的定时功能。一般是由外部晶振提供给芯片输入时钟,芯片向软件模块提供一组配置寄存器,接受控制输入,到达设定时间值后芯片中断控制器产生时钟中断。硬件定时器的精度一般很高,可以达到纳秒级别,并且是中断触发方式。

软件定时器是由底层系统提供的一类系统接口,它构建在硬件定时器基础之上,使系统能够提供不受硬件定时器资源限制的定时器服务,它实现的功能与硬件定时器也是类似的。


ESP32 Arduino开发框架内的定时器类

【ESP32 C++教程】Unit6-1 定时器
其中SwTimer为软件定时器,封装了FreeRTOS中的timer相关函数;
HwTimer为硬件定时器,封装了Arduino-ESP32中内置的Ticker库;


下面用两个例子来说明定时器的使用

从 https://gitee.com/billyzh/esp32-cpp-lesson 下载本教程的源码到本地硬盘文件夹,如d:\esp32-cpp-lesson
在VSCode中,选择【文件】->【打开文件夹...】选择上一步保存的文件夹打开

示例一:用定时器控制LED闪烁

打开项目后,选择config.h文件,修改第10行为
#define APP_LESSON61_A 1

打开unit6-lesson61a/app_config.h文件,设置启用软件定时器,
#define CONFIG_USE_SW_TIMER 1
打开unit6-lesson61a/board_config.h文件,设置LED使用的引脚,
#define BUILTIN_LED_PIN GPIO_NUM_4
配置启用GpioLed
#define CONFIG_USE_LED_GPIO 1

创建LED实例,代码如下(unit6-lesson61a/my_board.cpp):

MyBoard::MyBoard() : Board() {
    Log::Info(TAG, "===== Create Board ...... =====");

    Log::Info(TAG, "initial led.");
    led_ = new GpioLed(BUILTIN_LED_PIN, false);

    Log::Info( TAG, "===== Board config completed. =====");
}

程序很简单,就是创建一个GpioLed实例。

控制LED闪烁

控制代码如下(unit6-lesson61a/my_application.cpp):

void MyApplication::OnInit() {
    // 创建定时器
    timer1_ = TimerFactory::CreateTimer("timer1");
    timer1_->Start(1000, [this]() {
        Blink();
    });
}

void MyApplication::Blink() {
    Led *led = Board::GetInstance().GetLed();
    if (state_==0) {
        led->TurnOn();
        state_ = 1;
    } else {
        led->TurnOff();
        state_ = 0;
    }
}

程序解读
1. 在OnInit方法中使用TimerFactory类创建定时器,然后启动定时器,时间间隔为1000ms,回调函数为Lambda表达式,最终调用Blink成员函数;
2. 在Blink方法中先获取Led实例,然后根据state_变量的值打开或关闭LED,以实现闪烁效果;

与前面小节在Loop方法中使用delay来实现LED闪烁相比,使用定时器类似于多任务并行执行,更加灵活,也可避免在Loop方法中处理太多功能。

编译项目并上传开发板检验

示例二:多个定时器

打开项目后,选择config.h文件,修改第10行为
#define APP_LESSON61_B 1

打开unit6-lesson61b/app_config.h文件,设置启用软件定时器,
#define CONFIG_USE_SW_TIMER 1
打开unit6-lesson61b/board_config.h文件,设置LED使用的引脚,
#define BUILTIN_LED_PIN GPIO_NUM_4
配置启用GpioLed
#define CONFIG_USE_LED_GPIO 1

创建LED实例,代码如下(unit6-lesson61b/my_board.cpp):

MyBoard::MyBoard() : Board() {
    Log::Info(TAG, "===== Create Board ...... =====");

    Log::Info(TAG, "initial led.");
    led_ = new GpioLed(BUILTIN_LED_PIN, false);

    Log::Info( TAG, "===== Board config completed. =====");
}

程序很简单,就是创建一个GpioLed实例。

定时器任务

控制代码如下(unit6-lesson61b/my_application.cpp):

void MyApplication::OnInit() {
    // 创建定时器
    timer1_ = TimerFactory::CreateTimer("timer1");
    timer1_->Start(1000, [this]() {
        Timer1Task();
    });

    timer2_ = TimerFactory::CreateTimer("timer2");
    timer2_->Start(500, [this]() {
        Timer2Task();
    });
}

void MyApplication::Timer1Task() {
    Log::Info(TAG, "Time1Task execute.");

    Led *led = Board::GetInstance().GetLed();
    if (state_==0) {
        led->TurnOn();
        state_ = 1;
    } else {
        led->TurnOff();
        state_ = 0;
    }
}

void MyApplication::Timer2Task() {
    Log::Info(TAG, "Time2Task execute...");
    
    // 执行时间大于定时器时间(500s)
    delay(600);
}

程序解读
1. 在OnInit方法中使用TimerFactory类创建两个定时器,然后启动定时器;
2. 在Timer1Task方法中实现LED闪烁效果;
3. 在Timer2Task方法中延时600ms;

编译项目并上传开发板检验

FreeRTOS定时器说明

使用定时器的初衷是可以实现一些复杂的定时任务,但是实际上使用xTimerStart得到的实例只能处理一些简单的任务,I/O,数据的运算。注意有一些操作会导致回调函数不执行,如刷新IIC接口的屏幕,在本框架中,可以使用Schedule方法将操作调度到事件循环中执行。

定时任务的规则其实是按照绝对时间来启用任务的,但是如果回调函数运行时长超时,也会一直运行到正常结束。这样子程序是稳定了,但是会不会按照意愿进行就不能保证了。

举个例子:定时任务500ms执行一次,但是任务执行1ms。这是非常复合预期的结果。事实上就是每500ms运行一次。如果任务执行时间为600ms。则在600ms时任务就会变为就绪状态。600ms任务执行完成,则会直接再次执行任务。

【ESP32 C++教程】Unit6-1 定时器

在多任务应用中,单纯的使用定时器,很有可能造成一些不可估量的后果。如果存在两个任务都定时为50ms,但是运行时间都是30ms。则会出现两个任务每30ms交替运行一次。一次循环就是60ms。和预期的50ms出现了不符。如果再极端一点,任务执行时间为100ms,则一次循环就是200ms,若任务优先级不一样,则时间一直会被高优先级的任务独占,低优先级的无法运行。

ESP32-Arduino开发框架中的定时器

本开发框架中需要定时执行的任务都是使用的Timer类,如Sensor类(传感器)就是使用Timer来定时获取传感器数据并传递到Application中进行后续处理。

例1中的LED闪烁,可直接调用Led类的Blink方法来实现,Blink方法内部使用Timer类来实现闪烁效果。


作业

1.修改示例程序使用硬件定时器(HwTimer)
2.在使用HwTimer时,观察任务执行时间超过定时间隔时的触发情况

- 本文由用户 老张 发布,文中观点仅代表作者本人,不代表本站立场。
- 如需转载,请联系作者;如有侵权,请联系本站处理。

02-17   阅读(3)   评论(0)
 标签: 编程 ESP32 FreeRTOS

涨知识
图灵测试

图灵测试的方法是:被测试人,和一个待测试的机器。测试时,测试人与被测试人是分开的,测试人只有以纯文本的方式向被测试人问一些问题,这些问题随便是什么问题都可以。问过一些问题后,如果测试人能够正确地分出谁是人谁是机器,那机器就没有通过图灵测试,如果测试人没有分出谁是机器谁是人,那这个机器就是有智能的。

评论:
相关文章
【ESP32 C++教程】Unit6-2 FreeRTOS多任务

本节主要讲解Task类,FreeRTOS多任务的使用。


【ESP32 C++教程】Unit5-2 执行器件之舵机

本节主要讲解舵机驱动类和用按键控制舵机。


【ESP32 C++教程】Unit5-1 执行器件之继电器

本节主要讲解执行器件类型和用按键控制继电器。


【ESP32 C++教程】Unit4-3 红外接收和遥控

本小节主要讲解红外接收和遥控器件,以及遥控操作LED。


【ESP32 C++教程】Unit4-2 模拟量传感器

本小节讲解模拟量传感器使用,旋转电位器,DHT11温湿度传感器和实现自定义传感器类。


【ESP32 C++教程】Unit4-1 数字量传感器

本小节讲解Sensor类及派生类、数字量传感器使用和传感器的推荐交互流程。


【ESP32 C++教程】Unit3-2 触摸输入

本小节讲解ESP32内置触摸引脚的用法,


【ESP32 C++教程】Unit3-1 按键输入

本小节主要介绍按键信号转换、Button类及派生类、和Button交互推荐流程。


【ESP32 C++教程】Unit2-2 Ws2812灯珠

本小节主要介绍Ws2812灯珠的使用、对父类进行扩展实现自定义功能,和指针向下强制转换的使用。


【ESP32 C++教程】Unit2-1 RGB三色LED

本小节主要介绍RGB三色LED的使用,以及多态的具体实现。