当前位置: 首页 > news >正文

freeRTOS学习

2. FreeRTOS版本问题
V2的内核版本更高,功能更多,在大多数情况下V1版本的内核完全够用。
3.FreeRTOS各配置选项卡的解释

  • Events:事件相关的创建
  • Task and Queues:任务与队列的创建
  • Timers and Semaphores:定时器和信号量的创建
  • Mutexes:互斥量的创建
  • FreeRTOS HeapUsage:用于查看堆使用情况
  • config parameters:内核参数设置,用户根据自己的实际应用来裁剪定制FreeRTOS内核
  • Includeparameters:FreeRTOS部分函数的使能
  • UserConstants:相关宏的定义,可以自建一些常量在工程中使用
  • Advanced settings:高级设置

4.内核配置、函数使能的一些翻译

freeRTOS内核配置的相关说明,可以参考下面这篇文章。

FreeRTOS系列第6篇---FreeRTOS内核配置说明_vassertcalled-CSDN博客

三、任务的创建与删除

1.什么是任务?


任务可以理解为进程/线程,创建一个任务,就会在内存开辟一个空间。
比如:
玩游戏、陪女朋友,都可以视为任务
Windows 系统中的MarkText、谷歌浏览器、记事本,都是任务。
任务通常都含有While(1)死循环。

2.任务创建与删除相关函数


任务创建与删除相关函数有如下三个:


任务动态创建与静态创建的区别: 

动态创建任务的堆栈由系统分配,而静态创建任务的堆栈由用户自己传递。
通常情况下使用动态方式创建任务。


xTaskCreate函数原型

1.pvTaskCode:指向任务函数的指针,任务必须实现为永不返回(即连续循环);
2.pcName:任务的名字,主要是用来调试,默认情况下最大长度是16;
3. pvParameters:指定的任务栈的大小;

4.uxPriority:任务优先级,数值越大,优先级越大;
5.pxCreatedTask:用于返回已创建任务的句柄可以被引用。

官方提供的案例:

/* Task to be created. */
void vTaskCode( void * pvParameters )
{/* The parameter value is expected to be 1 as 1 is passed in thepvParameters value in the call to xTaskCreate() below.configASSERT( ( ( uint32_t ) pvParameters ) == 1 );for( ;; ){/* Task code goes here. */}
}/* Function that creates a task. */
void vOtherFunction( void )
{
BaseType_t xReturned;
TaskHandle_t xHandle = NULL;/* Create the task, storing the handle. */xReturned = xTaskCreate(vTaskCode,       /* Function that implements the task. */"NAME",          /* Text name for the task. */STACK_SIZE,      /* Stack size in words, not bytes. */( void * ) 1,    /* Parameter passed into the task. */tskIDLE_PRIORITY,/* Priority at which the task is created. */&xHandle );      /* Used to pass out the created task's handle. */if( xReturned == pdPASS ){/* The task was created. Use the task's handle to delete the task. */vTaskDelete( xHandle );}
}

vTaskDelete函数原型

void vTaskDelete(TaskHandle_t xTaskToDelete);

只需将待删除的任务句柄传入该函数,即可将该任务删除。
当传入的参数为NULL,则代表删除任务自身(当前正在运行的任务)。

3.实操

 
四、任务的状态

什么是任务调度?
调度器就是使用相关的调度算法来决定当前需要执行的哪个任务。
FreeRTOS中开启任务调度的函数是vTaskStartScheduler(),但在CubeMX中被封装为
osKernelStart()。


FreeRTOs的任务调度规则是怎样的?
FreeRTOS是一个实时操作系统,它所奉行的调度规则:

  1. 高优先级抢占低优先级任务,系统永远执行最高优先级的任务(即抢占式调度)
  2. 同等优先级的任务轮转调度(即时间片调度)

还有一种调度规则是协程式调度,但官方已明确表示不更新,主要是用在小容量的芯片上,用得也不多。

抢占式调度运行过程

Task 1:玩游戏
Task 2:老妈喊你吃饭
Task 3:女朋友喊你看电视


总结:
1.高优先级任务,优先执行;
2.高优先级任务不停止,低优先级任务无法执行;
3.被抢占的任务将会进入就绪态

时间片调度运行过程

总结:
1.同等优先级任务,轮流执行,时间片流转
2.一个时间片大小,取决为滴答定时器中断周期;
3.注意没有用完的时间片不会再使用,下次任务Task3得到执行,还是按照一个时间片的
时钟节拍运行

五、任务的状态

FreeRTOS中任务共存在4种状态:

  • Running 运行态

