打印
[活动专区]

【杰发科技AC7802x测评】10.定时器0实现调度器

[复制链接]
426|16
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
hehung|  楼主 | 2023-6-30 22:31 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 hehung 于 2023-7-1 09:33 编辑

#技术资源# #申请原创#

前言

由于AC7802x的板载FLASH和RAM资源有限,只能勉强带动一个FreeRTOS系统,之后在想要带系统驱动更多功能就无能为力了,所以我决定自己用定时器写一个调用器来用,为后续的作品任务调度做准备。

任务调用使用了timer(其实使用systick也是可以,只要有定时功能都OK)。实现了2ms,5ms,10ms,100ms,500ms,1000ms的任务周期性调度功能。

本文实现了使用在1000ms调度的任务中调用printf函数打印系统运行时间,并且带上串口工具自带的时间戳功能,用来验证任务时间是否准确。

1 定时器介绍

参考用户手册可以知道AC7802X的定时器的相关功能,如下所示,本文使用了定时器的中断功能,超时时间设置为1ms。

共有4个定时器,使用了定时器0来实现调度器的功能。



2 软件实现

2.1 调度器原理介绍

调度器依靠定时器提供时基,设置了定时器时基为1ms,每1ms进入一次定时器中断,在中断中计数:

* 计满2个:进入2ms任务中指定所有注册到2ms中的函数
* 计满5个:进入5ms任务中指定所有注册到5ms中的函数
* 计满10个:进入10ms任务中指定所有注册到10ms中的函数
* 计满100个:进入100ms任务中指定所有注册到100ms中的函数
* 计满500个:进入500ms任务中指定所有注册到500ms中的函数
* 计满1000个:进入1000ms任务中指定所有注册到1000ms中的函数

调度器原理还是很简单,代码实现如下。

其中:

* Schr_SchedulerRunning是调度器函数,用于周期性调用任务函数集
* Schr_StartScheduler是启动调度器的函数,其实就是初始化定时器
* Schr_CreateTask是注册任务的函数,有两个参数:
  * 第一个参数是任务函数地址
  * 第二个参数是任务的执行周期

调度器使用了堆空间,对分配的任务进行存储,属于动态分配。

/*
@hehung
2023-6-30
email: 1398660197@qq.com
wechat: hehung95
reproduced and please indicate the source @hehung
*/

// This is a simple OS, and base a timer

#include "app_scheduler.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "ac780x_timer.h"


#define Timer_CLK        (APB_BUS_FREQ)             /*!定时器时钟为APB时钟 */  
#define TimeoutS(n)      (Timer_CLK*n - 1)          /*!n 秒超时值 */
#define Timeout1ms       (Timer_CLK/1000-1)         /*!1ms超时值 */
#define Timeout500ms     (Timer_CLK/2-1)            /*!500ms超时值 */


static uint32_t time_escape = 1U;
static schr_task_func_t *task_2ms_list;
static uint8_t task_2ms_cnt = 0;
static schr_task_func_t *task_5ms_list;
static uint8_t task_5ms_cnt = 0;
static schr_task_func_t *task_10ms_list;
static uint8_t task_10ms_cnt = 0;
static schr_task_func_t *task_100ms_list;
static uint8_t task_100ms_cnt = 0;
static schr_task_func_t *task_500ms_list;
static uint8_t task_500ms_cnt = 0;
static schr_task_func_t *task_1000ms_list;
static uint8_t task_1000ms_cnt = 0;


static void Schr_SchedulerRunning(void);

// === User function declaration
static void Schr_TimerInit(void);
static void TIMER_CH0_Callback(void *device, uint32_t wpara, uint32_t lpara);
// === End user function declaration

// Scheduler Initialization
void Schr_Init(void)
{
    // === User Code
  
    // === End user code
}

