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

FreeRTOS_API模块综合应用篇(八)

一、提要

在前面的章节,我已经介绍过了FreeRTOS系统的队列、信号量、事件标志组、互斥锁、软件定时器等这几大API知识模块。

接下来,我将把这几大核心的API知识模块通过一个全面的、典型的应用场景,把他们全部串联联系起来,让大家能在自己的项目中更加协调熟练的使用这些API函数模块。

二、场景介绍

场景需求概述

  • 功能1:周期性采集温湿度、光照数据(1 秒一次),并在 LCD 显示;

  • 功能2:支持用户按键操作:短按(<1 秒)触发即时数据上传,长按(≥1 秒)触发低功耗模式;

  • 功能3:数据上传到服务器时,需检测超时(3 秒未收到应答则重传,最多 3 次);

  • 功能4:无任何操作(包括按键和数据上传)5 分钟后,自动进入休眠(关闭 LCD、暂停采集)。

我们可以设计一个"智能环境监测终端"的典型场景,同时也是为了后文的第四部分的“智能环境监测终端_FreeRTOS例程”的解释做铺垫。

三、智能环境监测终端_软件架构_文字说明

注:如果读者对互斥锁、软件定时器等这些API知识模块比较熟悉的话,也可以先跳到第四部分先粗看一遍代码,先对例程的结构有初步的认识,再看文字说明,效果会更好(以看代码为主,看文字为辅)。

3.1 整体架构

注:IPC机制其实就是队列、信号量、事件标志组等这些衔接任务之间通信的操作

3.2 功能1实现:周期性数据采集流程(队列 + 周期性定时器)

细节

  • 定时器用xTimerCreate("采集定时器", pdMS_TO_TICKS(1000), pdTRUE, ...)创建,回调中读取传感器数据(轻量操作),通过xQueueSend将数据(温湿度、光照)写入队列;

  • 采集任务阻塞等待队列(xQueueReceive),收到数据后更新 LCD 显示(复杂操作放任务,避免阻塞定时器服务)

总结:创建周期性定时器,在定时器回调函数每隔1s读传感器数据,然后将数据发送给队列。采集任务接收到队列的数据之后,进行滤波、计算等复杂操作,然后进行LCD显示。

3.3 功能2实现:按键交互处理流程(信号量 + 事件组 + 一次性定时器)

  • 细节

    • 按键按下时,中断服务函数通过xEventGroupSetBitsFromISR设置 “按键按下” 标志(bit0),交互任务(xEventGroupWaitBits)检测到后启动 “长按检测定时器”(1 秒一次性);

    • 若 1 秒内按键松开(中断设 “按键松开” 标志 bit1),交互任务停止长按定时器,通过xSemaphoreGive释放 “即时上传” 信号量,上传任务被唤醒执行一次上传;

    • 若 1 秒内未松开,长按定时器回调触发 “低功耗模式”(通知系统任务)。

总结:创建一次性定时器,回调函数实现按键扫描(和定时器按键同理)。当按键短按(不超过1s时),回调函数触发短按标志位释放信号量,释放信号量之后交互任务获得信号量

3.4 功能3实现:数据上传超时重传流程(一次性定时器 + 互斥锁)

  • 细节

    • 上传任务发送数据后,用xTimerStart启动 “3 秒超时定时器”;

    • 若收到服务器应答,上传任务调用xTimerStop停止定时器;

    • 若超时未应答,定时器回调通过互斥锁(xSemaphoreTake)保护重传计数,计数≤3 则触发重传(通知上传任务),否则记录失败日志。

3.5 功能4实现:无操作休眠控制流程(一次性定时器 + 任务挂起 / 恢复)

  • 细节

    • 系统任务监听所有操作事件(采集、按键、上传),任一事件发生时调用xTimerReset重置 “5 分钟休眠定时器”(刷新计时);

    • 若 5 分钟无任何操作,定时器回调通过vTaskSuspend挂起采集、上传、交互任务,关闭外设进入低功耗;

    • 低功耗中按键按下(中断),系统任务调用vTaskResume恢复所有任务,重置休眠定时器。

四、 智能环境监测终端_FreeRTOS例程

定义结构体变量

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
#include "timers.h"// -------------------------- 全局句柄定义 --------------------------
// 队列:传递传感器数据(温度+湿度)
QueueHandle_t xSensorQueue;
// 信号量:触发数据上传
SemaphoreHandle_t xUploadSem;
// 事件标志组:按键状态(bit0=按下,bit1=松开)
EventGroupHandle_t xKeyEvents;
// 互斥锁:保护重传计数
SemaphoreHandle_t xRetryMutex;
// 定时器句柄
TimerHandle_t xCollectTimer;   // 采集定时器(1秒周期)
TimerHandle_t xLongPressTimer; // 长按检测定时器(1秒一次性)
TimerHandle_t xUploadTimer;    // 上传超时定时器(3秒一次性)
TimerHandle_t xSleepTimer;     // 休眠定时器(5秒一次性)
// 任务句柄(用于休眠时挂起)
TaskHandle_t xCollectTaskHandle, xUploadTaskHandle, xKeyTaskHandle;int retryCnt = 0;//触发重传计数
// -------------------------- 模拟硬件函数 --------------------------
BaseType_t simWifiSend(float temp, float humi)// 模拟WiFi发送数据(随机返回是否成功)
{static int cnt = 0;cnt++;return (cnt % 2 == 0) ? pdTRUE : pdFALSE; // 模拟50%成功率
}

为了方便大家理解,我用简单的cnt计数来模拟wifi传数据的功能

创建四个软件定时器回调函数

// -------------------------- 定时器回调函数 --------------------------
// 1. 采集定时器回调(1s周期):读传感器→发队列
void vCollectTimerCb(TimerHandle_t xTimer)
{struct {float temp; float humi;}Sensor;simSensorRead(&Sensor.temp, &Sensor.humi);//模拟读取温湿度// 发送数据到队列(不阻塞,失败忽略)xQueueSend(xSensorQueue,&Sensor, 0);
}// 2. 长按检测定时器回调(1s一次性):触发长按逻辑
void vLongPressTimerCb(TimerHandle_t xTimer)
{printf("检测到长按→进入休眠\r\n");// 挂起核心任务vTaskSuspend(xCollectTaskHandle);vTaskSuspend(xUploadTaskHandle);vTaskSuspend(xKeyTaskHandle);
}// 3. 上传超时定时器回调(3s一次性):处理重传
void vUploadTimerCb(TimerHandle_t xTimer)
{xSemaphoreTake(xRetryMutex, portMAX_DELAY); // 保护重传计数(获取互斥锁)if (retryCnt < 3){retryCnt++;printf("上传超时→第%d次重传\r\n", retryCnt);xSemaphoreGive(xUploadSem); // 触发重传}else{retryCnt = 0;printf("重传次数耗尽→上传失败\r\n");}xSemaphoreGive(xRetryMutex);//释放互斥锁
}// 4. 休眠定时器回调(一次性):无操作超时休眠
void vSleepTimerCb(TimerHandle_t xTimer)
{printf("5秒无操作→自动休眠\r\n");// 挂起核心任务vTaskSuspend(xCollectTaskHandle);vTaskSuspend(xUploadTaskHandle);vTaskSuspend(xKeyTaskHandle);
}

在长按检测定时器回调中,由于后续的上传任务也使用到了retryCnt变量,所以我们需要使用互斥锁,来保护我们的共享资源retryCnt变量

创建三个任务

4.1 三个任务运行的时间线

前提:任务优先级与初始状态

  • 优先级:上传任务(3)> 按键任务(2)> 采集任务(1)(高优先级任务可抢占低优先级任务)。
  • 初始状态:系统启动后,三个任务均进入 while(1) 循环,但因等待资源(队列 / 信号量 / 事件组)而阻塞(不占用 CPU):
    • 采集任务:阻塞等待 xSensorQueue 有数据。
    • 按键任务:阻塞等待 xKeyEvents 事件组有按键标志。
    • 上传任务:阻塞等待 xUploadSem 信号量被释放。