当任务处于实际运行状态称之为运行态,即CPU的使用权被这个任务占用(同一时间仅一个
任务处于运行态)。

  • Ready 就绪态

处于就绪态的任务是指那些能够运行(没有被阻塞和挂起),但是当前没有运行的任务,因
为同优先级或更高优先级的任务正在运行。

  • Blocked 阻塞态

如果一个任务因延时,或等待信号量、消息队列、事件标志组等而处于的状态被称之为阻塞
态。

  • Suspended 挂起态

类似暂停,通过调用函数vTaskSuspend()对指定任务进行挂起,挂起后这个任务将不被执
行,只有调用函数xTaskResume)才可以将这个任务从挂起态恢复。

总结:
1.仅就绪态可转变成运行态
2.其他状态的任务想运行,必须先转变成就绪态

任务综合小实验:

实验需求
创建4个任务:taskLED1,taskLED2,taskKEY1,taskKEY2,任务要求如下:
taskLED1:间隔500ms闪烁LED1;
taskLED2:间隔1000ms 闪烁LED2;
taskKEY1:如果taskLED1存在,则按下KEY1后删除 taskLED1,否则创建taskLED1;
taskKEY2:如果taskLED2正常运行,则按下KEY2后挂起taskLED2,否则恢复taskLED2

cubeMX配置:

代码实现:

/* USER CODE END Header_StartTaskKEY01 */
void StartTaskKEY01(void const * argument)
{/* USER CODE BEGIN StartTaskKEY01 *//* Infinite loop */for(;;){if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0 == GPIO_PIN_RESET)){osDelay(20);if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0 == GPIO_PIN_RESET)){printf("KEY1按下!\r\n");if(taskLED01Handle == NULL)//判断按键1有没有按下{printf("任务1不存在,准备创建任务1\r\n");osThreadDef(taskLED01, StartTaskLED01, osPriorityNormal, 0, 128);taskLED01Handle = osThreadCreate(osThread(taskLED01), NULL);if(taskLED01Handle !=NULL )printf("任务1创建完成!\r\n");}else{printf("删除任务1\r\n");osThreadTerminate(taskLED01Handle);taskLED01Handle=NULL;}}while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET );}osDelay(10);}/* USER CODE END StartTaskKEY01 */
}

核心功能

循环检测按键(KEY01)是否被按下,通过按键状态控制taskLED01任务的创建与删除:

  • 当按键按下且taskLED01任务不存在时,创建该任务;
  • 当按键按下且taskLED01任务已存在时,删除该任务。

代码细节分析

  • 无限循环
    任务函数通过for(;;)实现无限循环,持续检测按键状态,符合 FreeRTOS 任务 “永不返回” 的特性。

  • 按键检测与消抖
     

    if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET)  //这是一个逻辑判断,结果为0或1
    {osDelay(20);  // 延时20ms消抖if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0 == GPIO_PIN_RESET))  // 再次检测确认{// 按键按下后的逻辑...while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET );  // 等待按键释放}
    }

if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)(先读引脚状态,再与 “低电平” 比较)。

消抖逻辑:第一次检测到按键按下后,延时 20ms(跳过机械抖动阶段),再次检测确认按下,避免误触发。

等待释放while循环等待按键松开,避免一次按下被多次识别。

  • 任务的创建与删除

    • taskLED01Handle == NULL(任务不存在)时:通过osThreadDef定义任务属性(名称、函数、优先级等),再用osThreadCreate创建任务,成功后更新句柄。
    • taskLED01Handle != NULL(任务已存在)时:用osThreadTerminate删除任务,并将句柄设为NULL(标记任务已删除)。
  • 延时调度循环末尾的osDelay(10)让任务让出 CPU,给其他任务运行机会,避免独占系统资源(FreeRTOS 的任务调度依赖延时函数触发)


/* USER CODE END Header_StartTaskKEY02 */
void StartTaskKEY02(void const * argument)
{/* USER CODE BEGIN StartTaskKEY02 */static int flag = 0;//标志位/* Infinite loop */for(;;){if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1 == GPIO_PIN_RESET))//当按键2呗按下{osDelay(20);//消抖if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1 == GPIO_PIN_RESET)){printf("KEY2按下!\r\n");if(flag == 0){osThreadSuspend(taskLED02Handle);//挂起printf("任务2已暂停\r\n");flag = 1;}else{osThreadResume(taskLED02Handle);printf("任务2已恢复\r\n");flag = 0;}}while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET );}osDelay(10);}/* USER CODE END StartTaskKEY02 */
}

 FreeRTOS 的按键检测任务函数(StartTaskKEY02),核心功能是通过检测按键(连接在 GPIOA 的 PIN1 引脚)的状态,来切换另一个 LED 任务(taskLED02)的 “挂起” 与 “恢复” 状态
 

