硬汉嵌入式论坛

 找回密码
 立即注册
查看: 4910|回复: 8
收起左侧

[FreeRTOS教程] 第15章 FreeRTOS临界段和开关中断

[复制链接]

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
发表于 2016-8-24 14:41:54 | 显示全部楼层 |阅读模式



第15章      FreeRTOS临界段和开关中断


    本章教程为大家讲解两个重要的概念,FreeRTOS的临界段和开关中断。
    本章教程配套的例子含Cortex-M3内核的STM32F103和Cortex-M4内核的STM32F407以及F429。
15.1 临界段
15.2 任务代码临界段处理
15.3 中断服务程序临界段处理
15.4 开关中断的实现
15.5 BSP板级支持包中开关中断的特别处理
15.6. 实验例程说明
15.7      总结


15.1  临界段

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

    注:FreeRTOS临界段相关知识补充
    FreeRTOS的源码中有多处临界段的地方,临界段虽然保护了关键代码的执行不被打断,但也会影响系统的实时性。比如此时某个任务正在调用系统API函数,而且此时中断正好关闭了,也就是进入到了临界区中,这个时候如果有一个紧急的中断事件被触发,这个中断就不能得到及时执行,必须等到中断开启才可以得到执行,如果关中断时间超过了紧急中断能够容忍的限度,危害是可想而知的。

    FreeRTOS源码中就有多处临界段的处理,跟FreeRTOS一样,uCOS-II和uCOS-III源码中都是有临界段的,而RTX的源码中不存在临界段。另外,除了FreeRTOS操作系统源码所带的临界段以外,用户写应用的时候也有临界段的问题,比如以下两种:
    (1)读取或者修改变量(特别是用于任务间通信的全局变量)的代码,一般来说这是最常见的临界代码。
    (2)调用公共函数的代码,特别是不可重入的函数,如果多个任务都访问这个函数,结果是可想而知的。
    总之,对于临界段要做到执行时间越短越好,否则会影响系统的实时性。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-8-24 14:47:45 | 显示全部楼层
15.2 任务代码临界段处理


    FreeRTOS任务代码中临界段的进入和退出主要是通过操作寄存器basepri实现的。进入临界段前操作寄存器basepri关闭了所有小于等于宏定义configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY所定义的中断优先级,这样临界段代码就不会被中断干扰到,而且实现任务切换功能的PendSV中断和滴答定时器中断是最低优先级中断,所以此任务在执行临界段代码期间是不会被其它高优先级任务打断的。退出临界段时重新操作basepri寄存器,即打开被关闭的中断这里我们不考虑不受FreeRTOS管理的更高优先级中断。FreeRTOS进入和退出临界段的函数如下:
  1. #define taskENTER_CRITICAL()         portENTER_CRITICAL()
  2. #define taskEXIT_CRITICAL()          portEXIT_CRITICAL()
复制代码
上面这两个函数是供用户调用的,其中函数taskENTER_CRITICAL是进入临界段,函数taskEXIT_CRITICAL是退出临界段。进一步跟踪宏定义的实现如下:
  1. #define portENTER_CRITICAL()         vPortEnterCritical()
  2. #define portEXIT_CRITICAL()          vPortExitCritical()
复制代码
再进一步跟踪宏定义的实现如下:
  1. void vPortEnterCritical( void )
  2. {
  3.      portDISABLE_INTERRUPTS();
  4.      uxCriticalNesting++;
  5.      /* This is not the interrupt safe version of the enter critical function so
  6.      assert() if it is being called from an interrupt context.  Only API
  7.      functions that end in "FromISR" can be used in an interrupt.  Only assert if
  8.      the critical nesting count is 1 to protect against recursive calls if the
  9.      assert function also uses a critical section. */
  10.      if( uxCriticalNesting == 1 )
  11.      {
  12.          configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
  13.      }
  14. }
  15. /*-----------------------------------------------------------*/
  16. void vPortExitCritical( void )
  17. {
  18.      configASSERT( uxCriticalNesting );
  19.      uxCriticalNesting--;
  20.      if( uxCriticalNesting == 0 )
  21.      {
  22.          portENABLE_INTERRUPTS();
  23.      }
  24. }
复制代码
通过上面的两个函数vPortEnterCritical和vPortExitCritical可以看出,进入临界段和退出临界段是通过函数调用开关中断函数portENABLE_INTERRUPTS和portDISABLE_INTERRUPTS实现的。细心的读者还会发现上面的这两个函数都对变量uxCriticalNesting进行了操作。这个变量比较重要,用于临界段的嵌套计数。初学的同学也许会问这里直接的开关中断不就可以了吗,为什么还要做一个嵌套计数呢?主要是因为直接的开关中断方式不支持在开关中断之间的代码里再次执行开关中断的嵌套处理,假如当前我们的代码是关闭中断的,嵌套了一个含有开关中断的临界区代码后,退出时中断就成开的了,这样就出问题了。通过嵌套计数就有效地防止了用户嵌套调用函数taskENTER_CRITICAL和taskEXIT_CRITICAL时出错。
    注:直接的开关中断方式不支持嵌套调用例子说明
      比如下面的例子:
      void FunctionA()
      {
       taskDISABLE_INTERRUPTS();  关闭中断
       FunctionB(); 调用函数B
       FunctionC(); 调用函数C
       taskENABLE_INTERRUPTS();  打开中断
      }

    void FunctionB()
      {
        taskDISABLE_INTERRUPTS();  关闭中断
        代码
        taskENABLE_INTERRUPTS();  打开中断
       }
         工程中调用了FunctionA就会出现执行完FunctionB后中断被打开的情况,此时FunctionC将不被保护了。

接下来继续说明开关中断的实现,我们要打破砂锅问到底:
  1. #define portDISABLE_INTERRUPTS()          vPortRaiseBASEPRI()
  2. #define portENABLE_INTERRUPTS()           vPortSetBASEPRI( 0 )
复制代码
函数vPortRaiseBASEPRI和vPortSetBASEPRI的源码实现如下:
  1. static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
  2. {
  3.      __asm
  4.      {
  5.          /* Barrier instructions are not used as this function is only used to
  6.          lower the BASEPRI value. */
  7.          msr basepri, ulBASEPRI
  8.      }
  9. }
  10. /*-----------------------------------------------------------*/
  11. static portFORCE_INLINE void vPortRaiseBASEPRI( void )
  12. {
  13. uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
  14.      __asm
  15.      {
  16.           /* Set BASEPRI to the max syscall priority to effect a critical
  17.          section. */
  18.          msr basepri, ulNewBASEPRI
  19.          dsb
  20.          isb
  21.      }
  22. }
复制代码
经过这么多次的宏定义后,终于来到了最终的原始函数。FreeRTOS的这种层层调用宏定义的方法在带来便利操作的同时,却让用户在分析源码的时候非常不方便。
    通过上面的源码实现可以看出,FreeRTOS的开关全局中断是通过操作寄存器basepri实现的,关于这个寄存器,我们已经在第12章进行了详细的讲解,这里不再赘述。
使用举例:
使用的时候一定要保证成对使用
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskLED
  4. *    功能说明: LED闪烁和串口打印
  5. *    形    参: pvParameters 是在创建该任务时传递的形参
  6. *    返 回 值: 无
  7. *   优 先 级: 2
  8. *********************************************************************************************************
  9. */
  10. static void vTaskLED(void *pvParameters)
  11. {
  12.      TickType_t xLastWakeTime;
  13.      const TickType_t xFrequency = 200;
  14.      /* 获取当前的系统时间 */
  15.     xLastWakeTime = xTaskGetTickCount();
  16.    
  17.     while(1)
  18.     {
  19.           /* 进入临界区 */  
  20.          taskENTER_CRITICAL();
  21.          printf("任务vTaskLED正在运行\\r\\n");
  22.           /* 退出临界区 */  
  23.          taskEXIT_CRITICAL();
  24.          bsp_LedToggle(2);
  25.          bsp_LedToggle(3);
  26.         
  27.         /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/
  28.         vTaskDelayUntil(&xLastWakeTime, xFrequency);
  29.     }
  30. }
