嵌入式实验二
数,就有任务恢复函数,通过该函数将被挂起的任务恢复,让调度器能够重新调度 该函数。UCOSII 提供的任务恢复函数原型为:INT8U OSTaskResume(INT8U prio) 四、实验内容 在UCOSII里面创建3个任务: 开始任务、 LED0任务和LED1 任务,开始任务用于创建其他(LED0和LED1)任务,之后挂 起;LED0任务用于控制DS0 的亮灭,DS0每秒钟亮80ms;LED1 任务用于控制DS1的亮灭,DS1亮300ms,灭300ms,依次循环。 所要用到的硬件资源如下: 1)指示灯DS0 、DS1 的硬件电路图如图 其中PWR是系统电源指示灯, 为蓝色。 LED0和LED1分别接在PB5 和PE5上。为了方便大家判断,我们选择了DS0为红色的LED, DS1为绿色的LED。 2)软件参数设置 在 os_cfg.h 里面定义 OS_TICKS_PER_SEC=200,也就是设置 UCOSII 的时钟节拍为 5ms。 OS_MAX_TASKS = 10,也就是最多 10 个任务(包括空闲任务和统计任务在内)。 前面提到,我们需要在sys.h里面设置SYSTEM_SUPPORT_UCOS为1,以支持UCOSII,通过这个设 置,不仅可以实现利用delay_init来初始化SYSTICK,产生UCOSII的系统时钟节拍,还可以让 delay_us和delay_ms函数在UCOSII下能够正常使用,这使得我们之前的代码可以十分方便的移植 到UCOSII下。虽然UCOSII也提供了延时函数:OSTimeDly和OSTimeDLyHMSM,但是这两个函数的最 少延时单位只能是1个UCOSII时钟节拍,在本次实验中,即5ms,显然不能实现us级的延时,而us 级的延时在很多时候非常有用: 比如IIC模拟时序, DS18B20等单总线器件操作等。 而通过delay_us 和delay_ms,则可以方便的提供us和ms的延时服务,这比UCOSII本身提供的延时函数更好用。在 设置SYSTEM_SUPPORT_UCOS为1之后,UCOSII的时钟节拍由SYSTICK的中断服务函数提 供,该部分代码如下: systick 中断服务函数,使用 ucos 时用到 void SysTick_Handler(void) { OSIntEnter();//进入中断 OSTimeTick(); //调用 ucos 的时钟服务程序 OSIntExit(); //触发任务切换软中断 } 以上代码,其中OSIntEnter是进入中断服务函数,用来记录中断嵌套层数(OSIntNesting增 加1);OSTimeTick是系统时钟节拍服务函数,在每个时钟节拍了解每个任务的延时状态,使已经 到达延时时限的非挂起任务进入就绪状态;OSIntExit是退出中断服务函数,该函数可能触发一次 任务切换(当OSIntNesting==0&&调度器未上锁&&就绪表最高优先级任务!=被中断的任务优先级 时),否则继续返回原来的任务执行代码(如果OSIntNesting不为0,则减1)。 事实上,任何中断服务函数,都应该加上OSIntEnter和OSIntExit函数,这是因为 UCOSII是一个可剥夺型的内核,中断服务子程序运行之后,系统会根据情况进行一次任务调度去 运行优先级别最高的就绪任务,而并不一定接着运行被中断的任务! -3 -
#include "sys.h" #include "usart.h" #include "delay.h" #include "led.h" #include "includes.h" ///////////////////////////////////////UCOSII任务设////////////////////////////////////////////// //START 任务 //设置任务优先级 #define START_TASK_PRIO //设置任务堆栈大小 #define START_STK_SIZE //任务堆栈 OS_STK START_TASK_STK[START_STK_SIZE]; //任务函数 void start_task(void *pdata); //LED0任务 //设置任务优先级 #define LED0_TASK_PRIO //设置任务堆栈大小 #define LED0_STK_SIZE //任务堆栈 OS_STK LED0_TASK_STK[LED0_STK_SIZE]; //任务函数 64 7 64 10 //开始任务的优先级设置为最低
-4 -
void led0_task(void *pdata); //LED1任务 //设置任务优先级 #define LED1_TASK_PRIO //设置任务堆栈大小 #define LED1_STK_SIZE //任务堆栈 OS_STK LED1_TASK_STK[LED1_STK_SIZE]; //任务函数 void led1_task(void *pdata); int main(void) { Stm32_Clock_Init(9); //系统时钟设置 delay_init(72); LED_Init(); LED_Init(); OSInit(); OSTaskCreate(start_task, (void *)0, (OS_STK *)&START_TASK_STK[START_STK_SIZE-1], START_TASK_PRIO );//创建起始任务 OSStart(); } //开始任务 void start_task(void *pdata) { OS_CPU_SR cpu_sr=0; pdata = pdata; OS_ENTER_CRITICAL(); OSTaskCreate(led0_task, (void *)0, (OS_STK*)&LED0_TASK_STK[LED0_STK_SIZE-1], LED0_TASK_PRIO); OSTaskCreate(led1_task, (void *)0, (OS_STK*)&LED1_TASK_STK[LED1_STK_SIZE-1], LED1_TASK_PRIO); OSTaskSuspend(START_TASK_PRIO); //挂起起始任务. OS_EXIT_CRITICAL(); } //退出临界区(可以被中断打断) //进入临界区(无法被中断打断) //初始化与LED连接的硬件接口 //延时初始化 64 6
最后,打开 test.c. 该部分代码我们创建了3个任务:start_task、led0_task和led1_task,优先级分别是10、7 和6,堆栈大小都是64(注意OS_STK为32位数据)。我们在main函数只创建了start_task一个任务, 然后在start_task再创建另外两个任务,在创建之后将自身(start_task)挂起。这里,我们单 独创建start_task,是为了提供一个单一任务,实现应用程序开始运行之前的准备工作。 在应用程序中经常有一些代码段必须不受任何干扰地连续运行,这样的代码段叫做临界段。 因此,为了使临界段在运行时不受中断所打断,在临界段代码前必须用关中断指令使CPU屏蔽中断 请求,而在临界段代码后必须用开中断指令解除屏蔽使得CPU可以响应中断请求。UCOSII提 OS_ENTER_CRITICAL和OS_EXIT_CRITICAL两个宏来实现,这两个宏需要在移植UCOSII的时候实现, 采OS_CRITICAL_METHOD为3来实现这两个宏。因为临界段代码不能被中断,将严重影响系统的实时 性,所以临界段代码越短越好! 在start_task任务中,我们在创建led0_task和led1_task的时候,不希望中断打断,故使用 了临界区。其他两个任务,就十分简单了,我们就不细说了,注意我们这里使用的延时函数还是 delay_ms,而不是直接使用的OSTimeDly。 另外,一个任务里面一般是必须有延时函数的,以释 放CPU使用权,否则可能导致低优先级的任务因高优先级的任务不释放CPU使用权而一直无法得到 CPU使用权,从而无法运行。 五、实验程序
龙岩学院物理与机电工程学院
实
班 级 学 室 实验日期
验
号 温
报
姓 名
告
同组人 绩_________
大气压_________成
实验项目:实验二:UCOS-II 任务管理
一、实验目的 1、掌握 UCOS-II 中任务管理的函数的应用。 2、掌握 UCOS-II 在 STM32 平台下对硬件的控制。 3、掌握开发 UCOS-II 应用的程序结构。 二、实验设备 1、PC 机 2、STM32 战舰开发板一台 3、keil4 三、实验步骤 四、1、UCOSII 工作原理 UCOSII提供系统时钟节拍,实现任务切换和任务延时等功能。这个时钟节拍由 OS_TICKS_PER_SEC (在os_cfg.h中定义) 设置, 一般我们设置UCOSII的系统时钟节拍为1ms~100ms。 本次实验利用STM32的SYSTICK定时器来提供UCOSII时钟节拍。 UCOSII 的任何任务都是通过一个叫任务控制块(TCB)的东西来控制的,每个任务管理块有 3 个最重要的参数:(1)任务函数指针;(2)任务堆栈指针;(3)任务优先级。 在UCOSII中,使用CPU的时候,优先级高(数值小)的任务比优先级低的任务具有优先使用权, 即任务就绪表中总是优先级最高的任务获得CPU使用权,只有高优先级的任务让出CPU使用权(比 如延时)时,低优先级的任务才能获得CPU使用权。UCOSII不支持多个任务优先级相同,也就是每 个任务的优先级必须不一样。任务的调度其实就是CPU运行环境的切换,即:PC指针、SP指针和寄 存器组等内容的存取过程 UCOSII的每个任务都是一个死循环。每个任务都处在以下5种状态之一的状态下,这5种状态 是:睡眠状态、就绪状态、运行状态、等待状态(等待某一事件发生)和中断服务状态。 睡眠状态,任务在没有被配备任务控制块或被剥夺了任务控制块时的状态。 就绪状态,系统为任务配备了任务控制块且在任务就绪表中进行了就绪登记,任务已经准备 好了,但由于该任务的优先级比正在运行的任务的优先级低,还暂时不能运行,这时任务的状态 叫做就绪状态。 运行状态,该任务获得CPU使用权,并正在运行中,此时的任务状态叫做运行状态等待状态, -1 -