STM32之FreeRTOS

发布者:yuehui最新更新时间:2020-04-19 来源: eefocus关键字:STM32  FreeRTOS  操作系统 手机看文章 扫描二维码
随时随地手机看文章

学习操作系统,我并没有一开始就学习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个任务了。如果再小,跑得更多!


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

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

关键字:STM32  FreeRTOS  操作系统 引用地址:STM32之FreeRTOS

上一篇:STM32单片机中,FreeRTOS RAM使用情况及优化方法
下一篇:STM32 使用 Keil MDK 中的软件逻辑分析仪参与硬件调试

推荐阅读最新更新时间:2024-11-11 12:00

基于STM32设计的环境检测设备
随着人们生活质量的提高,对于生活环境的问题,人们的关注度进一步提高,同时政府部门采取了许多措施来改善环境状况。但是总体上来说我国的环境监测技术水平比较落后,传统上的监测手段比较单一,监测数据也不够准确,耗尽了大量的人力和财力,却成效不高。 针对上述缺点,当前文章综合了嵌入式处理技术、传感器技术、无线网络通信等技术,设计了一个基于STM32的无线环境监测系统,系统主要实现了对湿度、温度、有毒气体、烟雾浓度、空气质量等参数进行实时监测的功能。为了实现无线数据传输功能,采用了无线wifi技术。系统的测试分析表明系统整体数据采集性能良好,数据传输稳定性可靠,到达了预期目标。 系统与传统的监测技术相比,具有监测数据准确,监测范围广,
[单片机]
基于<font color='red'>STM32</font>设计的环境检测设备
stm32单片机引脚介绍及功能
stm32是一种ARM Cortex-M内核单片机,下面小编为大家介绍一下单片机引脚介绍及功能。单片机旁边的一排金属针脚就是引脚,不同的引脚有不同的功能。 引脚大概分为4种,分别是电源、时钟、控制和I/O引脚。stm32的引脚一般有GPIO和AFIO两种用途。 比如有的引脚是电源正极和黑色标记的电源负极,这是来给单片机提供电源的,要把电流引到对应的引脚才能正常工作。 还有晶体震荡电路反向输入端和输出端引脚,晶体能够为单片机提供基本的震荡源。如果没有晶振的话,单有电源单片机是不能启动的,就和人们的心跳一样。 RESET(PC6)复位信号输入端引脚是用来重启单片机的,和电脑一样,有时候单片机也会死机,这个时候就需要RE
[单片机]
vivo发布全新操作系统OriginOS:新外观新交互 体验更加流畅
新浪数码讯 11月18日上午消息,vivo在深圳举办OriginOS特别活动,发布新一代操作系统OriginOS。   发布会上,vivo高级副总裁、首席技术官施玉坚表示:vivo成立25年来,一直秉承“本分、创新、消费者导向”理念,在设计、系统、影像、性能四个方面更好的满足用户的需求,用创新的技术、让复杂的生活变得更简单。而OriginOS将是一个新的起点,vivo会继续以打造全新数字世界出发点为目标,带来更好的产品。   接下来,vivo用户体验设计总监葛亚男正式介绍了OriginOS。在设计方面,OriginOS以华容道为设计灵感,首发华容网格,也就是针对桌面的底层框架设计的一套全新的桌面网格体系,涉及桌面上图
[手机便携]
STM32之外部中断和中断控制器
在外部中断和中断控制器中,我们首先阐述嵌套向量中断控制器(NVIC),他和处理器核的接口紧紧相连。可以实现低延时的中断处理和处理晚到的中断。主要具有以下几个特点: 1.60个可屏蔽中断通道; 2.16个可编程的优先等级; 3.低延时的异常和中断处理; 4.电源管理控制; 5.系统控制寄存器的实现。 其中每一个STM32系列芯片都会有一个产品向量表,博友们可以在相关 产品说明书中查看具体的中断问题。 接下来介绍中断优先级 在M3中有两个优先级的概念-抢占优先级和响应优先级。每一个中断源都必须指定这两种优先级。这里的中断抢占其实和89C51差不多,高中断优先级可以抢占正在进行中断处理的低中断优先级,也就是我们所说的中断嵌套。学习过51
[单片机]
STM32的HAL库与标准库的区别
新手在入门 STM32 的时候,一般大多数都会选用标准库和 HAL 库,而极少部分人会通过直接配置寄存器进行开发。 对于刚入门的朋友,可能没法直观了解这些不同开发发方式之间的区别,本文试图以一种非常直白的方式,用自己的理解去将这些东西表述出来。 配置寄存器 不少先学了 51单片机的朋友可能会知道,会有一小部分人或教程是通过汇编语言直接操作寄存器实现功能的,这种方法到了 STM32 就变得不太容易行得通了。 因为 STM32 的寄存器数量是 51单片机的十数倍,如此多的寄存器根本无法全部记忆,开发时需要经常的翻查芯片的数据手册,此时直接操作寄存器就变得非常的费力了。也有人喜欢去直接操作寄存器,因为这样更接近原理,代码更
[单片机]
<font color='red'>STM32</font>的HAL库与标准库的区别
Stm32按键中断使LED灯闪烁
/* *说明: *PA0:KEY1;PA1:KEY2; *PA2:LED1;PA3:LED2; *PA9:USART1_TX;PA10:USART1_RX */ #include stm32f10x.h #include stm32f10x_rcc.h #include stm32f10x_gpio.h #include stm32f10x_crc.h #include stm32f10x_exti.h #include system_stm32f10x.h #include misc.h void RCC_Configuration(void); void GPIO_Conf
[单片机]
创建keil工程并点亮STM32板子的LED灯
打开之前创建的工程,如下图所示(keil工程的创建在之前的文章中有,不了解的同学可以翻一下上一篇文章。) (上图是已经建立完成的工程) 然后,再次打开我们之前下载的对应开发板版本的例程包中的点亮LED灯的例程 (路径为:en.stsw-stm32077 \STM32L1xx_StdPeriph_Lib_V1.3.1 \project\STM32L1xx_StdPeriph_Examples \GPIO \IOToggle) 找到mian.c文件打开,全选复制里面的内容 再回到我们之前已经创建完毕的工程中,打开mian.c文件清空,并将GPIO文件中的main.c里面的内容粘贴到我们工程的mian.c中。 此时可以看到有很多
[单片机]
Stm32调试之assert_param() 断言机制
void TIM_DeInit(TIM_TypeDef* TIMx) { /* Check the parameters */ assert_param(IS_TIM_ALL_PERIPH(TIMx)); if (TIMx == TIM1) { RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM1, ENABLE); RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM1, DISABLE); } } 这段代码是Stm32固件库中的,IS_TIM_ALL_PERIPH(TIMx)这个函数的原形是判断TIMx是否等于TIM1-TI
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
随便看看

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved