加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

【RTX操作系统教程】第11章 临界段,任务锁和中断锁

(2015-12-28 17:18:57)
标签:

cosiii

emwin

rtx

stm32f407

安富莱

分类: RTX及其中间件

第11章     临界段,任务锁和中断锁

本章教程为大家讲解几个重要的概念,临界段,任务锁和中断锁。

本章教程配套的例子含Cortex-M3内核的STM32F103Cortex-M4内核的STM32F407

11.1 临界段

11.2 中断锁

11.3 任务锁

11.4 RTX任务锁的实现

11.5       实验例程说明

 

11.1  临界段

代码的临界段也称为临界区,一旦这部分代码开始执行,则不允许任何中断打断。为确保临界段代码的执行不被中断,在进入临界段之前须关中断,而临界段代码执行完毕后,要立即开中断。

由于Cortex-M3/M4RTX内核库中没有关闭中断的操作,也就是说RTX的源码中不存在临界段。如果源码中有临界段的话,会给系统带来什么问题呢?比如此时某个任务正在调用系统API函数,而且此时中断正好关闭了,也就是进入到了临界区中,这个时候如果有一个紧急的中断事件被触发,这个中断就不能得到及时执行,必须等到中断开启才可以得到执行,如果关中断时间超过了紧急中断能够容忍的限度,危害是可想而知的。像uCOS-IIuCOS-IIIFreeRTOS的源码中都是有临界段的。

除了上面说的操作系统源码所带的临界段以外,用户写应用的时候也有临界段的问题,比如以下两种:

u  读取或者修改变量(特别是任务间通信的全局变量)的代码,一般来说这是最常见的临界代码。

u  调用公共函数的代码,特别是不可重入的函数,如果多个任务都访问这个函数,结果是可想而知的。

总之,对于临界段要做到执行时间越短越好,要不会影响系统的实时性。

11.2 中断锁

中断锁就是RTOS提供的开关中断函数,因为Cortex-M3/M4RTX源码中没有关闭中断的操作,所以也就没有提供开关中断函数。

由于RTX没有提供开关中断函数,如果用户自己的应用代码需要开关中断的话怎么办呢?裸机时如何开关中断的,在使用了RTX后仍然使用以前的开关中断函数即可。

11.3 任务锁

简单的说,为了防止当前任务的执行被其它高优先级的任务打断而提供的锁机制就是任务锁。实现任务锁可以通过给调度器加锁或者直接关闭RTOS内核定时器(就是前面一直说的系统滴答定时器)来实现。

u  通过给调度器加锁实现

给调度器加锁的话,就无法实现任务切换,高优先级任务也就无法抢占低优先级任务的执行,同时高优先级任务也是无法向低优先级任务切换的。像uCOS-IIuCOS-III是采用的这种方法实现任务锁。特别注意,这种方式只是禁止了调度器工作,并没有关闭任何中断。

u  通过关闭RTOS内核定时器实现

关闭了RTOS内核定时器的话,也就关闭了通过RTOS内核定时器中断实现任务切换的功能,因为在退出定时器中断时需要检测当前需要执行的最高优先级任务,如果有高优先级任务就绪的话需要做任务切换。RTX操作系统是采用的这种方式实现任务锁的。

11.4RTX任务锁的实现

使用如下2个函数可以实现RTX任务的开锁和解锁:

u  tsk_lock

u  tsk_unlock

关于这2个函数的讲解及其使用方法可以看教程第33.3小节里面说的参考资料rlarm.chm文件

【RTX操作系统教程】第11章 <wbr>临界段,任务锁和中断锁

 

下面我们也对这两个函数做一下讲解。

11.4.1函数tsk_lock

函数原型:

void tsk_lock (void);

函数描述:

函数tsk_lock用于禁止RTX内核定时器中断,因此也就禁止了任务切换。

使用这个函数要注意以下问题:

1.     函数tsk_lock不支持嵌套调用。

2.     不允许在中断服务程序中调用tsk_lock

3.     RTX内核定时器被关闭期间,RTX内核任务调度器和需要时间片调度的任务被阻塞。设置的任务延迟时间不再工作。因此,强烈建议关RTX内核定时器中断的时间越短越好。

使用举例:

#include

 

void protect_critical_op () {

  tsk_lock ();

  do_critical_op ();

  tsk_unlock ();

}

11.4.2函数tsk_unlock

函数原型:

void tsk_unlock (void);

函数描述:

函数tsk_unlock用于使能RTX内核定时器中断,因此也就重新开启任务切换。注意tsk_unlock一定要跟tsk_lock配套使用。

使用这个函数要注意以下问题:

1.     函数tsk_lock不支持嵌套调用。

