ESP32 IDF开发 驱动篇⑥定时器的使用方法
- 1、博主写这篇技术文章的目的:
- 2、Timer库的介绍
- 3、软件设计
- 4、实例分析
- 5、以下是调试的结果:
别迷路-导航栏
快速导航找到你想要的(文章目录)
此篇文章如果对你有用,请点赞收藏,您的支持就是博主坚持的动力。
1、博主写这篇技术文章的目的:
(1)熟悉掌握esp_timer相关API;
(2)掌握esp32 timer的使用方法;
2、Timer库的介绍
有关Timer相关函数请参考esp-idf\components\esp_common\include\esp_timer.h
Timer库也比较简单主要分3步:
配置定时器、设置定时器回调函数、启动定时器。
/**
* @brief 初始化esp_timer库
* @return
*-ESP_OK成功
*-ESP_ERR_NO_MEM,如果分配失败
*-ESP_ERR_INVALID_STATE(如果已初始化)
*-来自中断分配器的其他错误
*/
esp_err_t esp_timer_init(void);
/**
* @brief 取消初始化esp_timer库
* @note 通常不应从应用程序中调用此函数
* @return
*-ESP_OK成功
*-ESP_ERR_INVALID_STATE(如果尚未初始化)
*/
esp_err_t esp_timer_deinit(void);
/**
* @brief 创建一个esp_timer实例
* @note 使用计时器完成后,请使用esp_timer_delete函数将其删除。
* @param create_args指向带有计时器创建参数的结构的指针。
*不是由库保存的,可以在堆栈上分配。
* @param [out] out_handle输出,指向esp_timer_handle_t变量的指针
*将保留创建的计时器句柄。
* @return
*-ESP_OK成功
*-如果某些create_args无效,则为ESP_ERR_INVALID_ARG
*-如果尚未初始化esp_timer库,则为ESP_ERR_INVALID_STATE
*-如果内存分配失败,则为ESP_ERR_NO_MEM
*/
esp_err_t esp_timer_create(const esp_timer_create_args_t* create_args,
esp_timer_handle_t* out_handle);
/**
* @brief 启动单发计时器
*调用此函数时,计时器不应运行。
* @param timer使用esp_timer_create创建的计时器句柄
* @param timeout_us计时器超时,以微秒为单位,相对于当前时刻
* @return
*-ESP_OK成功
*-ESP_ERR_INVALID_ARG(如果句柄无效)
*-ESP_ERR_INVALID_STATE,如果计时器已经在运行
*/
esp_err_t esp_timer_start_once(esp_timer_handle_t timer, uint64_t timeout_us);
/**
* @brief 启动定期计时器
*调用此函数时,计时器不应运行。 该功能将
*启动计时器,它将触发每个“周期”微秒。
* @param timer使用esp_timer_create创建的计时器句柄
* @param period计时器周期,以微秒为单位
* @return
*-ESP_OK成功
*-ESP_ERR_INVALID_ARG(如果句柄无效)
*-ESP_ERR_INVALID_STATE,如果计时器已经在运行
*/
esp_err_t esp_timer_start_periodic(esp_timer_handle_t timer, uint64_t period);
/**
* @brief 停止计时器
*此功能停止使用esp_timer_start_once启动的计时器
*或esp_timer_start_periodic。
* @param timer使用esp_timer_create创建的计时器句柄
* @return
*-ESP_OK成功
*-ESP_ERR_INVALID_STATE(如果计时器未运行)
*/
esp_err_t esp_timer_stop(esp_timer_handle_t timer);
/**
* @brief 删除esp_timer实例
*删除前必须停止计时器。 一次性计时器已过期
*无需停止。
* @param timer使用esp_timer_create分配的计时器句柄
* @return
*-ESP_OK成功
*-ESP_ERR_INVALID_STATE(如果计时器未运行)
*/
esp_err_t esp_timer_delete(esp_timer_handle_t timer);
/**
* @brief 自启动以后获取时间(以微秒为单位)
* @return 自调用esp_timer_init以来的微秒数(通常为
*发生在应用程序启动的之前)。
*/
int64_t esp_timer_get_time(void);
/**
* @brief 获取预计下次超时的时间戳
* @return 最近的计时器事件的时间戳,以微秒为单位。
*时基与esp_timer_get_time返回的值相同。
*/
int64_t esp_timer_get_next_alarm(void);
3、软件设计
首先我们来分析一下定时器的结构体:
/**
* @brief 定时器配置结构体
*/
typedef struct {
esp_timer_cb_t callback; //定时器计数到了,执行的回调函数
void* arg; //传递给回调的参数
esp_timer_dispatch_t dispatch_method; //从任务或ISR调用回调函数
const char* name; //定时器名称 用于esp_timer_dump函数
} esp_timer_create_args_t;
从上面的结构我们可以看出要实现一个简单的定时器只需要给callback赋值一个回调函数指针和定时器名字name即可
const esp_timer_create_args_t periodic_timer_args =
{
.callback = &periodic_timer_callback,//定时器计数到了,执行的回调函数
.name = “periodic_timer” //定时器名称
};
创建定时器结构体之后还需要一个存储定时器句柄的结构体:
struct esp_timer {
uint64_t alarm;
uint64_t period;
union {
esp_timer_cb_t callback;
uint32_t event_id;
};
void* arg;
#if WITH_PROFILING
const char* name;
size_t times_triggered;
size_t times_armed;
uint64_t total_callback_run_time;
#endif // WITH_PROFILING
LIST_ENTRY(esp_timer) list_entry;
};
主要看period表示:定时的时间,Callback:存储回调函数,arg:回调函数的参数,知道了这个结构体,我们就可以开始:
第一步创建定时器:主要实现的功能就是为Timer结构体分配内存初始化参数:
typedef struct esp_timer* esp_timer_handle_t;
esp_err_t esp_timer_create(const esp_timer_create_args_t* args, esp_timer_handle_t* out_handle)
{
esp_timer_handle_t result = (esp_timer_handle_t) calloc(1, sizeof(*result));//第一步给esp_timer结构体分配内存
result->callback = args->callback;//传递回调函数,及参数,名字
result->arg = args->arg;
result->name = args->name;
*out_handle = result;//最后导入到句柄结构体中esp_timer
}
第二步:为定时器设置定时器时间,并启动,主要实现的功能就是为esp_timer结构体赋值alarm参数和period参数
esp_err_t IRAM_ATTR esp_timer_start_periodic(esp_timer_handle_t timer, uint64_t period_us)
{
timer->alarm = esp_timer_get_time() + period_us;//设置多少us之后调用回调函数
timer->period = period_us; //传递定时时间,单位us
}
4、实例分析
复制上一个工程改名字为 idf_timer_example即可文件名字改为 idf_timer_example.C makefile文件也改成 PROJECT_NAME := idf_timer_example即可,然后复制一下代码测试。
/**********************************************************************
* 文件名: idf_timer_example.c
* 创建人:
* 创建日期:
* 修改人:
* 修改日期:
* 版本号: V1.1
* 备注:
* 公司:
********************************************************************/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "esp_timer.h"
#include "esp_log.h"
#include "esp_sleep.h"
#include "sdkconfig.h"
/***************************************************
* 创建两个计时器:
* 1.一个定期计时器,它将每0.5s运行一次,并显示一条消息
* 2.一键式定时器,将在5秒后触发,并定期重新启动
*计时器,周期为1秒。
*****************************************************/
static void periodic_timer_callback(void* arg);
static void oneshot_timer_callback(void* arg);
static const char* TAG = "timer";
/***********************************************************************
* 函数:
* 描述: 主函数
* 参数:
* 返回: 无
* 备注:
************************************************************************/
void app_main(void)
{
const esp_timer_create_args_t periodic_timer_args =
{
.callback = &periodic_timer_callback,//定时器计数到了,执行的回调函数
.name = "periodic_timer" //定时器名称
};
esp_timer_handle_t periodic_timer;//定时器指针句柄,创建后获得定时器的句柄,类似以打开文件句柄或创建系统任务句柄
//创建一个esp_timer
ESP_ERROR_CHECK(esp_timer_create(&periodic_timer_args, &periodic_timer));
const esp_timer_create_args_t oneshot_timer_args =
{
.callback = &oneshot_timer_callback,
//指定参数,周期定时器句柄,方便在oneshot_timer_callback定时器中拿到periodic_timer句柄
.arg = (void*) periodic_timer,
.name = "one-shot"
};
esp_timer_handle_t oneshot_timer;//定时器指针句柄,创建后获得定时器的句柄,类似以打开文件句柄或创建系统任务句柄
ESP_ERROR_CHECK(esp_timer_create(&oneshot_timer_args, &oneshot_timer));
//启动定时
ESP_ERROR_CHECK(esp_timer_start_periodic(periodic_timer, 500000));//周期定时,以微秒为单位,定时0.5s
ESP_ERROR_CHECK(esp_timer_start_once(oneshot_timer, 5000000));//定时一次,以微秒为单位,定时5s
ESP_LOGI(TAG, "current counting time: %lld us", esp_timer_get_time());
usleep(10000000);
//delete之前一定要先stop
ESP_ERROR_CHECK(esp_timer_stop(periodic_timer));
ESP_ERROR_CHECK(esp_timer_delete(periodic_timer));
ESP_ERROR_CHECK(esp_timer_delete(oneshot_timer));
ESP_LOGI(TAG, "Stopped and deleted timers");
}
/***********************************************************************
* 函数:
* 描述: 周期回调函数
* 参数:
* 返回: 无
* 备注:
************************************************************************/
static void periodic_timer_callback(void* arg)
{
int64_t time = esp_timer_get_time();
ESP_LOGI(TAG, "Periodic timer: %lld us", time);
}
/***********************************************************************
* 函数:
* 描述: 定时一次,再次设置定时器
* 参数:
* 返回: 无
* 备注:
************************************************************************/
static void oneshot_timer_callback(void* arg)
{
int64_t time_since_boot = esp_timer_get_time();
ESP_LOGI(TAG, "One-shot timer: %lld us", time_since_boot);
esp_timer_handle_t periodic_timer_handle = (esp_timer_handle_t) arg;
//重新设置定时器时间
ESP_ERROR_CHECK(esp_timer_stop(periodic_timer_handle));
ESP_ERROR_CHECK(esp_timer_start_periodic(periodic_timer_handle, 1000000));
time_since_boot = esp_timer_get_time();
ESP_LOGI(TAG, "Restarted periodic timer : %lld us",time_since_boot);
}
5、以下是调试的结果: