单次定时器和循环定时器

作者:陈之敏 时间:2020年08月15日 关键字:csdk、RDA8910、二次开发、定时器 ## 目录

点击这里查看所有博文

  最近听说有的小伙伴看了我的教程后,有一些问题都跑到官方的gitee上面去问去了。导致官方的人没搞懂问的是啥,小伙伴们也没能知道自己想要的答案。给大家造成了困扰,这里我说声抱歉。

  既然出现了这个问题,我这里就声明一下,本系列教程所涉及的内容(demo)不是官方的作品。我个人觉得官方的demo内容太多太全,往往都是把一个模块内所有的东西全部放在一起。这样的话对新手不是很友好,阅读起来也比较费劲。我就把官方的部分demo进行相关的简化,并推出教程这样的话可能会对新手朋友们有一定的帮助。

  有时候周末闲暇时间我也会加上一些我觉得好玩的模块在里面,这些可能在官方的demo都没有,比如cJSON、PAHO-MQTT、http-client。

  这就是官方的代码仓库。

git clone --recursive https://gitee.com/openLuat/Luat_CSDK_Air724U.git

  当然各位小伙伴在看本教程时,我建议还是使用我下面提供的仓库比较好,看完之后在迁移到官方的仓库⇧。

git clone --recursive https://gitee.com/chenxiahuaxu/RDA8910_CSDK.git

  再看本教程过程中如果遇到了问题,可以在本人的代码仓库下面评论。也可以在本人的博客下面评论。我要是看到的话,并且这个问题在我的能力范围的话我会尽力解答的(非官方,不要对我要求太多哦,要求太多我可能就不管啦)。

一、前言

1.1、那么什么是定时器呢?

  定时器通俗的讲就相当于一个闹钟。   我们的手机里面都有闹钟,每天早上负责叫醒我们上班不要迟到。只需要在手机里面设置好时间,然后可以按需选择每天都响,或者选择只响一次。作用是打断正在做的事(睡觉)。 在这里插入图片描述   在单片机系统中也是有定时器的。而定时器又分为两种,分别是硬件定时器和软件定时器。

  不管是51系列、stm32系列、avr系列、亦或是其他的单片机,它们在裸机编程上都是拥有硬件定时器的,一般硬件定时器数量有几个到十几个不等,占用的是硬件资源。 * 硬件定时器:一般硬件定时器集成在CPU的内部,有的可以使用外置的硬件定时器芯片,其使用的时基是系统内部的高速时钟源经过分频得到的,高速时钟源又是由外部晶振倍频得到,精度非常高,但是依赖硬件,能使用的数量有限。

  如果涉及到了操作系统编程,软件定时器那就必不可少。 * 软件定时器:一般软件定时器都是利用的是操作系统的延时阻塞性,在定时器启动和触发时记录下当前的系统时间,每隔一个时间片去查询下有没有到指定的定时时间,有的话就触发软件回调,精度相对硬件定时器要差得多,理论上是没有使用数量限制的,内存有多大,定时器就可以有多少。

二、编写测试程序

  RDA8910的CSDK并非是采用裸机编程,而是采用实时操作系统框架编程,上面前言说了既然涉及到了RTOS那sdk里面一般都会提供了软件定时器,那我们何不一起来看看呢。   使用定时器的驱动需要包含#include "am_openat.h""头文件,我们这里只用到了五个函数,分别是: >/**创建定时器 @param pFunc: 定时回调函数@param pParameter: 作为参数传递给定时器到时处理函数 *@return HANDLE:定时器句柄 **/

  • HANDLE OPENAT_create_timer( PTIMER_EXPFUNC pFunc,PVOID pParameter);

/**启动单次运行定时器 @param hTimer: 定时器句柄,create_timer接口返回值@param nMillisecondes: 定时器时间 *@return TRUE: 成功      FALSE: 失败 **/

  • BOOL OPENAT_start_timer0( HANDLE hTimer,UINT32 nMillisecondes);

