以下内容转载自安富莱电子:http://forum.armfly.com/forum.php

1、临界段

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

FreeRTOS 源码中就有多处临界段的处理,跟 FreeRTOS 一样,uCOS-II 和 uCOS-III 源码中都是有临界段的,而 RTX 的源码中不存在临界段。另外,除了 FreeRTOS 操作系统源码所带的临界段以外,用户写应用的时候也有临界段的问题,比如以下两种:

2、任务代码临界段处理

  FreeRTOS 任务代码中临界段的进入和退出主要是通过操作寄存器 basepri 实现的。进入临界段前操作寄存器 basepri 关闭了所有小于等于宏定义 configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY所定义的中断优先级,这样临界段代码就不会被中断干扰到,而且实现任务切换功能的 PendSV 中断和滴答定时器中断是最低优先级中断,所以此任务在执行临界段代码期间是不会被其它高优先级任务打断的。退出临界段时重新操作 basepri 寄存器,即打开被关闭的中断(这里我们不考虑不受 FreeRTOS 管理的更高优先级中断)。FreeRTOS 进入和退出临界段的函数如下:

通过上面的两个函数 vPortEnterCritical 和 vPortExitCritical 可以看出,进入临界段和退出临界段是通过函数调用开关中断函数 portENABLE_INTERRUPTS 和 portDISABLE_INTERRUPTS 实现的。细心的读者还会发现上面的这两个函数都对变量 uxCriticalNesting 进行了操作。这个变量比较重要,用于临界段的嵌套计数。初学的同学也许会问这里直接的开关中断不就可以了吗,为什么还要做一个嵌套计数呢?主要是因为直接的开关中断方式不支持在开关中断之间的代码里再次执行开关中断的嵌套处理,假如当前们

的代码是关闭中断的,嵌套了一个含有开关中断的临界区代码后,退出时中断就成开的了,这样就出问题了。通过嵌套计数就有效地防止了用户嵌套调用函数 taskENTER_CRITICAL 和 taskEXIT_CRITICAL 时出错。

经过这么多次的宏定义后,终于来到了最终的原始函数。FreeRTOS 的这种层层调用宏定义的方法在带来便利操作的同时,却让用户在分析源码的时候非常不方便。通过上面的源码实现可以看出,FreeRTOS 的开关全局中断是通过操作寄存器 basepri 实现的,关于这个寄存器,已经在 “FreeRTOS— 中断优先级配置” 进行了详细的讲解,这里不再赘述。

使用举例:
使用的时候一定要保证成对使用

嵌套使用举例:

3 、中 断 服 务 程 序 临 界 段 处 理

  与任务代码里临界段的处理方式类似,中断服务程序里面临界段的处理也有一对开关中断函数。

  

  通过上面的源码可以看出,中断服务程序里面的临界段代码的开关中断也是通过寄存器 basepri 实现的。初学的同学也许会问,这里怎么没有中断嵌套计数了呢?是的,这里换了另外一种实现方法,通过保存和恢复寄存器 basepri 的数值就可以实现嵌套使用。如果大家研究过 uCOS-II 或者 III 的源码,跟这里的实现方式是一样的,具体看下面的使用举例。

  使用举例:

  使用的时候一定要保证成对使用

 

4 、开 关 中 断 的 实 现

  FreeRTOS 也专门提供了一组开关中断函数,实现比较简单,其实就是前面 第2小节里面临界段进入和退出函数的精简版本,主要区别是不支持中断嵌套。具体实现如下:

从上面的源码可以看出,FreeRTOS 的全局中断开关是通过操作寄存器 basepri 实现的,关于这个寄存器,前面已经讲过,这里不再赘述。

  使用举例:

  使用的时候一定要保证成对使用

  

  

5 、BSP 板 级 支 持 包 中 开 关 中 断 的 特 别 处 理

  前面为大家讲解了 FreeRTOS 临界段的处理方法和开关中断方法,加上了 FreeRTOS 操作系统后,我们实际编写的外设驱动又该怎么修改呢?因为外设驱动编写时,有些地方有用到开关中断操作,这里以教程配套的 STM32F103,F407 和 F429 开发板为例进行说明,这三种开发板的外设驱动的编写架构都是统一的,用户只需将 bsp.h 文件里面的宏定义:

临界段开关中断实验,实验现象:

K1键按下 挂起任务VTaskLED

K2键按下 启动单次定时器中断,50ms后在定时器中断将任务vTaskLED恢复

接口消息处理函数:

