[活动专区] 【杰发科技AC7802x测评】10.定时器0实现调度器

[复制链接]
 楼主| 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来实现调度器的功能。

1688134415923.png

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是注册任务的函数,有两个参数:
  * 第一个参数是任务函数地址
  * 第二个参数是任务的执行周期

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

  1. /*
  2. @hehung
  3. 2023-6-30
  4. email: 1398660197@qq.com
  5. wechat: hehung95
  6. reproduced and please indicate the source @hehung
  7. */

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

  9. #include "app_scheduler.h"
  10. #include <stdlib.h>
  11. #include <stdio.h>
  12. #include <string.h>
  13. #include "ac780x_timer.h"


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


  18. static uint32_t time_escape = 1U;
  19. static schr_task_func_t *task_2ms_list;
  20. static uint8_t task_2ms_cnt = 0;
  21. static schr_task_func_t *task_5ms_list;
  22. static uint8_t task_5ms_cnt = 0;
  23. static schr_task_func_t *task_10ms_list;
  24. static uint8_t task_10ms_cnt = 0;
  25. static schr_task_func_t *task_100ms_list;
  26. static uint8_t task_100ms_cnt = 0;
  27. static schr_task_func_t *task_500ms_list;
  28. static uint8_t task_500ms_cnt = 0;
  29. static schr_task_func_t *task_1000ms_list;
  30. static uint8_t task_1000ms_cnt = 0;


  31. static void Schr_SchedulerRunning(void);

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

  36. // Scheduler Initialization
  37. void Schr_Init(void)
  38. {
  39.     // === User Code
  40.   
  41.     // === End user code
  42. }

  43. static void Schr_SchedulerRunning(void)
  44. {
  45.     uint8_t task_cnt;

  46. #if (SCHR_PERIOD_2MS != 0U)
  47.     if ((time_escape % SCHR_PERIOD_2MS) == 0U)
  48.     {
  49.         for (task_cnt = 0; task_cnt < task_2ms_cnt; task_cnt++)
  50.         {
  51.             task_2ms_list[task_cnt]();
  52.         }
  53.     }
  54. #endif
  55. #if (SCHR_PERIOD_5MS != 0U)
  56.     if ((time_escape % SCHR_PERIOD_5MS) == 0U)
  57.     {
  58.         for (task_cnt = 0; task_cnt < task_5ms_cnt; task_cnt++)
  59.         {
  60.             task_5ms_list[task_cnt]();
  61.         }
  62.     }
  63. #endif
  64. #if (SCHR_PERIOD_10MS != 0U)
  65.     if ((time_escape % SCHR_PERIOD_10MS) == 0U)
  66.     {
  67.         for (task_cnt = 0; task_cnt < task_10ms_cnt; task_cnt++)
  68.         {
  69.             task_10ms_list[task_cnt]();
  70.         }
  71.     }
  72. #endif
  73. #if (SCHR_PERIOD_100MS != 0U)
  74.     if ((time_escape % SCHR_PERIOD_100MS) == 0U)
  75.     {
  76.         for (task_cnt = 0; task_cnt < task_100ms_cnt; task_cnt++)
  77.         {
  78.             task_100ms_list[task_cnt]();
  79.         }
  80.     }
  81. #endif
  82. #if (SCHR_PERIOD_500MS != 0U)
  83.     if ((time_escape % SCHR_PERIOD_500MS) == 0U)
  84.     {
  85.         for (task_cnt = 0; task_cnt < task_500ms_cnt; task_cnt++)
  86.         {
  87.             task_500ms_list[task_cnt]();
  88.         }
  89.     }
  90. #endif
  91. #if (SCHR_PERIOD_1000MS != 0U)
  92.     if ((time_escape % SCHR_PERIOD_1000MS) == 0U)
  93.     {
  94.         for (task_cnt = 0; task_cnt < task_1000ms_cnt; task_cnt++)
  95.         {
  96.             task_1000ms_list[task_cnt]();
  97.         }
  98.     }
  99. #endif
  100.     time_escape ++;
  101.     if (time_escape > SCHR_PERIOD_1000MS)
  102.     {
  103.         time_escape = 1;
  104.     }
  105. }

  106. // Start timer to shceduler
  107. void Schr_StartScheduler(void)
  108. {
  109.     Schr_TimerInit();
  110. }

  111. void Schr_CreateTask(const schr_task_func_t task_func, uint32_t task_period)
  112. {
  113.     switch (task_period)
  114.     {
  115.         case SCHR_PERIOD_2MS:
  116.         {
  117.             task_2ms_cnt++;
  118.             task_2ms_list = (schr_task_func_t*)realloc(task_2ms_list, task_2ms_cnt * sizeof(schr_task_func_t));
  119.             task_2ms_list[task_2ms_cnt-1] = task_func;
  120.             break;
  121.         }
  122.         case SCHR_PERIOD_5MS:
  123.         {
  124.             task_5ms_cnt++;
  125.             task_5ms_list = (schr_task_func_t*)realloc(task_5ms_list, task_5ms_cnt * sizeof(schr_task_func_t));
  126.             task_5ms_list[task_5ms_cnt-1] = task_func;
  127.             break;
  128.         }
  129.         case SCHR_PERIOD_10MS:
  130.         {
  131.             task_10ms_cnt++;
  132.             task_10ms_list = (schr_task_func_t*)realloc(task_10ms_list, task_10ms_cnt * sizeof(schr_task_func_t));
  133.             task_10ms_list[task_10ms_cnt-1] = task_func;
  134.             break;
  135.         }
  136.         case SCHR_PERIOD_100MS:
  137.         {
  138.             task_100ms_cnt++;
  139.             task_100ms_list = (schr_task_func_t*)realloc(task_100ms_list, task_100ms_cnt * sizeof(schr_task_func_t));
  140.             task_100ms_list[task_100ms_cnt-1] = task_func;
  141.             break;
  142.         }
  143.         case SCHR_PERIOD_500MS:
  144.         {
  145.             task_500ms_cnt++;
  146.             task_500ms_list = (schr_task_func_t*)realloc(task_500ms_list, task_500ms_cnt * sizeof(schr_task_func_t));
  147.             task_500ms_list[task_500ms_cnt-1] = task_func;
  148.             break;
  149.         }
  150.         case SCHR_PERIOD_1000MS:
  151.         {
  152.             task_1000ms_cnt++;
  153.             task_1000ms_list = (schr_task_func_t*)realloc(task_1000ms_list, task_1000ms_cnt * sizeof(schr_task_func_t));
  154.             task_1000ms_list[task_1000ms_cnt-1] = task_func;
  155.             break;
  156.         }
  157.         default:
  158.         {
  159.             break;
  160.         }
  161.     }
  162. }
  163. ```

  164. 2.2 定时器0初始化

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

  166. ```c
  167. static void Schr_TimerInit(void)
  168. {
  169.     TIMER_ConfigType timerConfig;
  170.     memset(&timerConfig, 0, sizeof(timerConfig));

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

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

  179. static void TIMER_CH0_Callback(void *device, uint32_t wpara, uint32_t lpara)
  180. {
  181.     Schr_SchedulerRunning();
  182. }



2.3 主函数实现

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

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

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

  5.     timer++;
  6. }

  7. int main(void)
  8. {
  9.     InitDelay();
  10.   
  11.     UART_Cfg_Init();                          /*! 串口1初始化 */

  12.     Schr_CreateTask(Print_Running, SCHR_PERIOD_1000MS);
  13.     Schr_StartScheduler();


  14.     while(1)
  15.     {
  16.     }
  17. }



