我是卓波,我是一名嵌入式工程师,我万万没想到我会在这里跟大家吹牛皮。
嵌入式框架Zorb Framework搭建过程
嵌入式框架Zorb Framework搭建一:嵌入式环境搭建、调试输出和建立时间系统
嵌入式框架Zorb Framework搭建二:环形缓冲区的实现
一、前言
在开发程序时,有时候会发现单线程程序开发起来比较吃力,要是可以多线程那该多好。本篇要为Zorb Framework提供多线程功能,也就是多任务功能。
二、任务功能设计
我们先来看看要实现的任务提供什么功能:
初步要提供的功能如下:
1、可以开始和停止任务
2、任务有优先级区分
3、可以进行系统延时
4、可以知道任务的运行时间
5、可以动态创建和销毁任务
因此,初步设计的数据结构如下:
1 /* 任务状态 */ 2 typedef enum _TaskState 3 { 4 TASK_STATE_STOP = 0, /* 停止 */ 5 TASK_STATE_RUNNING /* 运行 */ 6 } TaskState; 7 8 /* 任务结构 */ 9 typedef struct _Task 10 { 11 uint32_t *pStkPtr; /* 堆栈指针 */ 12 uint32_t *pStkBase; /* 堆栈基地址 */ 13 uint32_t StkSize; /* 堆栈大小 */ 14 uint32_t DelayTime; /* 任务延时时间(系统周期) */ 15 uint8_t Priority; /* 任务优先级 */ 16 uint8_t State; /* 任务状态 */ 17 uint32_t RunTime; /* 任务总运行时间(系统周期) */ 18 19 /* 开始任务 */ 20 bool (*Start)(struct _Task * const pTask); 21 22 /* 停止任务 */ 23 bool (*Stop)(struct _Task * const pTask); 24 25 /* 销毁任务 */ 26 void (*Dispose)(struct _Task * const pTask); 27 28 /* 延时任务 */ 29 bool (*Delay)(struct _Task * const pTask, uint32_t tick); 30 } Task;
为Zorb Framework提供的任务功能比较简单,状态也只有运行和关闭两种状态。任务功能实现的关键在于任务调度,而任务调度的核心又在于任务堆栈的保存和恢复。这部分需要根据使用的芯片进行修改,在STM32中,通过触发PendSV异常进行任务切换:
1 /****************************************************************************** 2 * 描述 :PendSV异常处理 3 * 参数 :无 4 * 返回 :无 5 ******************************************************************************/ 6 __asm void PendSV_Handler(void) 7 { 8 IMPORT pCurrentTask 9 IMPORT pTopPriorityTask 10 11 /* 任务的保存,即把CPU寄存器的值存储到任务的堆栈中 */ 12 /* 关中断,NMI和HardFault除外 */ 13 CPSID I 14 15 /* 判断是否第一次运行 */ 16 MRS R0, PSP 17 CBZ R0, PendSVHandler_NotSave 18 19 /** 20 在进入PendSV异常的时候,当前CPU的xPSR,PC(任务入口地址), 21 R14,R12,R3,R2,R1,R0会自动存储到当前任务堆栈,同时递减PSP的值 22 **/ 23 /* 手动存储CPU寄存器R4-R11的值到当前任务的堆栈 */ 24 STMDB R0!, {R4-R11} 25 26 /* R0指向pCurrentTask的堆栈指针(指向空闲位置的顶部) */ 27 LDR R1, = pCurrentTask 28 LDR R1, [R1] 29 STR R0, [R1] 30 NOP 31 32 /* 任务的切换,即把下一个要运行的任务的堆栈内容加载到CPU寄存器中 */ 33 PendSVHandler_NotSave 34 35 /* 等效操作pCurrentTask = pTopPriorityTask; */ 36 LDR R0, = pCurrentTask 37 LDR R1, = pTopPriorityTask 38 LDR R2, [R1] 39 STR R2, [R0] 40 41 /* pTopPriorityTask的信息出栈 */ 42 LDR R0, [R2] 43 LDMIA R0!, {R4-R11} 44 45 /* 设置PSP指向下一个要执行的任务的堆栈的栈底(已弹出了寄存器信息) */ 46 MSR PSP, R0 47 /* 确保异常返回使用的堆栈指针是PSP */ 48 ORR LR, LR, #0x04 /* 设置LR寄存器的位2为1 */ 49 CPSIE I /* 开中断 */ 50 51 /** 52 函数返回,这个时候任务堆栈中的剩下内容将会自动加载到 53 xPSR,PC(任务入口地址),R14,R12,R3,R2,R1,R0(任务的形参) 54 同时PSP的值也将更新,即指向任务堆栈的栈顶。 55 在STM32中,堆栈是由高地址向低地址生长的 56 **/ 57 BX LR 58 NOP 59 }
具体实现请看附件代码或在文末的github地址拉框架源码。
三、任务结果测试
简单的测试代码如下:
1 /** 2 ***************************************************************************** 3 * @file app_task.c 4 * @author Zorb 5 * @version V1.0.0 6 * @date 2018-06-28 7 * @brief 任务测试的实现 8 ***************************************************************************** 9 * @history 10 * 11 * 1. Date:2018-06-28 12 * Author:Zorb 13 * Modification:建立文件 14 * 15 ***************************************************************************** 16 */ 17 18 #include "app_task.h" 19 #include "zf_includes.h" 20 21 static Task *pTask1; /* 任务1 */ 22 static Task *pTask2; /* 任务2 */ 23 static Task *pTask3; /* 任务3 */ 24 25 static void Process1(void *pArg); /* 任务1程序定义 */ 26 static void Process2(void *pArg); /* 任务2程序定义 */ 27 static void Process3(void *pArg); /* 任务3程序定义 */ 28 29 /****************************************************************************** 30 * 描述 :任务1程序 31 * 参数 :(in)-pArg 参数指针 32 * 返回 :无 33 ******************************************************************************/ 34 static void Process1(void *pArg) 35 { 36 ZF_DEBUG(LOG_D, "\r\n"); 37 ZF_DEBUG(LOG_D, "system time is %dms\r\n", ZF_SYSTIME_MS()); 38 ZF_DEBUG(LOG_D, "I am %s\r\n", (char *)pArg); 39 ZF_DEBUG(LOG_D, "task count is %d\r\n", TASK_GET_TASK_COUNT()); 40 41 ZF_DEBUG(LOG_D, "I will create task3\r\n"); 42 43 /* 创建任务3 */ 44 Task_create(&pTask3, Process3, "task3", 3, 256); 45 46 ZF_DEBUG(LOG_D, "task count is %d\r\n", TASK_GET_TASK_COUNT()); 47 48 ZF_DEBUG(LOG_D, "I will dispose myself\r\n"); 49 50 pTask1->Dispose(pTask1); 51 } 52 53 /****************************************************************************** 54 * 描述 :任务2程序 55 * 参数 :(in)-pArg 参数指针 56 * 返回 :无 57 ******************************************************************************/ 58 static void Process2(void *pArg) 59 { 60 while(1) 61 { 62 ZF_DEBUG(LOG_D, "\r\n"); 63 ZF_DEBUG(LOG_D, "system time is %dms\r\n", ZF_SYSTIME_MS()); 64 ZF_DEBUG(LOG_D, "I am %s\r\n", (char *)pArg); 65 ZF_DEBUG(LOG_D, "task count is %d\r\n", TASK_GET_TASK_COUNT()); 66 ZF_DEBUG(LOG_D, "I will sleep 1000ms\r\n"); 67 ZF_DEBUG(LOG_D, "wake up time is %dms\r\n", ZF_SYSTIME_MS() + 1000); 68 ZF_DELAY_MS(1000); 69 } 70 } 71 72 /****************************************************************************** 73 * 描述 :任务3程序 74 * 参数 :(in)-pArg 参数指针 75 * 返回 :无 76 ******************************************************************************/ 77 static void Process3(void *pArg) 78 { 79 while(1) 80 { 81 ZF_DEBUG(LOG_D, "\r\n"); 82 ZF_DEBUG(LOG_D, "system time is %dms\r\n", ZF_SYSTIME_MS()); 83 ZF_DEBUG(LOG_D, "I am %s\r\n", (char *)pArg); 84 ZF_DEBUG(LOG_D, "task count is %d\r\n", TASK_GET_TASK_COUNT()); 85 ZF_DEBUG(LOG_D, "I will sleep 1000ms\r\n"); 86 ZF_DEBUG(LOG_D, "wake up time is %dms\r\n", ZF_SYSTIME_MS() + 1000); 87 ZF_DELAY_MS(1000); 88 } 89 } 90 91 /****************************************************************************** 92 * 描述 :任务初始化 93 * 参数 :无 94 * 返回 :无 95 ******************************************************************************/ 96 void App_Task_init(void) 97 { 98 /* 创建任务1 */ 99 Task_create(&pTask1, Process1, "task1", 1, 512); 100 /* 创建任务1 */ 101 Task_create(&pTask2, Process2, "task2", 2, 512); 102 /* 运行任务系统 */ 103 Task_run(); 104 105 /* 程序不会到这 */ 106 } 107 108 /******************************** END OF FILE ********************************/
结果:
system time is 3ms I am task1 task count is 3 I will create task3 task count is 4 I will dispose myself system time is 13ms I am task2 task count is 3 I will sleep 1000ms wake up time is 1019ms system time is 21ms I am task3 task count is 3 I will sleep 1000ms wake up time is 1028ms system time is 1021ms I am task2 task count is 3 I will sleep 1000ms wake up time is 2027ms system time is 1030ms I am task3 task count is 3 I will sleep 1000ms wake up time is 2036ms system time is 2029ms I am task2 task count is 3 I will sleep 1000ms wake up time is 3035ms system time is 2038ms I am task3 task count is 3 I will sleep 1000ms wake up time is 3044ms 省略...
四、最后
本篇为Zorb Framework提供了任务功能。使用多任务功能进行开发是方便了许多,但同时任务间的协作和资源调用加大了调试和排错的难度。在享受多任务带来的快乐的同时,要做好排错时痛苦的准备。
Zorb Framework github:https://github.com/54zorb/Zorb-Framework
版权所有,转载请打赏哟
如果你喜欢我的文章,可以通过微信扫一扫给我打赏哟