【嵌入式】stm32+freeRTOS移植与应用

freeRTOS

源码移植

手动移植:

freeRTOS官网

  1. 点击Download FreeRTOS
  2. 点击Download
  3. 解压zip文件

FreeRTOS是内核文件夹,只需关注这个

FreeRTOS

  • Demo: 示例程序

  • License: 许可,每个源文件开头都要引用一段许可

  • Source: 内核源码

    • include: 头文件
    • portable: 与具体的硬件平台相关,也叫移植层文件夹

    对于MDK-ARM环境,需要Keil、MenMang和RVDS三个文件夹中的文件

    Keil文件夹中只有一个See-also-the-RVDS-directory.txt文件,意思是同RVDS文件夹

    RVDS文件夹中包含了ARM各个架构的MCU,stm32f1xx属于Cortex-M3,选ARM_CM3文件夹

    各个文件夹中的文件都相同,包含port.cportmacro.h两个文件

    • port.c: 是ARM内核与FreeRTOS的接口文件
    • portmacro.h: 定义了所有的硬件特定功能

移植成功与否,需要通过一个基础工程进行验证

步骤:

  1. 在stm32工程下新建FreeRTOS文件夹
  2. 将源码中的Source文件夹下全部文件复制到FreeRTOS文件夹中
  3. 打开portable文件夹,除了Keil、MemMang、RVDS这三个目录保留以外,其它目录全部删除
  4. FreeRTOSConfig.h文件进行配置和裁剪,FreeRTOSConfig.h文件可以从Demo中获得,放到FreeRTOS\include文件夹中

CubeMX自动移植:

STM32CubeMX移植FreeRTOS

配置:

  • Interface: CMSIS_V1

    CMSIS是一套接口标准,屏蔽软硬件差异以提高软件的兼容性,RTOSv1使软件可以在不同的操作系统下运行(屏蔽不同RTOS提供的API的差别),RTOSv2拓展了v1,兼容更多的CPU架构和RTOS,使用F1和F4时没必要选v2,更高兼容性的代价时更冗余的代码

配置裁剪

注意:

  • 每一个FreeRTOS应用项目都必须包含一个FreeRTOSConfig.h头文件,由于该文件不属于内核的一部分,所以可以直接放入Inc文件夹中

  • 并非所有的裁剪相关的宏都必须包含在FreeRTOSConfig.h头文件中,未包含的宏会在另一个文件FreeRTOS.h文件中被声明,并赋予默认值

    例如

    // FreeRTOS.h
    #ifndef configUSE_CO_ROUTINES
    	#define configUSE_CO_ROUTINES 0
    #endif
    

参数说明:

  • configUSE_PREEMPTION

    为1使用抢占式任务调度方式,为0使用协作式调度方式,协作式一般用于硬件资源及其有限的系统中使用,一般设为1

  • configUSE_PORT_OPTIMISED_TASK_SELECTION

    • 1:硬件方法查找下一个任务,速度快,但优先级有限制,对stm32,优先级最多32个
    • 0:纯软件方法查找下一个任务,速度慢,但是优先级数量无限制

断言

在调试阶段使用,会增加系统开销,调试完成后注释掉即可

// FreeRTOSConfig.h
#define configASSERT( x ) if ((x) == 0) {taskDISABLE_INTERRUPTS(); for( ;; );}

freeRTOS会在关键处调用configASSERT(),如果判断为0,则执行一些命令,比如说停机、串口发送消息、指示灯闪烁,可以自己定义

命名规则

FreeRTOS函数变量命名

Task任务

创建任务

  1. 引用FreeRTOS.htask.h头文件
  2. 创建任务句柄
  3. 创建任务函数
  4. 调用任务创建函数xTaskCreate()创建任务
  5. 开启调度器vTaskStartScheduler(),正常情况下,程序不会执行此语句后面的代码,main函数中的无限循环不会被执行

状态

任务有四种状态:运行态、就绪态、阻塞态、挂起态

  • 运行态:正在使用CPU的任务处于运行态,如果是单核CPU,则只有一个任务处于运行态
  • 就绪态:准备就绪,可以运行的任务,但是因为有更高优先级的任务正在运行而没有被调度器选中
  • 阻塞态:正在等待外部事件的发生,会有一个超时时间
  • 挂起态:类似于阻塞态,但是没有超时时间

任务函数

  • 任务函数包含一个无限循环,正常情况下不会有return,也不会执行到函数的末尾,如果需要跳出无限循环,则需要在函数末尾加上vTaskDelete(NULL);传入参数NULL代表删除当前正在运行的任务
  • 一个任务函数可以用于创建多个任务,其中每个任务会有单独的任务栈,所以每个任务的自动变量是独立的,但是静态变量则可以供所有任务访问

任务删除

  • 使用vTaskDelete()删除任务

  • 任务删除后就不复存在,此时不能再对任务进行操作,所以对任务进行任何操作前必须判断任务的状态,使用

    if(eTaskGetState(<Task>) != eDeleted){
        <对任务的操作>
    }
    

任务挂起与恢复

任务挂起:vTaskSuspend()

任务恢复:vTaskResume()