/**启动循环运行定时器 @param hTimer: 定时器句柄,create_timer接口返回值@param nMillisecondes: 定时器时间 *@return TRUE: 成功      FALSE: 失败 **/

  • BOOL OPENAT_loop_start_timer( HANDLE hTimer,UINT32 nMillisecondes);

/停止定时器 @param hTimer: 定时器句柄,create_timer接口返回值@return TRUE: 成功      FALSE: 失败/

  • BOOL OPENAT_stop_timer(HANDLE hTimer );

/删除定时器 @param hTimer: 定时器句柄,create_timer接口返回值@return TRUE: 成功      FALSE: 失败/

  • BOOL OPENAT_delete_timer(HANDLE hTimer);

2.1、编写主程序

  因为需要手动打开日志输出,在主程序中我选择在程序启动后让系统休眠一段时间,让我们不至于错过输出到日志信息。休眠时间到后会自动创建一个测试任务。

//main函数
int appimg_enter(void *param)
{
    //系统休眠
    iot_os_sleep(10000);
    //创建一个任务
    osiThreadCreate("TestTask", TestTask, NULL, OSI_PRIORITY_NORMAL, 2048, 0);
    return 0;
}

2.2、编写测试任务

  在测试任务中,这里创建了两个定时器,一个是单次运行定时器,一个是循环定时器。   多个定时器可以公用一个定时回调函数通过创建定时器时传入的参数区分当前触发的中断是定时器几,也可以使用多个回调函数。   创建完成之后分别调用启动单次定时器函数OPENAT_start_timer0,和启动循环定时器函数OPENAT_loop_start_timer让定时器开始运行   创建完成之后调用线程离开函数osiThreadExit,系统会在合适的时间回收任务所占用的资源。

static void TestTask(void *param)
{
    //创建两个个定时器
    timer1 = OPENAT_create_timer(timer_handle, "timer1");
    while (timer1 == NULL)
    {
        iot_debug_print("timer1 create FALSE");
        osiThreadSleep(1000);
    }

    timer2 = OPENAT_create_timer(timer_handle, "timer2");
    while (timer2 == NULL)
    {
        iot_debug_print("timer2 create FALSE");
        osiThreadSleep(1000);
    }

    //启动定时器1,为单次定时器
    //单次定时器运行结束后,会自动停止并且删除定时器
    BOOL err = OPENAT_start_timer0(timer1, 1000 * 10);
    while (!err)
    {
        iot_debug_print("timer1 start FALSE");
        osiThreadSleep(1000);
    }

    //启动定时器2,为循环定时器
    err = OPENAT_loop_start_timer(timer2, 1000 * 2);
    while (!err)
    {
        iot_debug_print("timer2 start FALSE");
        osiThreadSleep(1000);
    }
    //线程结束
    osiThreadExit();
}

2.3、编写定时回调函数

  在定时回调函数里面我们选择在定时器2进入中断5次后,重新创建并启动定时器1。我这里做过测试,如果不重新创建定时器1,直接启动会启动失败,那就说明一个问题单次运行定时器在触发一次回调后会自动退出定时器线程并且回收定时器资源。   当定时器2进入10次回调函数,选择停止定时器2的运行,并且回收定时器占用的资源。

void timer_handle(void *pParameter)
{
    BOOL err = 0;
    iot_debug_print(pParameter);
    if (strcmp(pParameter, "timer2") == 0)
    {
        timer2num += 1;
        switch (timer2num)
        {
        case 5://当定时器2进入5次后重新创建定时器1,
            //不创建会出现不能启动,证明了单次定时器运行结束会自动停止并删除
            timer1 = OPENAT_create_timer(timer_handle, "timer1");
            while (timer1 == NULL)
            {
                iot_debug_print("timer1 create FALSE");
                osiThreadSleep(1000);
            }
            //启动定时器1,为单次定时器
            err = OPENAT_start_timer0(timer1, 1000 * 10);
            while (!err)
            {
                iot_debug_print("timer1 start FALSE");
                osiThreadSleep(1000);
            }
            break;
        case 10://当定时器2进入10次后停止并删除
            /* 停止定时器接口 */
            OPENAT_stop_timer(timer2);
            /* 删除定时器接口 */
            OPENAT_delete_timer(timer2);
            break;
        default:
            break;
        }
    }
}