复制代码
嵌套使用举例:
  1. void FunctionB()
  2. {
  3.      taskENTER_CRITICAL()/* 进入临界区 */
  4.      临界段代码
  5.      taskEXIT_CRITICAL();/* 退出临界区 */
  6. }
  7. void FunctionA()
  8. {
  9.      taskENTER_CRITICAL();  /* 进入临界区 */
  10. FunctionB();            /* 临界段代码--调用函数B  */
  11. FunctionC();            /* 临界段代码--调用函数C  */   
  12.      taskEXIT_CRITICAL();    /* 退出临界区 */
  13. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-8-24 14:51:51 | 显示全部楼层
15.3 中断服务程序临界段处理


    与任务代码里临界段的处理方式类似,中断服务程序里面临界段的处理也有一对开关中断函数。
  1. #define taskENTER_CRITICAL_FROM_ISR()     portSET_INTERRUPT_MASK_FROM_ISR()
  2. #define taskEXIT_CRITICAL_FROM_ISR( x )   portCLEAR_INTERRUPT_MASK_FROM_ISR( x )
复制代码
进一步跟踪宏定义的实现如下:
  1. #define portSET_INTERRUPT_MASK_FROM_ISR()      ulPortRaiseBASEPRI()
  2. #define portCLEAR_INTERRUPT_MASK_FROM_ISR(x)   vPortSetBASEPRI(x)
复制代码
再进一步跟踪宏定义的实现如下:
  1. static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
  2. {
  3.      __asm
  4.      {
  5.          /* Barrier instructions are not used as this function is only used to
  6.          lower the BASEPRI value. */
  7.          msr basepri, ulBASEPRI
  8.      }
  9. }
  10. /*-----------------------------------------------------------*/
  11. static portFORCE_INLINE uint32_t ulPortRaiseBASEPRI( void )
  12. {
  13. uint32_t ulReturn, ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
  14.      __asm
  15.      {
  16.          /* Set BASEPRI to the max syscall priority to effect a critical
  17.          section. */
  18.          mrs ulReturn, basepri
  19.          msr basepri, ulNewBASEPRI
  20.          dsb
  21.          isb
  22.      }
  23.      return ulReturn;
  24. }
复制代码
    通过上面的源码可以看出,中断服务程序里面的临界段代码的开关中断也是通过寄存器basepri实现的。
    初学的同学也许会问,这里怎么没有中断嵌套计数了呢?是的,这里换了另外一种实现方法,通过保存和恢复寄存器basepri的数值就可以实现嵌套使用。如果大家研究过uCOS-II或者III的源码,跟这里的实现方式是一样的,具体看下面的使用举例。
使用举例:
使用的时候一定要保证成对使用
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: TIM6_DAC_IRQHandler
  4. *    功能说明: TIM6中断服务程序。
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void TIM6_DAC_IRQHandler( void )
  10. {
  11. UBaseType_t uxSavedInterruptStatus;
  12. uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); /* 进入临界区 */
  13.      临界区代码
  14. portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); /* 退出临界区 */
  15. }
复制代码
嵌套使用举例:
  1. void FunctionB()
  2. {
  3. UBaseType_t uxSavedInterruptStatus;
  4. uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); /* 进入临界区 */
  5.      临界区代码
  6. portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); /* 退出临界区 */
  7. }
  8. /*
  9. *********************************************************************************************************
  10. *    函 数 名: TIM6_DAC_IRQHandler
  11. *    功能说明: TIM6中断服务程序。
  12. *    形    参: 无
  13. *    返 回 值: 无
  14. *********************************************************************************************************
  15. */
  16. void TIM6_DAC_IRQHandler( void )
  17. {
  18. UBaseType_t uxSavedInterruptStatus;
  19. uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); /* 进入临界区 */
  20. FunctionB();  /* 临界段代码--调用函数B  */
  21. FunctionC();  /* 临界段代码--调用函数C  */   
  22. portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); /* 退出临界区 */
  23. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-8-24 14:54:21 | 显示全部楼层
15.4   开关中断的实现


    FreeRTOS也专门提供了一组开关中断函数,实现比较简单,其实就是前面15.2小节里面临界段进入和退出函数的精简版本,主要区别是不支持中断嵌套。具体实现如下:
  1. #define taskDISABLE_INTERRUPTS()   portDISABLE_INTERRUPTS()
  2. #define taskENABLE_INTERRUPTS()    portENABLE_INTERRUPTS()
复制代码
进一步跟踪宏定义的实现如下:
  1. #define portDISABLE_INTERRUPTS()          vPortRaiseBASEPRI()
  2. #define portENABLE_INTERRUPTS()           vPortSetBASEPRI( 0 )
复制代码
函数vPortRaiseBASEPRI和vPortSetBASEPRI的源码实现如下:
  1. static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
  2. {
  3.      __asm
  4.      {
  5.          /* Barrier instructions are not used as this function is only used to
  6.          lower the BASEPRI value. */
  7.          msr basepri, ulBASEPRI
  8.      }
  9. }
  10. /*-----------------------------------------------------------*/
  11. static portFORCE_INLINE void vPortRaiseBASEPRI( void )
  12. {
  13. uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
  14.      __asm
  15.      {
  16.           /* Set BASEPRI to the max syscall priority to effect a critical
  17.          section. */
  18.          msr basepri, ulNewBASEPRI
  19.          dsb
  20.          isb
  21.      }
  22. }
