Day1--FreeRTOS简介及多任务点灯、多任务传参、Mutex
实时操作系统(Real Time Operating System,简称RTOS)
Arduino任务执行流程:单线程执行任务
RTOS:可以同时执行所有Task,每个任务都有自己的循环
操作系统排行:LINUX WINDOWS FREERTOS
ESP32架构:ESP32-IDF的底层运行的就是freestos
默认core1:编写程序 core2:蓝牙、wifi功能
任务优先级:
优先级高的任务先执行,比如中断
实时性要求比较高的任务用高优先级
实时性要求比较低的任务用低优先级,比如屏幕刷新,数据显示
多任务其实是CPU分时完成的,1ms执行一个任务,频率1Khz
freertos传递参数只能采用指针的方式,重点:结构体和指针
内存管理:任务到底需要多少内存?分配空间大小1024*N
任务优先级:
任务的绝对频率:vTaskDelayUntil
软件定时器Timer:一次性的,周期性的,非常有用
freertos提供的三种数据结构:队列单数据、流媒体缓存、消息缓存
多任务全局变量:对资源进行保护
二进制信号量:0和1
计数信号量:0~N
事件组等待:
事件组同步:
任务通知
多任务点灯
1GB(GigaByte)=1024MB
1MB(MegaByte)=1024KB
1KB(KiloByte)=1024B(字节)
1B(byte)字节=8Bit(binary digit)位
#include <Arduino.h>void task1(void *pt)
{pinMode(23, OUTPUT);while (1){digitalWrite(23, !digitalRead(23));vTaskDelay(500);}
}void task2(void *pt)
{pinMode(21, OUTPUT);while (1){digitalWrite(21, !digitalRead(21));vTaskDelay(700);}
}void setup()
{// 参数1:task; 参数2:任务备注; 参数3:内存分配空间// 参数4:传递参数; 参数5:任务优先级; 参数6:对任务删除管理xTaskCreate(task1, "Blink 23", 1024, NULL, 1, NULL);xTaskCreate(task2, "Blink 21", 1024, NULL, 1, NULL);
}void loop()
{
}
通过空指针类型给task传递单个参数
#include <Arduino.h>byte led1 = 21;
byte led2 = 22;
byte led3 = 23;void task1(void *pt) // 接收的为空指针
{byte led_pin = *(byte *)pt; // 解耦pinMode(led_pin, OUTPUT);while (1){digitalWrite(led_pin, !digitalRead(led_pin));vTaskDelay(500);}
}void setup()
{// 参数1:task; 参数2:任务备注; 参数3:内存分配空间// 参数4:传递参数; 参数5:任务优先级; 参数6:对任务删除管理xTaskCreate(task1, "Blink 21", 1024, (void *)&led1, 1, NULL);
}void loop()
{
}
给任务传递多个参数(重要)
通过空指针类型传递结构体
/*向任务中进行传多个参数*/
#include <Arduino.h>typedef struct
{byte pin;int delayTime;
} LEDFLASH;
LEDFLASH led1, led2;void ledFlash(void *pt)
{LEDFLASH *ptLedFlash = (LEDFLASH *)pt; // 数据解耦byte pin = ptLedFlash->pin;int delayTime = ptLedFlash->delayTime;pinMode(pin, OUTPUT);while (1){digitalWrite(pin, !digitalRead(pin));vTaskDelay(delayTime);}
}void setup()
{/*局部变量,结构体赋值一定要在setup里面,在外面会出错在外边,需要写成全局变量的形式*/led1.pin = 23;led1.delayTime = 1000;led2.pin = 21;led2.delayTime = 3000;xTaskCreate(ledFlash, "***", 1024, (void *)&led1, 1, NULL);xTaskCreate(ledFlash, "***", 1024, (void *)&led2, 1, NULL);
}
void loop() {}
可以通过void *pt空指针的方式传递单个参数,可以通过void *struct传递多个参数
通过结构体传址的方式进行数据传输
任务共享全局变量(重要)
任务1:对商品的数量进行计算
任务2:显示商品的数量
重点:写操作只能有一个,读操作可以有多个
/*任务之间通过全局变量进行数据传递*/
#include <Arduino.h>/*养成良好习惯,被多进程和中断调用的变量使用 volatile修饰符*/
/*ESP32是32位的,一定要定义为uint32_t,因为同一个变量占用CPU同一个通道
*/
volatile u_int32_t inventory = 100; // 总库存
volatile u_int32_t retailCount = 0; // 线下销售量/*任务1:库存数量变化计算*/
void retailTask(void *pt)
{while (1){/*以下实现了带有随机延迟的库存减1等效为 inventory--; retailCount++;*/u_int32_t inv = inventory;for (byte i; i < random(10, 100); i++){vTaskDelay(i);}if (inventory > 0){inventory = inv - 1;retailCount++;}};vTaskDelay(10);
}/*任务2:显示库存和线下销售量*/
void showTask(void *pt)
{while (1){printf("Inventory : %d\n", inventory);printf(" Retail :%d\n", retailCount);if (inventory == 0){printf("\n------sales summary-------\n");printf("totail sales: %d\n\n", retailCount);}vTaskDelay(1000);}
}void setup()
{Serial.begin(115200);xTaskCreate(retailTask, "库存数量变化", 1024 * 4, NULL, 1, NULL);xTaskCreate(showTask, "数量显示", 1024 * 4, NULL, 1, NULL);
}
void loop() {}
使用相互排斥 Mutex 来解决竞争冒险Race Condition(重要)
注意:在对全局变量数据进行访问时,使用Mutex,不能在程序一开始就获取钥匙,在if里对共享资源计算完就立刻释放钥匙,不要把释放钥匙语句放到任务最后
在上面一个示例中,数据计算时容易出现竞争冒险的问题,采用Mutex对数据进行保护,解决多个任务同时对共享资源访问造成的问题。
Mutex互斥锁,先把共享资源放进保险柜里(只有一把钥匙),任务(例如task1)先申请钥匙,再对共享资源进行访问,Task2如果想要访问共享资源,需要等到task1归还钥匙。
使用步骤:
- 创建一把锁, create
- 在指定时间内获取钥匙, take
- 归还钥匙,give
语法:
SemaphoreHandle_t xHandler; 创建Handler
xHandler = xSemaphoreCreateMutex(); 创建一个MUTEX 返回NULL,或者handler
xSemaphoreGive(xHandler); 释放
xSemaphoreTake(xHanlder, timeout); 指定时间内获取信号量 返回pdPASS, 或者pdFAIL
理解方法:
MUTEX的工作原理可以想象成
共享的资源被锁在了一个箱子里,只有一把钥匙,有钥匙的任务才能对改资源进行访问
/*程序: Tasks之间数据传递有多任务同时写入,或者数据大小超过cpu内存通道时,或者对共享资源的访问时候,需要有防范机制使用MUTEX对数据对Cirtical Section的内容进行保护可以想象成MUTEX就是一把锁公众号:孤独的二进制语法:SemaphoreHandle_t xHandler; 创建HandlerxHandler = xSemaphoreCreateMutex(); 创建一个MUTEX 返回NULL,或者handlerxSemaphoreGive(xHandler); 释放xSemaphoreTake(xHanlder, timeout); 指定时间内获取信号量 返回pdPASS, 或者pdFAIL理解方法:MUTEX的工作原理可以想象成共享的资源被锁在了一个箱子里,只有一把钥匙,有钥匙的任务才能对改资源进行访问
*/// 养成良好习惯,被多进程和中断调用的变量使用 volatile 修饰符
volatile uint32_t inventory = 100; //总库存
volatile uint32_t retailCount = 0; //线下销售量
volatile uint32_t onlineCount = 0; //线上销售量SemaphoreHandle_t xMutexInventory = NULL; //创建信号量HandlerTickType_t timeOut = 1000; //用于获取信号量的Timeout 1000 ticksvoid retailTask(void *pvParam) {while (1) {// 在timeout的时间内如果能够获取就继续// 通俗一些:获取钥匙if (xSemaphoreTake(xMutexInventory, timeOut) == pdPASS) {//被MUTEX保护的内容叫做 Critical Section//以下实现了带有随机延迟的 inventory减1;//等效为 inventory--; retailCount++;uint32_t inv = inventory;for (int i; i < random(10, 100); i++) vTaskDelay(pdMS_TO_TICKS(i));if (inventory > 0) {inventory = inv - 1;retailCount++;//释放钥匙xSemaphoreGive(xMutexInventory);} else {//无法获取钥匙}};vTaskDelay(100); //老板要求慢一些,客户升级后,可以再加快速度}
}void onlineTask(void *pvParam) {while (1) {// 在timeout的时间内如果能够获取二进制信号量就继续// 通俗一些:获取钥匙if (xSemaphoreTake(xMutexInventory, timeOut) == pdPASS) {//被MUTEX保护的内容叫做 Critical Section//以下实现了带有随机延迟的 inventory减1;//等效为 inventory--; retailCount++;uint32_t inv = inventory;for (int i; i < random(10, 100); i++) vTaskDelay(pdMS_TO_TICKS(i));if (inventory > 0) {inventory = inv - 1;onlineCount++;//释放钥匙xSemaphoreGive(xMutexInventory);} else {//无法获取钥匙}};vTaskDelay(100); //老板要求慢一些,客户升级后,可以再加快速度}
}void showTask(void *pvParam) {while (1) {printf("Inventory : %d\n", inventory);printf(" Retail : %d, Online : %d\n", retailCount, onlineCount);if (inventory == 0 ) {uint32_t totalSales = retailCount + onlineCount;printf("-----SALES SUMMARY-----\n");printf(" Total Sales: %d\n", totalSales);printf(" OverSales: %d\n", 100 - totalSales);}vTaskDelay(pdMS_TO_TICKS(1000));}
}void setup() {// put your setup code here, to run once:Serial.begin(115200);xMutexInventory = xSemaphoreCreateMutex(); //创建MUTEXif (xMutexInventory == NULL) {printf("No Enough Ram, Unable to Create Semaphore.");} else {xTaskCreate(onlineTask,"Online Channel",1024 * 4,NULL,1,NULL);xTaskCreate(retailTask,"Retail Channel",1024 * 4,NULL,1,NULL);xTaskCreate(showTask,"Display Inventory",1024 * 4,NULL,1,NULL);}}void loop() {
}
MUTEX实例
使用MPU6050传感器时,可以创建一个结构体存储7个数据(芯片温度、3轴角速度、3轴角度)
MPU6050的数据写进结构体中,然后屏幕进行读取
两个任务,一个读,一个写,一定要用MUTEX进行数据保护
原因:不管是读操作还是写操作,它都是一个独立的task,这样用freertos运行多任务就会出现某个任务因为分配的时间到了,对数据的处理被迫中断,然后另一个任务又开始对数据进行操作,而这时的数据很可能只有一半是操作完成,另一半还未完成的状态,这样的数据状态会产生很大的运算错误,非常危险。所以一个参数只要有两个或以上task要对其进行操作,就必须上钥匙。
/*程序: MPU6050 & MUTEX公众号:孤独的二进制
*/
#include <Arduino.h>
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>LiquidCrystal_I2C lcd(0x27, 20, 4);Adafruit_MPU6050 mpu;
Adafruit_Sensor *mpu_temp, *mpu_accel, *mpu_gyro;typedef struct
{float temp;float accX;float accY;float accZ;float gyroX;float gyroY;float gyroZ;
} MPU6050;MPU6050 mpu6050;SemaphoreHandle_t xMutexMPU6050 = NULL; //创建信号量Handler
TickType_t timeOut = 1000; //用于获取信号量的Timeout 1000 ticksvoid mpu6050Task(void *pvParam)
{mpu.begin();mpu_temp = mpu.getTemperatureSensor();mpu_temp->printSensorDetails();mpu_accel = mpu.getAccelerometerSensor();mpu_accel->printSensorDetails();mpu_gyro = mpu.getGyroSensor();mpu_gyro->printSensorDetails();sensors_event_t accel;sensors_event_t gyro;sensors_event_t temp;while (1){if (xSemaphoreTake(xMutexMPU6050, timeOut) == pdPASS){//获取MPU数据mpu_temp->getEvent(&temp);mpu_accel->getEvent(&accel);mpu_gyro->getEvent(&gyro);mpu6050.temp = temp.temperature;mpu6050.accX = accel.acceleration.x;mpu6050.accY = accel.acceleration.y;mpu6050.accZ = accel.acceleration.z;mpu6050.gyroX = gyro.gyro.x;mpu6050.gyroY = gyro.gyro.y;mpu6050.gyroZ = gyro.gyro.z;xSemaphoreGive(xMutexMPU6050); //释放钥匙}else{// Unable to obtain MUTEX}vTaskDelay(500);}
}void lcdTask(void *ptParam)
{ // LCD任务主体lcd.init();lcd.backlight();//定义是 2004 LCDbyte lcdLine = 4;byte lcdChar = 20;//创建一个二维的的数组//注意长度是 lcdChar+1 最后还有一个位置要给换行符char line0[lcdChar + 1], line1[lcdChar + 1], line2[lcdChar + 1], line3[lcdChar + 1];char *line[] = {line0,line1,line2,line3,};while (1){if (xSemaphoreTake(xMutexMPU6050, timeOut) == pdPASS){// 组合数据sprintf(line0, " MPU6050 %d", xTaskGetTickCount() / 100);sprintf(line1, " Temperature %.2f", mpu6050.temp);sprintf(line2, " ACC %.2f %.2f %.2f", mpu6050.accX, mpu6050.accY, mpu6050.accZ);sprintf(line3, " GYRO %.2f %.2f %.2f", mpu6050.gyroX, mpu6050.gyroY, mpu6050.gyroZ);xSemaphoreGive(xMutexMPU6050); //释放钥匙}else{// Unable to obtain MUTEX}// 显示数据for (int i = 0; i < 4; i++){lcd.setCursor(0, i);lcd.print(line[i]);}vTaskDelay(1000);}
}void setup()
{Serial.begin(115200);xMutexMPU6050 = xSemaphoreCreateMutex(); //创建MUTEXxTaskCreate(mpu6050Task, "MPU6050", 1024 * 8, NULL, 1, NULL);vTaskDelay(1000); //让MPU6050提前先运行一秒获取第一笔数据xTaskCreate(lcdTask, "lcd", 1024 * 8, NULL, 1, NULL);
}void loop() {}
Day1--FreeRTOS简介及多任务点灯、多任务传参、Mutex相关推荐
- python爬虫02-提升爬取效率、多线程,多线程传参,多进程,线程及线程池概念,协程,多任务异步协程,异步请求aiohttp模块,视频站工作原理
1.提升爬取效率 使用多线程,多进程,携程,异步 2.多线程 进程是资源单位,每个进程,都会有一个默认的主线程 线程是执行单位 执行多线程需要导包: from threading import Thr ...
- week9 day1 HTML简介和常用标签
week9 day1 HTML简介和常用标签 一.前端内容介绍 二.HTML简介 2.1 HTML简介 2.2 HTML发展史 三.HTML标签与文档结构 3.1 HTML标签与文档结构 3.2 HT ...
- FreeRTOS系列|FreeRTOS简介
FreeRTOS简介 1. RTOS简介 RTOS全称为 Real Time Operation System,即实时操作系统.RTOS强调的是实时性,又分为硬实时和软实时.硬实时要求在规定的时间内必 ...
- 什么是多任务?多任务是什么?
多任务呢就是同一时刻多个任务同时执行,例如开演唱会时明星一边唱歌一边跳舞,开车时眼 睛看路手操作方向盘.这些都是多任务场景 多任务有什么好处呢? 那么我们先来一段代码来瞧瞧: import os de ...
- SpringMVC→简介、MVC、SpringMVC工作原理、Maven搭建第一个SpringMVC、请求参数接收、重定向、文件上传、AJAX异步访问、请求参数接收绑定JSON、@注解及传参
MVC SpringMVC工作原理 Maven搭建第一个SpringMVC 目录结构 web.xml *-servlet.xml Controller请求处理类 跳转页面 Maven运行服务器项目 浏 ...
- keras框架入门学习(一)——argparse传参模块调用
keras框架入门学习(一)--argparse传参模块调用 一.argparse模块简介 1.1 argparse的定义 1.2 argparse的优势 二.argparse模块使用 2.1 实现[ ...
- delphi中的函数传参如何传枚举参数_shell脚本的函数介绍使用和工作常用案例。建议收藏...
#前言:今天我们来聊聊shell脚本中的函数知识,看一下函数的优势,执行过程和相关的使用案例. #简介 1.函数也具有别名类似的功能 2.函数是把程序里多次调用相同的代码部分定义成一份,然后给这份代码 ...
- html路由怎样做div,路由传参练习.html
Document .new-list-container .new-list-ul{ list-style: none; padding: 0; } .new-list-container .new- ...
- python可变参数_Python 的四种共享传参详解
点击上方"Python数据之道",选择"星标公众号" 精品文章,第一时间送达 作者 | 杨仁聪 编辑 | Lemon 出品 | Python数据之道 本文来自公 ...
最新文章
- python中的linearregression_【python+机器学习(2)】python实现Linear Regression
- 2012年11月14日学习研究报告
- 新获融资1亿,聚焦全栈,云知声背后的AI下半场
- POJ1696 Space Ant
- java 调用存储过程
- 2014中国企业面对的五大挑战
- 嵌入生活的嵌入式,超市里的电子价签
- 技术博客么?开始每天一更
- ZooKeeper 典型的应用场景
- C/C++编程(尾积相乘)
- 带进度条的Flash多文件上传面板(SwfUploadPanel) (转载)
- Storyboard使用心得
- 双非二本院校,北京211,字节跳动 → 一个新秀的六年
- 6步学会VS封装DLL
- 单元测试-Mock Server
- python打包成exe其他电脑运行不了_将python文件打包成exe程序,复制到每台电脑都可以运行-exe是什么文件...
- Netty的概念和架构
- 中国叉车行业发展现状及趋势分析,无人叉车将成为未来发展趋势「图」
- python 如何爬虫wind api数据_Python网络爬虫实战之十:利用API进行数据采集
- 操作系统——磁盘操作
热门文章
- [AcWing] 1319.移棋子游戏 博弈论 Sg函数板子题
- 易源数据_易源数据-全国景点查询【最新版】_大数据可视化_数据API_数据应用-云市场-阿里云...
- J2EE项目异常处理
- 陕西计算机在职研究生院校排名,西安在职研究生院校
- 老码识途——在堆中构建mov和jmp指令
- Python使用turtle绘图
- “百度杯”CTF比赛 十一月场 - 嘀嘀嘀
- 【回顾九月份第一周】 为什么你的前端工作经验不值钱
- Macaca实战之一:环境搭建(亲测好用)
- 计算机网络学习笔记(三)——数据链路层