一、系统启动初期(0~1 秒):无操作,仅初始化

  1. 0msmain 函数完成初始化,启动调度器。三个任务创建后立即进入阻塞态(因无资源触发)。
  2. 0ms:采集定时器(1 秒周期)和休眠定时器(5 秒一次性)启动,开始倒计时。

二、正常采集阶段(1 秒后,无按键操作):采集任务周期性运行

  1. 1000ms:采集定时器超时,触发回调函数 vCollectTimerCb

    • 读取模拟温湿度,向 xSensorQueue 发送数据(队列从空→有数据)。
    • 此时,采集任务因队列有数据被唤醒(从阻塞态→就绪态)。
  2. 1000ms~1010ms:采集任务运行:

    • 从队列取数据,调用 simLcdShow 显示(如 “显示:温度 = 25.1℃,湿度 = 59.9%”)。
    • 调用 xTimerReset(xSleepTimer, 0) 重置休眠定时器(重新开始 5 秒倒计时,避免休眠)。
    • 显示完成后,采集任务再次阻塞等待队列(因队列已空)。
  3. 2000ms、3000ms...:重复步骤 1~2,每 1 秒触发一次采集→显示→阻塞,形成周期性运行。

    • 此阶段无其他任务干扰(按键任务和上传任务始终阻塞),CPU 主要被采集任务短时占用(显示操作)。

三、短按触发上传阶段(假设有按键操作,且按下时间 < 1 秒):高优先级任务抢占

假设在 3500ms 时发生短按(按下→松开,间隔 500ms):

  1. 3500ms:模拟按键按下(调用 simKeyPress):

    • 中断中设置 xKeyEvents 的 bit0(按下标志),按键任务因事件组有标志被唤醒(从阻塞态→就绪态)。
    • 因按键任务优先级(2)> 采集任务(1),若此时采集任务正在运行(如 3000ms 时的显示),会被按键任务抢占(采集任务暂停,按键任务立即执行)。
  2. 3500ms~3510ms:按键任务运行:

    • 检测到 bit0(按下),调用 xTimerStart(xLongPressTimer, 0) 启动长按定时器(1 秒一次性,开始倒计时)。
    • 处理完成后,按键任务再次阻塞等待事件组。
  3. 4000ms:模拟按键松开(调用 simKeyRelease):

    • 中断中设置 xKeyEvents 的 bit1(松开标志),按键任务再次被唤醒
  4. 4000ms~4010ms:按键任务运行:

    • 检测到 bit1(松开),调用 xTimerIsTimerActive(xLongPressTimer) 检查:此时长按定时器仅运行了 500ms(未超时),返回 pdTRUE
    • 调用 xTimerStop(xLongPressTimer, 0) 停止长按定时器(避免误判为长按)。
    • 调用 xSemaphoreGive(xUploadSem) 释放上传信号量,上传任务因信号量被唤醒(从阻塞态→就绪态)。
    • 调用 xTimerReset(xSleepTimer, 0) 重置休眠定时器(重新 5 秒倒计时)。
    • 处理完成后,按键任务再次阻塞。
  5. 4010ms~4030ms:上传任务运行(抢占按键任务):

    • 因上传任务优先级(3)> 按键任务(2),立即抢占 CPU。
    • 从队列取最新数据(如温度 25.4℃,湿度 59.6%),调用 simWifiSend 模拟上传。
    • 若上传成功(50% 概率):调用 xTimerStop(xUploadTimer, 0) 停止超时定时器,打印 “上传成功”。
    • 若上传失败:调用 xTimerStart(xUploadTimer, 0) 启动 3 秒超时定时器(准备重传)。
    • 调用 xTimerReset(xSleepTimer, 0) 重置休眠定时器。
    • 处理完成后,上传任务再次阻塞等待信号量。
  6. 4030ms 后:系统回到正常采集阶段,每 1 秒采集显示一次。

四、长按触发休眠阶段(假设有按键操作,且按下时间≥1 秒):任务被挂起

