STM32之FreeRTOS

2023-05-16

      学习操作系统,我并没有一开始就学习UCOS,而是选择了FreeRTOS。FreeRTOS可以方便地搭建在各个平台上,因为汇编相关,都已经由官方完成,我们要做的仅是添加自己的代码,可省去很多工作量。

      问题1:在使用多任务时,我想利用USART输出信息,但是如果直接放在任务中输出,往往会造成字符收发顺序不一致的情况,这是仿真时遇到的实际问题。为解决这个问题,可以在USART输出信息时挂起其它任务,利用vTaskSuspendAll函数挂起,再利用xTaskResumeAll重启内核调度。这样可以保证USART正确发送信息。但是,我觉得还不如为USART建立一个任务,这样子,USART就不会发生发送字符顺序错乱的问题了!而完成任务间的通信,就可以考虑使用队列!

       在此,队列的功能相当于任务与任务之间进行通信的通道。USART的任务可通过查询队列是否为空来决定是否输出。

队列的使用:

       定义队列:

      xQueueHandle USART_Q;

       建立队列:

       xQueueCreate( uxQueueLength, uxItemSize );

       第一个参数为队列中元素个数;第二个参数为队列中元素大小,单位为字节。

       也可以使用

       xQueueGenericCreate( uxQueueLength, uxItemSize, queueQUEUE_TYPE_BASE )创建队列,接下来的三个函数都有两种使用方法。

 

       发送消息:

      xQueueSend( xQueue, pvItemToQueue, xTicksToWait );

      第一个参数为队列句柄,第二个参数为队列元素,第三个参数为阻塞超时时间,如果在发送时队列已满,这个时间即是任务处于阻塞态等待队列空间有效的最长等待时间。

 

      接收消息:

      xQueueReceive( xQueue, pvBuffer, xTicksToWait )

      一般用法如:while( xQueueReceive( xLCDQueue, &xMessage, portMAX_DELAY ) != pdPASS );

以上为队列用法,因为学习粗浅,不能进行深入分析……

注意,经查看代码,发送元素到队列的过程中,是将元素复制进队列中的,而并不是指针引用。

 

演示代码:


xQueueHandle  USART1_MSGQ;   //To receive the usart characters's queue

volatile unsigned long mainDELAY_LOOP_COUNT=0xffff;
void vTask1( void *pvParameters )
{
    volatile unsigned long ul;
    for( ;; )
   {
        xQueueSend( USART1_MSGQ, "This is task 1 !\n",portMAX_DELAY);
        for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ );
   }
}
void vTask2( void *pvParameters )
{
   volatile unsigned long ul;
   for( ;; )
   {
       xQueueSend( USART1_MSGQ, "This is task 2 !\n",portMAX_DELAY);
       for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ );
    }
}
void vTask3( void *pvParameters )
{
    char str[100];
    volatile unsigned long ul;
    for( ;; )
   {
       while( xQueueReceive( USART1_MSGQ, str, portMAX_DELAY ) != pdPASS );
       printf(str);
    }
}

在main函数中添加:

USART1_MSGQ = xQueueCreate(10,(sizeof(char))*100);

xTaskCreate( vTask1, "Task 1", 1000, NULL, 1, NULL );
xTaskCreate( vTask2, "Task 2", 1000, NULL, 1, NULL );
xTaskCreate( vTask3, "Task 3", 1000, NULL, 2, NULL );

vTaskStartScheduler();

while(1);

注意,printf函数已定位到USART1串口上,使用Keil调试的时候,可以通过串口窗口看到输出内容。

 

 

 二进制信号量定义与使用:

      定义信号量:

      xSemaphoreHandle xBinarySemaphore;

      vSemaphoreCreateBinary( xBinarySemaphore );

      设置信号量:(在中断处理里面设置的时候,一定要使用以FromISR结尾的函数或宏)

      xSemaphoreGiveFromISR( xBinarySemaphore, &xHigherPriorityTaskWoken );

xSemaphoreGiveFromISR的第二个参数,。如果调用xSemaphoreGiveFromISR()使得一个任务解除阻塞,并且这个任务的优先级高于当前任务(也就是被中断的任务),那么xSemaphoreGiveFromISR()会在函数内部将*pxHigherPriorityTaskWoken 设为pdTRUE。

       如果xSemaphoreGiveFromISR() 将pxHigherPriorityTaskWoken设为pdTRUE,则在中断退出前应当进行一次上下文切换。这样才能保证中断直接返回到就绪态任务中优先级最高的任务中。

        上下文切换,使用宏portEND_SWITCHING_ISR()

