ESP32移植Openharmony设备开发---(4)Timer定时器

Timer内核定时器

官方文档:OpenAtom OpenHarmony

所需头文件:los_swtmr.h

头文件所在位置:

基本概念:

软件定时器

软件定时器,是基于系统Tick时钟中断且由软件来模拟的定时器,当经过设定的Tick时钟计数值后会触发用户定义的回调函数。定时精度与系统Tick时钟的周期有关。

硬件定时器受硬件的限制,数量上不足以满足用户的实际需求,因此为了满足用户需求,提供更多的定时器,OpenHarmony LiteOS-M内核提供软件定时器功能。软件定时器扩展了定时器的数量,允许创建更多的定时业务。

软件定时器功能上支持:

  • 静态裁剪:能通过宏关闭软件定时器功能。
  • 软件定时器创建。
  • 软件定时器启动。
  • 软件定时器停止。
  • 软件定时器删除。
  • 软件定时器剩余Tick数获取。
定时器状态
  • OS_SWTMR_STATUS_UNUSED(未使用) 系统在定时器模块初始化的时候将系统中所有定时器资源初始化成该状态。
  • OS_SWTMR_STATUS_CREATED(创建未启动/停止) 在未使用状态下调用LOS_SwtmrCreate接口或者启动后调用LOS_SwtmrStop接口后,定时器将变成该状态。
  • OS_SWTMR_STATUS_TICKING(计数) 在定时器创建后调用LOS_SwtmrStart接口,定时器将变成该状态,表示定时器运行时的状态。
定时器模式

OpenHarmony LiteOS-M内核的软件定时器提供三类定时器机制:

  • 第一类是单次触发定时器,这类定时器在启动后只会触发一次定时器事件,然后定时器自动删除。
  • 第二类是周期触发定时器,这类定时器会周期性的触发定时器事件,直到用户手动地停止定时器,否则将永远持续执行下去。
  • 第三类也是单次触发定时器,但与第一类不同之处在于这类定时器超时后不会自动删除,需要调用定时器删除接口删除定时器。

BUILD.gn

因为用到了liteos-m的软件定时器,所以需要引用liteos-m内核库,具体可以参考上节课程中的BUILD.gn

LOS_Timer_example.c

#include "los_swtmr.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "los_task.h"
#include "ohos_run.h"

void Timer1_Callback(uint32_t arg);
void Timer2_Callback(uint32_t arg);

#define LOSCFG_BASE_CORE_SWTMR_ALIGN = 1
#define TASK_PRIO 5


UINT32 g_timercount1 = 0;
UINT32 g_timercount2 = 0;
UINT32 g_testTaskId01;


void Timer1_Callback(uint32_t arg) // 回调函数1
{
    unsigned long tick_last1;
    g_timercount1++;
    tick_last1=(UINT32)LOS_TickCountGet(); // 获取当前Tick数
    printf("g_timercount1=%d\n",g_timercount1);
    printf("tick_last1=%d\n",tick_last1);
}

void Timer2_Callback(uint32_t arg) // 回调函数2
{
    unsigned long tick_last2;
    tick_last2=(UINT32)LOS_TickCountGet();
    g_timercount2 ++;
    printf("g_timercount2=%d\n",g_timercount2);
    printf("tick_last2=%d\n",tick_last2);
}