假设在 6000ms 时发生长按(按下后保持 1.5 秒再松开):

  1. 6000ms:按键按下,触发 simKeyPress→按键任务被唤醒,启动长按定时器(1 秒倒计时)。

  2. 7000ms:长按定时器超时(按下已 1 秒),触发回调 vLongPressTimerCb

    • 打印 “检测到长按→进入休眠”,调用 vTaskSuspend 依次挂起采集任务、上传任务、按键任务。
    • 此时三个任务均进入挂起态(停止运行,需 vTaskResume 恢复),系统 “休眠”。
  3. 7500ms:按键松开(调用 simKeyRelease),但因按键任务已被挂起,事件组标志无人处理,无任何反应。

五、无操作休眠阶段(5 秒内无任何操作):任务自动挂起

假设从 10000ms 开始,无采集(实际采集仍在进行,但采集属于 “系统操作”?不,采集是系统自动操作,会重置休眠定时器,这里假设极端情况:采集定时器被意外停止,仅举例):

  1. 10000ms:最后一次操作(如采集显示)完成,休眠定时器开始 5 秒倒计时。

  2. 15000ms:休眠定时器超时,触发回调 vSleepTimerCb

    打印 “5 秒无操作→自动休眠”,挂起三个核心任务,系统进入休眠态。