static void vTaskTaskUserIF(void *pvParameters)
{while(1){    if(ucKeyCode != 0){switch( ucKeyCode){/* K1键按下 挂起任务VTaskLED */case 1:taskENTER_CRITICAL();   /* 进入临界区 */        printf("K2键按下,挂起任务vTaskLED\r\n");                        taskEXIT_CRITICAL();      /* 退出临界区 */    vTaskSuspend(xHandleTaskLED1);            ucKeyCode = 0;break;/* K2键按下 启动单次定时器中断,50ms后在定时器中断将任务vTaskLED恢复 */case 2:taskENTER_CRITICAL();   /* 进入临界区 */                    printf("K3键按下,启动单次定时器中断,50ms后在定时器中断将任务vTaskLED恢复\r\n");taskEXIT_CRITICAL();      /* 退出临界区 */BASIC_TIMx_Mode_Config();ucKeyCode = 0;break;                        /* 其他的键值不处理 */default:                     break;                    }    vTaskDelay(20);}}
}

K1键按下,程序执行case1,把原本闪烁的LED灯挂起。

k2键按下,程序执行case2,然后调用定时器初始化结构体,BASIC_TIMx_Mode_Config();

 void BASIC_TIMx_Mode_Config(void)
{TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;// 开启TIMx_CLK,x[6,7]
  RCC_APB1PeriphClockCmd(BASIC_TIMx_CLK, ENABLE); /* 累计 TIM_Period个后产生一个更新或者中断*/        //当定时器从0计数到49,即为50次,为一个定时周期TIM_TimeBaseStructure.TIM_Period = 50-1;       //定时器时钟源TIMxCLK = 2 * PCLK1  //                PCLK1 = HCLK / 4 //                => TIMxCLK=HCLK/2=SystemCoreClock/2=90MHz// 设定定时器频率为=TIMxCLK/(TIM_Prescaler+1)=1MHzTIM_TimeBaseStructure.TIM_Prescaler = 90-1;     /* T = 50/1000000 = 50us */// 初始化定时器TIMx, x[2,3,4,5]TIM_TimeBaseInit(BASIC_TIMx, &TIM_TimeBaseStructure);// 清除定时器更新中断标志位
    TIM_ClearFlag(BASIC_TIMx, TIM_FLAG_Update);// 开启定时器更新中断
    TIM_ITConfig(BASIC_TIMx,TIM_IT_Update,ENABLE);// 使能定时器
    TIM_Cmd(BASIC_TIMx, ENABLE);
}

定时器开始定时,执行定时器中断函数,

void  BASIC_TIMx_IRQHandler(void)
{if ( TIM_GetITStatus( BASIC_TIMx, TIM_IT_Update) != RESET ) {    ulHighFrequencyTimerTicks++; TimeOut++;TIM_ClearITPendingBit(BASIC_TIMx , TIM_IT_Update);           }if(TimeOut == 1000) //50us*1000 = 50ms
    {TIM_Cmd(BASIC_TIMx, DISABLE);      //不使能定时器,此时定时器不定时TimeOut = 0;                                //让TimeOut重新置零
            BaseType_t xYieldRequired;UBaseType_t uxSavedInterruptStatus;uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();  /* 进入临界区 */{/* 用户可以在这里添加临界段代码,我们这里暂时未用到 */}portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); /* 退出临界区 *//* 恢复挂起任务 */xYieldRequired = xTaskResumeFromISR(xHandleTaskLED1);/* 退出中断后是否需要执行任务切换 */    if( xYieldRequired == pdTRUE ){portYIELD_FROM_ISR(xYieldRequired);}}}

定时器定时到50ms时,TIM_Cmd(BASIC_TIMx, DISABLE);  //不使能定时器,此时定时器不定时。

等到下次按下K2的时候再调用用定时器初始化结构体,并使能定时器 TIM_Cmd(BASIC_TIMx, ENABLE);

notice

/*===========================================可屏蔽的中断优先级配置====================================================*/
/** 用于配置STM32的特殊寄存器basepri寄存器的值,用于屏蔽中断,当大于basepri值的优先级的中断将被全部屏蔽。basepri只有4bit有效,* 默认只为0,即全部中断都没有被屏蔽。configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY配置为:5,意思就是中断优先级大于5的中断都被屏蔽。* 当把配置好的优先级写到寄存器的时候,是按照8bit来写的,所以真正写的时候需要经过转换,公式为:* ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff),其中的priority就是我们配置的真正的优先级。经过这个公式之后得到的是下面的这个宏:* configMAX_SYSCALL_INTERRUPT_PRIORITY** 在FreeRTOS中,关中断是通过配置basepri寄存器来实现的,关掉的中断由配置的basepri的值决定,小于basepri值的* 中断FreeRTOS是关不掉的,这样做的好处是系统设计者可以人为的控制那些非常重要的中断不能被关闭,在紧要的关头必须被响应。* 而在UCOS中,关中断是通过控制PRIMASK来实现的,PRIMASK是一个单1的二进制位,写1则除能除了NMI和硬 fault的所有中断。当UCOS关闭* 中断之后,即使是你在系统中设计的非常紧急的中断来了都不能马上响应,这加大了中断延迟的时间,如果是性命攸关的场合,那后果估计挺严重。* 相比UCOS的关中断的设计,FreeRTOS的设计则显得人性化很多。**/
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY    2
#define configMAX_SYSCALL_INTERRUPT_PRIORITY     ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )

FreeRTOS — 临界段和开关中断相关推荐

  1. FreeRTOS 临界段和开关中断

    以下转载自安富莱电子: http://forum.armfly.com/forum.php 临界段 代码的临界段也称为临界区,一旦这部分代码开始执行,则不允许任何中断打断.为确保临界段代码 的执行不被 ...

  2. freertos临界段保护

    freertos临界段保护 中断的基础知识 cortex-m里面开中断.关中断指令 关中断和开中断 进入临界段和退出临界段 中断的基础知识 嵌套: 嵌套向量中断控制器 NVIC(Nested Vect ...

  3. FreeRTOS临界段的保护

    什么是临界段 临界段用一句话概括就是一段在执行的时候不能被中断的代码段.在 FreeRTOS 里面, 这个临界段最常出现的就是对全局变量的操作,全局变量就好像是一个枪把子,谁都可以 对他开枪,但是我开 ...

  4. FreeRTOS临界段

    临界断代码也叫做临界区,是指那些必须完整运行,不能被打断的代码段,FreeRTOS与临界断代码保护有关的函数有4个: taskENTER_CRITICAL() --任务级进入临界段 taskEXIT_ ...

  5. FreeRTOS源码分析与应用开发01:中断配置与临界段

    目录 1. 异常与中断的基本概念 1.1 异常分类 1.2 中断概述 1.2.1 中断处理宜短暂 1.2.2 临界段影响中断实时性 1.3 中断硬件基础 1.3.1 外设 1.3.2 中断控制器 1. ...

  6. 【FreeRTOS】06 临界段的保护——关中断和关调度

    本节来讲一讲FreeRTOS如何保护临界段,先讲临界段的概念,再讲保护临界段的方法. 1)临界段的概念 简单来讲,临界段是一段执行时不允许被中断(或其他任务)打断的代码:如果被打断,就有可能运行出错. ...

  7. FreeRTOS内核详解(1) —— 临界段保护原理

    什么是临界段 临界段用一句话概括就是一段在执行的时候不能被中断的代码段. 在 FreeRTOS 里面,这个临界段最常出现的就是对全局变量的操作,由于不同任务间可以切换运行,当一个任务在访问某个全局变量 ...

  8. FreeRTOS内核实现03:临界段的保护

    目录 1. 临界段概述 1.1 什么是临界段 1.2 临界段何时会被打断 2. 关中断操作 2.1 不带中断保护 2.2 带中断保护 3. 开中断操作 4. 进入临界段操作 4.1 不带中断保护 4. ...

  9. FreeRTOS中断配置与临界段

    Cortex-M中断 中断是指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序(中断服务程序),处理完毕后又返回原被暂停的程序继续运行.Cortex- ...

最新文章

  1. 初始化Mysql系统报错,begin failesd--conpilation aborted at scripts........
  2. 大端和小端的区别,以及如何判断一台机器是大端还是小端?
  3. 【STM32】GPIO模拟I2C程序示例
  4. IIS 之 Asp.Net项目内部运行详解
  5. 史上最全的 MySQL 高性能优化实战总结
  6. vue底部选择器_vue实现动态显示与隐藏底部导航的方法分析
  7. java 上调下移_java – 在调整框架大小时,JLabel的位置会更...
  8. 【算法学习笔记】16.暴力求解法04 回溯法03 剪枝法 带宽
  9. matlab08a调节字体大小,MATLAB低通滤波器的设计代码
  10. 如何让远程数据库中的1张表导入到本地数据库中
  11. linux 内核 内存管理 bootmem alloctor 的初始化
  12. 智能实验室-全能优化(Guardio) 4.6.0.760
  13. java实例 之 商品管理系统
  14. phpstudy php redis,phpstudy拓展redis
  15. acdsee 5.0简体中文免费下载【非常流行的看图工具】
  16. python参数类型为uint8,将图像数据类型从uint16转换为uint8
  17. 计算机音乐谱子 追光者,精选追光者简谱
  18. 红米怎么打开USB调试(Android4.2.2),系统中隐藏开发者选项(红米,MTK6589,三星i9500 /S4打开开发者选项)
  19. 2022年如何解锁nexus6p的bl锁
  20. 华为云,奔跑的感觉爽吗?

热门文章

  1. 封装运动框架多个属性
  2. 下载服务器文件到本地
  3. Hibernate中Entity实体类的写法
  4. 深入super,看Python如何解决钻石继承难题
  5. Spring学习笔记(三) AOP_annotation,AOP_XML
  6. Synbak 2.1 发布,系统备份工具
  7. Ruby 三元一次线性方程组
  8. 《Shell脚本学习指南》第四章 文本处理工具
  9. linux tar命令 打包 解压
  10. es java match_ES multi_match 和match查询