注意:每种处理器架构都有自己的宏定义,一般在portmacro.h文件里面,声明为portEND_SWITCHING_ISR()

        xHigherPriorityTaskWoken参数为portBASE_TYPE类型。取值为pdFALSE或者pdTRUE.

例如:


static void __interrupt __far vExampleInterruptHandler( void )

{

   static portBASE_TYPE xHigherPriorityTaskWoken;

   xHigherPriorityTaskWoken = pdFALSE;

   xSemaphoreGiveFromISR( xBinarySemaphore, &xHigherPriorityTaskWoken );

   portEND_SWITCHING_ISR(xHigherPriorityTaskWoken );

}


      获取信号量(获取不到,则进入阻塞态):

      xSemaphoreTake( xBinarySemaphore, portMAX_DELAY );

      注意,xSemaphoreTake函数的第二个参数设置为portMAX_DELAY,且在FreeRTOSConig.h 中设定INCLUDE_vTaskSuspend 为1,那么阻塞等待将没有超时限制。

 

 

 

计数信号量定义与使用:

       因为之前没有接触过RTOS,在学习的过程中,我就不明白,为什么要搞个计数信号量,有什么意义?还好资料给我解除了这个疑惑。一个二值信号量最多只可以锁存一个中断事件,如果中断延迟处理任务还没有执行完,而中断又发生了多次,就会导致中断信息丢失。如果采用计数信号量,中断次数就能很好地保存下来。

       定义计数信号量:

xSemaphoreHandle为计数信号量的变量类型

       创建计数信号量:

xSemaphoreHandle xSemaphoreCreateCounting( unsigned portBASE_TYPE uxMaxCount,unsigned portBASE_TYPE uxInitialCount );

返回值为xSemaphoreHandle变量

uxMaxCount参数为最大计数值,也就是队列深度。

uxInitialCount参数为计数始值。

       设置信号量:(在中断处理里面设置的时候,一定要使用以FromISR结尾的函数或宏)

同样使用函数:xSemaphoreGiveFromISR,也要注意上下文切换。

       获取信号量:

同样使用函数xSemaphoreTake;

 


用于临界值的一组宏:

taskENTER_CRITICAL();进入临界区

taskEXIT_CRITICAL();退出临界区


调度器挂起与运行:

void vTaskSuspendAll( void );

portBASE_TYPE xTaskResumeAll( void );


互斥量(及二值信号量)

互斥量是一种特殊的二值信号量,用于控制在两个或多个任务间访问共享资源。可以理解为得到互斥量,即可得到资源。但是用完资源,必须归还互斥量。

互斥量的变量类型为:

xSemaphoreHandle xMutex;

互斥量的创建:

xSemaphoreHandle xSemaphoreCreateMutex( void );

互斥量的取得:

xSemaphoreTake( xMutex, portMAX_DELAY );

互斥量的归还:

xSemaphoreGive( xMutex );

运行时的栈侦测等最后会完善。

 


FreeRTOS的软时钟与任务延时阻塞:

用硬件定时器有时候并不是很方便,因为得写中断,得分配信号量;而且这类开支会让定时也不能特别精确!如果使用FreeRTOS的软时钟,会比较合适。而且也能省去很多代码。

1、任务延时阻塞vTaskDelay()函数与

vTaskDelay函数的使用

INCLUDE_vTaskDelay must be defined as 1 for this function to be available. See the configuration section for more information.

Delay a task for a given number of ticks. The actual time that the task remains blocked depends on the tick rate. The constant portTICK_RATE_MS can be used to calculate real time from the tick rate .

INCLUDE_vTaskDelay应该定义为1才能使能该函数。可以通过查看配置部分获取更多信息。函数可通过给定数值延时一个任务。任务的实际阻塞时间依赖于时钟频率。常量portTICK_RATE_MS能用来计算时钟频率的实际时间。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
voidvTaskFunction(void* pvParameters )
 {
 /* Block for 500ms. */
 constportTickType xDelay = 500 / portTICK_RATE_MS;
 
     for( ;; )
     {
         /* Simply toggle the LED every 500ms, blocking between each toggle. */
         vToggleLED();
         vTaskDelay( xDelay );
     }
 }