// 1. 采集任务:从队列取数据→显示
void vCollectTask(void *pvParam)
{struct {float t; float h;} data;while (1){//阻塞等待队列数据xQueueReceive(xSensorQueue, &data, portMAX_DELAY);LCDShow(data.t, data.h);//用LCD显示屏显示温度,湿度数据// 有操作→重置休眠定时器xTimerReset(xSleepTimer, 0);}
}// 2. 交互任务:处理按键事件(短按/长按)
void vKeyTask(void *pvParam)
{EventBits_t xBits;while (1){// 等待按键按下(bit0)或松开(bit1)xBits = xEventGroupWaitBits(xKeyEvents,(1 << 0) | (1 << 1), // 等待的标志位pdTRUE,               // 清除标志位pdFALSE,              // 任意标志满足portMAX_DELAY);if (xBits & (1 << 0)) // 按键按下{printf("按键按下→启动长按检测\r\n");xTimerStart(xLongPressTimer, 0); // 启动1秒定时器}else if (xBits & (1 << 1)) // 按键松开{printf("按键松开→停止长按检测\r\n");// 若长按定时器未触发(1秒内松开)→ 短按if (xTimerIsTimerActive(xLongPressTimer))//判断是否超时{// 若1秒内松开→短按,触发上传xTimerStop(xLongPressTimer, 0);// 停止定时器xSemaphoreGive(xUploadSem); // 给信号量:触发上传任务}// 有操作→重置休眠定时器xTimerReset(xSleepTimer, 0);}}
}// 3. 上传任务:等待上传信号→发送数据→处理超时
void vUploadTask(void *pvParam)
{struct {float t; float h;} data;while (1){// 等待上传信号(短按或重传触发)xSemaphoreTake(xUploadSem, portMAX_DELAY);// 从队列取最新数据(非阻塞,取最近的)xQueueReceive(xSensorQueue, &data, 0);printf("开始上传:温度=%.1f,湿度=%.1f\r\n", data.t, data.h);if (simWifiSend(data.t, data.h)){// 发送成功→停止超时定时器xTimerStop(xUploadTimer, 0);printf("上传成功\r\n");//上传成功→重置重传计数xSemaphoreTake(xRetryMutex, portMAX_DELAY);//获取互斥锁retryCnt = 0; xSemaphoreGive(xRetryMutex);//释放互斥锁}else{// 发送失败→启动超时定时器(3秒后重传)xTimerStart(xUploadTimer, 0);}// 有操作→重置休眠定时器xTimerReset(xSleepTimer, 0);}
}

4.2  if (xTimerIsTimerActive(xLongPressTimer))的运行逻辑

我重点说说交互任务中的if (xTimerIsTimerActive(xLongPressTimer))

LongPressTimer 是一个 1 秒一次性定时器(用于检测按键长按),xTimerIsTimerActive(xLongPressTimer) 的作用是 区分 “短按” 和 “长按”

  • 当按键松开时,调用 xTimerIsTimerActive(xLongPressTimer) 检查:

    • 若返回 pdTRUE:说明定时器仍在活动(1 秒倒计时未结束)→ 按键按下时间 <1 秒 → 判定为 “短按”,触发数据上传。

    • 若返回 pdFALSE:说明定时器已超时(1 秒倒计时已结束,且回调已执行)→ 按键按下时间 ≥ 1 秒 → 判定为 “长按”,已触发休眠(无需额外处理)。

main函数

int main(void)
{// 1. 创建IPC组件:队列、信号量、事件组、互斥锁(无依赖,先创建)xSensorQueue = xQueueCreate(5, sizeof(struct {float t; float h;}));// 队列大小5,元素大小为结构体大小xUploadSem = xSemaphoreCreateBinary();// 创建二值信号量(初始空)xKeyEvents = xEventGroupCreate();// 创建事件组xRetryMutex = xSemaphoreCreateMutex();// 创建互斥锁// 2. 创建定时器:指定名称、周期、类型、参数、回调函数xCollectTimer = xTimerCreate("CollectTimer", pdMS_TO_TICKS(1000), pdTRUE, NULL, vCollectTimerCb);xLongPressTimer = xTimerCreate("LongPressTimer", pdMS_TO_TICKS(1000), pdFALSE, NULL, vLongPressTimerCb);xUploadTimer = xTimerCreate("UploadTimer", pdMS_TO_TICKS(3000), pdFALSE, NULL, vUploadTimerCb);xSleepTimer = xTimerCreate("SleepTimer", pdMS_TO_TICKS(5000), pdFALSE, NULL, vSleepTimerCb);// 3. 创建任务:指定任务函数、名称、栈大小(128字,视系统调整)、参数、优先级、任务句柄xTaskCreate(vCollectTask, "CollectTask", 128, NULL, 1, &xCollectTaskHandle);xTaskCreate(vKeyTask, "KeyTask", 128, NULL, 2, &xKeyTaskHandle);xTaskCreate(vUploadTask, "UploadTask", 128, NULL, 3, &xUploadTaskHandle);// 4. 启动定时器:采集定时器(1秒周期)和休眠定时器(5秒无操作)先启动xTimerStart(xCollectTimer, 0);xTimerStart(xSleepTimer, 0); // 启动无操作计时// 启动调度器vTaskStartScheduler();
}
http://www.dtcms.com/a/453338.html

相关文章:

  • tuchuang_myfilesshare文件列表_共享文件
  • GJOI 10.4/10.5 题解
  • C语言入门教程(第2讲):数据类型与变量详解与实战讲解
  • 哪些网站建设公司wordpress悬浮联系表
  • 5g互联如何取消网站备案中山seo代理商
  • 生成式人工智能对学习生态的重构:从“辅助工具”到“依赖风险”的平衡难题
  • 电商推广联盟大型网站技术架构演进与性能优化
  • short-term memory 和long-term memtory有什么区别
  • 公司网站建设与维护工作计划网站建设背景及意义
  • 技术支持上海网站建设广州做网站哪个平台好
  • 企业如何在网站上做宣传wordpress移动站点
  • 13.排序(下)
  • 软考 系统架构设计师系列知识点之杂项集萃(171)
  • 医院网站优化策划网站这么做301
  • 后续:Github账户被标记流程记录
  • 网站建设的设立方式推广方案是什么
  • 鸿蒙NEXT跨设备通信:掌握URPC,实现远程程序调用
  • 传统纸媒公司网站建设需求容桂网站建设
  • python爬虫(四) ---- yaml文件配置简单日志
  • 免费网站专业建站班级网页设计图片
  • 网站建设与功能模块最好的淘宝网站建设
  • Flink 内置 Watermark 生成器单调递增与有界乱序怎么选?
  • 怎么下载网站备案号wordpress首页显示文章图片
  • 扩展云镜像磁盘空间案例:AlmaLinux 9 云镜像扩展
  • 招聘网站上找在家做seo最好的工具
  • 常用的网站建设技术软件开发工程师招聘简章pdf
  • ppt免费网站专门查企业信息的网站
  • 临时造参数查全量数据
  • 国产能谱仪设计与验证核心经验教训简化表
  • 0.6 卷积神经网络