STM32 使用 FreeRTOS过程记录

时间:2021-10-02 15:26:00

资源:http://blog.csdn.net/zhzht19861011/article/category/6191478

资源:可以下载安富莱的STM32-V5开发版资料中的FreeRTOS教程,里面有详细介绍各个功能,可以在http://wenku.baidu.com/search?word=%B0%B2%B8%BB%C0%B3STM32-V6%BF%AA%B7%A2%B0%E5FreeRTOS%BD%CC%B3%CC&lm=0&od=0&fr=top_home中查找相应的文档

移植的步骤在网上都能找到,这里只指出使用过程中遇到的问题

一、

1. 关于systick的设置

    一般在stm32上运行实时操作系统,都会使用systick作为实时系统的时钟滴答,所以会涉及到systick的设置,在freeRTOS的FreeRTOSConfig.h文件中可以找到:

#define configCPU_CLOCK_HZ			( ( unsigned long ) 72000000 )
#define configTICK_RATE_HZ ( ( TickType_t ) 1000 )

   这个表示CPU的时钟为72MHZ,FreeRtos的时钟频率为1000HZ,这两个值可以根据实际的配置情况进行更改,

  在port.c中有如下条件语句:

#ifndef configOVERRIDE_DEFAULT_TICK_CONFIGURATION
#define configOVERRIDE_DEFAULT_TICK_CONFIGURATION 0
#endif

  表示如果我们没有配置systick,那么freeRTOS就会根据 configCPU_CLOCK_HZ 和 configTICK_RATE_HZ 两个宏来对systick的寄存器进行配置;

2. FreeRTOS与ucos-ii的内存管理

UCOS-II把内存区域(memory partition)分成相同大小的内存块来使用,从而避免了内存碎化(fragmentation)带来的困扰。这种内存管理方式的缺点在于:大小不同的内存块必须提前订制,无法通用。

freeRTOS里最常用到的内存管理方式是:不固定内存块的大小;选择满足要求的最小空闲内存块使用,并将多余的部分划分出去形成一个新的内存块。这种管理方式有可能产生碎片,不过使用起来要方便一些。

3. FreeRTOS中断分级

  3.1 在官方推荐中,强烈建议STM32的优先级分组采用4,即:

/* 优先级分组设置为4, 优先配置好NVIC */ 
 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

   也就是说只有4个位的抢占优先级(0 ~ 16),而没有从优先级;为什么这样设计? 这得从FreeRTOS的对中断优先级的处理说起。在FreeRTOS中,FreeRTOS将优先级通过

   configMAX_SYSCALL_INTERRUPT_PRIORITY(在FreeRTOSConfig.h中)分成两组进行处理:

    1. 当中断的优先级数值高于configMAX_SYSCALL_INTERRUPT_PRIORITY的值(实际该中断优先级要小于configMAX_SYSCALL_INTERRUPT_PRIORITY对应的优先级,因为在STM32中,优先级数值越高,表明优先级越低)时,说明该中断可以被FreeRTOS系统进行屏蔽(通过FreeRTOS进入临界区屏蔽),同时,在该中断的中断服务例程中可以使用FreeRTOS 的API函数;

    2.当中断的优先级数值低于configMAX_SYSCALL_INTERRUPT_PRIORITY的值时,说明该中断可以不能被FreeRTOS系统进行屏蔽(即使FreeRTOS进入临界区也不能),同时,在该中断的中断服务例程中不能使用FreeRTOS 的API函数;这些中断称为:不受FreeRTOS管理的中断

 3.2 不受FreeRTOS管理的中断

    讲解不受FreeRTOS 管理的中断之前要说一个小知识点----中断延迟。中断延迟时间是衡量RTOS 实时操作系统的一项重要指标,那什么又是中断延迟呢?从中断触发到执行中断服务程序的第一条指令这段时间就是中断延迟时间。

    FreeRTOS 内核源码中有多处开关全局中断的地方,这些开关全局中断会加大中断延迟时间。比如在源码的某个地方关闭了全局中断,但是此时有外部中断触发,这个中断的服务程序就需要等到再次开启全局中断后才可以得到执行。开关中断之间的时间越长,中断延迟时间就越大,这样极其影响系统的实时性。如果这是一个紧急的中断事件,得不到及时执行的话,后果是可想而知的。针对这种情况,FreeRTOS 就专门做了一种新的开关中断实现机制。关闭中断时仅关闭受FreeRTOS管理的中断,不受FreeRTOS 管理的中断不关闭,这些不受管理的中断都是高优先级的中断,用户可以在这些中断里面加入需要实时响应的程序。FreeRTOS 能够实现这种功能的奥秘就在于FreeRTOS 开关中断使用的是寄存器basepri,而像uCOS 这种使用的是primask,详情请看下面整理的表格:

 STM32 使用 FreeRTOS过程记录

 STM32 使用 FreeRTOS过程记录    对寄存器basepri 我们举一个例子,帮助大家理解,比我们配置寄存器basepri 的数值为16,所有优先级数值大于等于16 的中断都会被关闭,优先级数值小于16 的中断不会被关闭。对寄存器basepri 寄存器赋值0,那么被关闭的中断会被打开。这个就是FreeRTOS 开关中断的实现方案。

4. FreeRTOS优先级

  4.1 在FreeRTOS中,不同任务可以设置同一个优先级;多个任务可以共享一个优先级,RTOS调度器为相同优先级的任务分享CPU时间,在每一个RTOS 系统节拍中断到来时进行任务切换

  4.2  在RTOS内核中,每个有效优先级都会消耗一定量的RAM,因此这个值不要超过你的应用实际需要的优先级数目。每一个任务都会被分配一个优先级,优先级值从0~     (configMAX_PRIORITIES - 1)之间。低优先级数表示低优先级任务。空闲任务的优先级为0(tskIDLE_PRIORITY),因此它是最低优先级任务。    处于就绪状态的相同优先级任务使用时间片调度机制共享处理器时间。

