OpenHarmony定时器与点灯案例

1. Timer 定时器

1.1 定时器概述

定时器(Timer)是用于实现定时任务的组件,可在指定时间间隔后执行特定任务,支持一次性执行或周期性重复执行。其典型应用场景包括:

  • 智能设备的定时控制(如智能鱼缸定时喂食、灯光定时开关);
  • 周期性数据采集(如传感器定时上报数据);
  • 定时提醒(如耗材更换提醒、系统维护提示)。

在 OpenHarmony 中,定时器的时间周期以 "系统嘀嗒(tick)" 为单位,1 个嘀嗒默认对应 10ms,因此定时参数需根据实际需求转换为嘀嗒数(如 500ms = 50 个嘀嗒)。

1.2 定时器代码实现

以下代码演示了在 OpenHarmony 中使用定时器实现周期性任务的完整流程,包括定时器创建、启动、任务执行及销毁。

1.2.1 完整代码

cpp 复制代码
/* C语言标准库头文件 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* OpenHarmony相关头文件 */
#include "ohos_init.h"       // 系统初始化头文件
#include "cmsis_os2.h"       // 实时操作系统接口头文件
#include "hi_timer.h"        // 海思定时器头文件

/* 全局变量:记录定时任务执行次数 */
int task_count = 0;

/* 定时器ID:用于标识和操作定时器 */
osTimerId_t timer_id;

/* 函数声明:定时器任务函数(定时执行的目标内容) */
void timer_target(void *arg);

/* 线程函数:创建并启动定时器 */
void thread_timer(void *arg) {
    /* 
    创建定时器
    函数原型:osTimerId_t osTimerNew(
        osTimerFunc_t func,         // 定时器任务函数(定时执行的函数)
        osTimerType_t type,         // 定时器类型(osTimerPeriodic:周期性;osTimerOnce:一次性)
        void *argument,             // 传递给任务函数的参数(NULL表示无参数)
        const osTimerAttr_t *attr   // 定时器属性(NULL表示默认属性)
    )
    此处创建周期性定时器,任务函数为timer_target
    */
    timer_id = osTimerNew(timer_target, osTimerPeriodic, NULL, NULL);
    if (timer_id == NULL) {  // 检查定时器创建是否成功
        perror("[osTimerNew] create Timer Failed!");
        exit(1);
    }

    /* 
    启动定时器
    函数原型:osStatus_t osTimerStart(
        osTimerId_t timer_id,   // 目标定时器ID
        uint32_t ticks          // 定时周期(单位:系统嘀嗒,1 tick = 10ms)
    )
    此处设置定时周期为500ms(500ms / 10ms = 50 ticks)
    */
    osStatus_t status = osTimerStart(timer_id, 50);
    if (status != osOK) {  // 检查启动是否成功
        perror("[osTimerStart] start Timer Failed!");
        exit(status);
    }
}

/* 初始化任务:创建定时器线程 */
static void TimerTestTask(void) {
    /* 配置线程属性 */
    osThreadAttr_t timer_thread_attr;
    memset(&timer_thread_attr, 0, sizeof(osThreadAttr_t));
    timer_thread_attr.name = "Timer_thread";  // 线程名称
    timer_thread_attr.stack_size = 1024;       // 栈大小(1024字节)
    timer_thread_attr.priority = osPriorityNormal;  // 优先级(正常)

    /* 创建线程:用于执行定时器的创建和启动 */
    osThreadId_t timer_thread_id = osThreadNew(thread_timer, NULL, &timer_thread_attr);
    if (timer_thread_id == NULL) {
        perror("[osThreadNew] create thread [Timer_thread] failed!");
        exit(1);
    }
}

/* 注册初始化任务:系统启动时自动执行TimerTestTask */
APP_FEATURE_INIT(TimerTestTask);

/* 定时器任务函数:定时执行的内容(每500ms执行一次) */
void timer_target(void *arg) {
    // 打印当前任务执行次数
    printf("task_count : %d\n", task_count);
    task_count++;  // 次数加1

    // 当任务执行10次后,停止并销毁定时器
    if (task_count == 10) {
        /* 
        停止定时器
        函数原型:osStatus_t osTimerStop(osTimerId_t timer_id)
        */
        osStatus_t status = osTimerStop(timer_id);
        if (status != osOK) {
            perror("[osTimerStop] stop Timer Failed!");
            exit(status);
        }

        /* 
        销毁定时器
        函数原型:osStatus_t osTimerDelete(osTimerId_t timer_id)
        */
        status = osTimerDelete(timer_id);
        if (status != osOK) {
            perror("[osTimerDelete] delete Timer Failed!");
            exit(status);
        }
        printf("Timer stopped and deleted after 10 tasks.\n");
    }
}

2. GPIO 和点灯大师

2.1 1 Hi3861****实物图和原理图

2.2 Hi3861 内置GPIO控制方式

GPIO (General Purpose Input/Output) 即通用输入输出接口,是嵌入式系统和微控制器中最基 本、最常用的外设接口之一。它允许微控制器 MCU 与外部设备进行简单的数字信号交互。
在 OpenHarmony 中将所有的 GPIO 进行编号,提供了一个标准的【枚举类型】从 0 开始到 15
结束