复制代码
从上面的源码可以看出,FreeRTOS的全局中断开关是通过操作寄存器basepri实现的,关于这个寄存器,我们已经在第12章进行了详细的讲解,这里不再赘述。
使用举例:
使用的时候一定要保证成对使用
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskLED
  4. *    功能说明: LED闪烁和串口打印
  5. *    形    参: pvParameters 是在创建该任务时传递的形参
  6. *    返 回 值: 无
  7. *   优 先 级: 2
  8. *********************************************************************************************************
  9. */
  10. static void vTaskLED(void *pvParameters)
  11. {
  12.      TickType_t xLastWakeTime;
  13.      const TickType_t xFrequency = 200;
  14.      /* 获取当前的系统时间 */
  15.     xLastWakeTime = xTaskGetTickCount();
  16.    
  17.     while(1)
  18.     {
  19.           /* 关闭中断 */   
  20.          taskDISABLE_INTERRUPTS();
  21.          printf("任务vTaskLED正在运行\\r\\n");
  22.           /* 开启中断 */   
  23.           taskENABLE_INTERRUPTS();
  24.          bsp_LedToggle(2);
  25.          bsp_LedToggle(3);
  26.         
  27.         /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/
  28.         vTaskDelayUntil(&xLastWakeTime, xFrequency);
  29.     }
  30. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-8-24 14:56:22 | 显示全部楼层
15.5   BSP板级支持包中开关中断的特别处理


    前面为大家讲解了FreeRTOS临界段的处理方法和开关中断方法,加上了FreeRTOS操作系统后,我们实际编写的外设驱动又该怎么修改呢?因为外设驱动编写时,有些地方有用到开关中断操作,这里以此教程配套的STM32F103,F407和F429开发板为例进行说明,这三种开发板的外设驱动的编写架构都是统一的,用户只需将bsp.h文件里面的宏定义:
  1. /* 开关全局中断的宏 */
  2. #define ENABLE_INT()   __set_PRIMASK(0)   /* 使能全局中断 */
  3. #define DISABLE_INT()  __set_PRIMASK(1)   /* 禁止全局中断 */
复制代码
修改为如下的形式:
  1. #define  USE_FreeRTOS      1
  2. #if USE_FreeRTOS == 1
  3.      #include "FreeRTOS.h"
  4.      #include "task.h"
  5.      #define DISABLE_INT()    taskENTER_CRITICAL()
  6.      #define ENABLE_INT()     taskEXIT_CRITICAL()
  7. #else
  8.      /* 开关全局中断的宏 */
  9.      #define ENABLE_INT()   __set_PRIMASK(0)   /* 使能全局中断 */
  10.      #define DISABLE_INT()  __set_PRIMASK(1)   /* 禁止全局中断 */
  11. #endif
复制代码

    (1)#define  USE_FreeRTOS      1

    将中断开关设置改成了条件编译的形式,这样在使用裸机或者使用FreeRTOS时,切换自如。此宏定义配置为1表示使用FreeRTOS的开关中断API函数,配置为0表示使用裸机的方式开关中断。
(2)采用taskENTER_CRITICAL()taskEXIT_CRITICAL()实现开关中断
    因为BSP驱动包的源码基本没有在中断里面进行开关中断,都是在中断以外,所以开关中断是采用的任务代码里面临界段的处理函数,而且支持嵌套调用。
    大家写的工程代码也可以采用类似的方案,方便裸机和FreeRTOS的切换,或者采用其它适合自己的方案。另外要注意,因为FreeRTOS存在不受其控制的更高优先级中断,用户需要根据实际情况进行特别处理,可以不采用FreeRTOS的开关中断函数,而是直接使用__set_PRIMASK实现全局中断的开关。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-8-24 15:02:38 | 显示全部楼层
15.6 实验例程说明



15.6.1 STM32F103开发板实验


配套例子:
    V4-309_FreeRTOS实验_临界段和开关中断
实验目的:
    1.     学习FreeRTOS的临界段和开关中断设置
实验内容:
    1.     K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。
    2.     K2按键按下,挂起任务vTaskLED。
    3.     K3按键按下,启动单次定时器中断,50ms后在定时器中断将任务vTaskLED恢复。
    4.     本实验printf函数的多任务调用通过任务代码中临界段的进入和退出函数taskENTER_CRITICAL()和taskEXIT_CRITICAL()实现互斥效果。

    5.     本实验中断服务程序中临界段的调用通过函数portSET_INTERRUPT_MASK_FROM_ISR()和portCLEAR_INTERRUPT_MASK_FROM_ISR()实现保护。
    6.     各个任务实现的功能如下:
              vTaskUserIF任务   :按键消息处理。
              vTaskLED任务     :LED闪烁和串口打印。
              vTaskMsgPro任务 :消息处理,这里是用作LED闪烁和串口打印。
              vTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。
FreeRTOS的配置:
    FreeRTOSConfig.h文件中的配置如下:
  1. /* Ensure stdint is only used by the compiler, and not the assembler. */
  2. #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
  3. #include <stdint.h>
  4. extern volatile uint32_t ulHighFrequencyTimerTicks;
  5. #endif
  6. #define configUSE_PREEMPTION         1
  7. #define configUSE_IDLE_HOOK          0
  8. #define configUSE_TICK_HOOK          0
  9. #define configCPU_CLOCK_HZ           ( ( unsigned long ) 72000000 )  
  10. #define configTICK_RATE_HZ           ( ( TickType_t ) 1000 )
  11. #define configMAX_PRIORITIES         ( 5 )
  12. #define configMINIMAL_STACK_SIZE     ( ( unsigned short ) 128 )
  13. #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
  14. #define configMAX_TASK_NAME_LEN      ( 16 )
  15. #define configUSE_TRACE_FACILITY      1
  16. #define configUSE_16_BIT_TICKS       0
  17. #define configIDLE_SHOULD_YIELD      1
  18. /* Run time and task stats gathering related definitions. */
  19. #define configGENERATE_RUN_TIME_STATS                1
  20. #define configUSE_STATS_FORMATTING_FUNCTIONS         1
  21. #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()     (ulHighFrequencyTimerTicks = 0ul)
  22. #define portGET_RUN_TIME_COUNTER_VALUE()             ulHighFrequencyTimerTicks
  23. //#define portALT_GET_RUN_TIME_COUNTER_VALUE           1
  24. /* Co-routine definitions. */
  25. #define configUSE_CO_ROUTINES            0
  26. #define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
  27. /* Set the following definitions to 1 to include the API function, or zero
  28. to exclude the API function. */
  29. #define INCLUDE_vTaskPrioritySet          1
  30. #define INCLUDE_uxTaskPriorityGet         1
  31. #define INCLUDE_vTaskDelete               1
  32. #define INCLUDE_vTaskCleanUpResources      0
  33. #define INCLUDE_vTaskSuspend              1
  34. #define INCLUDE_vTaskDelayUntil           1
  35. #define INCLUDE_vTaskDelay                1
  36. /* Cortex-M specific definitions. */
  37. #ifdef __NVIC_PRIO_BITS
  38.      /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
  39.      #define configPRIO_BITS              __NVIC_PRIO_BITS
  40. #else
  41.      #define configPRIO_BITS              4        /* 15 priority levels */
  42. #endif
  43. /* The lowest interrupt priority that can be used in a call to a "set priority"
  44. function. */
  45. #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY              0x0f
  46. /* The highest interrupt priority that can be used by any interrupt service
  47. routine that makes calls to interrupt safe FreeRTOS API functions.  DO NOT CALL
  48. INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
  49. PRIORITY THAN THIS! (higher priorities are lower numeric values. */
  50. #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY         0x01
复制代码
几个重要选项说明:
1、#define configUSE_PREEMPTION        1
         使能抢占式调度器
2、#define configCPU_CLOCK_HZ      ( ( unsigned long ) 72000000 )   
        系统主频72MHz。
3、#define configTICK_RATE_HZ              ( ( TickType_t ) 1000 )
        系统时钟节拍1KHz,即1ms。
4、#define configMAX_PRIORITIES          ( 5 )
       定义可供用户使用的最大优先级数,如果这个定义的是5,那么用户可以使用的优先级号是0,1,2,3,4,不包含5,对于这一点,初学者要特别的注意。
5、#define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
        定义堆大小,FreeRTOS内核,用户动态内存申请,任务栈等都需要用这个空间。
6、configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY          0x01
       定义受FreeRTOS管理的最高优先级中断。简单的说就是允许用户在这个中断服务程序里面调用FreeRTOS的API的最高优先级。为了进一步说明这个宏定义的的作用,解释如下:
    (1)使用CM内核的MCU,官方强烈建议将NVIC的优先级分组配置为全抢占式优先级,全部配置为抢占式优先级的好处就是方便管理。
    (2)对于STM32来说,设置NVIC的优先级分组为4时,NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4)就是全部配置为抢占式优先级。又因为STM32的优先级设置仅使用CM内核8bit中的高4bit,即只能区分2^4 = 16种优先级。因此当优先级分组设置为4的时候可供用户选择抢占式优先级为015,共16个优先级,配置为0表示最高优先级,配置为15表示最低优先级,不存在子优先级。
    (3)这里配置configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY0x01表示用户可以在抢占式优先级为115的中断里面调用FreeRTOSAPI函数,抢占式优先级为0的中断里面是不允许调用的。
     更多关于这个参数说明请参看第12章。
FreeRTOS任务调试信息(按K1按键,串口打印):
15.1.jpg

上面截图中打印出来的任务状态字母B, R, D, S对应如下含义:
    #definetskBLOCKED_CHAR          ( 'B' )  任务阻塞
    #definetskREADY_CHAR           ( 'R' ) 任务就绪
    #definetskDELETED_CHAR           ( 'D' )  任务删除
    #definetskSUSPENDED_CHAR   ( 'S' ) 任务挂起
程序设计:
任务栈大小分配:
    vTaskUserIF任务   :2048字节
    vTaskLED任务     :2048字节
    vTaskMsgPro任务 :2048字节
    vTaskStart任务    :2048字节
    任务栈空间是在任务创建的时候从FreeRTOSConfig.h文件中定义的heap空间中申请的
    #defineconfigTOTAL_HEAP_SIZE        ( ( size_t )( 17 * 1024 ) )
系统栈大小分配:
15.2.jpg

FreeROTS初始化:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: main
  4. *    功能说明: 标准c程序入口。
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. int main(void)
  10. {
  11.      /*
  12.        在启动调度前,为了防止初始化STM32外设时有中断服务程序执行,这里禁止全局中断(除了NMI和HardFault)。
  13.        这样做的好处是:
  14.        1. 防止执行的中断服务程序中有FreeRTOS的API函数。
  15.        2. 保证系统正常启动,不受别的中断影响。
  16.        3. 关于是否关闭全局中断,大家根据自己的实际情况设置即可。
  17.        在移植文件port.c中的函数prvStartFirstTask中会重新开启全局中断。通过指令cpsie i开启,__set_PRIMASK(1)
  18.        和cpsie i是等效的。
  19.      */
  20.      __set_PRIMASK(1);
  21.    
  22.      /* 硬件初始化 */
  23.      bsp_Init();
  24.    
  25.      /* 1. 初始化一个定时器中断,精度高于滴答定时器中断,这样才可以获得准确的系统信息 仅供调试目的,实际项
  26.            目中不要使用,因为这个功能比较影响系统实时性。
  27.         2. 为了正确获取FreeRTOS的调试信息,可以考虑将上面的关闭中断指令__set_PRIMASK(1); 注释掉。
  28.      */
  29.      vSetupSysInfoTest();
  30.    
  31.      /* 创建任务 */
  32.      AppTaskCreate();
  33.    
  34.     /* 启动调度,开始执行任务 */
  35.     vTaskStartScheduler();
  36.      /*
  37.        如果系统正常启动是不会运行到这里的,运行到这里极有可能是用于定时器任务或者空闲任务的
  38.        heap空间不足造成创建失败,此要加大FreeRTOSConfig.h文件中定义的heap大小:
  39.        #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
  40.      */
  41.      while(1);
  42. }