5. FreeRTOS堆栈

  堆栈大小不是以字节为单位而是以字为单位的,比如在32位架构下,栈大小为100表示栈内存占用400字节的空间。

6.configUSE_TASK_NOTIFICATIONS  

  设置宏configUSE_TASK_NOTIFICATIONS为1(或不定义宏configUSE_TASK_NOTIFICATIONS)将会开启任务通知功能,有关的API函数也会被编译。设置宏  configUSE_TASK_NOTIFICATIONS为0则关闭任务通知功能,相关API函数也不会被编译。默认这个功能是开启的。开启后,每个任务多增加8字节RAM。

这是个很有用的特性,一大亮点。

每个RTOS任务具有一个32位的通知值,RTOS任务通知相当于直接向任务发送一个事件,接收到通知的任务可以解除任务的阻塞状态(因等待任务通知而进入阻塞状态)。相对于以前必须分别创建队列、二进制信号量、计数信号量或事件组的情况,使用任务通知显然更灵活。更好的是,相比于使用信号量解除任务阻塞,使用任务通知可以快45%(使用GCC编译器,-o2优化级别)。

7. 互斥信号量和二值信号量

关于互斥量和二进制信号量简单说:

  • 互斥型信号量必须是同一个任务申请,同一个任务释放,其他任务释放无效。当多个任务使用同一个资源时,可以使用互斥信号量对共享资源进行互斥;
  • 二进制信号量,一个任务申请成功后,可以由另一个任务释放。
  • 互斥型信号量是二进制信号量的子集
  • FreeRTOS的互斥信号量有一个优先级继承机制,可以防止优先级翻转的问题;而二值信号量则没有;

   优先级继承防止优先级翻转问题:  

  1. 创建2个任务Task1和Task2,优先级分别为1和3,也就是任务Task2的优先级最高。
  2. 任务Task1和Task2互斥访问串口打印printf。
  3. 使用FreeRTOS的互斥信号量实现串口打印printf的互斥访问。
  4. 低优先级任务Task1执行过程中先获得互斥资源printf的执行。此时任务Task2抢占了任务Task1的执行,任务Task1被挂起。任务Task2得到执行。
  5. 任务Task2执行过程中也需要调用互斥资源,但是发现任务Task1正在访问,此时任务Task1的优先级会被提升到与Task2同一个优先级,也就是优先级3,这个就是所谓的优先级继承(Priority inheritance),这样就有效地防止了优先级翻转问题。任务Task2被挂起,任务Task1有新的优先级继续执行。
  6. 任务Task1执行完毕并释放互斥资源后,优先级恢复到原来的水平。由于互斥资源可以使用,任务 Task2获得互斥资源后开始执行。

    上面就是一个简单的FreeRTOS互斥信号量的实现过程。

8. 任务划分

  8.1 任务的结构

    任务的结构按照任务的执行方式可以分为三类:单次执行类周期执行类事件触发类

9. 数据通信

  注意: 当对共享资源进行写操作的任务只有一个时,只要其优先级高于所有其他进行读操作的任务,就可以在进行写操作时不必关中断,这是因为低优先级任务执行读操作不可能在这个时候夺取运行权。而低优先级执行任务执行读操作必须采取关中断措施来访问全局变量,这是因为高优先级任务在进行写操作时,随时有可能被触发而获得运行权。

二、需要了解

  1. RTOS的软件定时器功能

  2. 任务切换方式(任务管理)和调度器

    2.1 任务切换

      任务切换的实现在不同的嵌入式实时操作系统中区别不大,基本相同的硬件内核架构,任务切换也是相似的

    2.2 调度器

      调度器的核心是调度算法,嵌入式实时操作系统中,调度算法会有些区别

  1. 调度方式 

   FreeRTOS支持:抢占式调度、时间片调度和合作式调度;实际应用主要是抢占式调度和时间片调度,合作式调度用到很少;在FreeRTOS代码中会保持合作式调度,但不会再进行更新

   3.1 抢占式调度

      每个任务都有不同的优先级,任务会一直运行直到被高优先级的任务抢占或遇到阻塞式的API函数,如vTaskDelay();

   3.2 时间片调度

      每个任务都有相同的优先级,任务会运行固定的时间片个数或遇到阻塞式的API函数,比如vTaskDelay,才会执行同优先级任务之间的人物切换

二、任务划分

  STM32 使用 FreeRTOS过程记录

  • IRQ任务:IRQ任务是指通过中断服务程序进行触发的任务,此类任务应该设置为所有任务里面优先级最高的。

  • 高优先级后台任务:比如按键检测,触摸检测,USB消息处理,串口消息处理等,都可以归为这一类任务。

  • 低优先级的时间片调度任务:比如emWin的界面显示,LED数码管的显示等不需要实时执行的都可以归为这一类任务。实际应用中用户不必拘泥于将这些任务都设置为优先级1的同优先级任务,可以设置多个优先级,只需注意这类任务不需要高实时性。

  • 空闲任务:空闲任务是系统任务。

  • 特别注意:IRQ任务和高优先级任务必须设置为阻塞式(调用消息等待或者延迟等函数即可),只有这样,高优先级任务才会释放CPU的使用权,,从而低优先级任务才有机会得到执行。 这里的优先级分配方案是我们推荐的一种方式,实际项目也可以不采用这种方法。调试出适合项目需求的才是最好的。