嵌入式框架Zorb Framework搭建七:任务的实现

我是卓波,我是一名嵌入式工程师,我万万没想到我会在这里跟大家吹牛皮。

嵌入式框架Zorb Framework搭建过程

嵌入式框架Zorb Framework搭建一:嵌入式环境搭建、调试输出和建立时间系统

嵌入式框架Zorb Framework搭建二:环形缓冲区的实现

嵌入式框架Zorb Framework搭建三:列表的实现

嵌入式框架Zorb Framework搭建四:状态机的实现

嵌入式框架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

  版权所有,转载请打赏哟

 

如果你喜欢我的文章,可以通过微信扫一扫给我打赏哟

posted @ 2018-07-19 19:02  54zorb  阅读(260)  评论(0编辑  收藏