复制代码
硬件外设初始化
    硬件外设的初始化是在bsp.c文件实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_Init
  4. *    功能说明: 初始化硬件设备。只需要调用一次。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。
  5. *             全局变量。
  6. *    形    参: 无
  7. *    返 回 值: 无
  8. *********************************************************************************************************
  9. */
  10. void bsp_Init(void)
  11. {
  12.      /*
  13.          由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。
  14.          启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。
  15.          系统时钟缺省配置为72MHz,如果需要更改,可以修改 system_stm32f10x.c 文件
  16.      */
  17.    
  18.      /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
  19.      bsp_InitUart();    /* 初始化串口 */
  20.      bsp_InitLed();     /* 初始LED指示灯端口 */
  21.      bsp_InitKey();     /* 初始化按键 */
  22.      bsp_InitHardTimer(); /* 初始化TIM2定时器 */
  23. }
复制代码
FreeRTOS任务创建:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppTaskCreate
  4. *    功能说明: 创建应用任务
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void AppTaskCreate (void)
  10. {
  11.     xTaskCreate( vTaskTaskUserIF,   /* 任务函数  */
  12.                  "vTaskUserIF",     /* 任务名    */
  13.                  512,               /* 任务栈大小,单位word,也就是4字节 */
  14.                  NULL,              /* 任务参数  */
  15.                  1,                 /* 任务优先级*/
  16.                  &xHandleTaskUserIF );  /* 任务句柄  */
  17.    
  18.    
  19.      xTaskCreate( vTaskLED,           /* 任务函数  */
  20.                  "vTaskLED",         /* 任务名    */
  21.                  512,                /* 任务栈大小,单位word,也就是4字节 */
  22.                  NULL,               /* 任务参数  */
  23.                  2,                  /* 任务优先级*/
  24.                  &xHandleTaskLED ); /* 任务句柄  */
  25.    
  26.      xTaskCreate( vTaskMsgPro,            /* 任务函数  */
  27.                  "vTaskMsgPro",           /* 任务名    */
  28.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  29.                  NULL,                    /* 任务参数  */
  30.                  3,                       /* 任务优先级*/
  31.                  &xHandleTaskMsgPro );  /* 任务句柄  */
  32.    
  33.    
  34.      xTaskCreate( vTaskStart,             /* 任务函数  */
  35.                  "vTaskStart",            /* 任务名    */
  36.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  37.                  NULL,                    /* 任务参数  */
  38.                  4,                       /* 任务优先级*/
  39.                  &xHandleTaskStart );   /* 任务句柄  */
  40. }
复制代码
四个FreeRTOS任务的实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskTaskUserIF
  4. *    功能说明: 接口消息处理。
  5. *    形    参: pvParameters 是在创建该任务时传递的形参
  6. *    返 回 值: 无
  7. *   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
  8. *********************************************************************************************************
  9. */
  10. static void vTaskTaskUserIF(void *pvParameters)
  11. {
  12.      uint8_t ucKeyCode;
  13.      uint8_t pcWriteBuffer[500];
  14.     while(1)
  15.     {
  16.          ucKeyCode = bsp_GetKey();
  17.         
  18.          if (ucKeyCode != KEY_NONE)
  19.          {
  20.               switch (ucKeyCode)
  21.               {
  22.                    /* K1键按下 打印任务执行情况 */
  23.                    case KEY_DOWN_K1:
  24.                        taskENTER_CRITICAL();   /* 进入临界区 */      
  25.                        printf("=================================================\\r\\n");
  26.                        printf("任务名      任务状态 优先级   剩余栈 任务序号\\r\\n");
  27.                        vTaskList((char *)&pcWriteBuffer);
  28.                        printf("%s\\r\\n", pcWriteBuffer);
  29.                   
  30.                        printf("\\r\\n任务名       运行计数         使用率\\r\\n");
  31.                        vTaskGetRunTimeStats((char *)&pcWriteBuffer);
  32.                        printf("%s\\r\\n", pcWriteBuffer);
  33.                        taskEXIT_CRITICAL();  /* 退出临界区 */  
  34.                        break;
  35.                   
  36.                    /* K2键按下,挂起任务vTaskLED */
  37.                    case KEY_DOWN_K2:
  38.                        taskENTER_CRITICAL();   /* 进入临界区 */
  39.                        printf("K2键按下,挂起任务vTaskLED\\r\\n");
  40.                        taskEXIT_CRITICAL();  /* 退出临界区 */  
  41.                        vTaskSuspend(xHandleTaskLED);
  42.                        break;
  43.                   
  44.                    /* K3键按下 启动单次定时器中断,50ms后在定时器中断将任务vTaskLED恢复 */
  45.                    case KEY_DOWN_K3:
  46.                         taskENTER_CRITICAL();   /* 进入临界区 */                    
  47.                        printf("K3键按下,启动单次定时器中断,50ms后在定时器中断将任务vTaskLED恢复\\r\\n");
  48.                        taskEXIT_CRITICAL();  /* 退出临界区 */
  49.                        bsp_StartHardTimer(1 ,50000, (void *)TIM_CallBack1);
  50.                        break;
  51.                    /* 其他的键值不处理 */
  52.                    default:                    
  53.                        break;
  54.               }
  55.          }
  56.         
  57.          vTaskDelay(20);
  58.      }
  59. }
  60. /*
  61. *********************************************************************************************************
  62. *    函 数 名: vTaskLED
  63. *    功能说明: LED闪烁和串口打印
  64. *    形    参: pvParameters 是在创建该任务时传递的形参
  65. *    返 回 值: 无
  66. *   优 先 级: 2
  67. *********************************************************************************************************
  68. */
  69. static void vTaskLED(void *pvParameters)
  70. {
  71.     while(1)
  72. {
  73.      taskENTER_CRITICAL();   /* 进入临界区 */
  74.      printf("任务vTaskLED正在运行\\r\\n");
  75.      taskEXIT_CRITICAL();  /* 退出临界区 */  
  76.      bsp_LedToggle(2);
  77.      vTaskDelay(500);
  78.     }
  79. }
  80. /*
  81. *********************************************************************************************************
  82. *    函 数 名: vTaskMsgPro
  83. *    功能说明: 消息处理,这里是用作LED闪烁和串口打印  
  84. *    形    参: pvParameters 是在创建该任务时传递的形参
  85. *    返 回 值: 无
  86. *   优 先 级: 3
  87. *********************************************************************************************************
  88. */
  89. static void vTaskMsgPro(void *pvParameters)
  90. {
  91.     while(1)
  92. {
  93.      taskENTER_CRITICAL();   /* 进入临界区 */
  94.      printf("任务vTaskMsgPro正在运行\\r\\n");
  95.      taskEXIT_CRITICAL();   /* 退出临界区 */  
  96.      bsp_LedToggle(3);
  97.      vTaskDelay(600);
  98.     }
  99. }
  100. /*
  101. *********************************************************************************************************
  102. *    函 数 名: vTaskStart
  103. *    功能说明: 启动任务,也就是最高优先级任务,这里用作按键扫描。
  104. *    形    参: pvParameters 是在创建该任务时传递的形参
  105. *    返 回 值: 无
  106. *   优 先 级: 4
  107. *********************************************************************************************************
  108. */
  109. static void vTaskStart(void *pvParameters)
  110. {
  111.     while(1)
  112.     {
  113.          /* 按键扫描 */
  114.          bsp_KeyScan();
  115.         vTaskDelay(10);
  116.     }
  117. }
复制代码
定时器中断回调函数中将任务从挂起状态恢复:
    定时器中断的初始化和中断函数在bsp_timer.c 文件中实现,这个不是教程的重点,故不作介绍。 这里主要关心中断服务程序中临界段的实现方法。
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: TIM_CallBack1
  4. *    功能说明: 定时器中断的回调函数,此函数被bsp_StartHardTimer所调用。                        
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void TIM_CallBack1(void)
  10. {
  11.      BaseType_t xYieldRequired;
  12.      UBaseType_t uxSavedInterruptStatus;
  13.      uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();  /* 进入临界区 */
  14.      {
  15.          /* 用户可以在这里添加临界段代码,我们这里暂时未用到 */
  16.      }
  17.      portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); /* 退出临界区 */
  18.     /* 恢复挂起任务 */
  19.      xYieldRequired = xTaskResumeFromISR(xHandleTaskLED);
  20.       /* 退出中断后是否需要执行任务切换 */
  21.      if( xYieldRequired == pdTRUE )
  22.      {
  23.          portYIELD_FROM_ISR(xYieldRequired);
  24.      }
  25. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-8-24 15:08:35 | 显示全部楼层