2.3 Hi3861芯片OHOS****对外所有引脚的名称枚举

对应所在头文件是 #include "hi_io.h"

cpp 复制代码
/**
* @ingroup iot_io
*
* GPIO pin ID. CNcomment:IO硬件管脚编号。CNend
*/
typedef enum {
    HI_IO_NAME_GPIO_0, /**< GPIO0 */
    HI_IO_NAME_GPIO_1, /**< GPIO1 */
    HI_IO_NAME_GPIO_2, /**< GPIO2 */
    HI_IO_NAME_GPIO_3, /**< GPIO3 */
    HI_IO_NAME_GPIO_4, /**< GPIO4 */
    HI_IO_NAME_GPIO_5, /**< GPIO5 */
    HI_IO_NAME_GPIO_6, /**< GPIO6 */
    HI_IO_NAME_GPIO_7, /**< GPIO7 */
    HI_IO_NAME_GPIO_8, /**< GPIO8 */
    HI_IO_NAME_GPIO_9, /**< GPIO9 */
    HI_IO_NAME_GPIO_10, /**< GPIO10 */
    HI_IO_NAME_GPIO_11, /**< GPIO11 */
    HI_IO_NAME_GPIO_12, /**< GPIO12 */
    HI_IO_NAME_GPIO_13, /**< GPIO13 */
    HI_IO_NAME_GPIO_14, /**< GPIO14 */
    HI_IO_NAME_SFC_CSN, /**< SFC_CSN */
    HI_IO_NAME_SFC_IO1, /**< SFC_IO1 */
    HI_IO_NAME_SFC_IO2, /**< SFC_IO2 */
    HI_IO_NAME_SFC_IO0, /**< SFC_IO0 */
    HI_IO_NAME_SFC_CLK, /**< SFC_CLK */
    HI_IO_NAME_SFC_IO3, /**< SFC_IO3 */
    HI_IO_NAME_MAX,
} hi_io_name;

2.4 Hi3861芯片OHOS****针对于不同引脚功能控制枚举

对应所在头文件是 #include "hi_io.h"
以 GPIO7 引脚对应功能枚举为例
在 OHOS 中对所有引脚功能都有对应的枚举类型进行功能描述,后续代码中,只需要赋
值对应的枚举值,即可完成对应的切换功能。

cpp 复制代码
/**
* @ingroup iot_io
*
* GPIO_7 pin function.CNcomment:GPIO_7管脚功能。CNend
*/
typedef enum {
HI_IO_FUNC_GPIO_7_GPIO,
HI_IO_FUNC_GPIO_7_UART1_CTS_N = 2,
HI_IO_FUNC_GPIO_7_SPI0_RXD,
HI_IO_FUNC_GPIO_7_PWM0_OUT = 5,
HI_IO_FUNC_GPIO_7_I2S0_BCLK,
HI_IO_FUNC_GPIO_7_BT_ACTIVE,
} hi_io_func_gpio_7;

2.5 标准GPIO输入输出控制枚举类型

对应所在头文件是 #include "hi_gpio.h"
dir ==> direction 方向

cpp 复制代码
/**
* @ingroup iot_gpio
*
* I/O direction. CNcomment:GPIO方向。CNend
*/
typedef enum {
    HI_GPIO_DIR_IN = 0, /**< Input.CNcomment:输入方向CNend*/
    HI_GPIO_DIR_OUT /**< Output.CNcomment:输出方向CNend*/
} hi_gpio_dir;

2.6 IO****引脚对应的电平高低

对应所在头文件是 #include "hi_io.h"
HI_IO_PULL_UP 上拉 --> 高电平
HI_IO_PULL_DOWN 下拉 --> 低电平

cpp 复制代码
/**
* @ingroup iot_io
*
* GPIO pull-up configuration.CNcomment:IO上下拉功能CNend
*/
typedef enum {
    HI_IO_PULL_NONE, /**< Disabled.CNcomment:无拉CNend */
    HI_IO_PULL_UP, /**< Pull-up enabled.CNcomment:上拉CNend */
    HI_IO_PULL_DOWN, /**< Pull-down enabled.CNcomment:下拉CNend */
    HI_IO_PULL_MAX, /**< Invalid.CNcomment:无效值CNend */
} hi_io_pull;

2.7 点灯 LED3 Warm

2.7.1 原理图分析

2.7.2 LED****初始化函数

LED 初始化函数用于配置 LED 对应的 GPIO 引脚工作模式,确保 LED 能够正常响应高低电平控制。以下是针对 LED3(连接到 IO2 引脚)的初始化实现:

cpp 复制代码
/*
当前两个宏定义:
- LED_3_PIN:指定LED3对应的引脚为IO2
- LED_3_FUNC:指定IO2引脚的工作模式为GPIO模式
*/
#define LED_3_PIN HI_IO_NAME_GPIO_2
#define LED_3_FUNC HI_IO_FUNC_GPIO_2_GPIO

