背景
使用liteos相关api实现,一个定时器实现1s亮红灯,0.1s亮白灯,5s轮训亮红白灯,上面使用一个定时器,用完可以删除重新建,第二个定时器实现一个信号量的互斥,有三个阶段测试,第一阶段实现1s亮红灯,老化测试lcd屏幕,第二阶段实现0.1s亮白灯,老化测试网口收发包,第三阶段实现5s轮训亮红白灯,老化测试flash读写,三个阶段每一个分别亮10min,没有跑完10min不能打断。
设计方案
主任务创建信号量(初始值1)和定时器2(周期1ms,但暂不启动)。
循环三个阶段:
请求信号量(Pend)------若当前阶段尚未完成,阻塞等待。
获得信号量后,重置计时器2的累计值(g_elapsed_ms = 0)。
根据阶段编号:
设置相应的灯光状态机初始状态。
启动对应的老化测试任务/函数(例如启动 LCD 连续读写)。
启动定时器1(首次调用其回调函数,内部会创建定时器)。
启动定时器2,开始 10 分钟倒计时。
主任务继续等待信号量(阶段运行期间,主任务被 Pend 阻塞,不会切换到下一阶段)。
定时器2 累计到 10 分钟 → 停止定时器1,关闭 LED,释放信号量(Post)。
主任务获得信号量,进入下一阶段。
三个阶段全部完成后,清理定时器2。
代码实现
#include "los_tick.h"
#include "los_task.h"
#include "los_timer.h"
#include "los_sem.h"
#include "los_typedef.h"
#include <stdio.h>
/*========================== 硬件抽象接口(根据实际硬件替换) ==========================*/
#define LED_RED 0
#define LED_WHITE 1
#define LED_ON(color) do { printf("LED %s ON\n", (color)==LED_RED ? "RED" : "WHITE"); } while(0)
#define LED_OFF() printf("LED OFF\n")
#define LCD_Aging_Test() printf("LCD Aging Test Running...\n")
#define Net_Aging_Test() printf("Net Aging Test Running...\n")
#define Flash_Aging_Test() printf("Flash Aging Test Running...\n")
/*========================== 全局变量 ==========================*/
UINT32 g_timer1_id = 0xFFFFFFFF; // 灯光定时器 ID
UINT32 g_timer2_id = 0xFFFFFFFF; // 计时定时器 ID
UINT32 g_sem_id = 0; // 信号量 ID
UINT32 g_task_id = 0; // 老化测试任务 ID
/* 灯光状态机(包含轮询红白交替) */
typedef enum {
LIGHT_RED_1S = 0, // 亮红灯,持续1s
LIGHT_WHITE_01S, // 亮白灯,持续0.1s
LIGHT_POLL_RED, // 轮询亮红灯,持续5s
LIGHT_POLL_WHITE // 轮询亮白灯,持续5s
} Light_State;
Light_State g_light_state = LIGHT_RED_1S;
/* 测试阶段 */
typedef enum {
STAGE1_LCD = 0,
STAGE2_NET,
STAGE3_FLASH
} Stage_State;
volatile UINT32 g_elapsed_ms = 0; // 当前阶段已运行毫秒数
#define TEN_MINUTE_MS (10 * 60 * 1000) // 600000ms
/*========================== 定时器1 回调(灯光控制,自删除重建) ==========================*/
VOID Timer1_Callback(UINT32 arg)
{
UINT32 next_interval_ms = 0;
// 1. 执行当前灯光状态,并决定下一状态
switch (g_light_state) {
case LIGHT_RED_1S:
LED_ON(LED_RED);
g_light_state = LIGHT_WHITE_01S;
next_interval_ms = 100; // 下次触发间隔 100ms
break;
case LIGHT_WHITE_01S:
LED_ON(LED_WHITE);
g_light_state = LIGHT_POLL_RED;
next_interval_ms = 5000; // 进入轮询阶段,间隔 5s
break;
case LIGHT_POLL_RED:
LED_ON(LED_RED);
g_light_state = LIGHT_POLL_WHITE;
next_interval_ms = 5000; // 继续轮询,切换为白灯
break;
case LIGHT_POLL_WHITE:
LED_ON(LED_WHITE);
g_light_state = LIGHT_RED_1S; // 轮询结束,回到红灯1s模式
next_interval_ms = 1000;
break;
default:
break;
}
// 2. 删除当前定时器
if (g_timer1_id != 0xFFFFFFFF) {
(VOID)LOS_TimerDelete(g_timer1_id);
g_timer1_id = 0xFFFFFFFF;
}
// 3. 重建定时器,设定新的间隔
UINT32 ret = LOS_TimerCreate(&g_timer1_id, LOS_HW_MODE_SINGLE,
Timer1_Callback, NULL, next_interval_ms);
if (ret == LOS_OK) {
(VOID)LOS_TimerStart(g_timer1_id);
} else {
printf("Timer1 recreate failed, ret=%u\n", ret);
}
}
/*========================== 定时器2 回调(计时 & 阶段结束释放信号量) ==========================*/
VOID Timer2_Callback(UINT32 arg)
{
g_elapsed_ms++;
if (g_elapsed_ms >= TEN_MINUTE_MS) {
g_elapsed_ms = 0;
// 停止计时器(本次阶段结束,下一阶段重新启动)
if (g_timer2_id != 0xFFFFFFFF) {
(VOID)LOS_TimerStop(g_timer2_id);
}
// 停止并删除灯光定时器,关闭LED
if (g_timer1_id != 0xFFFFFFFF) {
(VOID)LOS_TimerStop(g_timer1_id);
(VOID)LOS_TimerDelete(g_timer1_id);
g_timer1_id = 0xFFFFFFFF;
}
LED_OFF();
// 释放信号量,允许进入下一阶段
(VOID)LOS_SemPost(g_sem_id);
}
}
/*========================== 老化测试主任务 ==========================*/
VOID Aging_Test_Task(UINT32 arg)
{
UINT32 ret;
Stage_State stage;
// 创建定时器2(周期1ms,但先不启动)
ret = LOS_TimerCreate(&g_timer2_id, LOS_HW_MODE_PERIODIC,
Timer2_Callback, NULL, 1);
if (ret != LOS_OK) {
printf("Timer2 create failed\n");
return;
}
// 三个阶段依次执行
for (stage = STAGE1_LCD; stage <= STAGE3_FLASH; stage++) {
// 获取信号量(阻塞直到阶段完成)
ret = LOS_SemPend(g_sem_id, LOS_WAIT_FOREVER);
if (ret != LOS_OK) {
printf("Semaphore pend error\n");
break;
}
// 重置计时器累计值
g_elapsed_ms = 0;
// 根据阶段设置灯光初始状态并启动老化测试
switch (stage) {
case STAGE1_LCD:
g_light_state = LIGHT_RED_1S;
LCD_Aging_Test();
break;
case STAGE2_NET:
g_light_state = LIGHT_WHITE_01S;
Net_Aging_Test();
break;
case STAGE3_FLASH:
g_light_state = LIGHT_POLL_RED; // 从轮询红灯开始
Flash_Aging_Test();
break;
}
// 启动灯光定时器(首次调用回调,内部会创建定时器)
Timer1_Callback(0);
// 启动计时定时器,开始10分钟倒计时
ret = LOS_TimerStart(g_timer2_id);
if (ret != LOS_OK) {
printf("Timer2 start failed\n");
break;
}
// 主任务在此阻塞,等待定时器2释放信号量
// 10分钟后,信号量被释放,任务继续,进入下一阶段
}
// 三个阶段全部完成,清理定时器2
if (g_timer2_id != 0xFFFFFFFF) {
(VOID)LOS_TimerStop(g_timer2_id);
(VOID)LOS_TimerDelete(g_timer2_id);
g_timer2_id = 0xFFFFFFFF;
}
printf("All aging tests completed.\n");
}
/*========================== 系统初始化 ==========================*/
VOID App_Init(VOID)
{
UINT32 ret;
// 1. 创建信号量,初始值1(空闲状态)
ret = LOS_SemCreate(1, &g_sem_id);
if (ret != LOS_OK) {
printf("Semaphore create failed\n");
return;
}
// 2. 创建老化测试任务
TSK_INIT_PARAM_S task_param = {0};
task_param.pfnTaskEntry = (TSK_ENTRY_FUNC)Aging_Test_Task;
task_param.uwStackSize = 0x2000; // 8KB 栈空间
task_param.pcName = "AgingTestTask";
task_param.usTaskPrio = 5; // 优先级适中
ret = LOS_TaskCreate(&g_task_id, &task_param);
if (ret != LOS_OK) {
printf("Task create failed\n");
return;
}
printf("Aging test system initialized.\n");
}
代码分析
1,互斥与不可打断性
-
初始信号量值 = 1,主任务第一个阶段可直接获取。
-
获取后信号量变为 0,任何其他任务调用
LOS_SemPend将被阻塞(LOS_WAIT_FOREVER)。 -
只有定时器2 在累计满 10 分钟后执行
LOS_SemPost,信号量恢复为 1,主任务才能继续下一阶段。 -
定时器2 在释放信号量后立即停止,防止重复释放,确保阶段切换的严格顺序。
2. 定时器1 的自删除与重建
-
使用单次触发模式(
LOS_HW_MODE_SINGLE),每次回调触发后立即删除自身。 -
根据当前状态计算下一次的间隔(1000/100/5000 ms),重新创建并启动,实现了"用完即删、按需重建"的设计。
-
轮询状态拆分为红白两个子状态,交替间隔均为 5s,实现了真正的红白交替闪烁。
3. 阶段与灯光状态分离
- 三个阶段分别对应不同的灯光初始状态,老化测试函数与灯光控制独立,便于扩展和替换硬件接口。