本系列文章主要围绕实现带有恒温、恒光照、换水、喂食及联网操控的鱼缸控制套件。
主控采用成本较低的 STM32F103 系列MCU,因此内存资源较为匮乏,无法直接搭载RTOS,但好在对于实现所述套件来讲,RTOS并不是刚需。
定时任务往往是一个必不可少的需求,例如每隔若干秒检测一次水温,每隔若干天喂食一次,因此,首先要实现一个简易的定时任务模块。中心思想是利用MCU的系统滴答时钟或定时器产生一个固定的心跳,例如使用定时器创建1ms的中断,系统以此作为计时基数。
在实现定时任务之前,首先要考虑会有怎样的实际需求。例如定时任务仅需运行一次后就删除、每隔固定时间间隔运行一次、运行N次后删除。
/**
* @file timed_task.h
* @brief All the interfaces related to the timer task are in this file
*/
#ifndef __TIMED_TASK_H
#define __TIMED_TASK_H
#include "stddef.h"
#include "stdint.h"
#include "string.h"
// Maximum number of tasks. The number of tasks you create cannot exceed this value
#define TIMER_TASK_MAX_SIZE 8
// Maximum length of task name. The length of this task cannot exceed this value
#define TIMER_TASK_NAME_MAX_LENGTH 32
typedef struct timer_task_worker TimerTask_Ctx_TypeDef;
typedef void(*timer_task_handler)(void *usr_var);
enum TimerTask_Event {
TimerTaskEvent_None = 0x0000,
TimerTaskEvent_Reached,
TimerTaskEvent_NotReach
};
/**
* @defgroup 任务句柄
*/
typedef struct timer_task {
char task_name[TIMER_TASK_NAME_MAX_LENGTH]; // 任务名称
timer_task_handler handler; // 任务句柄
void *data; // 任务传参指针
} Task_Ctx_TypeDef;
/**
* @defgroup 定时任务
*/
struct timer_task_worker {
uint32_t tick_cnt; // 记录时间
uint8_t reach_flag; // 定时到达,运行一次
uint32_t repeat; // 重复次数; 0, 每隔设定市场执行一次;1-65535,执行N次后删除该任务
uint32_t tick_span; // 任务触发的时间间隔
uint32_t trigger_times; // 任务已触发的次数
Task_Ctx_TypeDef *task_ctx; // 需要运行的任务句柄
// 任务链为环形链,last_node 实际指向的是任务链的末尾
struct timer_task_worker* last_node; // 上一个任务节点
struct timer_task_worker* next_node; // 下一个任务节点
};
typedef struct timer_task_global {
uint32_t tick;
int8_t spinlock;
TimerTask_Ctx_TypeDef task_head;
TimerTask_Ctx_TypeDef task_arr[TIMER_TASK_MAX_SIZE];
} TimerTaskWorker_TypeDef;
/**
* @defgroup
*/
extern TimerTaskWorker_TypeDef timer_task_global;
/**
* @brief Run in the interrupt function. Such as TIM1 which generate interruption base on 1ms
*/
void timer_task_tick_inc();
/**
* @brief Initialize the global context of timed-task before the mainloop
* */
void timer_task_worker_init();
/**
* @brief Run in the mainloop.
*/
void timer_task_main();
/**
* @brief Delete an timed task
*/
void timer_task_del(TimerTask_Ctx_TypeDef *ctx);
/**
* @brief Create an timed task
* @param[in] tick_cnt How many milliseconds will this task run. unit: ms
* @param[in] repeat How many times will his task run? 0-Infinite number of times; others: Only xx
* */
int timer_task_add(Task_Ctx_TypeDef *ctx, uint32_t tick_cnt, uint32_t repeat);
/**
* Convert the number of days into milliseconds
* */
uint32_t timer_task_day_to_ms(uint32_t day);
/**
* Convert the number of hours into milliseconds
* */
uint32_t timer_task_hour_to_ms(uint32_t hour);
/**
* Convert the number of minutes into milliseconds
* */
uint32_t timer_task_minute_to_ms(uint32_t minute);
/**
* Convert the number of seconds into milliseconds
* */
uint32_t timer_task_second_to_ms(uint32_t second);
/**
* @defgroup public user API
*/
void task_init(Task_Ctx_TypeDef *ctx, char *task_name, timer_task_handler func, void *data);
#ifdef __cplusplus
}
#endif
#endif
源代码实现
#include "TimedTask/timed_task.h"
TimerTaskWorker_TypeDef timer_task_worker;
/**
* @brief 初始化定时任务上下文
*/
static void timer_task_ctx_init(TimerTask_Ctx_TypeDef* ctx)
{
ctx->tick_cnt = 0x0000;
ctx->reach_flag = TimerTaskEvent_NotReach;
ctx->repeat = 0x0001;
ctx->tick_span = 0x0000;
ctx->trigger_times = 0x0000;
ctx->task_ctx = NULL;
ctx->last_node = NULL;
ctx->next_node = NULL;
}
void timer_task_worker_init()
{
timer_task_worker.tick = 0x00000000;
timer_task_worker.spinlock = 0;
for (int i = 0; i < TIMER_TASK_MAX_SIZE; i++)
{
timer_task_ctx_init(&timer_task_worker.task_arr[i]);
}
timer_task_ctx_init(&timer_task_worker.task_head);
}
void timer_task_main()
{
TimerTask_Ctx_TypeDef *ctx;
ctx = &timer_task_worker.task_head;
// 从 timer_task_head 开始查找任务
// 若当前任务链中没有包含任务,则直接退出
if (ctx->next_node == NULL)
{
return;
}
// 判断并执行任务链中的每个任务
for ( ;; )
{
// 任务链中的最后一个任务,退出
if (ctx->next_node == NULL)
{ break; }
ctx = ctx->next_node;
if (ctx->reach_flag == TimerTaskEvent_NotReach)
{ continue; }
ctx->task_ctx->handler(ctx->task_ctx->data);
ctx->trigger_times++;
ctx->reach_flag = TimerTaskEvent_NotReach;
ctx->tick_cnt += ctx->tick_span;
// 判断是无限次定时任务,还是有限次定时任务
// 若 repeat != 0 则表示此为有限次任务,故判断是否达到运行次数
if (ctx->repeat != 0)
{
// 达到运行次数,从任务链中去除当前任务
if ((--ctx->repeat) == 0)
{
timer_task_del(ctx);
}
}
}
}
void task_init(Task_Ctx_TypeDef* ctx, char* task_name, timer_task_handler func, void* data)
{
uint16_t name_len = strlen(task_name);
name_len = (name_len < TIMER_TASK_NAME_MAX_LENGTH) ? name_len : TIMER_TASK_NAME_MAX_LENGTH;
memset(ctx->task_name, 0, TIMER_TASK_NAME_MAX_LENGTH);
strncpy(ctx->task_name, task_name, name_len);
ctx->handler = func;
ctx->data = data;
}
void timer_task_tick_inc()
{
TimerTask_Ctx_TypeDef *ctx;
timer_task_worker.tick++;
// 判断任务链表是否为空
if (timer_task_worker.task_head.next_node == NULL)
{
return;
}
ctx = &timer_task_worker.task_head;
for ( ;; )
{
if (ctx->next_node == NULL)
{ break; }
ctx = ctx->next_node;
if (ctx->tick_cnt == 0 || ctx->tick_cnt > timer_task_worker.tick)
{ continue; }
ctx->reach_flag = TimerTaskEvent_Reached;
}
}
int timer_task_add(Task_Ctx_TypeDef* ctx, uint32_t tick_cnt, uint32_t repeat)
{
int idx = -1;
// Search a free task-worker from memory
for (int i = 0; i < TIMER_TASK_MAX_SIZE; i++)
{
if (timer_task_worker.task_arr[i].task_ctx == NULL)
{
idx = i;
break;
}
}
if (idx == -1)
{ return -1; }
timer_task_worker.task_arr[idx].task_ctx = ctx;
timer_task_worker.task_arr[idx].tick_cnt = timer_task_worker.tick + tick_cnt;
timer_task_worker.task_arr[idx].tick_span = tick_cnt;
timer_task_worker.task_arr[idx].repeat = repeat;
// 任务链表为空
if (timer_task_worker.task_head.next_node == NULL )
{
timer_task_worker.task_head.next_node = &timer_task_worker.task_arr[idx];
timer_task_worker.task_head.last_node = &timer_task_worker.task_arr[idx];
}
else
{
timer_task_worker.task_arr[idx].last_node = timer_task_worker.task_head.last_node;
timer_task_worker.task_head.last_node->next_node = &timer_task_worker.task_arr[idx];
timer_task_worker.task_head.last_node = &timer_task_worker.task_arr[idx];
}
return 0;
}
void timer_task_del(TimerTask_Ctx_TypeDef* ctx)
{
// 判断是否为任务链中唯一的任务
if (timer_task_worker.task_head.next_node == ctx && timer_task_worker.task_head.last_node == ctx)
{
timer_task_worker.task_head.next_node = NULL;
timer_task_worker.task_head.last_node = NULL;
timer_task_ctx_init(ctx);
return;
}
// 判断是否为第一个任务,即上一个任务节点指向 task_head
if (ctx == timer_task_worker.task_head.next_node)
{
timer_task_worker.task_head.next_node = ctx->next_node;
timer_task_ctx_init(ctx);
return;
}
// 判断是否为最后一个任务
if (ctx == timer_task_worker.task_head.last_node)
{
ctx->last_node->next_node = NULL;
timer_task_worker.task_head.last_node = ctx->last_node;
timer_task_ctx_init(ctx);
return;
}
ctx->last_node->next_node = ctx->next_node;
timer_task_ctx_init(ctx);
}
uint32_t timer_task_day_to_ms(uint32_t day)
{
return (day * 24 * 60 * 60 * 1000);
}
uint32_t timer_task_hour_to_ms(uint32_t hour)
{
return (hour * 60 * 60 * 1000);
}
uint32_t timer_task_minute_to_ms(uint32_t minute)
{
return (minute * 60 * 1000);
}
uint32_t timer_task_second_to_ms(uint32_t second)
{
return (second * 1000);
}