核心功能

循环检测按键(KEY02)是否被按下,通过一个标志位(flag)记录taskLED02的当前状态,每次按键按下时切换状态:

  • taskLED02正在运行,则将其挂起(暂停执行);
  • taskLED02已被挂起,则将其恢复(继续执行)。

代码细节分析

  1. 静态标志位flag
     

    static int flag = 0; // 标志位

  2. static修饰确保变量只在当前任务函数内可见,且任务运行期间不会被重置(保留上次的值)。
  3. 作用:记录taskLED02的状态 ——0表示任务正在运行,1表示任务已被挂起。
  4. 无限循环与按键检测
    for(;;)  // 无限循环,持续检测按键
    {if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1 )== GPIO_PIN_RESET)  // 检测按键是否按下{osDelay(20);  // 延时20ms消抖(跳过机械抖动阶段)if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1 == GPIO_PIN_RESET))  // 再次确认按键按下{// 按键按下后的逻辑...while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET );  // 等待按键释放}}osDelay(10);  // 让出CPU,给其他任务运行机会
    }

  • if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET)(先读取引脚状态,再与 “低电平(按下)” 比较)。
  • 消抖逻辑:两次检测按键状态(间隔 20ms),避免按键机械抖动导致的误触发。
  • 等待释放while循环等待按键松开,确保一次按下只触发一次状态切换。

任务的挂起与恢复当按键确认按下后,根据flag的值执行操作:

if(flag == 0)  // 若标志位为0(任务正在运行)
{osThreadSuspend(taskLED02Handle);  // 挂起taskLED02(暂停执行)printf("任务2已暂停\r\n");flag = 1;  // 更新标志位为“已挂起”
}
else  // 若标志位为1(任务已挂起)
{osThreadResume(taskLED02Handle);  // 恢复taskLED02(继续执行)printf("任务2已恢复\r\n");flag = 0;  // 更新标志位为“运行中”
}
    • osThreadSuspend:FreeRTOS 函数,暂停指定任务(任务进入 “挂起态”,不再参与调度)。
    • osThreadResume:FreeRTOS 函数,恢复被挂起的任务(任务重新进入 “就绪态”,等待调度执行)。
  1. 任务调度循环末尾的osDelay(10)是 FreeRTOS 的延时函数,作用是让当前任务(StartTaskKEY02)暂时让出 CPU,允许系统调度其他任务运行,避免当前任务独占资源。

五、队列

什么是队列?

队列又称消息队列,是一种常用于任务间通信的数据结构,队列可以在任务与任务间、中断
和任务间传递信息。
为什么不使用全局变量?
如果使用全局变量,免子(任务1)修改了变量a,等待树獭(任务3)处理,但树獭处理速
度很慢,在处理数据的过程中,狐狸(任务2)有可能又修改了变量a,导致树獭有可能得
到的不是正确的数据。

在这种情况下,就可以使用队列。免子和狐狸产生的数据放在流水线上,树獭可以慢慢一个
个依次处理。
关于队列的几个名词:
队列项目:队列中的每一个数据;
队列长度:队列能够存储队列项目的最大数量;
创建队列时,需要指定队列长度及队列项目大小。

队列特点

1. 数据入队出队方式

通常采用先进先出(FIFO)的数据存储缓冲机制,即先入队的数据会先从队列中被读取。
也可以配置为后进先出(LIFO)方式,但用得比较少。

2.数据传递方式

采用实际值传递,即将数据拷贝到队列中进行传递,也可以传递指针,在传递较大的数据的
时候采用指针传递。

3.多任务访问

队列不属于某个任务,任何任务和中断都可以向队列发送/读取消息

4.出队、入队阻塞

当任务向一个队列发送消息时,可以指定一个阻塞时间,假设此时当队列已满无法入队。
阻塞时间如果设置为:

  • 0:直接返回不会等待;
  • 0-port_MAX_DELAY:等待设定的阻塞时间,若在该时间内还无法入队,超时后直接返回不再等待;
  • port_MAX_DELAY:死等,一直等到可以入队为止。出队阻塞与入队阻塞类似;

队列相关API函数

1. 创建队列
QueueHandle_t xQueueCreate(UBaseType_t uxQueueLength,
UBaseType_t uxItemSize);

参数:

  • uxQueueLength:队列可同时容纳的最大项目数。
  • uxltemSize:存储队列中的每个数据项所需的大小(以字节为单位)。

返回值:
如果队列创建成功,则返回所创建队列的句柄。如果创建队列所需的内存无法分配,则返回NULL。