15.6.2   STM32F407开发板实验


配套例子:
    V5-309_FreeRTOS实验_临界段和开关中断
实验目的:
    1. 学习FreeRTOS的临界段和开关中断设置
实验内容:
    1.     K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。
    2.     K2按键按下,挂起任务vTaskLED。
    3.     K3按键按下,启动单次定时器中断,50ms后在定时器中断将任务vTaskLED恢复。
     4.     本实验printf函数的多任务调用通过任务代码中临界段的进入和退出函数taskENTER_CRITICAL()和taskEXIT_CRITICAL()实现互斥效果。

    5.     本实验中断服务程序中临界段的调用通过函数portSET_INTERRUPT_MASK_FROM_ISR()和portCLEAR_INTERRUPT_MASK_FROM_ISR()实现保护。
    6.     各个任务实现的功能如下:
              vTaskUserIF任务   :按键消息处理。
              vTaskLED任务     :LED闪烁和串口打印。
              vTaskMsgPro任务 :消息处理,这里是用作LED闪烁和串口打印。
              vTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。
FreeRTOS的配置:
    FreeRTOSConfig.h文件中的配置如下:
  1. /* Ensure stdint is only used by the compiler, and not the assembler. */
  2. #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
  3. #include <stdint.h>
  4. extern volatile uint32_t ulHighFrequencyTimerTicks;
  5. /* Ensure stdint is only used by the compiler, and not the assembler. */
  6. #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
  7. #include <stdint.h>
  8. extern volatile uint32_t ulHighFrequencyTimerTicks;
  9. #endif
  10. #define configUSE_PREEMPTION         1
  11. #define configUSE_IDLE_HOOK          0
  12. #define configUSE_TICK_HOOK           0
  13. #define configCPU_CLOCK_HZ           ( ( unsigned long ) 168000000 )
  14. #define configTICK_RATE_HZ           ( ( TickType_t ) 1000 )
  15. #define configMAX_PRIORITIES         ( 5 )
  16. #define configMINIMAL_STACK_SIZE     ( ( unsigned short ) 128 )
  17. #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 30 * 1024 ) )
  18. #define configMAX_TASK_NAME_LEN      ( 16 )
  19. #define configUSE_TRACE_FACILITY      1
  20. #define configUSE_16_BIT_TICKS       0
  21. #define configIDLE_SHOULD_YIELD      1
  22. /* Run time and task stats gathering related definitions. */
  23. #define configGENERATE_RUN_TIME_STATS                1
  24. #define configUSE_STATS_FORMATTING_FUNCTIONS         1
  25. #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()     (ulHighFrequencyTimerTicks = 0ul)
  26. #define portGET_RUN_TIME_COUNTER_VALUE()             ulHighFrequencyTimerTicks
  27. //#define portALT_GET_RUN_TIME_COUNTER_VALUE           1
  28. /* Co-routine definitions. */
  29. #define configUSE_CO_ROUTINES             0
  30. #define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
  31. /* Set the following definitions to 1 to include the API function, or zero
  32. to exclude the API function. */
  33. #define INCLUDE_vTaskPrioritySet     1
  34. #define INCLUDE_uxTaskPriorityGet         1
  35. #define INCLUDE_vTaskDelete               1
  36. #define INCLUDE_vTaskCleanUpResources 0
  37. #define INCLUDE_vTaskSuspend              1
  38. #define INCLUDE_vTaskDelayUntil           1
  39. #define INCLUDE_vTaskDelay                1
  40. /* Cortex-M specific definitions. */
  41. #ifdef __NVIC_PRIO_BITS
  42.      /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
  43.      #define configPRIO_BITS              __NVIC_PRIO_BITS
  44. #else
  45.      #define configPRIO_BITS              4        /* 15 priority levels */
  46. #endif
  47. /* The lowest interrupt priority that can be used in a call to a "set priority"
  48. function. */
  49. #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY              0x0f
  50. /* The highest interrupt priority that can be used by any interrupt service
  51. routine that makes calls to interrupt safe FreeRTOS API functions.  DO NOT CALL
  52. INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
  53. PRIORITY THAN THIS! (higher priorities are lower numeric values. */
  54. #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY    0x01
复制代码

几个重要选项说明:
1、#define configUSE_PREEMPTION        1
       使能抢占式调度器
2、#define configCPU_CLOCK_HZ      ( ( unsigned long ) 168000000 )
        系统主频168MHz。
3、#define configTICK_RATE_HZ              ( ( TickType_t ) 1000 )
        系统时钟节拍1KHz,即1ms。
4、#define configMAX_PRIORITIES          ( 5 )
       定义可供用户使用的最大优先级数,如果这个定义的是5,那么用户可以使用的优先级号是0,1,2,3,4,不包含5,对于这一点,初学者要特别的注意。
5、#define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 30 * 1024 ) )
       定义堆大小,FreeRTOS内核,用户动态内存申请,任务栈等都需要用这个空间。
6、configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY          0x01
        定义受FreeRTOS管理的最高优先级中断。简单的说就是允许用户在这个中断服务程序里面调用FreeRTOS的API的最高优先级。为了进一步说明这个宏定义的的作用,解释如下:
(1)使用CM内核的MCU,官方强烈建议将NVIC的优先级分组配置为全抢占式优先级,全部配置为抢占式优先级的好处就是方便管理。
(2)对于STM32来说,设置NVIC的优先级分组为4时,NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4)就是全部配置为抢占式优先级。又因为STM32的优先级设置仅使用CM内核8bit中的高4bit,即只能区分2^4 = 16种优先级。因此当优先级分组设置为4的时候可供用户选择抢占式优先级为015,共16个优先级,配置为0表示最高优先级,配置为15表示最低优先级,不存在子优先级。
(3)这里配置configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY0x01表示用户可以在抢占式优先级为115的中断里面调用FreeRTOSAPI函数,抢占式优先级为0的中断里面是不允许调用的。
    更多关于这个参数说明请参看第12章。
FreeRTOS任务调试信息(按K1按键,串口打印):
15.3.jpg

上面截图中打印出来的任务状态字母B, R, D, S对应如下含义:
    #definetskBLOCKED_CHAR          ( 'B' )  任务阻塞
    #definetskREADY_CHAR           ( 'R' ) 任务就绪
    #definetskDELETED_CHAR           ( 'D' )  任务删除
    #definetskSUSPENDED_CHAR   ( 'S' ) 任务挂起
程序设计:
任务栈大小分配:
      vTaskUserIF任务   :2048字节
      vTaskLED任务     :2048字节
      vTaskMsgPro任务 :2048字节
      vTaskStart任务    :2048字节
      任务栈空间是在任务创建的时候从FreeRTOSConfig.h文件中定义的heap空间中申请的
       #defineconfigTOTAL_HEAP_SIZE        ( ( size_t )( 30 * 1024 ) )
系统栈大小分配:
15.4.jpg

FreeROTS初始化:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: main
  4. *    功能说明: 标准c程序入口。
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. int main(void)
  10. {
  11.      /*
  12.        在启动调度前,为了防止初始化STM32外设时有中断服务程序执行,这里禁止全局中断(除了NMI和HardFault)。
  13.        这样做的好处是:
  14.        1. 防止执行的中断服务程序中有FreeRTOS的API函数。
  15.        2. 保证系统正常启动,不受别的中断影响。
  16.        3. 关于是否关闭全局中断,大家根据自己的实际情况设置即可。
  17.        在移植文件port.c中的函数prvStartFirstTask中会重新开启全局中断。通过指令cpsie i开启,__set_PRIMASK(1)
  18.        和cpsie i是等效的。
  19.      */
  20.      __set_PRIMASK(1);
  21.    
  22.      /* 硬件初始化 */
  23.      bsp_Init();
  24.    
  25.      /* 1. 初始化一个定时器中断,精度高于滴答定时器中断,这样才可以获得准确的系统信息 仅供调试目的,实际项
  26.            目中不要使用,因为这个功能比较影响系统实时性。
  27.         2. 为了正确获取FreeRTOS的调试信息,可以考虑将上面的关闭中断指令__set_PRIMASK(1); 注释掉。
  28.      */
  29.      vSetupSysInfoTest();
  30.    
  31.      /* 创建任务 */
  32.      AppTaskCreate();
  33.    
  34.     /* 启动调度,开始执行任务 */
  35.     vTaskStartScheduler();
  36.      /*
  37.        如果系统正常启动是不会运行到这里的,运行到这里极有可能是用于定时器任务或者空闲任务的
  38.        heap空间不足造成创建失败,此要加大FreeRTOSConfig.h文件中定义的heap大小:
  39.        #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
  40.      */
  41.      while(1);
  42. }