但是,vTaskDelay并不能给你一个绝对的时延,它是相对于自身的延时!因为任务切换或中断发生等原因会影响vTaskDelay函数的使用。如果要使用绝对的时延,可以使用函数vTaskDelayUntil


vTaskDelayUntil函数的使用

INCLUDE_vTaskDelayUntil must be defined as 1 for this function to be available.

使能函数,INCLUDE_vTaskDelayUntil必须设置为1。

Delay a task until a specified time. This function can be used by cyclical tasks to ensure a constant execution frequency.

延迟一个任务直到指定时间。该函数能用于循环任务保持流畅执行。

This function differs from vTaskDelay() in one important aspect: vTaskDelay() specifies a time at which the task wishes to unblock relative to the time at which vTaskDelay() is called, whereas vTaskDelayUntil() specifies an absolute time at which the task wishes to unblock.

该函数与vTaskDelay函数最大不同的一方面在于:vTaskDelay指定的时间是相对于vTaskDelay调用时开始的,然而vTaskDelayUntil指定的时间是绝对的时间,到时间一定解除阻塞。

vTaskDelayUntil函数的使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Perform an action every 10 ticks.
 voidvTaskFunction(void* pvParameters )
 {
 portTickType xLastWakeTime;
 constportTickType xFrequency = 10;
 
     // Initialise the xLastWakeTime variable with the current time.
     xLastWakeTime = xTaskGetTickCount();
     for( ;; )
     {
         // Wait for the next cycle.
         vTaskDelayUntil( &xLastWakeTime, xFrequency );
         // Perform action here.
     }
 }









听说FreeRTOS任务可以同优先级,但是发现同优先级创建3个以上任务就无法调度?不知道是什么原因? 

原因:一般情况下,刚开始学FreeRTOS都习惯将任务栈空间设置为1000,这意味着将要在系统里面申请4000个字节(栈空间以4字节为单位);创建3个任务,申请的约为12K左右的样子,如果再创建一个任务,也是用的1000; 那么意味着向系统申请16K左右的栈空间。加上空闲任务的栈空间,你FreeRTOSConfig.h文件里面的栈空间默认值为#define configTOTAL_HEAP_SIZE( ( size_t ) ( 17 * 1024 ) );也就是17K。显然运行不了,是因为堆栈空间不足的原因!

解决:两种办法:1、任务栈空间调小点,变成500;2、系统栈空间调大点,变成30K ;一般就足够你跑5到6个任务了。如果再小,跑得更多!


调度器只能调度一次就崩溃了,移植汇编都没问题?

原因:你的任务函数没放在死循环里面!


转自: hi.baidu.com/prorgramstudio/item/150fbf41fdd2e0e21f19bcc2   点击打开链接

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32之FreeRTOS 的相关文章

