目录
一、原理和任务的简单介绍
1.程序(Tasks之间数据传递)
有多任务同时写入,或者数据大小超过cpu内存通道时,或者对共享资源的访问时候,需要有防范机制。资源共享时,多个任务对同一资源进行访问,写入和读出操作可能会发送冲突。
使用MUTEX对数据对Cirtical Section的内容进行保护,可以想象成MUTEX就是一把锁,在对数据进行写入和读出操作时先进行上锁,再进行解锁,这样各个任务之间可以避免冲突(当然,也可以利用中断进行,后期介绍)
2.语法
SemaphoreHandle_t xHandler; 创建Handler
xHandler = xSemaphoreCreateMutex(); 创建一个MUTEX 返回NULL,或者handler
xSemaphoreGive(xHandler); 释放
xSemaphoreTake(xHanlder, timeout); 指定时间内获取信号量 返回pdPASS, 或者pdFAIL
3.理解方法
MUTEX的工作原理可以想象成,共享的资源被锁在了一个箱子里,只有一把钥匙,有钥匙的任务才能对改资源进行访问。
二、程序运行
实验平台:ESP32 on Wokwi - Online ESP32, STM32, Arduino Simulator
这里模拟售货机的操作,销售渠道分为线上和线下,这样在同一时刻会有多个任务对共享资源(存货量)进行访问和修改,这样互斥锁的作用就可见一斑。
直接上代码:
// 养成良好习惯,被多进程和中断调用的变量使用 volatile 修饰符
volatile uint32_t inventory = 100; //总库存
volatile uint32_t retailCount = 0; //线下销售量
volatile uint32_t onlineCount = 0; //线上销售量
SemaphoreHandle_t xMutexInventory = NULL; //创建信号量Handler
TickType_t timeOut = 1000; //用于获取信号量的Timeout 1000 ticks
void 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(); //创建MUTEX
if (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() {
}
三、实验现象
四、拓展任务—多任务传参
typedef struct {
byte pin;
int delayTime;
} LEDFLASH;
/*
void ledFlash(void *pt) {
LEDFLASH * ptLedFlash = (LEDFLASH *)pt;
LEDFLASH led = *ptLedFlash;
pinMode(led.pin,OUTPUT);
while (1) {
digitalWrite(led.pin, !digitalRead(led.pin));
vTaskDelay(led.delayTime);
}
}
*/
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);
}
}
LEDFLASH led1, led2;//如果不是全局变量,setup执行完成后参数会被删除
void setup() {
Serial.begin(9600);
led1.pin = 23;
led1.delayTime = 1000;
led2.pin = 21;
led2.delayTime = 3000;
if (xTaskCreate(ledFlash,
"FLASH LED",
1024,
(void *)&led1,
1,
NULL) == pdPASS)
Serial.println("led1 flash task Created.");
if (xTaskCreate(ledFlash,
"FLASH LED",
1024,
(void *)&led2,
1,
NULL) == pdPASS)
Serial.println("led2 flash task Created.");
}
void loop() {
}
欢迎大家交流和指正!!!
特别说明,本系列来源于b站@孤独的二进制