复制代码

硬件外设初始化
    硬件外设的初始化是在bsp.c文件实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_Init
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void bsp_Init(void)
  10. {
  11.      /*
  12.          由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。
  13.          启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。
  14.          系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件
  15.      */
  16.      /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/
  17.      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
  18.      bsp_InitUart();    /* 初始化串口 */
  19.      bsp_InitKey();     /* 初始化按键变量 */
  20.      bsp_InitLed();     /* 初始LED指示灯端口 */
  21.      bsp_InitHardTimer(); /* 初始化TIM2定时器 */
  22. }
复制代码

FreeRTOS任务创建:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppTaskCreate
  4. *    功能说明: 创建应用任务
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void AppTaskCreate (void)
  10. {
  11.     xTaskCreate( vTaskTaskUserIF,   /* 任务函数  */
  12.                  "vTaskUserIF",     /* 任务名    */
  13.                  512,               /* 任务栈大小,单位word,也就是4字节 */
  14.                  NULL,              /* 任务参数  */
  15.                  1,                 /* 任务优先级*/
  16.                  &xHandleTaskUserIF );  /* 任务句柄  */
  17.    
  18.    
  19.      xTaskCreate( vTaskLED,           /* 任务函数  */
  20.                  "vTaskLED",         /* 任务名    */
  21.                  512,                /* 任务栈大小,单位word,也就是4字节 */
  22.                  NULL,               /* 任务参数  */
  23.                  2,                  /* 任务优先级*/
  24.                  &xHandleTaskLED ); /* 任务句柄  */
  25.    
  26.      xTaskCreate( vTaskMsgPro,            /* 任务函数  */
  27.                  "vTaskMsgPro",           /* 任务名    */
  28.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  29.                  NULL,                    /* 任务参数  */
  30.                  3,                       /* 任务优先级*/
  31.                  &xHandleTaskMsgPro );  /* 任务句柄  */
  32.    
  33.    
  34.      xTaskCreate( vTaskStart,             /* 任务函数  */
  35.                  "vTaskStart",            /* 任务名    */
  36.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  37.                  NULL,                    /* 任务参数  */
  38.                  4,                       /* 任务优先级*/
  39.                  &xHandleTaskStart );   /* 任务句柄  */
  40. }
复制代码

四个FreeRTOS任务的实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskTaskUserIF
  4. *    功能说明: 接口消息处理。
  5. *    形    参: pvParameters 是在创建该任务时传递的形参
  6. *    返 回 值: 无
  7. *   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
  8. *********************************************************************************************************
  9. */
  10. static void vTaskTaskUserIF(void *pvParameters)
  11. {
  12.      uint8_t ucKeyCode;
  13.      uint8_t pcWriteBuffer[500];
  14.     while(1)
  15.     {
  16.          ucKeyCode = bsp_GetKey();
  17.         
  18.          if (ucKeyCode != KEY_NONE)
  19.          {
  20.               switch (ucKeyCode)
  21.               {
  22.                    /* K1键按下 打印任务执行情况 */
  23.                    case KEY_DOWN_K1:
  24.                        taskENTER_CRITICAL();   /* 进入临界区 */      
  25.                        printf("=================================================\\r\\n");
  26.                        printf("任务名      任务状态 优先级   剩余栈 任务序号\\r\\n");
  27.                        vTaskList((char *)&pcWriteBuffer);
  28.                        printf("%s\\r\\n", pcWriteBuffer);
  29.                   
  30.                        printf("\\r\\n任务名       运行计数         使用率\\r\\n");
  31.                        vTaskGetRunTimeStats((char *)&pcWriteBuffer);
  32.                        printf("%s\\r\\n", pcWriteBuffer);
  33.                        taskEXIT_CRITICAL();  /* 退出临界区 */  
  34.                        break;
  35.                   
  36.                    /* K2键按下,挂起任务vTaskLED */
  37.                    case KEY_DOWN_K2:
  38.                        taskENTER_CRITICAL();   /* 进入临界区 */
  39.                        printf("K2键按下,挂起任务vTaskLED\\r\\n");
  40.                        taskEXIT_CRITICAL();  /* 退出临界区 */  
  41.                        vTaskSuspend(xHandleTaskLED);
  42.                        break;
  43.                   
  44.                    /* K3键按下 启动单次定时器中断,50ms后在定时器中断将任务vTaskLED恢复 */
  45.                    case KEY_DOWN_K3:
  46.                         taskENTER_CRITICAL();   /* 进入临界区 */                    
  47.                        printf("K3键按下,启动单次定时器中断,50ms后在定时器中断将任务vTaskLED恢复\\r\\n");
  48.                        taskEXIT_CRITICAL();  /* 退出临界区 */
  49.                        bsp_StartHardTimer(1 ,50000, (void *)TIM_CallBack1);
  50.                        break;
  51.                    /* 其他的键值不处理 */
  52.                    default:                    
  53.                        break;
  54.               }
  55.          }
  56.         
  57.          vTaskDelay(20);
  58.      }
  59. }
  60. /*
  61. *********************************************************************************************************
  62. *    函 数 名: vTaskLED
  63. *    功能说明: LED闪烁和串口打印
  64. *    形    参: pvParameters 是在创建该任务时传递的形参
  65. *    返 回 值: 无
  66. *   优 先 级: 2
  67. *********************************************************************************************************
  68. */
  69. static void vTaskLED(void *pvParameters)
  70. {
  71.     while(1)
  72. {
  73.      taskENTER_CRITICAL();   /* 进入临界区 */
  74.      printf("任务vTaskLED正在运行\\r\\n");
  75.      taskEXIT_CRITICAL();  /* 退出临界区 */  
  76.      bsp_LedToggle(2);
  77.      vTaskDelay(500);
  78.     }
  79. }
  80. /*
  81. *********************************************************************************************************
  82. *    函 数 名: vTaskMsgPro
  83. *    功能说明: 消息处理,这里是用作LED闪烁和串口打印  
  84. *    形    参: pvParameters 是在创建该任务时传递的形参
  85. *    返 回 值: 无
  86. *   优 先 级: 3
  87. *********************************************************************************************************
  88. */
  89. static void vTaskMsgPro(void *pvParameters)
  90. {
  91.     while(1)
  92. {
  93.      taskENTER_CRITICAL();   /* 进入临界区 */
  94.      printf("任务vTaskMsgPro正在运行\\r\\n");
  95.      taskEXIT_CRITICAL();   /* 退出临界区 */  
  96.      bsp_LedToggle(3);
  97.      vTaskDelay(600);
  98.     }
  99. }
  100. /*
  101. *********************************************************************************************************
  102. *    函 数 名: vTaskStart
  103. *    功能说明: 启动任务,也就是最高优先级任务,这里用作按键扫描。
  104. *    形    参: pvParameters 是在创建该任务时传递的形参
  105. *    返 回 值: 无
  106. *   优 先 级: 4
  107. *********************************************************************************************************
  108. */
  109. static void vTaskStart(void *pvParameters)
  110. {
  111.     while(1)
  112.     {
  113.          /* 按键扫描 */
  114.          bsp_KeyScan();
  115.         vTaskDelay(10);
  116.     }
  117. }
复制代码

定时器中断回调函数中将任务从挂起状态恢复:
    定时器中断的初始化和中断函数在bsp_timer.c 文件中实现,这个不是教程的重点,故不作介绍。 这里主要关心中断服务程序中临界段的实现方法。
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: TIM_CallBack1
  4. *    功能说明: 定时器中断的回调函数,此函数被bsp_StartHardTimer所调用。                        
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void TIM_CallBack1(void)
  10. {
  11.      BaseType_t xYieldRequired;
  12.      UBaseType_t uxSavedInterruptStatus;
  13.      uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();  /* 进入临界区 */
  14.      {
  15.          /* 用户可以在这里添加临界段代码,我们这里暂时未用到 */
  16.      }
  17.      portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); /* 退出临界区 */
  18.     /* 恢复挂起任务 */
  19.      xYieldRequired = xTaskResumeFromISR(xHandleTaskLED);
  20.       /* 退出中断后是否需要执行任务切换 */
  21.      if( xYieldRequired == pdTRUE )
  22.      {
  23.          portYIELD_FROM_ISR(xYieldRequired);
  24.      }
  25. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-8-24 15:14:40 | 显示全部楼层
15.6.3    STM32F429开发板实验


配套例子:
     V6-309_FreeRTOS实验_临界段和开关中断