void Timer_example(void)
{
    UINT32 id1;
    UINT32 id2; // timer id
    UINT32 uwTick;

    /* 创建单次软件定时器,Tick数为1000,启动到1000Tick数时执行回调函数1 */
    LOS_SwtmrCreate (1000, LOS_SWTMR_MODE_ONCE, Timer1_Callback, &id1, 1, OS_SWTMR_ROUSES_ALLOW, OS_SWTMR_ALIGN_INSENSITIVE);
    
    /* 创建周期性软件定时器,每100Tick数执行回调函数2 */
    LOS_SwtmrCreate(100, LOS_SWTMR_MODE_PERIOD, Timer2_Callback, &id2, 1, OS_SWTMR_ROUSES_ALLOW, OS_SWTMR_ALIGN_INSENSITIVE);
    printf("create Timer1 success\n");

    LOS_SwtmrStart (id1); //启动单次软件定时器
    printf("start Timer1 success\n");
    LOS_TaskDelay(200); // 延时200Tick数
    LOS_SwtmrTimeGet(id1, &uwTick); // 获得单次软件定时器剩余Tick数
    printf("uwTick =%d\n", uwTick);

    LOS_SwtmrStop(id1); // 停止软件定时器
    printf("stop Timer1 success\n");
    

    LOS_SwtmrStart(id1);
    LOS_TaskDelay(1000);
    LOS_SwtmrDelete(id1); // 删除软件定时器
    printf("delete Timer1 success\n");

    LOS_SwtmrStart(id2); // 启动周期性软件定时器
    printf("start Timer2\n");

    LOS_TaskDelay(1000);
    LOS_SwtmrStop(id2);
    LOS_SwtmrDelete(id2);
}

UINT32 Example_TaskEntry(VOID)
{
    UINT32 ret;
    TSK_INIT_PARAM_S task1;
    /* 锁任务调度 */
    LOS_TaskLock();

    /* 创建任务1 */
    ret = memset_s(&task1, sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S));
    if (ret != 0) {
        printf("memset_s fail!\r\n");
    }
    task1.pfnTaskEntry = (TSK_ENTRY_FUNC)Timer_example;
    task1.pcName = "TimerTsk";
    task1.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
    task1.usTaskPrio = TASK_PRIO;
    ret = LOS_TaskCreate(&g_testTaskId01, &task1);
    if (ret != LOS_OK) {
        printf("TimerTsk create failed.\n");
        return LOS_NOK;
    }

    /* 解锁任务调度 */
    LOS_TaskUnlock();

    return LOS_OK;
}

OHOS_APP_RUN(Example_TaskEntry);

代码分析:

  • 首先定义了用于保存定时器ID的变量id1id2,以及用于获取定时器剩余时间的变量uwTick
  • 接下来,创建了一个单次运行的软件定时器(id1),设置其在1000个时钟滴答后触发Timer1_Callback
  • 创建了一个周期性运行的软件定时器(id2),设置其每隔100个时钟滴答就触发Timer2_Callback
  • 启动定时器id1并延时200个时钟滴答后获取其剩余时间,并打印出来。
  • 停止并重新启动定时器id1,等待1000个时钟滴答后删除定时器id1
  • 启动周期性定时器id2,等待1000个时钟滴答后停止并删除定时器id2

编译并烧录

修改工作文件夹级的BUILD.gn文件,将编译的文件夹指定为新建的003_LOS_Timer

在源码根目录下使用hb工具对写好的代码进行编译

选择mini级系统

同理 产品选择esp公司下的esp32

选择完毕后在源码根目录下执行hb build -f 进行编译

编译完成后会有如下界面,并且编译后的代码固件位于:out\esp32\esp32

验证结果

打开串口工具->选择COM5->打开串口

按下ESP32开发板上的EN键,即可观察到实验现象:

API参考

LOS_SwtmrCreate()

LOS_SwtmrCreate(UINT32 interval,
                  UINT8 mode,
                  SWTMR_PROC_FUNC handler,
                  UINT32 *swtmrID,
                  UINT32 arg);

描述:

创建一个一次性或周期性计时器,并将其与一个带参数的回调函数相关联。计时器在LOS_SwtmrStart启动之前一直处于停止状态。可以在RTOS启动(调用 LOS_Start)之前安全地调用该函数,但不能在内核初始化 (调用 LOS_KernelInit)之前调用该函数。

注意 :不能在中断服务调用该函数

参数:

名字

描述

interval

要创建的软件计时器的定时时长,单位ms

mode

定时器类型,课参考EnSwTmrType,包括单次定时器、周期性定时器以及单次定时器但不删除定时器三种类型

handler

函数指针指向回调函数

swtmrID

定时器ID

argument

定时器回调函数的参数

arg

回调函数handler的入参

