本帖最后由 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秒钟,算是比较精确。
|