ESP-32开发踩坑历程(三)ESP-IDF 定时器使用


前言

设计ESP32的乐鑫公司,是一家非常优秀的中国公司。ESP32价格实惠,功能强大。在这个领域可以说是国货之光。但是纵观国内开发环境,关于ESP32的本地开发资料却少之又少,基本上停留在点亮一个LED,输出一个helloword级别,缺乏深入的应用分享。反而国外的ESP32资料却较为丰富。因此我们跳过helloword和点灯,直接从定时器开始。


一、开发基础

FreeRTOS 内核耗能低,性能高和设计灵活,因此在乐鑫的物联网开发框架(ESP-IDF),ESP8266 和 ESP32 两款芯片的软件开发工具包(SDK)中就包含了 FreeRTOS 这一款免费的实时操作系统内核。

因此基于官方的SDK开发ESP32,需要一定的FreeRTOS基础。FreeRTOS 的教程丰富,这里不展开分析了。

二、定时器使用

1.官方描述

非常遗憾即便是中文页面这部分的描述也是英文的,这里分享一下我自己的理解。

https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32s2/api-reference/system/esp_timer.html

先是介绍了FreeRTOS提供的软件定时器的局限性:

1.定时器的分辨率不会超过系统滴答。

2.定时器的回调是在一个低优先级的任务中完成的。

硬件定时器也有局限性:

硬件定时器功能比较简单,触发的时候需要额外设置,将一些事件挂起。

中断优先级的设置:CONFIG_ESP_TIMER_INTERRUPT_LEVEL 默认1 ,最高3。

esp_timer提供了一系列的API,下一个章节分享几个常用的。

esp_timer性能:基于64位硬件定时器,微秒分辨率,一次性或周期模式(也就是自动重载)。

定时器回调方式,有两种:
1.ESP_TIMER_TASK
这个任务需要一个较高的优先级,以便及时将状态反馈出来。回调中最好不要处理具体工作,而是把相关数据传到队列里面,由优先级低一些的任务来处理。
如果其他优先级更高的任务正在运行,则这个任务会被延迟。
2.ESP_TIMER_ISR
需要使能CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD 开启。(默认关闭)。
直接在定时器中断中配置定时器回调。优点是延迟低。

定时器的启动需要时间,因此对于单次定时器:20us以下的,会在20us左右才会启动,对于周期定时器,要求周期不能小于50us。

2.esp_timer API介绍

代码如下(示例):

  • 定时器初始化
esp_err_tesp_timer_init(void)

在启动代码中调用。
返回值:
ESP_OK 成功

ESP_ERR_NO_MEM 分配失败

ESP_ERR_INVALID_STATE 已经初始化

其他错误。

  • 定时器结构体&句柄
typedef struct esp_timer *esp_timer_handle_t
struct esp_timer_create_args_t

创建之前首先需要一个这样的结构体和句柄,然后传给创建函数。
成员:
esp_timer_cb_t callback:回调函数
void *arg :传给回调函数的参数
esp_timer_dispatch_tdispatch_method:使用回调函数的方法:在任务中还是在中断中
const char *name: 定时器名字

  • 创建/删除定时器
esp_err_t esp_timer_create(constesp_timer_create_args_t *create_args, esp_timer_handle_t *out_handle)
esp_err_t esp_timer_delete(esp_timer_handle_t timer)

参数:
create_args:指向定时器结构体
[out] out_handle:输出量,将创建的定时器保存到句柄中
返回值:
ESP_OK 成功

ESP_ERR_INVALID_ARG 一般是没有定时器结构体

ESP_ERR_INVALID_STATE 没有初始化

ESP_ERR_NO_MEM 内存分配失败
可以看到这里返回的仅仅是状态,定时器的实体通过指针保存在了句柄中。
删除的时候只需要传入句柄即可,而且已经过期的一次性定时器不需要额外删除。

  • 开始/暂停定时器
esp_err_t esp_timer_start_once(esp_timer_handle_ttimer, uint64_t timeout_us)
esp_err_t esp_timer_start_periodic(esp_timer_handle_ttimer, uint64_t period)

