ALIENTEK MiniSTM32 下 ucos应用笔记之一 fficeffice" />
——任务的建立MyFistTask
本贴借用原子的ALIENTEK MiniSTM32平台针对扩展实验8对stm32下ucos任务的建立做以简单的介绍。对于大多数欲玩转ucos却一直遥遥观望无从入手的朋友这篇文章或许会带你入门(确切的说是让你找准大门的方向,入门且谈不上),对于已经熟悉ucos的朋友请绕行,无需花费您宝贵的时间看菜鸟级的闲谈。废话不多说,下面直入主题。
一、 平台配置:
这里借用原子哥的工程模板,打开扩展实验8 uCOSII控制DS0 DS1亮灭,左侧的工作空间如图所示
图中USER、SYSTEM、HARDWAER这三个文件夹相信大家都相当熟悉不必介绍。 uC/OS-II CODE、uC/OS-II PORT、uC/OS-II CONFIGS三个文件夹是操作系统内核本身相关代码研究其源码之前可以不去理会它。
二、test.c文件介绍
扩展实验8中任务函数跟主函数在同一个文件test.c中编写,下面对主程序进行介绍,打开tesk.c 文件,文件开头包含如下头文件
前面5个头文件不在敷述,最后一个头文件includes.h本身包含了操作系统本身相关的所有头文件,在这里一次性包含进工程里。接下来定义了任务相关写数据结构,对一些函数做了声明
对于某个已经编写的任务建立之前我们要做三件事:
1 、定义任务堆栈
2 、定义任务优先级
3、创建任务
例程中的代码如下:
任务堆栈定义定义部分:
//设置任务堆栈大小
#define LED_STK_SIZE 64
#define LED1_STK_SIZE 64
#define START_STK_SIZE 128
任务优先级定义部分:
//任务堆栈
OS_STK TASK_LED1_STK[LED_STK_SIZE];
OS_STK TASK_LED_STK[LED_STK_SIZE];
OS_STK TASK_START_STK[START_STK_SIZE];
任务堆栈:就是任务运行时所占内存(ram)的空间,根据实际情况我们可以自己设置其大小(注意宁可多分配,不要设置太小,不然堆栈溢出会产生难以预料的后果);这里是通过定义数组对其进行分配的,空间大小通过宏定义来完成便于修改。
注意:此处定义了三个堆栈空间,分别对应着三个不同的任务TaskLed1、TaskLed、TaskStart。
对于任务的创建在后面就行讲解。
主程序结构说明:
int main (void) // 主函数
{
///////////////////////////////////////初始化/////////////////////////////////////////////
Stm32_Clock_Init(9); //系统时钟设置
// delay_init(72); //延时初始化
uart_init(72,9600); //串口初始化为9600
LED_Init(); //初始化与LED连接的硬件接口
SysTick_Configuration();系统时钟初始化设置
/////////////////////////////////////////操作系统部分//////////////////////////////////////////////////
OSInit();//操作系统初始话
OSTaskCreate( TaskStart, //任务代码地址
(void *)0, //任务参数
(OS_STK*)&TASK_START_STK[START_STK_SIZE-1],
//任务堆栈栈顶
START_TASK_Prio//任务优先级
);
OSStart();//启动操作系统,开始任务调度
return 0;
}
主程序大体分为两部分的内容:硬件初始化部分、操作系统相关部分。
初始话部分主要对相关硬件进行配置、操作系统部分主要做了三件事:第一系统初始化OSInit、第二任务创建OSTaskCreate、第三系统启动OSStart;
这里有必要对任务的创建进行简单的说明,我们通过右击光标goto到OSTaskCreate的定义处,函数原型为
INT8U OSTaskCreate (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT8U prio)
一共包含4个参数分别是三个指针型和一个无符号整型。它们分别代表着任务代码地址(任务函数的名称),任务参数(传递给任务函数的参数),任务堆栈栈顶(任务堆栈数组)和任务优先级。
任务函数结构说明:
对于任务函数的编写多数情况下采用如下周期性循环的格式
void MyTask (void *pdata) //周期性执行的任务函数
{
相关初始化部分
while(1) //无限循环
{
任务功能代码部分;
调用系统延时函数; // OSTimeDlyHMSM( ) 或者OSTimeDly( )
}
}
任务函数包括三部分内容:1相关初始化部分 进行任务相关的变量的定义、初始话和相关硬件的初始话;
2 任务功能代码部分:完成任务的具体功能
3 调用系统延时函数:周期性的执行任务,延时开始时挂起自己释放cup让其他就绪任务执行,延时结束后回到就绪态运行(注意这里延时结束后不是任务不是直接运行的,还有个优先级的问题,如果多个任务共同处于就绪态的时候优先级高的先运行)。
三、任务的建立:
上面的啰嗦主要是为任务的建立做个铺垫,在上述的讲述的基础上,下面进行任务建立:
创建一个任务需要以下几个步骤:
1、 定义任务堆栈
2、定义任务优先级
3、 编写任务函数
4、 创建任务(在主函数中或者其它任务中创建任务)
前三个步骤以任务1为例进行说明,其余两个任务的操作与此类似。
第一定义任务堆栈
分为2小步:
1、设置任务堆栈大小: #define LED_STK_SIZE 64;
2、定义堆栈数组: OS_STK TASK_LED_STK[LED_STK_SIZE];
这里注意OS_STK 我们可以右键goto到定义出typedef unsigned int OS_STK,可见其等价于unsigned int。
第二 定义任务优先级
#define LED_TASK_Prio 11
这里定义任务的优先级为11,注意ucos最多可以运行64个任务最低任务优先级数为0,系统任务自身占用2个优先级(优先级数63和62统计和空闲任务占用)因此我们设置优先级数时应该不大于62。Ucos中还可对最多任务数量上限进行设置在os_cfg.h里面有一个: #define OS_MAX_TASKS 11 这里原子的默认设置为11,所以此时我们最多能够创建的任务数为11个。还有一处#define OS_LOWEST_PRIO 12,这里设置任务最低优先级为12 所以在这里我们在设置任务优先级的时候不可大于10(最后两个优先级被统计和空闲任务所占据)。因此如果创建的任务多于11个时适当修改OS_MAX_TASKS 跟OS_LOWEST_PRIO的值即可。
第三 编写任务函数
原子的代码中共编写了3个任务函数,分别是TaskStart,TaskLed ,TaskLed1
//任务1
//控制DS0的亮灭.
void TaskLed(void *pdata)
{
while(1)
{
LED0=!LED0;
OSTimeDlyHMSM(0,0,0,500);
}
}
说明:函数无返回值,参数为任意指针型变量此处无参数传递因此在OSTaskCreate中赋值为(void * )0。
这里OSTimeDlyHMSM为任务延时函数5个参数分别对应着时、分、秒、毫秒。
While循环中先执行一次LED0=!LED0然后隔释放cup挂起自己,每隔500ms,反复一次。(个人觉得这种模式很像普通的定时器中断,
OSTimeDlyHMSM(0,0,0,500)相当于给计数器赋初值,简单的可以这样理解实际上任务的调度过程是比较复杂的)
任务2函数TaskLed1的编写跟TaskLed类似 。
TaskStart任务的编写在后面介绍。
第四 创建任务
原子的例程中TaskStart任务的创建在main函数中完成,其它两个任务的创建在TaskStart中完成,代码如下:
TaskStart的创建:
OSTaskCreate( TaskStart, //task pointer
(void *)0, //parameter
(OS_STK *)&TASK_START_STK[START_STK_SIZE-1], //task stack top pointer
START_TASK_Prio
); //task priority
TaskLed ,TaskLed1的创建:
void TaskStart(void * pdata)
{
pdata = pdata; //1
OS_ENTER_CRITICAL(); //2
OSTaskCreate(TaskLed,(void*)0, (OS_STK *)&TASK_LED_STK[LED_STK_SIZE-1], LED_TASK_Prio);//3
OSTaskCreate(TaskLed1,(void*)0,(OS_STK*)&TASK_LED1_STK[LED1_STK_SIZE-1], LED1_TASK_Prio);//4
OSTaskSuspend(START_TASK_Prio); // 5
OS_EXIT_CRITICAL();//6
}
程序中3、4处分别创建两个任务,任务1 TaskLed和任务2 TaskLed1;
程序1处将pdata = pdata是将参数传递给自己这样做的目的是防止编译器报错,其他两个任务中原子省略了这句。
程序2处进入临界区关中断禁止任务切换,防止其他任务同时调用OSTaskCreate函数而产生不可预料的后果;
程序5处 通过调用OSTaskSuspend函数TaskStar任务挂起自己,后面系统不再执行TaskStart任务
程序6处 退出临界区开中断,允许任务切换。
四、总结
至此所有的任务创建工作已经完成,回头看看似乎讲的很散乱,下面做个简单的总结:创建一个新的任务步骤如下1、为其定义任务堆栈,2设置其优先级3、按照上述格式编写任务函数代码4、调用OSTaskCreate函数创建任务(可在main或者其他任务函数中完成)
上述实例中任务的执行流程如下:main函数执行后首先做了硬件相关的初始话,这里的延时跟串口初始话没有用到;然后是初始化操作系统,创建任务TaskStart,最后开启调度,任务开启调度后开始运行TaskStart然后在TaskStart中创建了任务1和任务2,随后TaskStart挂起自己不再运行。而任务1跟任务2一直在执行。
上述的TaskStar任务跟任务1和任务2性质没有区别只是功能不同而已,只不过TaskStar是通过main函数创建,功能是建立任务1和任务2。而任务1和任务2功能分别是使两个led周期性的闪烁。其实直接在main函数中通过两个OSTaskCreate函数创建任务1和任务2省略TaskStar任务也可以达到同样的效果。有兴趣的话大家可以试试。
程序中Configuration和SysTick_Handler分别为系统时钟配置和系统时钟中断服务函数,它们为操作系统所用建立任务时不用理会。
为了便于观察理解本人在三个任务分别添加了串口打印代码
以下是keil下仿真截图:
时间不早了,先讲到这。由于本人尚处在学习阶段,有错误之处希望大家批评指正。
上面写只是对原子的工程实例做了些说明,各位如果觉得有帮助,本人后面将继续发表些原子开发板下通用模板建立、和后续的应用实例相关的帖子。
|