实时操作系统(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归还钥匙。

使用步骤:

  1. 创建一把锁, create
  2. 在指定时间内获取钥匙, take
  3. 归还钥匙,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相关推荐

  1. python爬虫02-提升爬取效率、多线程,多线程传参,多进程,线程及线程池概念,协程,多任务异步协程,异步请求aiohttp模块,视频站工作原理

    1.提升爬取效率 使用多线程,多进程,携程,异步 2.多线程 进程是资源单位,每个进程,都会有一个默认的主线程 线程是执行单位 执行多线程需要导包: from threading import Thr ...

  2. week9 day1 HTML简介和常用标签

    week9 day1 HTML简介和常用标签 一.前端内容介绍 二.HTML简介 2.1 HTML简介 2.2 HTML发展史 三.HTML标签与文档结构 3.1 HTML标签与文档结构 3.2 HT ...

  3. FreeRTOS系列|FreeRTOS简介

    FreeRTOS简介 1. RTOS简介 RTOS全称为 Real Time Operation System,即实时操作系统.RTOS强调的是实时性,又分为硬实时和软实时.硬实时要求在规定的时间内必 ...

  4. 什么是多任务?多任务是什么?

    多任务呢就是同一时刻多个任务同时执行,例如开演唱会时明星一边唱歌一边跳舞,开车时眼 睛看路手操作方向盘.这些都是多任务场景 多任务有什么好处呢? 那么我们先来一段代码来瞧瞧: import os de ...

  5. SpringMVC→简介、MVC、SpringMVC工作原理、Maven搭建第一个SpringMVC、请求参数接收、重定向、文件上传、AJAX异步访问、请求参数接收绑定JSON、@注解及传参

    MVC SpringMVC工作原理 Maven搭建第一个SpringMVC 目录结构 web.xml *-servlet.xml Controller请求处理类 跳转页面 Maven运行服务器项目 浏 ...

  6. keras框架入门学习(一)——argparse传参模块调用

    keras框架入门学习(一)--argparse传参模块调用 一.argparse模块简介 1.1 argparse的定义 1.2 argparse的优势 二.argparse模块使用 2.1 实现[ ...

  7. delphi中的函数传参如何传枚举参数_shell脚本的函数介绍使用和工作常用案例。建议收藏...

    #前言:今天我们来聊聊shell脚本中的函数知识,看一下函数的优势,执行过程和相关的使用案例. #简介 1.函数也具有别名类似的功能 2.函数是把程序里多次调用相同的代码部分定义成一份,然后给这份代码 ...

  8. html路由怎样做div,路由传参练习.html

    Document .new-list-container .new-list-ul{ list-style: none; padding: 0; } .new-list-container .new- ...

  9. python可变参数_Python 的四种共享传参详解

    点击上方"Python数据之道",选择"星标公众号" 精品文章,第一时间送达 作者 | 杨仁聪 编辑 | Lemon 出品 | Python数据之道 本文来自公 ...

最新文章

  1. python中的linearregression_【python+机器学习(2)】python实现Linear Regression
  2. 2012年11月14日学习研究报告
  3. 新获融资1亿,聚焦全栈,云知声背后的AI下半场
  4. POJ1696 Space Ant
  5. java 调用存储过程
  6. 2014中国企业面对的五大挑战
  7. 嵌入生活的嵌入式,超市里的电子价签
  8. 技术博客么?开始每天一更
  9. ZooKeeper 典型的应用场景
  10. C/C++编程(尾积相乘)
  11. 带进度条的Flash多文件上传面板(SwfUploadPanel) (转载)
  12. Storyboard使用心得
  13. 双非二本院校,北京211,字节跳动 → 一个新秀的六年
  14. 6步学会VS封装DLL
  15. 单元测试-Mock Server
  16. python打包成exe其他电脑运行不了_将python文件打包成exe程序,复制到每台电脑都可以运行-exe是什么文件...
  17. Netty的概念和架构
  18. 中国叉车行业发展现状及趋势分析,无人叉车将成为未来发展趋势「图」
  19. python 如何爬虫wind api数据_Python网络爬虫实战之十:利用API进行数据采集
  20. 操作系统——磁盘操作

热门文章

  1. [AcWing] 1319.移棋子游戏 博弈论 Sg函数板子题
  2. 易源数据_易源数据-全国景点查询【最新版】_大数据可视化_数据API_数据应用-云市场-阿里云...
  3. J2EE项目异常处理
  4. 陕西计算机在职研究生院校排名,西安在职研究生院校
  5. 老码识途——在堆中构建mov和jmp指令
  6. Python使用turtle绘图
  7. “百度杯”CTF比赛 十一月场 - 嘀嘀嘀
  8. 【回顾九月份第一周】 为什么你的前端工作经验不值钱
  9. Macaca实战之一:环境搭建(亲测好用)
  10. 计算机网络学习笔记(三)——数据链路层