参数均为需要挂起与恢复的任务句柄,可以使用xTaskGetHandle()获取任务句柄,参数是任务名

**注意:**任务挂起后再恢复,从挂起语句后面开始继续运行,而非从头运行

任务调度

  • 抢占式调度:适用于任务有不同优先级的场合,任务会一直运行,直到被更高优先级的任务抢占,或者遇到阻塞式API函数,比如vTaskDelay()才让出CPU使用权

  • 时间片调度:适用于多个任务有相同优先级的场合,每个相同优先级的任务运行一个时间片后就让出CPU使用权

    使用时间片调度方法,需要

    • configUSE_PREEMPTION设置为1
    • configUSE_TIME_SLICING设置为1

    时间片长度由configTICK_RATE_HZ定义的系统时间节拍决定

优先级

优先级数值越大,代表优先级越高;与stm32的中断优先级相反

调度器确保高优先级的处于运行态或就绪态任务获得CPU使用权

当多个任务优先级相同时,FreeRTOS将使用时间片调度方式

任务切换

taskYIELD(),实际是启动PendSV中断

时钟节拍追加

vTaskStepTick()一般用于低功耗tickless模式,恢复系统时钟节拍后,停止运行的时钟节拍数就可以用该函数补上

Queue消息队列

CubeMX FreeRTOS消息队列

注意:

  • 接收任务的栈大小至少需要512words
  • 接受任务的优先级要比发送任务高一级

临界段

https://blog.csdn.net/Qrsleizhipeng/article/details/83377340

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

taskENTER_CRITICAL(); // 进入临界段
taskENTER_CRITICAL_FROM_ISR(); // 进入临界段,用于中断服务函数中
/* 临界段内的代码要求执行时间越短越好,否则会影响系统的实时性 */
taskEXIT_CRITICAL(); // 退出临界段
taskEXIT_CRITICAL_FROM_ISR(); // 退出临界段,用于中断服务函数中

不能被打断的代码称为临界段,能打断程序执行的往往是中断,因此临界段首先要关闭打开中断

  • 临界段代码尽量短,以免破坏实时性
  • 临界段函数要成对使用
  • 临界段会关闭打开FreeRTOS能管理的中断,高于能管理的中断不会被关闭

挂起和恢复调度器

  • 挂起调度器:vTaskSuspendAll()
  • 恢复调度器:vTaskResumeAll()

vTaskSuspendAll()vTaskResumeAll()之间的代码不会被更高优先级的任务抢占,调度器被禁止,不用关闭中断

Debug

freeRTOS介绍:https://blog.csdn.net/qq_37634122/article/details/104283673

Error: selected FPU does not support instruction – `vstmdbeq r0!,{s16-s31}’

https://www.jianshu.com/p/54412fefd538

不能单纯修改CMakeLists.txt,要修改CMakeLists_template.txt

C语言宏函数的特殊用法

#和##的用法

LED1Hz频率闪烁任务,频率明显低于预设值

原因是任务优先级设置不正确,有优先级更高的任务一直占用着CPU资源

freeRTOS优先级介绍

freeRTOS中,优先级数值越高,优先级越高

这点与中断抢占优先级不同

FreeRTOS 中任务的最高优先级是通过 FreeRTOSConfig.h 文件中的 configMAX_PRIORITIES 进行
配置的,用户实际可以使用的优先级范围是 0 到 configMAX_PRIORITIES – 1

使用freeRTOS在任务中使用printf会进入HardFault_Handler

https://www.cnblogs.com/tianxxl/p/11983353.html

https://blog.csdn.net/weixin_45045399/article/details/104001209

原因:任务栈分配太小,如果要使用printf函数,usStackDepth至少要设置512

解决方法:任务的Stack Size从128改为512即可

只有发送任务有反应,接收任务无反应

将接收任务设置为阻塞状态,然后调高优先级

两个相同优先级的任务同时使用printf()函数,串口输出混乱】

原因:printf()函数任一时刻只允许一个任务访问,多个任务同时使用时,将造成输出的混乱

解决:

  • 方法一:通过使用临界段代码保护函数taskENTER_CRITICAL()taskEXIT_CRITICAL()来避免多个任务同时向串口输出字符

    void LedTask0(void *pvParameters) {
        uint16_t counts = 0;
        while (1) {
            taskENTER_CRITICAL(); // 进入临界段
            printf("任务0运行%d次\n",++counts);
            taskEXIT_CRITICAL(); // 退出临界段
    
            vTaskDelay(pdMS_TO_TICKS(1000));
        }
    }
    
  • 方法二:通过vTaskSuspendAll()xTaskResumeAll()挂起调度器

    void LedTask0(void *pvParameters) {
        uint16_t counts = 0;
        while (1) {
            vTaskSuspendAll();
            printf("任务0运行%d次\n",++counts);
            xTaskResumeAll();
    
            vTaskDelay(pdMS_TO_TICKS(1000));
        }
    }
    
  • 方法三:可能是因为时间片长度太短,CPU的频率太低,在一个时间片的时间长度内,printf()来不及完成,解决方法,将TICK_RATE_HZ从1000修改为500


参考文献:

  1. 《嵌入式实时操作系统FreeRTOS原理及应用——基于STM32微控制器》
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值