static void Schr_SchedulerRunning(void)
{
    uint8_t task_cnt;

#if (SCHR_PERIOD_2MS != 0U)
    if ((time_escape % SCHR_PERIOD_2MS) == 0U)
    {
        for (task_cnt = 0; task_cnt < task_2ms_cnt; task_cnt++)
        {
            task_2ms_list[task_cnt]();
        }
    }
#endif
#if (SCHR_PERIOD_5MS != 0U)
    if ((time_escape % SCHR_PERIOD_5MS) == 0U)
    {
        for (task_cnt = 0; task_cnt < task_5ms_cnt; task_cnt++)
        {
            task_5ms_list[task_cnt]();
        }
    }
#endif
#if (SCHR_PERIOD_10MS != 0U)
    if ((time_escape % SCHR_PERIOD_10MS) == 0U)
    {
        for (task_cnt = 0; task_cnt < task_10ms_cnt; task_cnt++)
        {
            task_10ms_list[task_cnt]();
        }
    }
#endif
#if (SCHR_PERIOD_100MS != 0U)
    if ((time_escape % SCHR_PERIOD_100MS) == 0U)
    {
        for (task_cnt = 0; task_cnt < task_100ms_cnt; task_cnt++)
        {
            task_100ms_list[task_cnt]();
        }
    }
#endif
#if (SCHR_PERIOD_500MS != 0U)
    if ((time_escape % SCHR_PERIOD_500MS) == 0U)
    {
        for (task_cnt = 0; task_cnt < task_500ms_cnt; task_cnt++)
        {
            task_500ms_list[task_cnt]();
        }
    }
#endif
#if (SCHR_PERIOD_1000MS != 0U)
    if ((time_escape % SCHR_PERIOD_1000MS) == 0U)
    {
        for (task_cnt = 0; task_cnt < task_1000ms_cnt; task_cnt++)
        {
            task_1000ms_list[task_cnt]();
        }
    }
#endif
    time_escape ++;
    if (time_escape > SCHR_PERIOD_1000MS)
    {
        time_escape = 1;
    }
}

// Start timer to shceduler
void Schr_StartScheduler(void)
{
    Schr_TimerInit();
}

void Schr_CreateTask(const schr_task_func_t task_func, uint32_t task_period)
{
    switch (task_period)
    {
        case SCHR_PERIOD_2MS:
        {
            task_2ms_cnt++;
            task_2ms_list = (schr_task_func_t*)realloc(task_2ms_list, task_2ms_cnt * sizeof(schr_task_func_t));
            task_2ms_list[task_2ms_cnt-1] = task_func;
            break;
        }
        case SCHR_PERIOD_5MS:
        {
            task_5ms_cnt++;
            task_5ms_list = (schr_task_func_t*)realloc(task_5ms_list, task_5ms_cnt * sizeof(schr_task_func_t));
            task_5ms_list[task_5ms_cnt-1] = task_func;
            break;
        }
        case SCHR_PERIOD_10MS:
        {
            task_10ms_cnt++;
            task_10ms_list = (schr_task_func_t*)realloc(task_10ms_list, task_10ms_cnt * sizeof(schr_task_func_t));
            task_10ms_list[task_10ms_cnt-1] = task_func;
            break;
        }
        case SCHR_PERIOD_100MS:
        {
            task_100ms_cnt++;
            task_100ms_list = (schr_task_func_t*)realloc(task_100ms_list, task_100ms_cnt * sizeof(schr_task_func_t));
            task_100ms_list[task_100ms_cnt-1] = task_func;
            break;
        }
        case SCHR_PERIOD_500MS:
        {
            task_500ms_cnt++;
            task_500ms_list = (schr_task_func_t*)realloc(task_500ms_list, task_500ms_cnt * sizeof(schr_task_func_t));
            task_500ms_list[task_500ms_cnt-1] = task_func;
            break;
        }
        case SCHR_PERIOD_1000MS:
        {
            task_1000ms_cnt++;
            task_1000ms_list = (schr_task_func_t*)realloc(task_1000ms_list, task_1000ms_cnt * sizeof(schr_task_func_t));
            task_1000ms_list[task_1000ms_cnt-1] = task_func;
            break;
        }
        default:
        {
            break;
        }
    }
}
```

2.2 定时器0初始化

初始化代码如下,设置超时时间为1s,在定时器0的中断服务函数中进行时间累计。

```c
static void Schr_TimerInit(void)
{
    TIMER_ConfigType timerConfig;
    memset(&timerConfig, 0, sizeof(timerConfig));

    /*!配置定时器.*/
    timerConfig.periodValue = Timeout1ms;                  /*! 定义超时值为1ms */
    timerConfig.linkModeEn  = DISABLE;                      /*! 禁能链接模式 */
    timerConfig.interruptEn = ENABLE;                       /*! 使能定时器中断 */
    timerConfig.timerEn     = ENABLE;                       /*! 打开定时器 */
    timerConfig.callBack    = TIMER_CH0_Callback;           /*! 中断回调函数 */

    TIMER_Init(TIMER_CHANNEL0, &timerConfig);               /*! TIMER初始化函数生效 */
}