实验目的:
    1. 学习FreeRTOS的临界段和开关中断设置
实验内容:
    1.     K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。
    2.     K2按键按下,挂起任务vTaskLED。
    3.     K3按键按下,启动单次定时器中断,50ms后在定时器中断将任务vTaskLED恢复。
    4.     本实验printf函数的多任务调用通过任务代码中临界段的进入和退出函数taskENTER_CRITICAL()和taskEXIT_CRITICAL()实现互斥效果。

    5.     本实验中断服务程序中临界段的调用通过函数portSET_INTERRUPT_MASK_FROM_ISR()和portCLEAR_INTERRUPT_MASK_FROM_ISR()实现保护。
    6.     各个任务实现的功能如下:
              vTaskUserIF任务   :按键消息处理。
              vTaskLED任务     :LED闪烁和串口打印。
              vTaskMsgPro任务 :消息处理,这里是用作LED闪烁和串口打印。
              vTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。
FreeRTOS的配置:
    FreeRTOSConfig.h文件中的配置如下:
  1. /* Ensure stdint is only used by the compiler, and not the assembler. */
  2. #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
  3. #include <stdint.h>
  4. extern volatile uint32_t ulHighFrequencyTimerTicks;
  5. #endif
  6. #define configUSE_PREEMPTION         1
  7. #define configUSE_IDLE_HOOK          0
  8. #define configUSE_TICK_HOOK          0
  9. #define configCPU_CLOCK_HZ           ( ( unsigned long ) 168000000 )
  10. #define configTICK_RATE_HZ           ( ( TickType_t ) 1000 )
  11. #define configMAX_PRIORITIES         ( 5 )
  12. #define configMINIMAL_STACK_SIZE     ( ( unsigned short ) 128 )
  13. #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 30 * 1024 ) )
  14. #define configMAX_TASK_NAME_LEN      ( 16 )
  15. #define configUSE_TRACE_FACILITY      1
  16. #define configUSE_16_BIT_TICKS       0
  17. #define configIDLE_SHOULD_YIELD      1
  18. /* Run time and task stats gathering related definitions. */
  19. #define configGENERATE_RUN_TIME_STATS                1
  20. #define configUSE_STATS_FORMATTING_FUNCTIONS         1
  21. #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()     (ulHighFrequencyTimerTicks = 0ul)
  22. #define portGET_RUN_TIME_COUNTER_VALUE()             ulHighFrequencyTimerTicks
  23. //#define portALT_GET_RUN_TIME_COUNTER_VALUE           1
  24. /* Co-routine definitions. */
  25. #define configUSE_CO_ROUTINES            0
  26. #define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
  27. /* Set the following definitions to 1 to include the API function, or zero
  28. to exclude the API function. */
  29. #define INCLUDE_vTaskPrioritySet          1
  30. #define INCLUDE_uxTaskPriorityGet         1
  31. #define INCLUDE_vTaskDelete               1
  32. #define INCLUDE_vTaskCleanUpResources      0
  33. #define INCLUDE_vTaskSuspend              1
  34. #define INCLUDE_vTaskDelayUntil           1
  35. #define INCLUDE_vTaskDelay                1
  36. /* Cortex-M specific definitions. */
  37. #ifdef __NVIC_PRIO_BITS
  38.      /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
  39.      #define configPRIO_BITS              __NVIC_PRIO_BITS
  40. #else
  41.      #define configPRIO_BITS              4        /* 15 priority levels */
  42. #endif
  43. /* The lowest interrupt priority that can be used in a call to a "set priority"
  44. function. */
  45. #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY              0x0f
  46. /* The highest interrupt priority that can be used by any interrupt service
  47. routine that makes calls to interrupt safe FreeRTOS API functions.  DO NOT CALL
  48. INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
  49. PRIORITY THAN THIS! (higher priorities are lower numeric values. */
  50. #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY         0x01
复制代码
几个重要选项说明:
1、#define configUSE_PREEMPTION        1
       使能抢占式调度器
2、#define configCPU_CLOCK_HZ      ( ( unsigned long ) 168000000 )
        系统主频168MHz。
3、#define configTICK_RATE_HZ              ( ( TickType_t ) 1000 )
        系统时钟节拍1KHz,即1ms。
4、#define configMAX_PRIORITIES          ( 5 )
       定义可供用户使用的最大优先级数,如果这个定义的是5,那么用户可以使用的优先级号是0,1,2,3,4,不包含5,对于这一点,初学者要特别的注意。
5、#define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 30 * 1024 ) )
        定义堆大小,FreeRTOS内核,用户动态内存申请,任务栈等都需要用这个空间。
6、configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY          0x01
        定义受FreeRTOS管理的最高优先级中断。简单的说就是允许用户在这个中断服务程序里面调用FreeRTOS的API的最高优先级。为了进一步说明这个宏定义的的作用,解释如下:
(1)使用CM内核的MCU,官方强烈建议将NVIC的优先级分组配置为全抢占式优先级,全部配置为抢占式优先级的好处就是方便管理。
(2)对于STM32来说,设置NVIC的优先级分组为4时,NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4)就是全部配置为抢占式优先级。又因为STM32的优先级设置仅使用CM内核8bit中的高4bit,即只能区分2^4 = 16种优先级。因此当优先级分组设置为4的时候可供用户选择抢占式优先级为015,共16个优先级,配置为0表示最高优先级,配置为15表示最低优先级,不存在子优先级。
(3)这里配置configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY0x01表示用户可以在抢占式优先级为115的中断里面调用FreeRTOSAPI函数,抢占式优先级为0的中断里面是不允许调用的。
    更多关于这个参数说明请参看第12章。
FreeRTOS任务调试信息(按K1按键,串口打印):
15.5.jpg

上面截图中打印出来的任务状态字母B, R, D, S对应如下含义:
    #definetskBLOCKED_CHAR          ( 'B' )  任务阻塞
    #definetskREADY_CHAR           ( 'R' ) 任务就绪
    #definetskDELETED_CHAR           ( 'D' )  任务删除
    #definetskSUSPENDED_CHAR   ( 'S' ) 任务挂起
程序设计:
任务栈大小分配:
    vTaskUserIF任务   :2048字节
    vTaskLED任务     :2048字节
    vTaskMsgPro任务 :2048字节
    vTaskStart任务    :2048字节
    任务栈空间是在任务创建的时候从FreeRTOSConfig.h文件中定义的heap空间中申请的
    #defineconfigTOTAL_HEAP_SIZE        ( ( size_t )( 30 * 1024 ) )
系统栈大小分配:
15.6.jpg

FreeROTS初始化:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: main
  4. *    功能说明: 标准c程序入口。
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. int main(void)
  10. {
  11.      /*
  12.        在启动调度前,为了防止初始化STM32外设时有中断服务程序执行,这里禁止全局中断(除了NMI和HardFault)。
  13.        这样做的好处是:
  14.        1. 防止执行的中断服务程序中有FreeRTOS的API函数。
  15.        2. 保证系统正常启动,不受别的中断影响。
  16.        3. 关于是否关闭全局中断,大家根据自己的实际情况设置即可。
  17.        在移植文件port.c中的函数prvStartFirstTask中会重新开启全局中断。通过指令cpsie i开启,__set_PRIMASK(1)
  18.        和cpsie i是等效的。
  19.      */
  20.      __set_PRIMASK(1);
  21.    
  22.      /* 硬件初始化 */
  23.      bsp_Init();
  24.    
  25.      /* 1. 初始化一个定时器中断,精度高于滴答定时器中断,这样才可以获得准确的系统信息 仅供调试目的,实际项
  26.            目中不要使用,因为这个功能比较影响系统实时性。
  27.         2. 为了正确获取FreeRTOS的调试信息,可以考虑将上面的关闭中断指令__set_PRIMASK(1); 注释掉。
  28.      */
  29.      vSetupSysInfoTest();
  30.    
  31.      /* 创建任务 */
  32.      AppTaskCreate();
  33.    
  34.     /* 启动调度,开始执行任务 */
  35.     vTaskStartScheduler();
  36.      /*
  37.        如果系统正常启动是不会运行到这里的,运行到这里极有可能是用于定时器任务或者空闲任务的
  38.        heap空间不足造成创建失败,此要加大FreeRTOSConfig.h文件中定义的heap大小:
  39.        #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
  40.      */
  41.      while(1);
  42. }