开始有两个分别是开始一个周期定时器和单次定时器。
参数:
timer:创建好的定时器句柄
timeout_us:定时时间,单位是微秒
返回值:
ESP_OK 成功

ESP_ERR_INVALID_ARG 句柄无效

ESP_ERR_INVALID_STATE 定时器已经在运行

  • 获取定时器当前状态
int64_t esp_timer_get_time(void) 

返回的是定时器开始到现在的计数,单位是微秒。

还有一些其他的API,这里就不展开介绍了。可以再官方文档中看到。


三、代码

#include <stdio.h>
#include <stdint.h>
#include "esp_types.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "soc/timer_group_struct.h"
#include "driver/periph_ctrl.h"
#include "driver/timer.h"
#include "esp_timer.h"


//定时器回调函数定义
void timer_periodic_test (void *arg);
void timer_once_test (void *arg);

//定义两个定时器句柄
esp_timer_handle_t  timer_once_handle = 0;
esp_timer_handle_t  timer_periodic_handle = 0;
//单次定时器结构体
esp_timer_create_args_t test_once_arg = {
		.callback = &timer_once_test,//设置回调函数
		.arg = NULL,//没有参数
		.name = "test once timer"//名字
};
//周期定时器结构体
esp_timer_create_args_t test_periodic_arg = {
		.callback = &timer_periodic_test,//设置回调函数
		.arg = NULL,//没有参数
		.name = "test periodic timer"//名字
};

void timer_periodic_test (void *arg)
{
	int64_t tick = esp_timer_get_time();

	printf("method name: %s , time = %lld \r\n", __func__, tick);

	if(tick > 100000000)
	{
		//停止定时器
		esp_err_t err = esp_timer_stop(timer_periodic_handle);
		printf("Stop timer name :%s , Stop state: %s",test_periodic_arg.name, err == ESP_OK ? "ok!\r\n" : "failed!\r\n");
		//删除定时器
		err = esp_timer_delete(timer_periodic_handle);
		printf("Delete timer name :%s ,Delete state: %s",test_periodic_arg.name, err == ESP_OK ? "ok!\r\n" : "failed!\r\n");

	}
	//还是逃不出点灯
	gpio_set_level(2, 0);
	//延迟
	vTaskDelay(1000 / portTICK_PERIOD_MS);
	//高电平
	gpio_set_level(2, 1);
	//延迟
	vTaskDelay(1000 / portTICK_PERIOD_MS);

}

void timer_once_test (void *arg)
{
	int64_t tick = esp_timer_get_time();

	printf("Timer method name: %s , time = %lld \r\n", __func__, tick);

		//关闭定时器
	//esp_err_t err = esp_timer_stop(timer_once_handle);

	//printf("Stop timer name :%s , Stop state: %s",test_once_arg.name, err == ESP_OK ? "ok!\r\n" : "failed!\r\n");
		//删除定时器
	esp_err_t err = esp_timer_delete(timer_once_handle);
	printf("Delete timer name :%s ,Delete state: %s",test_once_arg.name, err == ESP_OK ? "ok!\r\n" : "failed!\r\n");


}


void app_main(void)
{

	gpio_pad_select_gpio(2);
	gpio_set_direction(2, GPIO_MODE_OUTPUT);

	//开始单次定时器
	esp_err_t err = esp_timer_create(&test_once_arg , &timer_once_handle);
	err = esp_timer_start_once(timer_once_handle , 10*1000*1000);
	printf("one time :%s", err == ESP_OK ? "OK!\r\n" : "failed!\r\n");
	//开始周期定时器
	err = esp_timer_create(&test_periodic_arg , &timer_periodic_handle);
	err = esp_timer_start_periodic(timer_periodic_handle , 1000*1000);
	printf("perodic time :%s", err == ESP_OK ? "OK!\r\n" : "failed!\r\n");

}

效果:

在这里插入图片描述
在这里插入图片描述
可以看到,单次任务计时达到要求后输出一次,周期任务每次+2,直到达到目标值,手动删除。

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值