3 试验效果

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

1688133300888.png



  
tpgf 发表于 2023-7-7 14:16 | 显示全部楼层
这是使用了一个定时器实现了不同的定时效果是吧

评论

是的,相当于一个定时器实现多个任务  发表于 2023-7-8 19:27
晓伍 发表于 2023-7-7 14:49 | 显示全部楼层
定时中断只有一个 在一个中断里边对不同的标志位进行累加是吗

评论

是的,一个定时器实现,多个标志同时累计  发表于 2023-7-8 19:27
wakayi 发表于 2023-7-7 15:42 | 显示全部楼层
想要实现这样一个调度器 是不是必须跑系统才能保证实时性呢

评论

有系统可以使用的情况下就不需要调度器了,就是因为使用系统之后的资源不够用,所有才使用的定时器来做的一个调度器,不需要系统。  发表于 2023-7-8 19:28
木木guainv 发表于 2023-7-7 19:15 | 显示全部楼层
这样做的话 是不是需要把定时器的中断等级调高啊

评论

是的,最好设置高一点,根据具体情况而定  发表于 2023-7-8 19:29
paotangsan 发表于 2023-7-7 19:46 | 显示全部楼层
在串口发送的过程中 如果定时器中断到了  应该先处理哪个呢

评论

如果需要保证实时性,还是定时器优先级高一些比较好,但是如果串口数据很短的话,可以串口优先  发表于 2023-7-8 19:31
renzheshengui 发表于 2023-7-7 20:17 | 显示全部楼层
一般这种功能使用普通的定时器就可以实现吧

评论

是的,系统滴答定时器都可以  发表于 2023-7-8 19:30
拉风的小牛皮 发表于 2023-7-18 14:17 | 显示全部楼层
想问下,对于定时器的使用。有没有一直向上计数的要求?比如到达设定的比较值后,不清零可以继续向上计数直到溢出。

评论

[url=home.php?mod=space&uid=2743526]@hehung[/url] :类似RTC的计数器功能,OS调度任务有碰到过需求。就想了解下 是否是必须要求  发表于 2023-7-24 09:45
这种需求一般比较少吧。AC7802X提供的库时不支持,估计直接操作寄存器时可以实现的  发表于 2023-7-18 15:37
supernan 发表于 2023-9-7 22:30 | 显示全部楼层
一个定时器实现多个任务
您需要登录后才可以回帖 登录 | 注册

本版积分规则

10

主题

66

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部

10

主题

66

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部