/**
 * LED3 对应 GPIO 初始化函数
 * 功能:配置IO2引脚为GPIO输出模式,为LED控制做准备
 */
void led_init(void)
{
    /* 1. 初始化GPIO模块 */
    /* 
    函数原型:hi_u32 hi_gpio_init(hi_void);
    作用:初始化OpenHarmony的GPIO模块,使能GPIO功能
    */
    hi_gpio_init();

    /* 2. 设置LED3对应IO2的工作模式为GPIO */
    /* 
    函数原型:hi_u32 hi_io_set_func(hi_io_name id, hi_u8 val);
    参数说明:
    - id:引脚编号(此处为LED_3_PIN,即IO2)
    - val:引脚功能(此处为LED_3_FUNC,即GPIO模式)
    作用:将IO2引脚从默认功能切换为GPIO功能,用于LED控制
    */
    hi_io_set_func(LED_3_PIN, LED_3_FUNC);

    /* 3. 设置IO2为GPIO输出模式 */
    /* 
    函数原型:hi_u32 hi_gpio_set_dir(hi_gpio_idx id, hi_gpio_dir dir);
    参数说明:
    - id:GPIO引脚编号(与hi_io_name一致,即IO2)
    - dir:方向模式(HI_GPIO_DIR_OUT表示输出模式)
    作用:配置IO2为输出模式,允许通过软件控制其输出高低电平
    */
    hi_gpio_set_dir(LED_3_PIN, HI_GPIO_DIR_OUT);

    /* 4. 设置IO2引脚为上拉模式(默认高电平) */
    /* 
    函数原型:hi_u32 hi_io_set_pull(hi_io_name id, hi_io_pull val);
    参数说明:
    - id:引脚编号(IO2)
    - val:上下拉模式(HI_IO_PULL_UP表示上拉,默认输出高电平)
    作用:确保引脚在未被主动控制时处于高电平状态,避免电平不确定
    */
    hi_io_set_pull(LED_3_PIN, HI_IO_PULL_UP);
}

2.7.3 LED 线程任务代码

LED 线程任务通过周期性切换 GPIO 引脚的高低电平,实现 LED 的闪烁效果。具体实现如下:

cpp 复制代码
/**
 * LED 灯线程任务代码
 * 功能:周期性切换LED3的亮灭状态(闪烁效果)
 */
void led_main(void *arg)
{
    // 1. 初始化LED对应的GPIO(调用上面定义的led_init函数)
    led_init();

    int n = 0;  // 计数变量,用于控制LED状态切换
    while (1)  // 无限循环,持续控制LED
    {
        if (n % 2)  // 当n为奇数时
        {
            /* 
            函数原型:hi_u32 hi_gpio_set_ouput_val(hi_gpio_idx id, hi_gpio_value val);
            参数说明:
            - id:GPIO引脚编号(IO2)
            - val:输出电平(HI_GPIO_VALUE0表示低电平)
            作用:设置IO2输出低电平,假设LED为低电平点亮,则此时LED亮
            */
            hi_gpio_set_ouput_val(LED_3_PIN, HI_GPIO_VALUE0);
        }
        else  // 当n为偶数时
        {
            /* 
            设置IO2输出高电平,假设LED为低电平点亮,则此时LED灭
            */
            hi_gpio_set_ouput_val(LED_3_PIN, HI_GPIO_VALUE1);
        }

        n += 1;  // 计数加1
        osDelay(100);  // 延迟100个系统嘀嗒(1个嘀嗒=10ms,即延迟1000ms=1秒)
    }
}

2.7.4 线程创建

通过创建线程来运行 LED 控制任务,确保 LED 闪烁功能在系统启动后自动执行:

cpp 复制代码
/**
 * 初始化任务:创建LED控制线程
 */
static void LedTestTask(void)
{
    // 配置线程属性
    osThreadAttr_t led_thread_attr;
    memset(&led_thread_attr, 0, sizeof(osThreadAttr_t));  // 初始化属性结构体
    led_thread_attr.name = "Led_thread";  // 线程名称(唯一标识)
    led_thread_attr.stack_size = 1024;    // 线程栈大小(1024字节)
    led_thread_attr.priority = osPriorityNormal;  // 线程优先级(正常优先级)

    /* 
    创建LED控制线程
    函数原型:osThreadId_t osThreadNew(osThreadFunc_t func, void *arg, const osThreadAttr_t *attr);
    参数说明:
    - func:线程入口函数(此处为led_main,即LED控制逻辑)
    - arg:传递给线程的参数(NULL表示无参数)
    - attr:线程属性(上面配置的led_thread_attr)
    作用:创建并启动线程,执行LED闪烁任务
    */
    osThreadId_t led_thread_id = osThreadNew(led_main, NULL, &led_thread_attr);
    if (led_thread_id == NULL)  // 检查线程创建是否成功
    {
        perror("[osThreadNew] create thread [Led_thread] failed!");
        exit(1);  // 创建失败则退出程序
    }
}

// 注册初始化任务,系统启动时自动执行LedTestTask
APP_FEATURE_INIT(LedTestTask);

https://github.com/0voice