LOS_SwtmrStart()

UINT32 LOS_SwtmrStart(UINT32 swtmrID);

描述:

启动或重新启动指定参数swtmrID的计时器。

注意 :不能在中断服务调用该函数

参数:

名字

描述

swtmrID

由osTimerNew获得的计时器ID

LOS_SwtmrStop()

UINT32 LOS_SwtmrStop(UINT32 swtmrID);

描述:

暂停指定参数swtmrID的计时器。

参数:

名字

描述

swtmrID

由LOS_SwtmrCreate获得的计时器ID

LOS_SwtmrDelete()

UINT32 LOS_SwtmrDelete(UINT32 swtmrID);

描述:

删除指定参数swtmrID的计时器。

参数:

名字

描述

swtmrID

由LOS_SwtmrCreate获得的计时器ID

ESP32-C3定时器开发文档 ESP32-C3是一款低功耗、高性能的Wi-Fi和蓝牙双模SoC,它集成了多个定时器,可以用于各种应用场景,如PWM控制、定时中断、计时等。本文将介绍ESP32-C3定时器的使用方法。 一、ESP32-C3定时器的类型 ESP32-C3有四种类型的定时器,分别为:通用定时器、高精度定时器、看门狗定时器、RTC定时器。 1.通用定时器 ESP32-C3有两个通用定时器,可以用于PWM控制、定时中断、计时等。这两个定时器分别为定时器0和定时器1,每个定时器有16位的自由运行计数器和一个16位的比较器。通用定时器的主要特点如下: - 可以配置为定时器或计数器 - 支持自由运行计数器和比较器 - 支持自动重载计数器 - 支持PWM输出 - 支持定时中断 2.高精度定时器 ESP32-C3有一个高精度定时器,可以用于需要高精度计时的应用场景。这个定时器是单向计时器,可以自由设置计时周期和定时中断时间。高精度定时器的主要特点如下: - 可以设置自由运行计数器的计数周期 - 可以设置定时中断时间 - 支持单向计时模式 - 支持自动重载计数器 3.看门狗定时器 ESP32-C3有一个看门狗定时器,可以用于应用程序中的异常保护。当应用程序出现异常时,看门狗定时器会自动重启系统。看门狗定时器的主要特点如下: - 可以设置看门狗定时器的计数周期 - 支持看门狗定时器中断 - 支持自动重载计数器 4.RTC定时器 ESP32-C3有一个RTC定时器,可以用于实时时钟应用。RTC定时器的主要特点如下: - 可以设置RTC计数器的计数周期 - 支持RTC定时器中断 - 支持自动重载计数器 二、ESP32-C3定时器的使用步骤 1.初始化定时器 在使用定时器前,需要先初始化定时器。以定时器0为例,初始化代码如下: // 配置定时器0的参数 timer_config_t timer_cfg = { .divider = 16, // 定时器分频系数 .counter_dir = TIMER_COUNT_UP, // 定时器计数器方向 .counter_en = TIMER_PAUSE, // 定时器计数器是否启动 .alarm_en = TIMER_ALARM_EN, // 定时器报警是否启动 .auto_reload = TIMER_AUTORELOAD_EN, // 定时器是否自动重载 .counter_bit_width = TIMER_DATA_WIDTH_16BIT, // 定时器计数器位宽 .alarm_value = 10000, // 定时器报警值 }; // 初始化定时器0 timer_init(TIMER_GROUP_0, TIMER_0, &timer_cfg); 2.启动定时器 初始化定时器后,需要启动定时器才能开始计时。以定时器0为例,启动代码如下: timer_start(TIMER_GROUP_0, TIMER_0); 3.设置定时器中断 如果需要在定时器到达一定时间后触发中断,则需要设置定时器中断。以定时器0为例,设置中断代码如下: // 配置定时器0中断参数 timer_isr_t isr_cfg = { .func = timer0_isr, // 定时器中断处理函数 .arg = NULL, // 中断处理函数参数 }; // 注册定时器0中断 timer_isr_register(TIMER_GROUP_0, TIMER_0, &isr_cfg, NULL, 0); 4.停止定时器 如果需要停止定时器,则可以使用以下代码: timer_pause(TIMER_GROUP_0, TIMER_0); 5.重启定时器 如果定时器已经停止,则可以使用以下代码重启定时器timer_start(TIMER_GROUP_0, TIMER_0); 6.设置定时器报警值 如果需要定时器到达一定时间后触发报警,则需要设置定时器报警值。以定时器0为例,设置报警值代码如下: timer_set_alarm_value(TIMER_GROUP_0, TIMER_0, 20000); 7.获取定时器计数器值 如果需要获取定时器的计数器值,则可以使用以下代码: uint64_t timer_value; timer_get_counter_value(TIMER_GROUP_0, TIMER_0, &timer_value); 8.清除定时器中断标志 当定时器中断被触发后,需要清除中断标志才能继续触发中断。以定时器0为例,清除中断标志代码如下: timer_group_clr_intr_status(TIMER_GROUP_0, TIMER_0); 三、ESP32-C3定时器应用示例 以下是一个使用定时器0实现PWM输出的示例代码: #include "driver/timer.h" #define LED_PIN 2 #define PWM_FREQ 1000 #define PWM_MAX_DUTY 1023 void timer0_isr(void *arg) { timer_group_clr_intr_status(TIMER_GROUP_0, TIMER_0); static uint32_t pwm_count = 0; static uint16_t pwm_duty = 0; pwm_count++; if (pwm_count >= (1000000 / PWM_FREQ)) { pwm_count = 0; pwm_duty++; if (pwm_duty > PWM_MAX_DUTY) { pwm_duty = 0; } timer_set_alarm_value(TIMER_GROUP_0, TIMER_0, (1000000 / PWM_FREQ)); timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0); ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, pwm_duty); ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0); } } void app_main() { // 初始化LED gpio_pad_select_gpio(LED_PIN); gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT); gpio_set_level(LED_PIN, 0); // 配置定时器0的参数 timer_config_t timer_cfg = { .divider = 16, // 定时器分频系数 .counter_dir = TIMER_COUNT_UP, // 定时器计数器方向 .counter_en = TIMER_PAUSE, // 定时器计数器是否启动 .alarm_en = TIMER_ALARM_EN, // 定时器报警是否启动 .auto_reload = TIMER_AUTORELOAD_EN, // 定时器是否自动重载 .counter_bit_width = TIMER_DATA_WIDTH_16BIT, // 定时器计数器位宽 .alarm_value = (1000000 / PWM_FREQ), // 定时器报警值 }; // 初始化定时器0 timer_init(TIMER_GROUP_0, TIMER_0, &timer_cfg); // 注册定时器0中断 timer_isr_t isr_cfg = { .func = timer0_isr, // 定时器中断处理函数 .arg = NULL, // 中断处理函数参数 }; timer_isr_register(TIMER_GROUP_0, TIMER_0, &isr_cfg, NULL, 0); // 设置PWM输出 ledc_timer_config_t ledc_timer_cfg = { .duty_resolution = LEDC_TIMER_10_BIT, // PWM位宽 .freq_hz = PWM_FREQ, // PWM频率 .speed_mode = LEDC_LOW_SPEED_MODE, // PWM速度模式 .timer_num = LEDC_TIMER_0, // PWM定时器号 .clk_cfg = LEDC_AUTO_CLK, // PWM时钟配置 }; ledc_timer_config(&ledc_timer_cfg); ledc_channel_config_t ledc_channel_cfg = { .channel = LEDC_CHANNEL_0, // PWM通道号 .duty = 0, // PWM占空比 .gpio_num = LED_PIN, // PWM输出引脚 .speed_mode = LEDC_LOW_SPEED_MODE, // PWM速度模式 .timer_sel = LEDC_TIMER_0, // PWM定时器号 }; ledc_channel_config(&ledc_channel_cfg); ledc_fade_func_install(0); // 启动定时器0 timer_start(TIMER_GROUP_0, TIMER_0); }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值