随机推荐

  • Win7安装Ubuntu1804双系统

    靖哥哥我平时使用ubuntu不多 xff0c 所以都是在虚拟机安装ubuntn 不过因为有一次物理机强制关机 xff0c 导致虚拟机文件损坏 xff0c 此后再使用虚拟机时 xff0c 常遇到死机的情况 xff0c 所以琢磨一下安装双系统
  • C++编译之make cmake bazel模板

    前面文章介绍了C 43 43 编译过程 xff1a 预处理 编译 汇编 链接 xff0c 内容比较简单 xff0c 只要会使用命令行 xff0c 就能根据文章的内容实践操作 xff0c 直观的了解编译全过程 一个项目往往不只一两个cpp文件
  • android之Fragment(官网资料翻译)

    Fragment要点 Fragment作为Activity界面的一部分组成出现 可以在一个Activity中同时出现多个Fragment xff0c 并且 xff0c 一个Fragment亦可在多个Activity中使用 在Activity
  • 个人面试经历经验谈

    到昨天接到金蝶得Offer xff0c 我想我为期三个星期的找工作面试之旅应该是告一段落了 原以为接到Offer会有点高兴 xff0c 但是一回味这三个星期的起起落落 xff0c 便实在是高兴不起来 xff0c 虽然手上有好几个Offer可
  • linux 查看端口占用情况

    1 查看系统端口 netstat anptl显示所有正在监听的端口 2 刷选某个端口 netstat anptl grep 39 3350 39 3 查看占用端口的应用程序 ps lt PID gt 下图中可以看到端口8080 的PID 6
  • eclipse tomcat部署项目开发环境修改访问路径

    eclipse tomcat部署项目开发环境修改访问路径
  • 欢迎使用CSDN-markdown编辑器

    欢迎使用Markdown编辑器写博客 本Markdown编辑器使用StackEdit修改而来 xff0c 用它写博客 xff0c 将会带来全新的体验哦 xff1a Markdown和扩展Markdown简洁的语法代码块高亮图片链接和图片上传
  • PLSQL 11 注册码

    PLSQL 11 注册码 注册码 xff1a Product Code xff1a 4t46t6vydkvsxekkvf3fjnpzy5wbuhphqz serial Number xff1a 601769 password xff1a x
  • ORA-28000: the account is locked-的解决办法

    ORA 28000 the account is locked 第一步 xff1a 使用PL SQL xff0c 登录名为system 数据库名称不变 xff0c 选择类型的时候把Normal修改为Sysdba 第二步 xff1a 选择my
  • IOS中bootstrap-select 动态加载的下拉框点击不展示(已解决)

    问题描述 bootstrap select 动态加载的option 在安卓浏览器中能点击后展示 但是在ios浏览器中点击没反应 发现原因 在重新渲染方法后加了一行 s e
  • S7-PLCSIM 无法找到STEP 7 V15 许可证(必须在此计算机上安装STEP V15应用程序)。-----(已解决)

    已经安装过step7 并且已经授权过了 xff0c 但是启动时提示下图错误 记得右键已管理员身份运行
  • 新建springboot项目, pom.xml报错 Unkown error 解决思路

    新建项目pom xml 报错 网上解决思路 xff1a 1 大多都是项目右键 Maven Update Project 选中Force Update of Snapshots Releases 进行强制更新 2 1 5 改成了2 1 3 修
  • 嵌入式软件面试总结

    背景 先说说本人的背景 xff0c 我 xff0c 一个大专人 xff0c 从事嵌入式开发两年了 xff0c 之前在一家公司是负责单片机和物联网开发的 2020年年底我选择了裸辞 xff08 主要想出去玩 xff09 直到春节结束后 xff
  • Intel NUC安装ubuntu系统的方法

    使用intel nuc安装ubuntu系统 xff0c 试验了好多次UEFI安装 xff0c 但是结果都是开机时会出现 A bootable device 除了这句话都是黑屏的现象 原因我查了很多 xff0c 也不敢确定 xff0c 现在总
  • 白骑士的树莓派教学(二):镜像烧录

    本期内容让我们来了解一下树莓派操作系统镜像烧录的操作 xff0c 所需的设备 xff1a PC机 xff0c U盘 xff0c 树莓派相关设备 什么是镜像 xff1f 所谓镜像文件其实和ZIP压缩包类似 xff0c 它将特定的一系列文件按照
  • VS Code Remote SSH远程连接异常:Resolver error: Error: Running the contributed command

    VS Code Remote SSH远程连接异常 问题描述原因分析解决方案扩展Remote SSH首次连接插件做了什么Remote SSH对于远程Linux的要求 问题描述 通过VS Code插件Remote SSH连接一台新主机时 xff
  • PHP常用六大设计模式

    单例模式 特点 xff1a 三私一公 xff1a 私有的静态变量 xff08 存放实例 xff09 xff0c 私有的构造方法 xff08 防止创建实例 xff09 xff0c 私有的克隆方法 防止克隆对象 xff0c 公有的静态方法 xf
  • matlab中文乱码的解决(UTF-8不支持的问题)

    1 解决editor中的UTF 8不支持的问题 xff0c 需要加入下面几行 在matlab 安装的目录的bin子文件夹中找到lcdata xml文件 xff1a 打开加入 lt Locale entries example gt lt l
  • FreeRTOS分析

    freertos是一个轻量级的rtos xff0c 它目前实现了一个微内核 xff0c 并且port到arm7 avr pic18 coldfire等众多处理器上 xff1b 目前已经在rtos的市场上占有不少的份额 它当然不是一个与vxw
  • STM32之FreeRTOS

    学习操作系统 xff0c 我并没有一开始就学习UCOS xff0c 而是选择了FreeRTOS FreeRTOS可以方便地搭建在各个平台上 xff0c 因为汇编相关 xff0c 都已经由官方完成 xff0c 我们要做的仅是添加自己的代码 x