复制代码
硬件外设初始化
    硬件外设的初始化是在bsp.c文件实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_Init
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void bsp_Init(void)
  10. {
  11.      /*
  12.          由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。
  13.          启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。
  14.          系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件
  15.      */
  16.      /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/
  17.      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
  18.    
  19.      SystemCoreClockUpdate();    /* 根据PLL配置更新系统时钟频率变量 SystemCoreClock */
  20.      bsp_InitUart();    /* 初始化串口 */
  21.      bsp_InitKey();     /* 初始化按键变量 */
  22.      bsp_InitExtIO();   /* FMC总线上扩展了32位输出IO, 操作LED等外设必须初始化 */
  23.      bsp_InitLed();     /* 初始LED指示灯端口 */
  24.      bsp_InitHardTimer(); /* 初始化TIM2定时器 */
  25. }
复制代码
FreeRTOS任务创建:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppTaskCreate
  4. *    功能说明: 创建应用任务
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void AppTaskCreate (void)
  10. {
  11.     xTaskCreate( vTaskTaskUserIF,   /* 任务函数  */
  12.                  "vTaskUserIF",     /* 任务名    */
  13.                  512,               /* 任务栈大小,单位word,也就是4字节 */
  14.                  NULL,              /* 任务参数  */
  15.                  1,                 /* 任务优先级*/
  16.                  &xHandleTaskUserIF );  /* 任务句柄  */
  17.    
  18.    
  19.      xTaskCreate( vTaskLED,           /* 任务函数  */
  20.                  "vTaskLED",         /* 任务名    */
  21.                  512,                /* 任务栈大小,单位word,也就是4字节 */
  22.                  NULL,               /* 任务参数  */
  23.                  2,                  /* 任务优先级*/
  24.                  &xHandleTaskLED ); /* 任务句柄  */
  25.    
  26.      xTaskCreate( vTaskMsgPro,            /* 任务函数  */
  27.                  "vTaskMsgPro",           /* 任务名    */
  28.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  29.                  NULL,                    /* 任务参数  */
  30.                  3,                       /* 任务优先级*/
  31.                  &xHandleTaskMsgPro );  /* 任务句柄  */
  32.    
  33.    
  34.      xTaskCreate( vTaskStart,             /* 任务函数  */
  35.                  "vTaskStart",            /* 任务名    */
  36.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  37.                  NULL,                    /* 任务参数  */
  38.                  4,                       /* 任务优先级*/
  39.                  &xHandleTaskStart );   /* 任务句柄  */
  40. }
复制代码
四个FreeRTOS任务的实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskTaskUserIF
  4. *    功能说明: 接口消息处理。
  5. *    形    参: pvParameters 是在创建该任务时传递的形参
  6. *    返 回 值: 无
  7. *   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
  8. *********************************************************************************************************
  9. */
  10. static void vTaskTaskUserIF(void *pvParameters)
  11. {
  12.      uint8_t ucKeyCode;
  13.      uint8_t pcWriteBuffer[500];
  14.     while(1)
  15.     {
  16.          ucKeyCode = bsp_GetKey();
  17.         
  18.          if (ucKeyCode != KEY_NONE)
  19.          {
  20.               switch (ucKeyCode)
  21.               {
  22.                    /* K1键按下 打印任务执行情况 */
  23.                    case KEY_DOWN_K1:
  24.                        taskENTER_CRITICAL();   /* 进入临界区 */      
  25.                        printf("=================================================\\r\\n");
  26.                        printf("任务名      任务状态 优先级   剩余栈 任务序号\\r\\n");
  27.                        vTaskList((char *)&pcWriteBuffer);
  28.                        printf("%s\\r\\n", pcWriteBuffer);
  29.                   
  30.                        printf("\\r\\n任务名       运行计数         使用率\\r\\n");
  31.                        vTaskGetRunTimeStats((char *)&pcWriteBuffer);
  32.                        printf("%s\\r\\n", pcWriteBuffer);
  33.                        taskEXIT_CRITICAL();  /* 退出临界区 */  
  34.                        break;
  35.                   
  36.                    /* K2键按下,挂起任务vTaskLED */
  37.                    case KEY_DOWN_K2:
  38.                        taskENTER_CRITICAL();   /* 进入临界区 */
  39.                        printf("K2键按下,挂起任务vTaskLED\\r\\n");
  40.                        taskEXIT_CRITICAL();  /* 退出临界区 */  
  41.                        vTaskSuspend(xHandleTaskLED);
  42.                        break;
  43.                   
  44.                    /* K3键按下 启动单次定时器中断,50ms后在定时器中断将任务vTaskLED恢复 */
  45.                    case KEY_DOWN_K3:
  46.                         taskENTER_CRITICAL();   /* 进入临界区 */                    
  47.                        printf("K3键按下,启动单次定时器中断,50ms后在定时器中断将任务vTaskLED恢复\\r\\n");
  48.                        taskEXIT_CRITICAL();  /* 退出临界区 */
  49.                        bsp_StartHardTimer(1 ,50000, (void *)TIM_CallBack1);
  50.                        break;
  51.                    /* 其他的键值不处理 */
  52.                    default:                    
  53.                        break;
  54.               }
  55.          }
  56.         
  57.          vTaskDelay(20);
  58.      }
  59. }
  60. /*
  61. *********************************************************************************************************
  62. *    函 数 名: vTaskLED
  63. *    功能说明: LED闪烁和串口打印
  64. *    形    参: pvParameters 是在创建该任务时传递的形参
  65. *    返 回 值: 无
  66. *   优 先 级: 2
  67. *********************************************************************************************************
  68. */
  69. static void vTaskLED(void *pvParameters)
  70. {
  71.     while(1)
  72. {
  73.      taskENTER_CRITICAL();   /* 进入临界区 */
  74.      printf("任务vTaskLED正在运行\\r\\n");
  75.      taskEXIT_CRITICAL();  /* 退出临界区 */  
  76.      bsp_LedToggle(2);
  77.      vTaskDelay(500);
  78.     }
  79. }
  80. /*
  81. *********************************************************************************************************
  82. *    函 数 名: vTaskMsgPro
  83. *    功能说明: 消息处理,这里是用作LED闪烁和串口打印  
  84. *    形    参: pvParameters 是在创建该任务时传递的形参
  85. *    返 回 值: 无
  86. *   优 先 级: 3
  87. *********************************************************************************************************
  88. */
  89. static void vTaskMsgPro(void *pvParameters)
  90. {
  91.     while(1)
  92. {
  93.      taskENTER_CRITICAL();   /* 进入临界区 */
  94.      printf("任务vTaskMsgPro正在运行\\r\\n");
  95.      taskEXIT_CRITICAL();   /* 退出临界区 */  
  96.      bsp_LedToggle(3);
  97.      vTaskDelay(600);
  98.     }
  99. }
  100. /*
  101. *********************************************************************************************************
  102. *    函 数 名: vTaskStart
  103. *    功能说明: 启动任务,也就是最高优先级任务,这里用作按键扫描。
  104. *    形    参: pvParameters 是在创建该任务时传递的形参
  105. *    返 回 值: 无
  106. *   优 先 级: 4
  107. *********************************************************************************************************
  108. */
  109. static void vTaskStart(void *pvParameters)
  110. {
  111.     while(1)
  112.     {
  113.          /* 按键扫描 */
  114.          bsp_KeyScan();
  115.         vTaskDelay(10);
  116.     }
  117. }
复制代码
定时器中断回调函数中将任务从挂起状态恢复:
    定时器中断的初始化和中断函数在bsp_timer.c 文件中实现,这个不是教程的重点,故不作介绍。 这里主要关心中断服务程序中临界段的实现方法。
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: TIM_CallBack1
  4. *    功能说明: 定时器中断的回调函数,此函数被bsp_StartHardTimer所调用。                        
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void TIM_CallBack1(void)
  10. {
  11.      BaseType_t xYieldRequired;
  12.      UBaseType_t uxSavedInterruptStatus;
  13.      uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();  /* 进入临界区 */
  14.      {
  15.          /* 用户可以在这里添加临界段代码,我们这里暂时未用到 */
  16.      }
  17.      portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); /* 退出临界区 */
  18.     /* 恢复挂起任务 */
  19.      xYieldRequired = xTaskResumeFromISR(xHandleTaskLED);
  20.       /* 退出中断后是否需要执行任务切换 */
  21.      if( xYieldRequired == pdTRUE )
  22.      {
  23.          portYIELD_FROM_ISR(xYieldRequired);
  24.      }
  25. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-8-24 15:15:16 | 显示全部楼层
15.7 总结


    本章教程为大家讲解两个重要的概念,FreeRTOS的临界段和开关中断,望初学者认真掌握。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-4-30 18:04 , Processed in 0.368882 second(s), 28 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表