三、编译并下载程序

  完整代码在这,自取。

/*
 * @Author: your name
 * @Date: 2020-05-19 14:05:32
 * @LastEditTime: 2020-05-21 16:29:00
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: \RDA8910_CSDK\USER\user_main.c
 */

#include "string.h"
#include "cs_types.h"

#include "osi_log.h"
#include "osi_api.h"

#include "am_openat.h"
#include "am_openat_vat.h"
#include "am_openat_common.h"

#include "iot_debug.h"
#include "iot_uart.h"
#include "iot_os.h"
#include "iot_gpio.h"
#include "iot_pmd.h"
#include "iot_adc.h"
#include "iot_vat.h"

//timer  软件定时器


HANDLE timer1 = NULL, timer2 = NULL;
uint8 timer2num = 0;
void timer_handle(void *pParameter)
{
    BOOL err = 0;
    iot_debug_print(pParameter);
    if (strcmp(pParameter, "timer2") == 0)
    {
        timer2num += 1;
        switch (timer2num)
        {
        case 5://当定时器2进入5次后重新创建定时器1,
            //不创建会出现不能启动,证明了单次定时器运行结束会自动停止并删除
            timer1 = OPENAT_create_timer(timer_handle, "timer1");
            while (timer1 == NULL)
            {
                iot_debug_print("timer1 create FALSE");
                osiThreadSleep(1000);
            }
            //启动定时器1,为单次定时器
            err = OPENAT_start_timer0(timer1, 1000 * 10);
            while (!err)
            {
                iot_debug_print("timer1 start FALSE");
                osiThreadSleep(1000);
            }
            break;
        case 10://当定时器2进入10次后停止并删除
            /* 停止定时器接口 */
            OPENAT_stop_timer(timer2);
            /* 删除定时器接口 */
            OPENAT_delete_timer(timer2);
            break;
        default:
            break;
        }
    }
}

static void TestTask(void *param)
{
    //创建两个个定时器
    timer1 = OPENAT_create_timer(timer_handle, "timer1");
    while (timer1 == NULL)
    {
        iot_debug_print("timer1 create FALSE");
        osiThreadSleep(1000);
    }

    timer2 = OPENAT_create_timer(timer_handle, "timer2");
    while (timer2 == NULL)
    {
        iot_debug_print("timer2 create FALSE");
        osiThreadSleep(1000);
    }

    //启动定时器1,为单次定时器
    //单次定时器运行结束后,会自动停止并且删除定时器
    BOOL err = OPENAT_start_timer0(timer1, 1000 * 10);
    while (!err)
    {
        iot_debug_print("timer1 start FALSE");
        osiThreadSleep(1000);
    }

    //启动定时器2,为循环定时器
    err = OPENAT_loop_start_timer(timer2, 1000 * 2);
    while (!err)
    {
        iot_debug_print("timer2 start FALSE");
        osiThreadSleep(1000);
    }
    //线程结束
    osiThreadExit();
}

//main函数
int appimg_enter(void *param)
{
    //系统休眠
    iot_os_sleep(10000);
    //创建一个任务
    osiThreadCreate("TestTask", TestTask, NULL, OSI_PRIORITY_NORMAL, 2048, 0);
    return 0;
}

//退出提示
void appimg_exit(void)
{
    OSI_LOGI(0, "application image exit");
}

  查看输出,定时器1一共进入了2次中断。定时器2一共进入了10次中断,验证了程序运行是正确的。通过输出的时间戳也可以发现,定时器2连续两次进入回调其中间隔的时间并不是严格的2秒,说明软件定时器精度不高,但是也可以凑合使用。 image1

不会下载的点击这里,进去查看我的RDA8910 CSDK二次开发入门教程专题第一篇博文1、RDA8910CSDK二次开发:环境搭建里面讲了怎么下载 这里只是我的学习笔记,拿出来给大家分享,欢迎大家批评指正,本篇教程到此结束