static void TIMER_CH0_Callback(void *device, uint32_t wpara, uint32_t lpara)
{
    Schr_SchedulerRunning();
}



2.3 主函数实现

主函数中,用于创建任务以及启动调度器。

- Print_Running()为新建的任务
- Schr_CreateTask()动态注册任务
- Schr_StartScheduler()启动调度器

void Print_Running(void)
{
    static uint32_t timer = 0;
    printf ("Timer escape for %ds\r\n", timer);

    timer++;
}

int main(void)
{
    InitDelay();
  
    UART_Cfg_Init();                          /*! 串口1初始化 */

    Schr_CreateTask(Print_Running, SCHR_PERIOD_1000MS);
    Schr_StartScheduler();


    while(1)
    {
    }
}



3 试验效果

如下图所示,可以看到,与串口助手自带的时间戳上的时间进行对比,调度器还是十分准确的,记了10秒钟,算是比较精确。





  

使用特权

评论回复

相关帖子

沙发
tpgf| | 2023-7-7 14:16 | 只看该作者
这是使用了一个定时器实现了不同的定时效果是吧

使用特权

评论回复
评论
hehung 2023-7-8 19:27 回复TA
是的,相当于一个定时器实现多个任务 
板凳
晓伍| | 2023-7-7 14:49 | 只看该作者
定时中断只有一个 在一个中断里边对不同的标志位进行累加是吗

使用特权

评论回复
评论
hehung 2023-7-8 19:27 回复TA
是的,一个定时器实现,多个标志同时累计 
地板
wakayi| | 2023-7-7 15:42 | 只看该作者
想要实现这样一个调度器 是不是必须跑系统才能保证实时性呢

使用特权

评论回复
评论
hehung 2023-7-8 19:28 回复TA
有系统可以使用的情况下就不需要调度器了,就是因为使用系统之后的资源不够用,所有才使用的定时器来做的一个调度器,不需要系统。 
5
木木guainv| | 2023-7-7 19:15 | 只看该作者
这样做的话 是不是需要把定时器的中断等级调高啊

使用特权

评论回复
评论
hehung 2023-7-8 19:29 回复TA
是的,最好设置高一点,根据具体情况而定 
6
paotangsan| | 2023-7-7 19:46 | 只看该作者
在串口发送的过程中 如果定时器中断到了  应该先处理哪个呢

使用特权

评论回复
评论
hehung 2023-7-8 19:31 回复TA
如果需要保证实时性,还是定时器优先级高一些比较好,但是如果串口数据很短的话,可以串口优先 
7
renzheshengui| | 2023-7-7 20:17 | 只看该作者
一般这种功能使用普通的定时器就可以实现吧

使用特权

评论回复
评论
hehung 2023-7-8 19:30 回复TA
是的,系统滴答定时器都可以 
8
拉风的小牛皮| | 2023-7-18 14:17 | 只看该作者
想问下,对于定时器的使用。有没有一直向上计数的要求?比如到达设定的比较值后,不清零可以继续向上计数直到溢出。

使用特权

评论回复
评论
拉风的小牛皮 2023-7-24 09:45 回复TA
@hehung :类似RTC的计数器功能,OS调度任务有碰到过需求。就想了解下 是否是必须要求 
hehung 2023-7-18 15:37 回复TA
这种需求一般比较少吧。AC7802X提供的库时不支持,估计直接操作寄存器时可以实现的 
9
supernan| | 2023-9-7 22:30 | 只看该作者
一个定时器实现多个任务

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

10

主题

66

帖子

1

粉丝