2.写队列

写队列总共有以下几个函数:

BaseType_t xQueueSend(QueueHandle_t xQueue,const void * pvItemToQueue,TickType_t xTicksToWait
);

参数:

  • xQueue:队列的句柄,数据项将发送到此队列。
  • pvItemToQueue:待写入数据
  • xTicksToWait:阻塞超时时间

返回值:
如果成功写入数据,返回pdTRUE,否则返回errQUEUE_FULL。

3.读队列

读队列总共有以下几个函数:

BaseType_t xQueueReceive(QueueHandle_t xQueue,void *pvBuffer,TickType_t xTicksToWait);

参数:

  • xQueue:待读取的队列
  • pvItemToQueue:数据读取缓冲区
  • xTicksToWait:阻塞超时时间

返回值:
成功返回pdTRUE,否则返回pdFALSE。

实操

实验需求

创建一个队列,按下KEY1向队列发送数据,按下KEY2向队列读取数据。

cube MX配置

六、二值信号量

什么是信号量

信号量(Semaphore),是在多任务环境下使用的一种机制,是可以用来保证两个或多个关
键代码段不被并发调用。
信号量这个名字,我们可以把它拆分来看,信号可以起到通知信号的作用,然后我们的量还
可以用来表示资源的数量,当我们的量只有0和1的时候,它就可以被称作二值信号量,只有
两个状态,当我们的那个量没有限制的时候,它就可以被称作为计数型信号量。
信号量也是队列的一种。


什么是二值信号量?
二值信号量其实就是一个长度为1,大小为零的队列,只有0和1两种状态,通常情况下,我
们用它来进行互斥访问或任务同步。
互斥访问:比如门钥匙,只有获取到钥匙才可以开门
任务同步:比如我录完视频你才可以看视频

二值信号量相关API函数

1.创建二值信号量
SemaphoreHandle_t xSemaphoreCreateBinary(void)

参数:

返回值:
成功,返回对应二值信号量的句柄;
失败,返回NULL。

2.释放二值信号量
BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore)

参数:
xSemaphore:要释放的信号量句柄
返回值:
成功,返回pdPASS;
失败,返回errQUEUE_FULL。

3.获取二值信号量
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore,TickType_t xTicksToWait );

参数:
xSemaphore:要获取的信号量句柄
xTicksToWait:超时时间,0表示不超时,port_MAX_DELAY表示卡死等待;
返回值:
成功,返回 pdPASS;
失败,返回errQUEUE_FULL。

http://www.dtcms.com/a/520075.html

相关文章:

  • K8s 集群环境搭建 - yaml 版本(一)
  • RAM和ROM的定义和区别总结!!!
  • GELU(高斯误差线性单元)激活函数全面解析
  • 企业网站可以做淘宝客吗wordpress 用户密码加密
  • WordPress + React 无头架构搭建指南
  • 聚类算法实战:从 KMeans 到 DBSCAN
  • 网站信息登记表网络营销考试题及答案
  • 宁夏建设工程招投标管理中心网站工程建设标准化期刊网站
  • 网站建设模板ppt模板微信公众网站开发
  • ElasticSearch倒排索引、ES核心概念、JAVA集成ES操作
  • window安装Elasticsearch(es)
  • 【AI编程实战】零基础用ChatGPT+Cursor开发完整Web应用:30分钟从idea到上线
  • 亚马逊网站建设评述wordpress php环境
  • 网站收录是什么意思最新网站网址永久发布
  • MySQL的增删改查
  • 反无人机蜂群杀伤链动态构建策略研究
  • GCC /Clang __attribute__
  • 阮一峰《TypeScript 教程》学习笔记——Enum 类型
  • 人工只能综合项目开发8---手势识别data_processing
  • C primer plus (第六版)第十一章 编程练习第13题
  • 网站被k申述泉州专业网站建设公司
  • FLUMINER福禄T3 115T挖矿机深度评测:智能管理与高效性能如何平衡?
  • 怎么调网站兼容性公益网站怎么做
  • 压缩与缓存调优实战指南:从0到1根治性能瓶颈(四)
  • 嵌入式软件架构--显示界面架构(工厂流水线模型,HOME界面,命令界面)
  • Ubuntu20.04 + QT5.14.2 + Android23的开发平台搭建总结
  • 【思维链条CoT与React模式深度解析】AI智能体的核心推理框架
  • svchost第一个是rpcss第二个是termsvcs第三个是NetworkService第四个是LocalService第五个是netsvcs----备忘
  • 餐饮网站模板免费下载jetpack wordpress
  • Hadoop High Availability 简介