2.     不允许在中断服务程序中调用tsk_lock

使用举例:

#include

 

void protect_critical_op () {

  tsk_lock ();

  do_critical_op ();

  tsk_unlock ();

}

11.5实验例程说明

11.5.1STM32F103开发板实验

配套例子:

V4-407_RTX实验_任务锁

实验目的:

1. 学习RTX的任务锁使用

2. RTX的任务锁是通过关闭系统时钟节拍中断实现的

实验内容:

1. K1按键按下,串口打印。

2. 在调用printf函数的地方都加上任务锁,防止多个任务调用此函数造成冲突,以至于串口打印出现乱码。

3. 各个任务实现的功能如下:

AppTaskUserIF任务  :按键消息处理。

AppTaskLED任务     LED闪烁,并串口打印任务正在运行。

AppTaskMsgPro任务 :消息处理,这里是用作LED闪烁和串口打印任务正在运行。

AppTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。

RTX配置:

RTX配置向导详情如下:

【RTX操作系统教程】第11章 <wbr>临界段,任务锁和中断锁

 

u  Task Configuration

l  Number of concurrent running tasks

允许创建4个任务,实际创建了如下四个任务

              AppTaskUserIF任务   :按键消息处理。

              AppTaskLED任务     LED闪烁。

              AppTaskMsgPro任务 :消息处理,这里是用作LED闪烁和串口打印任务正在运行。

              AppTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。

l  Number of tasks with user-provided stack

创建的4个任务都是采用自定义堆栈方式。

RTX任务调试信息:

【RTX操作系统教程】第11章 <wbr>临界段,任务锁和中断锁

 

程序设计:

u  任务栈大小分配:

static uint64_t AppTaskUserIFStk[512/8];   

static uint64_t AppTaskLEDStk[256/8];      

static uint64_t AppTaskMsgProStk[512/8]; 

static uint64_t AppTaskStartStk[512/8];     

将任务栈定义成uint64_t类型可以保证任务栈是8字节对齐的,8字节对齐的含义就是数组的首地址对8求余等于0。如果不做8字节对齐的话,部分C语言库函数,浮点运算和uint64_t类型数据运算会出问题。

u  系统栈大小分配:

【RTX操作系统教程】第11章 <wbr>临界段,任务锁和中断锁

 

u  RTX初始化:

 

int main (void)

  

    

     bsp_Init();

    

    

     os_sys_init_user (AppTaskStart,            

                       4,                       

                       &AppTaskStartStk,        

                       sizeof(AppTaskStartStk));

     while(1);

}

u  RTX任务创建:

 

static void AppTaskCreate (void)

{

     HandleTaskUserIF = os_tsk_create_user(AppTaskUserIF,            

                                           1,                        

                                           &AppTaskUserIFStk,        

                                           sizeof(AppTaskUserIFStk));

    

     HandleTaskLED = os_tsk_create_user(AppTaskLED,             

                                        2,                      

                                        &AppTaskLEDStk,         

                                        sizeof(AppTaskLEDStk)); 

    

     HandleTaskMsgPro = os_tsk_create_user(AppTaskMsgPro,            

                                           3,                        

                                           &AppTaskMsgProStk,        

                                           sizeof(AppTaskMsgProStk));

}

u  四个RTX任务的实现:

 

__task void AppTaskUserIF(void)

{

     uint8_t ucKeyCode;

 

    while(1)

    {

         ucKeyCode = bsp_GetKey();

        

         if (ucKeyCode != KEY_NONE)

         {

              switch (ucKeyCode)

              {

                  

                   case KEY_DOWN_K1:

                      

                       tsk_lock();

                       printf("K1键按下,使用MDK中自带的RTX调试组件,请务必使用MDK4.74版本进行调试\r\n");

                      

                       tsk_unlock();

                       break;                     

                  

                  

                  

                   default:                     

                       break;

              }

         }

        

         os_dly_wait(20);

     }

}

 

 

__task void AppTaskLED(void)

{

    while(1)

    {

         bsp_LedToggle(2);

         bsp_LedToggle(3); 

        

        

         tsk_lock();

         printf("任务AppTaskLED正在运行\r\n");

        

         tsk_unlock();

        os_dly_wait(1500);

    }

}

 

 

__task void AppTaskMsgPro(void)

{

    while(1)

    {

         bsp_LedToggle(1);

         bsp_LedToggle(4);

        

        

         tsk_lock();

         printf("任务AppTaskMsgPro正在运行\r\n");

        

         tsk_unlock();

        os_dly_wait(300);

    }

}

 

 

__task void AppTaskStart(void)

{

     AppTaskCreate();

    

    while(1)

    {

        

         bsp_KeyScan();

        os_dly_wait(10);

    }

}

11.5.2STM32F407开发板实验

配套例子:

V5-407_RTX实验_任务锁

实验目的:

1. 学习RTX的任务锁使用

2. RTX的任务锁是通过关闭系统时钟节拍中断实现的

实验内容:

1. K1按键按下,串口打印。

2. 在调用printf函数的地方都加上任务锁,防止多个任务调用此函数造成冲突,以至于串口打印出现乱码。

3. 各个任务实现的功能如下:

AppTaskUserIF任务  :按键消息处理。

AppTaskLED任务     LED闪烁,并串口打印任务正在运行。

AppTaskMsgPro任务 :消息处理,这里是用作LED闪烁和串口打印任务正在运行。

AppTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。

RTX配置:

RTX配置向导详情如下:

【RTX操作系统教程】第11章 <wbr>临界段,任务锁和中断锁

 

u  Task Configuration

l  Number of concurrent running tasks

允许创建4个任务,实际创建了如下四个任务

              AppTaskUserIF任务   :按键消息处理。

              AppTaskLED任务     LED闪烁。

              AppTaskMsgPro任务 :消息处理,这里是用作LED闪烁和串口打印任务正在运行。

              AppTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。

l  Number of tasks with user-provided stack

创建的4个任务都是采用自定义堆栈方式。

RTX任务调试信息:

【RTX操作系统教程】第11章 <wbr>临界段,任务锁和中断锁

 

程序设计:

u  任务栈大小分配:

static uint64_t AppTaskUserIFStk[512/8];   

static uint64_t AppTaskLEDStk[256/8];      

static uint64_t AppTaskMsgProStk[512/8]; 

static uint64_t AppTaskStartStk[512/8];     

将任务栈定义成uint64_t类型可以保证任务栈是8字节对齐的,8字节对齐的含义就是数组的首地址对8求余等于0。如果不做8字节对齐的话,部分C语言库函数,浮点运算和uint64_t类型数据运算会出问题。

u  系统栈大小分配:

【RTX操作系统教程】第11章 <wbr>临界段,任务锁和中断锁

 

u  RTX初始化:

 

int main (void)

  

    

     bsp_Init();

    

    

     os_sys_init_user (AppTaskStart,            

                       4,                        

                       &AppTaskStartStk,        

                       sizeof(AppTaskStartStk));

     while(1);

}

u  RTX任务创建:

 

static void AppTaskCreate (void)

{

     HandleTaskUserIF = os_tsk_create_user(AppTaskUserIF,            

                                           1,                        

                                           &AppTaskUserIFStk,        

                                           sizeof(AppTaskUserIFStk));

    

     HandleTaskLED = os_tsk_create_user(AppTaskLED,             

                                        2,                      

                                        &AppTaskLEDStk,         

                                        sizeof(AppTaskLEDStk)); 

    

     HandleTaskMsgPro = os_tsk_create_user(AppTaskMsgPro,            

                                           3,                        

                                           &AppTaskMsgProStk,        

                                           sizeof(AppTaskMsgProStk));

}

u  四个RTX任务的实现:

 

__task void AppTaskUserIF(void)

{

     uint8_t ucKeyCode;

 

    while(1)

    {

         ucKeyCode = bsp_GetKey();

        

         if (ucKeyCode != KEY_NONE)

         {

              switch (ucKeyCode)

              {

                  

                   case KEY_DOWN_K1:

                      

                       tsk_lock();

                       printf("K1键按下,使用MDK中自带的RTX调试组件,请务必使用MDK4.74版本进行调试\r\n");

                      

                       tsk_unlock();

                       break;                     

                  

                  

                  

                   default:                    

                       break;

              }

         }

        

         os_dly_wait(20);

     }

}

 

 

__task void AppTaskLED(void)

{

    while(1)

    {

         bsp_LedToggle(2);

         bsp_LedToggle(3); 

        

        

         tsk_lock();

         printf("任务AppTaskLED正在运行\r\n");

        

         tsk_unlock();

        os_dly_wait(1500);

    }

}

 

 

__task void AppTaskMsgPro(void)

{

    while(1)

    {

         bsp_LedToggle(1);

         bsp_LedToggle(4);

        

        

         tsk_lock();

         printf("任务AppTaskMsgPro正在运行\r\n");

        

         tsk_unlock();

        os_dly_wait(300);

    }

}

 

 

__task void AppTaskStart(void)

{

     AppTaskCreate();

    

    while(1)

    {

        

         bsp_KeyScan();

        os_dly_wait(10);

    }

}

11.6总结

本章节主要给大家介绍了一下RTOS方面的几个重要概念,更多这方面的知识可以查阅相关资料进行学习。

 

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有