中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

良品鋪?zhàn)泳W(wǎng)站建設(shè)百度推廣優(yōu)化是什么?

良品鋪?zhàn)泳W(wǎng)站建設(shè),百度推廣優(yōu)化是什么?,可不可以異地建設(shè)網(wǎng)站,濮陽市建設(shè)工程交易網(wǎng)2023.5.11 FreeRTOS中文數(shù)據(jù)手冊(cè):https://www.freertos.org/zh-cn-cmn-s/RTOS.html 感謝以下兩位B站UP主的教程:孤獨(dú)的二進(jìn)制、Michael_ee 1.Task 創(chuàng)建任務(wù)常用API: 任務(wù)函數(shù)描述xTaskCreate()使用動(dòng)態(tài)的方法創(chuàng)建一個(gè)任務(wù)xTaskCreatePinne…

2023.5.11
FreeRTOS中文數(shù)據(jù)手冊(cè):https://www.freertos.org/zh-cn-cmn-s/RTOS.html
感謝以下兩位B站UP主的教程:孤獨(dú)的二進(jìn)制、Michael_ee

1.Task

創(chuàng)建任務(wù)常用API:

任務(wù)函數(shù)描述
xTaskCreate()使用動(dòng)態(tài)的方法創(chuàng)建一個(gè)任務(wù)
xTaskCreatePinnedToCore指定任務(wù)的運(yùn)行核心(最后一個(gè)參數(shù))
vTaskDelete(NULL)刪除當(dāng)前任務(wù)
BaseType_t xTaskCreate(TaskFunction_t pxTaskCode,                 // 任務(wù)函數(shù)名const char *const pcName,                  // 任務(wù)備注const configSTACK_DEPTH_TYPE usStackDepth, // 棧大小void *const pvParameters,                  // 傳入的參數(shù)UBaseType_t uxPriority,                    // 任務(wù)優(yōu)先級(jí)TaskHandle_t *const pxCreatedTask);        // 任務(wù)句柄

任務(wù)間傳參

任務(wù)間傳參可以使用多種方式,常見的為:

  1. 使用全局變量:需要注意并發(fā)讀寫的問題,當(dāng)有兩個(gè)任務(wù)及以上對(duì)全局變量進(jìn)行讀寫時(shí),需要使用信號(hào)量或互斥量進(jìn)行保護(hù)。
  2. 使用隊(duì)列:需要注意隊(duì)列的大小和數(shù)據(jù)類型的一致性,不需要使用信號(hào)量或互斥量進(jìn)行保護(hù)隊(duì)列的讀寫效率相比全局變量慢一些

使用全局變量進(jìn)行傳參時(shí):

  • 傳入?yún)?shù):傳遞的為指針,且必須進(jìn)行強(qiáng)制類型轉(zhuǎn)換為空指針(void *)pt
  • 接收參數(shù):把傳遞過來的空指針進(jìn)行強(qiáng)制類型轉(zhuǎn)換,轉(zhuǎn)換為對(duì)應(yīng)傳輸?shù)念愋椭羔?/li>
傳遞整數(shù)
#include <Arduino.h>int a = 1;void mytask(void *pt)
{int *b = (int *)pt;Serial.println(*b);while (1){}
}void setup()
{Serial.begin(115200);xTaskCreatePinnedToCore(mytask, "", 1024 * 3, (void *)&a, 1, NULL, 1);
}void loop() {}

輸出結(jié)果為1

傳遞數(shù)組
#include <Arduino.h>int arr[] = {1, 2, 3};void mytask(void *pt)
{int *b = (int *)pt;int len = sizeof(arr) / sizeof(int); // 數(shù)組的長度,注意這里指針占4個(gè)字節(jié),要用原數(shù)組名Serial.println(len);for (int i = 0; i < len; i++){Serial.print(*(b + i)); // 輸出數(shù)組元素Serial.print(",");}while (1){}
}void setup()
{Serial.begin(115200);// 數(shù)組名代表數(shù)組元素的首地址,所以不需要&xTaskCreatePinnedToCore(mytask, "", 1024 * 3, (void *)arr, 1, NULL, 1);vTaskDelete(NULL);
}void loop() {}
傳遞結(jié)構(gòu)體
#include <Arduino.h>typedef struct
{int a;int b;
} Mystruct;Mystruct test1 = {1, 2};void mytask(void *pt)
{Mystruct *test2 = (Mystruct *)pt; // 強(qiáng)制類型轉(zhuǎn)換為結(jié)構(gòu)體指針Serial.println(test2->a);Serial.println(test2->b);while (1) {}
}void setup()
{Serial.begin(115200);xTaskCreatePinnedToCore(mytask, "", 1024 * 3, (void *)&test1, 1, NULL, 1);vTaskDelete(NULL);
}void loop() {}
傳遞字符串
#include <Arduino.h>const char *str = "hello,world!";void mytask(void *pt)
{char *pstr = (char *)pt;Serial.println(pstr);  // 輸出hello,worldvTaskDelete(NULL);
}void setup()
{Serial.begin(115200);xTaskCreatePinnedToCore(mytask, "", 1024 * 3, (void *)str, 1, NULL, 1);vTaskDelete(NULL);
}void loop() {}

任務(wù)的優(yōu)先級(jí)

注意:中斷任務(wù)的優(yōu)先級(jí)永遠(yuǎn)高于任何任務(wù)的優(yōu)先級(jí)。
在ESP32中,默認(rèn)一共有25個(gè)優(yōu)先級(jí)別,最低為0,最高為24。(可修改相關(guān)的配置函數(shù)進(jìn)行修改優(yōu)先級(jí)的數(shù)目超過25,但是不建議,級(jí)別越高,越占內(nèi)存)。

  • 同優(yōu)先級(jí)的任務(wù):FreeRTOS將采用循環(huán)調(diào)度算法來運(yùn)行他們,也就是交替執(zhí)行同優(yōu)先級(jí)的任務(wù)。每個(gè)任務(wù)執(zhí)行一個(gè)時(shí)間片,然后將CPU時(shí)間片分配給另一個(gè)任務(wù)。
  • 優(yōu)先級(jí)別高的任務(wù)先被創(chuàng)建和運(yùn)行。
    任務(wù)的調(diào)度:
  • 在FreeRTOS中,vTaskDelay()vTaskDelayUntil()函數(shù)可以暫停當(dāng)前任務(wù)的執(zhí)行,等待一段時(shí)間后再繼續(xù)執(zhí)行。(讓其他任務(wù)有機(jī)會(huì)執(zhí)行)
  • taskYIELD()函數(shù):立即將CPU時(shí)間片退讓給同等級(jí)或更高優(yōu)先級(jí)的任務(wù),如果沒有其他任務(wù)等待執(zhí)行,則當(dāng)前任務(wù)會(huì)立即繼續(xù)執(zhí)行。(簡單的說,就是讓其他任務(wù)執(zhí)行)

任務(wù)的掛起和恢復(fù)

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-PQOP5J54-1684032842836)(images/1.png)]
任務(wù)的狀態(tài):running、ready、blocked、suspended(掛起,暫停)

  • running:運(yùn)行狀態(tài),如果MCU只有一個(gè)內(nèi)核,那么在任何給定時(shí)間內(nèi)只能有一個(gè)任務(wù)處于運(yùn)行狀態(tài)。
  • ready:準(zhǔn)備狀態(tài)(任務(wù)剛被創(chuàng)建時(shí),準(zhǔn)備執(zhí)行),不處于堵塞或掛起狀態(tài)(沒有獲得CPU執(zhí)行權(quán)限,等待執(zhí)行狀態(tài)),因?yàn)橥燃?jí)或更高優(yōu)先級(jí)的任務(wù)正在執(zhí)行
  • blocked:使用了vTaskDelay()或delay()函數(shù)
  • suspended:掛起狀態(tài),掛起之后,任務(wù)被恢復(fù)才能繼續(xù)執(zhí)行
// API:
TaskHandle_t pxtask = NULL; // 創(chuàng)建任務(wù)的句柄
xTaskCreatePinnedToCore(task1, "", 1024 * 2, NULL, 1, &pxtask, 1);vTaskSuspend(pxtask);  // 掛起任務(wù),任務(wù)不再執(zhí)行
vTaskResume(pxtask);  // 恢復(fù)被掛起的任務(wù),繼續(xù)執(zhí)行
vTaskSuspendAll();    // 掛起所有函數(shù),掛起后不可以執(zhí)行
vTaskResumeAll();     // 恢復(fù)所有掛起函數(shù)

任務(wù)的堆棧設(shè)置和調(diào)試

創(chuàng)建任務(wù)時(shí),如果給任務(wù)分配的內(nèi)存空間過小,會(huì)導(dǎo)致程序不斷重啟。如果分配的內(nèi)存空間過多,會(huì)造成資源浪費(fèi)。

// API:
ESP.getHeapSize() //  本程序Heap最大尺寸(空間總大小)
ESP.getFreeHeap() //  當(dāng)前Free Heap最大尺寸(當(dāng)前可用剩余空間大小)
uxTaskGetStackHighWaterMark(taskHandle) // 計(jì)算當(dāng)前任務(wù)剩余多少內(nèi)存

示例程序:

TaskHandle_t taskHandle; // 創(chuàng)建任務(wù)的句柄
void setup()
{Serial.begin(115200);xTaskCreatePinnedToCore(mytask, "", 1024*3 , NULL, 1, &taskHandle, 1);int waterMark = uxTaskGetStackHighWaterMark(taskHandle);Serial.print("Task Free Memory: "); // 任務(wù)剩余空間Serial.print(waterMark);vTaskDelete(NULL);
}

vTaskDelay()和delay()

一個(gè)tick的時(shí)間是由FreeRTOS的時(shí)鐘節(jié)拍周期和時(shí)鐘頻率決定的,可以通過配置文件進(jìn)行設(shè)置。默認(rèn)情況下1 tick = 1ms

  • vTaskDelay()函數(shù):以系統(tǒng)時(shí)鐘節(jié)拍(tick)為單位進(jìn)行延時(shí),例如vTaskDelay(100)表示讓任務(wù)暫停100個(gè)系統(tǒng)時(shí)鐘節(jié)拍的時(shí)間。
  • delay()函數(shù):是一個(gè)簡單的延時(shí)函數(shù),它通常在不需要多任務(wù)處理和系統(tǒng)保護(hù)的應(yīng)用中使用。使用后會(huì)后邊的程序都會(huì)被延遲執(zhí)行。

vTaskDelayUntil()

vTaskDelayUntil函數(shù)比vTaskDelay函數(shù)定時(shí)精準(zhǔn)。

// API
TickType_t xLastWakeTime = xTaskGetTickCount();  // 獲取當(dāng)前時(shí)間
const TickType_t xFrequency = 3000;   // 需要的時(shí)間間隔
vTaskDelayUntil(&xLastWakeTime, xFrequency);while(1){vTaskDelayUntil(&xLastWakeTime, xFrequency);// 下邊為需要運(yùn)行的函數(shù)
}
// 

示例程序:

#include <Arduino.h>
void mytask(void *pt)
{TickType_t xLastWakeTime = xTaskGetTickCount(); // 獲取當(dāng)前時(shí)間const TickType_t xFrequency = 1000;             // 需要的時(shí)間間隔while (1){vTaskDelayUntil(&xLastWakeTime, xFrequency);Serial.println(xTaskGetTickCount()); // 輸出當(dāng)前時(shí)間進(jìn)行驗(yàn)證}
}void setup()
{Serial.begin(115200);xTaskCreatePinnedToCore(mytask, "", 1024 * 3, NULL, 1, NULL, 1);vTaskDelete(NULL);
}void loop() {}

2.Queue

2023.5.12

隊(duì)列:先入先出(FIFO,first in first out)
使用方法:

  1. 創(chuàng)建隊(duì)列:長度,尺寸(每個(gè)內(nèi)存空間存儲(chǔ)的數(shù)據(jù)大小)
  2. 發(fā)送數(shù)據(jù)到隊(duì)列中
  3. 從隊(duì)列中取數(shù)據(jù)
// portMAX_DELAY - 無限Block
// TickType_t timeOut = portMAX_DELAY;  // 無限等待,直到隊(duì)列中有數(shù)據(jù),或者等待數(shù)據(jù)有空位置可以存儲(chǔ)新數(shù)據(jù)
TickType_t timeOut = 10;
xStatus = xQueueSend(Qhandle, &i,  timeOut);  // 往隊(duì)列里發(fā)送數(shù)據(jù),如果隊(duì)列里內(nèi)容是滿的就等待10ms再次嘗試發(fā)送
API描述
xQueueCreate()創(chuàng)建一個(gè)隊(duì)列
xQueueSend()往隊(duì)列里寫數(shù)據(jù)
xQueueReceive從隊(duì)列里讀數(shù)據(jù)
uxQueueMessagesWaiting(隊(duì)列句柄)返回值為隊(duì)列中參數(shù)的個(gè)數(shù),可用于接收數(shù)據(jù)時(shí),先判斷一下隊(duì)列里是否有數(shù)據(jù)
// 創(chuàng)建一個(gè)隊(duì)列
QueueHandle_t Qhandle = xQueueCreate(5, sizeof(int)); // 創(chuàng)建一個(gè)隊(duì)列,長度為5,每個(gè)空間的大小為int

隊(duì)列存儲(chǔ)int數(shù)據(jù)

#include <Arduino.h>// 創(chuàng)建隊(duì)列的句柄
QueueHandle_t Qhandle = xQueueCreate(5, sizeof(int));void send(void *pt)
{int i = 0;while (1){if (xQueueSend(Qhandle, &i, portMAX_DELAY) != pdPASS){Serial.println(F("隊(duì)列數(shù)據(jù)發(fā)送失敗"));}else{Serial.print(F("發(fā)送成功:"));Serial.println(i);}i++;if (i == 8)i = 0;vTaskDelay(1000);}
}void receive(void *pt)
{int j = 0; // 存儲(chǔ)接收的隊(duì)列數(shù)據(jù)while (1){if (xQueueReceive(Qhandle, &j, portMAX_DELAY) != pdPASS){Serial.println(F("接收失敗"));}else{Serial.print(F("接收成功:"));Serial.println(j);}}
}void setup()
{Serial.begin(115200);xTaskCreatePinnedToCore(send, "", 1024 * 5, NULL, 1, NULL, 1);    // 發(fā)送數(shù)據(jù)xTaskCreatePinnedToCore(receive, "", 1024 * 5, NULL, 1, NULL, 1); // 接收數(shù)據(jù)vTaskDelete(NULL);
}void loop() {}

運(yùn)行結(jié)果:

發(fā)送成功:0
接收成功:0
發(fā)送成功:1
接收成功:1
發(fā)送成功:2
接收成功:2
發(fā)送成功:3
接收成功:3

隊(duì)列傳遞結(jié)構(gòu)體(重點(diǎn))

跟上面的案例類似,只是隊(duì)列中每個(gè)元素類型為struct,并且發(fā)送和接收的數(shù)據(jù)存儲(chǔ)也要設(shè)置為struct類型

#include <Arduino.h>// 創(chuàng)建一個(gè)結(jié)構(gòu)體
typedef struct
{int a;int b;
} Mystruct;// 創(chuàng)建隊(duì)列的句柄
QueueHandle_t Qhandle = xQueueCreate(5, sizeof(Mystruct));void send(void *pt)
{Mystruct struct1 = {1, 2};while (1){if (xQueueSend(Qhandle, &struct1, portMAX_DELAY) != pdPASS){Serial.println(F("隊(duì)列數(shù)據(jù)發(fā)送失敗"));}else{Serial.print(F("發(fā)送成功:"));struct1.a++;Serial.println(struct1.a);}vTaskDelay(1000);}
}void receive(void *pt)
{Mystruct struct2; // 接收結(jié)構(gòu)體數(shù)據(jù)while (1){if (xQueueReceive(Qhandle, &struct2, portMAX_DELAY) != pdPASS){Serial.println(F("接收失敗"));}else{Serial.print(F("接收成功:"));Serial.println(struct2.a);}}
}void setup()
{Serial.begin(115200);xTaskCreatePinnedToCore(send, "", 1024 * 5, NULL, 1, NULL, 1);    // 發(fā)送數(shù)據(jù)xTaskCreatePinnedToCore(receive, "", 1024 * 5, NULL, 1, NULL, 1); // 接收數(shù)據(jù)vTaskDelete(NULL);
}void loop() {}

運(yùn)行結(jié)果:按照FIFO的規(guī)則進(jìn)行數(shù)據(jù)的發(fā)送和接收

發(fā)送成功:2
接收成功:1
發(fā)送成功:3
接收成功:2
發(fā)送成功:4
接收成功:3

隊(duì)列傳遞大型數(shù)據(jù)時(shí)

例如傳遞字符串。傳遞大型數(shù)據(jù)時(shí),把指針對(duì)應(yīng)的數(shù)據(jù)進(jìn)行傳遞。

  • malloc()函數(shù):在使用malloc開辟空間時(shí),使用完一定要釋放空間,如果不釋放會(huì)造成內(nèi)存泄漏。malloc()函數(shù)返回的實(shí)際是一個(gè)無類型指針,必須在其前面加上指針類型強(qiáng)制轉(zhuǎn)換才可以使用。指針自身 = (指針類型*)malloc(sizeof(指針類型)*數(shù)據(jù)數(shù)量)
int *p = NULL;
p = (int *)malloc(sizeof(int)*10);// 使用完之后采用free()進(jìn)行釋放
free(p);
p = NULL; // 讓其重新指向NULL

隊(duì)列的多進(jìn)單出:多個(gè)任務(wù)寫,一個(gè)任務(wù)讀


多個(gè)任務(wù)把數(shù)據(jù)寫入一個(gè)隊(duì)列,一個(gè)任務(wù)進(jìn)行讀。設(shè)置寫入的任務(wù)級(jí)別為同級(jí)別,讀任務(wù)的優(yōu)先級(jí)別要比寫任務(wù)高一級(jí)別。

  • 不推薦這種方式:容易造成系統(tǒng)工作混亂。最好的工作方式是一個(gè)隊(duì)列只有一個(gè)寫操作,可以有多個(gè)讀操作,但是寫操作只能有一個(gè)。

隊(duì)列集合(常用):

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-qgLZATfT-1684032842837)(images/2.png)]
多個(gè)隊(duì)列,但是每個(gè)隊(duì)列只有一個(gè)寫操作,一個(gè)讀操作(讀取所有隊(duì)列)
實(shí)現(xiàn)步驟:

  1. 創(chuàng)建隊(duì)列集合的句柄:同時(shí)指定隊(duì)列集合的總長度
  2. 將已創(chuàng)建的隊(duì)列添加到集合中
  3. 創(chuàng)建一個(gè)句柄:從隊(duì)列集合中獲取有數(shù)據(jù)的隊(duì)列
QueueHandle_t Qhandle1 = xQueueCreate(5, sizeof(int)); // 隊(duì)列1
QueueHandle_t Qhandle2 = xQueueCreate(5, sizeof(int)); // 隊(duì)列2QueueSetHandle_t QueueSet = xQueueCreateSet(10); // 隊(duì)列集合句柄,10為隊(duì)列的總長度xQueueAddToSet(Qhandle1, QueueSet); // 把隊(duì)列1加入到隊(duì)列集合中
xQueueAddToSet(Qhandle2, QueueSet); // 把隊(duì)列2加入到隊(duì)列集合中QueueSetMemberHandle_t QueueData = xQueueSelectFromSet(QueueSet, portMAX_DELAY); // 從隊(duì)列集合中獲取有數(shù)據(jù)的隊(duì)列, QueueData為句柄

示例程序:這個(gè)程序編譯不成功,還沒有解決

#include <Arduino.h>QueueHandle_t Qhandle1 = xQueueCreate(5, sizeof(int)); // 隊(duì)列1
QueueHandle_t Qhandle2 = xQueueCreate(5, sizeof(int)); // 隊(duì)列2QueueSetHandle_t QueueSet = xQueueCreateSet(10); // 隊(duì)列集合句柄xQueueAddToSet(Qhandle1, QueueSet); // 把隊(duì)列1加入到隊(duì)列集合中
xQueueAddToSet(Qhandle2, QueueSet); // 把隊(duì)列2加入到隊(duì)列集合中QueueSetMemberHandle_t QueueData = xQueueSelectFromSet(QueueSet, portMAX_DELAY); // 從隊(duì)列集合中獲取有數(shù)據(jù)的隊(duì)列void send1(void *pt)
{int i = 1; // 任務(wù)1要發(fā)送的數(shù)據(jù)while (1){if (xQueueSend(Qhandle1, &i, portMAX_DELAY) != pdPASS){Serial.println("發(fā)送失敗");}else{Serial.println("發(fā)送成功");}vTaskDelay(1000);}
}void send2(void *pt)
{int i = 2; // 任務(wù)2要發(fā)送的數(shù)據(jù)while (1){if (xQueueSend(Qhandle2, &i, portMAX_DELAY) != pdPASS){Serial.println("發(fā)送失敗");}else{Serial.println("發(fā)送成功");}vTaskDelay(1000);}
}void receive(void *pt)
{int i; // 存儲(chǔ)接收數(shù)據(jù)while (1){if (xQueueReceive(QueueData, &i, portMAX_DELAY) != pdPASS) // portMAX_DELAY,一直等待,直到隊(duì)列中有數(shù)據(jù){Serial.println("接收失敗");}else{Serial.print("接收成功:");Serial.println(i);}// vTaskDelay(1000); // 采用了portMAX_DELAY,這里就不需要delay了}
}void setup()
{Serial.begin(9600);Serial.println("隊(duì)列創(chuàng)建成功");xTaskCreatePinnedToCore(send1, "", 1024 * 5, NULL, 1, NULL, 1); // 兩個(gè)相同的優(yōu)先級(jí)別,輪流發(fā)送數(shù)據(jù)xTaskCreatePinnedToCore(send2, "", 1024 * 5, NULL, 1, NULL, 1);xTaskCreatePinnedToCore(receive, "", 1024 * 5, NULL, 2, NULL, 1); // 優(yōu)先級(jí)別2,只要隊(duì)列中有數(shù)據(jù),就讀
}void loop()
{
}

隊(duì)列郵箱(常用):

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-CUCtItFd-1684032842837)(images/3.png)]
只有一個(gè)隊(duì)列,一個(gè)任務(wù)寫,多個(gè)任務(wù)讀

// API
QueueHandle_t Mailbox = xQueueCreate(5, sizeof(int));  // 創(chuàng)建一個(gè)隊(duì)列郵箱
xQueueOverwrite(); // 往隊(duì)列中寫數(shù)據(jù)
xQueuePeek(); // 從隊(duì)列中讀數(shù)據(jù)

示例程序:運(yùn)行不成功

#include <Arduino.h>QueueHandle_t Mailbox = xQueueCreate(5, sizeof(int));void send(void *pt)
{int i = 1; // 任務(wù)1要發(fā)送的數(shù)據(jù)while (1){if (xQueueOverwrite(Mailbox, &i) != pdPASS){Serial.println("發(fā)送失敗");}else{Serial.println("發(fā)送成功");i++;}vTaskDelay(1000);}
}void receive1(void *pt)
{int i; // 存儲(chǔ)接收數(shù)據(jù)while (1){if (xQueuePeek(Mailbox, &i, 1000) != pdPASS) // portMAX_DELAY,一直等待,直到隊(duì)列中有數(shù)據(jù){Serial.println("接收失敗");}else{Serial.print("接收成功:");Serial.println(i);}}
}
void receive2(void *pt)
{int i; // 存儲(chǔ)接收數(shù)據(jù)while (1){if (xQueuePeek(Mailbox, &i,1000) != pdPASS) // portMAX_DELAY,一直等待,直到隊(duì)列中有數(shù)據(jù){Serial.println("接收失敗");}else{Serial.print("接收成功:");Serial.println(i);}}
}void setup()
{Serial.begin(9600);Serial.println("隊(duì)列創(chuàng)建成功");xTaskCreatePinnedToCore(send, "", 1024 * 5, NULL, 2, NULL, 1);xTaskCreatePinnedToCore(receive1, "", 1024 * 5, NULL, 2, NULL, 1);xTaskCreatePinnedToCore(receive2, "", 1024 * 5, NULL, 2, NULL, 1);
}void loop()
{
}

3.信號(hào)量

信號(hào)量分類:二進(jìn)制信號(hào)量、計(jì)數(shù)信號(hào)量、互斥信號(hào)量。
信號(hào)量就像紅綠燈一樣,控制車輛的通行。
信號(hào)量常用于控制對(duì)共享資源的訪問和任務(wù)同步。信號(hào)量對(duì)于控制共享資源訪問的場景相當(dāng)于一個(gè)上鎖機(jī)制,代碼只有獲得這個(gè)鎖的鑰匙才能執(zhí)行。

  • 使用隊(duì)列、信號(hào)量,都可以實(shí)現(xiàn)互斥訪問

二進(jìn)制信號(hào)量(常用)

二值信號(hào)量常用于互斥訪問或同步,二值信號(hào)量和互斥信號(hào)量非常類似,但是互斥信號(hào)量擁有優(yōu)先級(jí)繼承機(jī)制,二值信號(hào)量沒有優(yōu)先級(jí)繼承。

  • 二進(jìn)制信號(hào)量可以用于一個(gè)任務(wù)控制另一個(gè)任務(wù)的運(yùn)行與堵塞。
  • 二進(jìn)制信號(hào)量表示只有兩個(gè)值:0和1
  • 二值信號(hào)量相當(dāng)于長度為1的隊(duì)列
  • 二進(jìn)制信號(hào)量只有兩種狀態(tài):已觸發(fā)和未觸發(fā),類似于一個(gè)開關(guān)。當(dāng)一個(gè)任務(wù)等待一個(gè)已經(jīng)觸發(fā)的二進(jìn)制信號(hào)量是,它會(huì)立即獲得信號(hào)量,如果信號(hào)量未被觸發(fā),任務(wù)將被堵塞直到信號(hào)量被觸發(fā)。
  • 可以避免資源沖突和死鎖問題,提高系統(tǒng)的可靠性和效率
// API
SemaphoreHandle_t xHandler = xSemaphoreCreateBinary();  // 創(chuàng)建二進(jìn)制信號(hào)量
xSemaphoreGive(xHandler); // 釋放信號(hào)量
xSemaphoreTake(xHanlder, timeout); // 在指定時(shí)間內(nèi)獲取信號(hào)量,返回值為pdPASS, 或者pdFAIL

示例程序1:按鍵控制LED的亮滅(已驗(yàn)證)

#include <Arduino.h>SemaphoreHandle_t xHandler = xSemaphoreCreateBinary(); // 創(chuàng)建二進(jìn)制信號(hào)量
TickType_t timeOut = 1000;void task1(void *pt)
{pinMode(23, OUTPUT);while (1){if (xSemaphoreTake(xHandler, timeOut) == pdTRUE){digitalWrite(23, !digitalRead(23));}}
}void task2(void *pt)
{pinMode(22, INPUT_PULLUP);while (1){if (digitalRead(22) == LOW){xSemaphoreGive(xHandler);vTaskDelay(120); // button debounce}}
}void setup()
{Serial.begin(9600);xTaskCreatePinnedToCore(task1, "", 1024 * 5, NULL, 1, NULL, 1); // 兩個(gè)相同的優(yōu)先級(jí)別,輪流發(fā)送數(shù)據(jù)xTaskCreatePinnedToCore(task2, "", 1024 * 5, NULL, 1, NULL, 1);
}void loop()
{
}

示例2:采用二進(jìn)制信號(hào)量對(duì)任務(wù)進(jìn)行管理,對(duì)全局變量進(jìn)行讀寫

  • 該示例驗(yàn)證了,使用二進(jìn)制信號(hào)量可以很好的控制任務(wù)的執(zhí)行順序。
#include <Arduino.h>
int a = 0;
SemaphoreHandle_t xHandler = xSemaphoreCreateBinary(); // 創(chuàng)建二進(jìn)制信號(hào)量void task1(void *pt)
{while (1){xSemaphoreTake(xHandler, portMAX_DELAY); // 無限等待,直到獲取信號(hào)量for (int i = 0; i < 10; i++){a++;printf("mytask1 a = %d\n", a);}xSemaphoreGive(xHandler); // 執(zhí)行完之后,需要再次釋放信號(hào)量vTaskDelay(1000);}
}void task2(void *pt)
{while (1){xSemaphoreTake(xHandler, portMAX_DELAY); // 無限等待,直到獲取信號(hào)量for (int i = 0; i < 10; i++){a++;printf("mytask2 a = %d\n", a);}xSemaphoreGive(xHandler); // 執(zhí)行完之后,需要再次釋放信號(hào)量vTaskDelay(1000);}
}void setup()
{Serial.begin(115200);xSemaphoreGive(xHandler);                                       // 首先釋放一次信號(hào)量,不然運(yùn)行不了xTaskCreatePinnedToCore(task1, "", 1024 * 5, NULL, 1, NULL, 1); // task1先獲取信號(hào)量,執(zhí)行一次xTaskCreatePinnedToCore(task2, "", 1024 * 5, NULL, 1, NULL, 1); // 然后task2獲取信號(hào)量,執(zhí)行一次,task11再執(zhí)行
}void loop()
{
}

運(yùn)行結(jié)果:

  • 采用二進(jìn)制信號(hào)量:
mytask1 a = 1
mytask1 a = 2
mytask1 a = 3
mytask1 a = 4
mytask1 a = 5
mytask1 a = 6
mytask1 a = 7
mytask1 a = 8
mytask1 a = 9
mytask1 a = 10
mytask2 a = 11
mytask2 a = 12
mytask2 a = 13
mytask2 a = 14
mytask2 a = 15
mytask2 a = 16
mytask2 a = 17
mytask2 a = 18
mytask2 a = 19
mytask2 a = 20
mytask1 a = 21
  • 不采用二進(jìn)制信號(hào)量運(yùn)行結(jié)果:
ytask1 a = 1
mytask1 a = 2
mytask1 a = 3
mytask1 a = 4
mytask1 a = 6
mytask1 a = 7
mytask1 a = 8
mytask1 a = 9
**mytask1 a = 10**
**mytask1 a = 11**
**mytask2 a = 5**
**mytask2 a = 12**
mytask2 a = 13

計(jì)數(shù)信號(hào)量

與二進(jìn)制不同的是,計(jì)數(shù)信號(hào)量可以有更多的狀態(tài)。

  • 計(jì)數(shù)信號(hào)量相當(dāng)于長度大于1的隊(duì)列,同二值信號(hào)量一樣,不需要關(guān)系隊(duì)列中存儲(chǔ)了什么數(shù)據(jù),只需要關(guān)心隊(duì)列是否為空即可。
  • 例如在一個(gè)停車場中有10個(gè)車位,車輛進(jìn)入時(shí),車位被占用(計(jì)數(shù)器減1),車開出去后(計(jì)數(shù)器加1),為0時(shí)表示沒有可用的車位。
// API
uxSemaphoreGetCount( semphrHandle); // 獲得計(jì)數(shù)型信號(hào)量的值
SemaphoreHandle_t semphrHandle = xSemaphoreCreateCounting(10,0);// 創(chuàng)建計(jì)數(shù)型信號(hào)量,參數(shù)1:最大值,參數(shù)2:初始值
xSemaphoreGive(semphrHandle); // 釋放信號(hào)量
xSemaphoreTake(semphrHandle); // 獲取信號(hào)量

使用場合:事件計(jì)數(shù)、資源管理

  1. 共享資源的訪問控制:當(dāng)多個(gè)任務(wù)需要共享同一個(gè)資源時(shí),可以使用計(jì)數(shù)信號(hào)量來控制資源的訪問。每個(gè)任務(wù)需要訪問資源時(shí),都需要獲取一個(gè)計(jì)數(shù)信號(hào)量,如果計(jì)數(shù)信號(hào)量的值為0,則任務(wù)會(huì)被堵塞,直到其他任務(wù)釋放資源并增加計(jì)數(shù)信號(hào)量的值。這種方式可以避免資源沖突和死鎖等問題。
  2. 控制任務(wù)的執(zhí)行順序:有些情況下,需要控制任務(wù)的執(zhí)行順序,例如任務(wù)A必須在任務(wù)B執(zhí)行完成之后才能執(zhí)行。可以使用計(jì)數(shù)信號(hào)量來實(shí)現(xiàn)這種控制。任務(wù)B執(zhí)行完成后,可以增加計(jì)數(shù)信號(hào)量的值,任務(wù)A等待計(jì)數(shù)信號(hào)量的值為1時(shí),可以獲取信號(hào)量并開始執(zhí)行。
    示例1:模擬停車場的停車位
#include <Arduino.h>// 創(chuàng)建計(jì)數(shù)型信號(hào)量,參數(shù)1:最大值,參數(shù)2:初始值
SemaphoreHandle_t semphrHandle = xSemaphoreCreateCounting(5, 5); // 初值為5,代表初始有5個(gè)空車位void carintask(void *pt)
{int emptySpace = 0; // 空的停車位BaseType_t iResult;while (1){emptySpace = uxSemaphoreGetCount(semphrHandle);printf("emptySpace = %d\n", emptySpace);iResult = xSemaphoreTake(semphrHandle, 0); // 獲取信號(hào)量if (iResult == pdPASS)printf("One car in\n");elseprintf("No Space\n");vTaskDelay(1000);}
}void caroutTask(void *pt)
{while (1){vTaskDelay(6000);xSemaphoreGive(semphrHandle); // 釋放信號(hào)量printf("One car out\n");}
}void setup()
{Serial.begin(115200);xTaskCreatePinnedToCore(carintask, "", 1024 * 5, NULL, 1, NULL, 1);xTaskCreatePinnedToCore(caroutTask, "", 1024 * 5, NULL, 1, NULL, 1);
}void loop()
{
}

互斥信號(hào)量(常用)

與二進(jìn)制信號(hào)量十分相似。Mutex的工作原理可以想象成共享的資源被鎖在一個(gè)箱子里,只有一把鑰匙,有鑰匙的任務(wù)才能對(duì)共享資源進(jìn)行訪問。

  • 互斥量與二進(jìn)制信號(hào)量的區(qū)別:優(yōu)先級(jí)繼承,在FreeRTOS中,當(dāng)一個(gè)任務(wù)持有一個(gè)互斥量時(shí),該任務(wù)對(duì)共享資源的訪問是獨(dú)占的,其他試圖獲取該互斥量的任務(wù)將被堵塞。當(dāng)一個(gè)優(yōu)先級(jí)更高的任務(wù)試圖獲取已經(jīng)被持有的互斥量時(shí),FreeRTOS會(huì)自動(dòng)暫時(shí)提高持有互斥量任務(wù)的優(yōu)先級(jí)別,使其具有與試圖獲取互斥量的任務(wù)相同的優(yōu)先級(jí)別。這樣可以確保高優(yōu)先級(jí)別的任務(wù)在獲取共享資源時(shí)能夠及時(shí)執(zhí)行,并避免低優(yōu)先級(jí)別任務(wù)長時(shí)間持有共享資源。當(dāng)持有互斥量的任務(wù)釋放互斥量時(shí),其優(yōu)先級(jí)別將恢復(fù)到原始值,而不是保持被繼承的優(yōu)先級(jí)。這樣可以確保任務(wù)在不需要共享資源時(shí)恢復(fù)其原始優(yōu)先級(jí),以避免低優(yōu)先級(jí)任務(wù)一直持有高優(yōu)先級(jí)任務(wù)的優(yōu)先級(jí),導(dǎo)致高優(yōu)先級(jí)任務(wù)無法及時(shí)執(zhí)行其他任務(wù)。
  • 也就是對(duì)于不同優(yōu)先級(jí)別的任務(wù),采用mutex對(duì)共享資源進(jìn)行保護(hù),如果任務(wù)均為同優(yōu)先級(jí)別,可以采用二進(jìn)制信號(hào)量進(jìn)行共享資源保護(hù)
  • 可以理解為互斥量二進(jìn)制信號(hào)量的升級(jí)版
  • 注意:使用完立即釋放鑰匙
SemaphoreHandle_t mutexHandler = xSemaphoreCreateMutex();  // 創(chuàng)建一個(gè)Mutex互斥量
xSemaphoreGive(mutexHandler); // 釋放信號(hào)量
xSemaphoreTake(mutexHandler, timeout); // 在指定時(shí)間內(nèi)獲取信號(hào)量,返回值為pdPASS, 或者pdFAIL

示例程序:

// 對(duì)于互斥量通常創(chuàng)建3個(gè)任務(wù)
#include <Arduino.h>SemaphoreHandle_t mutexHandler = xSemaphoreCreateMutex(); // 創(chuàng)建mutex句柄void task1(void *pt)
{printf("task1 begin\n");while (1){xSemaphoreTake(mutexHandler, portMAX_DELAY);printf("tsak1 take\n");for (size_t i = 0; i < 15; i++){printf("task1 i = %d\n", i);vTaskDelay(1000);}xSemaphoreGive(mutexHandler);printf("tsak1 give\n");}
}
void task2(void *pt)
{printf("task2 begin\n");vTaskDelay(1000); // 讓低優(yōu)先級(jí)別的任務(wù)有機(jī)會(huì)執(zhí)行while (1){;}
}
void task3(void *pt)
{printf("task3 begin\n");vTaskDelay(1000); // 讓低優(yōu)先級(jí)別的任務(wù)有機(jī)會(huì)執(zhí)行while (1){xSemaphoreTake(mutexHandler, portMAX_DELAY); // 獲取信號(hào)量printf("tsak3 take\n");for (size_t i = 0; i < 10; i++){printf("task3 i = %d\n", i);vTaskDelay(1000);}xSemaphoreGive(mutexHandler); // 釋放信號(hào)量printf("tsak3 give\n");}
}void setup()
{Serial.begin(115200);xTaskCreatePinnedToCore(task1, "", 1024 * 5, NULL, 1, NULL, 1); // task1獲取到信號(hào)量時(shí),如果task3高優(yōu)先級(jí)的任務(wù)也嘗試獲取該信號(hào)量,會(huì)將task1的優(yōu)先級(jí)暫時(shí)升級(jí)為3xTaskCreatePinnedToCore(task2, "", 1024 * 5, NULL, 2, NULL, 1);xTaskCreatePinnedToCore(task3, "", 1024 * 5, NULL, 3, NULL, 1); // 優(yōu)先級(jí)別最高,最先執(zhí)行vTaskDelete(NULL); // 刪除當(dāng)前任務(wù)
}void loop()
{
}
遞歸互斥量

允許同一任務(wù)在持有互斥量的情況下再次獲取該互斥量,而不會(huì)導(dǎo)致死鎖。
遞歸互斥量可以用于需要對(duì)同一資源進(jìn)行多層保護(hù)的情況,例如嵌套調(diào)用的函數(shù)。

SemaphoreHandle_t mutexHandler = xSemaphoreCreateRecursiveMutex(); // 創(chuàng)建遞歸互斥量
xSemaphoreTakeRecursive(mutexHandler); // 獲取信號(hào)量
xSemaphoreGiveRecursive(mutexHandler); // 釋放信號(hào)量
  • 注意:使用遞歸互斥量時(shí),獲取和釋放的次數(shù)要相等,以避免死鎖的情況。
#include <Arduino.h>SemaphoreHandle_t mutexHandler = xSemaphoreCreateRecursiveMutex(); // 創(chuàng)建mutex句柄void task1(void *pt)
{while (1){printf("task1 begin\n");xSemaphoreTakeRecursive(mutexHandler, portMAX_DELAY); // 第一次取得信號(hào)量printf("tsak1 take\n");for (size_t i = 0; i < 5; i++){printf("task1 i = %d for A\n", i);vTaskDelay(1000);}xSemaphoreTakeRecursive(mutexHandler, portMAX_DELAY); // 第二次取得信號(hào)量for (size_t i = 0; i < 5; i++){printf("task1 i = %d for B\n", i);vTaskDelay(1000);}xSemaphoreGiveRecursive(mutexHandler);xSemaphoreGiveRecursive(mutexHandler);printf("tsak1 give\n");taskYIELD();}
}void task2(void *pt)
{vTaskDelay(1000);while (1){printf("task2 begin\n");xSemaphoreTakeRecursive(mutexHandler, portMAX_DELAY);printf("tsak2 take\n");xSemaphoreGiveRecursive(mutexHandler);printf("tsak2 give\n");taskYIELD();}
}void setup()
{Serial.begin(115200);xTaskCreatePinnedToCore(task1, "", 1024 * 5, NULL, 1, NULL, 1);xTaskCreatePinnedToCore(task2, "", 1024 * 5, NULL, 1, NULL, 1);vTaskDelete(NULL); // 刪除當(dāng)前任務(wù)
}void loop()
{
}

運(yùn)行結(jié)果:

task1 begin
tsak1 take
task1 i = 0 for A
task2 begin
task1 i = 1 for A
task1 i = 2 for A
task1 i = 3 for A
task1 i = 4 for A
task1 i = 0 for B
task1 i = 1 for B
task1 i = 2 for B
task1 i = 3 for B
task1 i = 4 for B
tsak1 give
tsak2 take
tsak2 give
task1 begin
task2 begin
tsak2 take
tsak2 give
tsak1 take

4.任務(wù)通知(重要)

從FreeRTOS V8.2.0版本,新增了任務(wù)通知(task notify)這個(gè)功能,可用使用任務(wù)通知來代替信號(hào)量、消息隊(duì)列、事件標(biāo)志組這些東西。使用任務(wù)通知可用提高系統(tǒng)的工作效率。

FreeRTOS的每個(gè)任務(wù)都有一個(gè)32位的通知值,任務(wù)控制塊中的成員變量ulNotifiedValue就是這個(gè)通知值。

  • 使用任務(wù)通知,可以控制任務(wù)的流向,執(zhí)行順序。

任務(wù)通知雖然可用提高速度,并且減少RAM的使用,但是任務(wù)通知也是有使用限制的:

  1. FreeRTOS的任務(wù)通知只能有一個(gè)接收任務(wù),大多數(shù)的應(yīng)用都是這種情況
  2. 接收任務(wù)可以因?yàn)榻邮杖蝿?wù)通知而進(jìn)入阻塞態(tài),但是發(fā)送任務(wù)不會(huì)因?yàn)槿蝿?wù)通知發(fā)送失敗而阻塞。

通知同步

示例程序:任務(wù)2通知任務(wù)1執(zhí)行,如果任務(wù)1沒有接收到任務(wù)通知,就一直處于阻塞狀態(tài)。更詳細(xì)的內(nèi)容,參考FreRTOS中文數(shù)據(jù)手冊(cè):2.18 xTaskNotifyGive()

#include <Arduino.h>static TaskHandle_t xTask1 = NULL, xTask2 = NULL; // 創(chuàng)建任務(wù)的句柄void task1(void *pt)
{while (1){printf("task1 wait notification!\n");ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // 阻塞自身,等待通知執(zhí)行下面的程序printf("task1 got notification\n");}
}void task2(void *pt)
{while (1){vTaskDelay(1000); // 1s發(fā)送1次通知printf("task2 notify task1!\n");xTaskNotifyGive(xTask1); // 通知任務(wù)1解鎖阻塞狀態(tài)}
}void setup()
{Serial.begin(115200);xTaskCreatePinnedToCore(task1, "", 1024 * 5, NULL, 1, &xTask1, 1);xTaskCreatePinnedToCore(task2, "", 1024 * 5, NULL, 1, &xTask2, 1);vTaskDelete(NULL); // 刪除當(dāng)前任務(wù)
}void loop() {}

運(yùn)行結(jié)果:

task1 wait notification!
task2 notify task1!
task1 got notification
task1 wait notification!

任務(wù)通知值

通過通知不同的值,可以控制任務(wù)進(jìn)入不同的處理流程。

BaseType_t xTaskNotifyWait(uint32_t ulBitsToClearOnEntry,  // 在進(jìn)入函數(shù)時(shí),清除所有函數(shù)的通知值uint32_t ulBitsToClearOnExit,   // 在退出的時(shí)候清楚uint32_t *pulNotificationValue, // 取得當(dāng)前任務(wù)通知的值TickType_t xTicksToWait);       // 等待時(shí)間BaseType_t xTaskNotify(TaskHandle_t xTaskToNotify, // 任務(wù)通知的句柄uint32_t ulValue,           // 需要發(fā)送的任務(wù)通知值eNotifyAction eAction);     // 常用eSetValueWithOverwrite                           

示例程序:通過設(shè)置不同的通知值,執(zhí)行不同的事件,參考數(shù)據(jù)手冊(cè):2.14 xTaskNotify()

#include <Arduino.h>static TaskHandle_t xTask1 = NULL, xTask2 = NULL; // 創(chuàng)建任務(wù)的句柄void task1(void *pt)
{uint32_t ulNotifiedValue;while (1){printf("task1 wait notification!\n");xTaskNotifyWait(0x00,             /* Don't clear any notification bits on entry. */ULONG_MAX,        /* Reset the notification value to 0 on exit. */&ulNotifiedValue, /* Notified value pass out in ulNotifiedValue. */portMAX_DELAY);   /* Block indefinitely. *//* Process any events that have been latched in the notified value. */if ((ulNotifiedValue & 0x01) != 0){/* Bit 0 was set - process whichever event is represented by bit 0. */printf("task1 process bit0 event!\n");}if ((ulNotifiedValue & 0x02) != 0){/* Bit 1 was set - process whichever event is represented by bit 1. */printf("task1 process bit1 event!\n");}if ((ulNotifiedValue & 0x04) != 0){/* Bit 2 was set - process whichever event is represented by bit 2. */printf("task1 process bit2 event!\n");}/* Etc. */}
}void task2(void *pt)
{while (1){vTaskDelay(1000); // 1s發(fā)送1次通知printf("task2 notify bit0!\n");xTaskNotify(xTask1, 0x01, eSetValueWithOverwrite);vTaskDelay(1000);printf("task2 notify bit1!\n");xTaskNotify(xTask1, 0x02, eSetValueWithOverwrite);vTaskDelay(1000);printf("task2 notify bit2!\n");xTaskNotify(xTask1, 0x04, eSetValueWithOverwrite);}
}void setup()
{Serial.begin(115200);xTaskCreatePinnedToCore(task1, "", 1024 * 5, NULL, 1, &xTask1, 1);xTaskCreatePinnedToCore(task2, "", 1024 * 5, NULL, 1, &xTask2, 1);vTaskDelete(NULL); // 刪除當(dāng)前任務(wù)
}void loop() {}

運(yùn)行結(jié)果:

task1 wait notification!
task2 notify bit0!
task1 process bit0 event!
task1 wait notification!
task2 notify bit1!
task1 process bit1 event!
task1 wait notification!
task2 notify bit2!
task1 process bit2 event!

任務(wù)通知取代信號(hào)量

使用直接任務(wù)通知取代二進(jìn)制信號(hào)量,由于沒有了二進(jìn)制信號(hào)量這個(gè)中間媒介,不僅節(jié)省了內(nèi)存,而且速度也會(huì)快45%。

  • 注意:設(shè)置任務(wù)的阻塞和通知順序要注意先后順序。
    直接任務(wù)通知不能取代二進(jìn)制信號(hào)量的場景:
  1. 直接任務(wù)通知相當(dāng)于嚴(yán)格指定任務(wù)的執(zhí)行順序,而采用二進(jìn)制信號(hào)量可以做到在等待事件內(nèi)各任務(wù)隨機(jī)搶占CPU執(zhí)行權(quán)
  2. 因此,當(dāng)有2個(gè)及以上需要接收信號(hào)量時(shí),最好采用二進(jìn)制信號(hào)量,而不是直接任務(wù)通知
#include <Arduino.h>static TaskHandle_t xTask1 = NULL, xTask2 = NULL; // 創(chuàng)建任務(wù)的句柄void task1(void *pt)
{while (1){xTaskNotifyGive(xTask2); // 通知任務(wù)2執(zhí)行/* Block to wait for prvTask2() to notify this task. */ulTaskNotifyTake(pdTRUE, portMAX_DELAY);}
}void task2(void *pt)
{while (1){/* Block to wait for prvTask1() to notify this task. */ulTaskNotifyTake(pdTRUE, portMAX_DELAY);xTaskNotifyGive(xTask1); // 通知任務(wù)1執(zhí)行}
}void setup()
{Serial.begin(115200);xTaskCreatePinnedToCore(task1, "", 1024 * 5, NULL, 1, &xTask1, 1);xTaskCreatePinnedToCore(task2, "", 1024 * 5, NULL, 1, &xTask2, 1);vTaskDelete(NULL); // 刪除當(dāng)前任務(wù)
}void loop() {}

直接任務(wù)當(dāng)作郵箱

可以通過設(shè)置任務(wù)通知值的方式達(dá)到想要的效果。

5.流媒體緩存

流媒體:音頻、視頻

  • 適合一個(gè)任務(wù)讀,一個(gè)任務(wù)寫,不適合多任務(wù)讀寫
  • 與隊(duì)列的不同:stream buffer讀寫的大小沒有限制,而隊(duì)列是預(yù)先設(shè)置好的固定值
#include <freertos/stream_buffer.h>  // 首先添加流媒體相關(guān)的頭文件// 創(chuàng)建streambuffer
StreamBufferHandle_t xStreamBufferCreate(size_t xBufferSizeBytes,  // 參數(shù)1:buffer的大小size_t xTriggerLevelBytes); // 參數(shù)2:最小一幀數(shù)據(jù)的大小,例如一個(gè)聲音最少8個(gè)字節(jié),則設(shè)置為8,// Stream Buffer內(nèi)數(shù)據(jù)超過這個(gè)數(shù)值,才會(huì)被讀取,否則一直接收進(jìn)行存儲(chǔ),達(dá)到這個(gè)值進(jìn)行一次讀取// 發(fā)送流媒體數(shù)據(jù)
size_t xStreamBufferSend(StreamBufferHandle_t xStreamBuffer,  // 句柄const void *pvTxData, // 需要發(fā)送數(shù)據(jù)的指針,需要進(jìn)行強(qiáng)制類型轉(zhuǎn)換size_t xDataLengthBytes, // 需要發(fā)送數(shù)據(jù)的長度,可以用sizeof()計(jì)算TickType_t xTicksToWait);  // 堵塞時(shí)間// 接收流媒體數(shù)據(jù)
size_t xStreamBufferReceive(StreamBufferHandle_t xStreamBuffer,void *pvRxData, // 接收數(shù)據(jù),需要進(jìn)行強(qiáng)制類型轉(zhuǎn)換size_t xBufferLengthBytes,  // 存儲(chǔ)接收數(shù)據(jù)的buffer長度TickType_t xTicksToWait);
  • 注意:創(chuàng)建stream buffer時(shí),xTriggerLevelBytes的設(shè)置非常重要。在接收函數(shù)中,如果buffer中有數(shù)據(jù),首先會(huì)將能夠接收的數(shù)據(jù)接收下來,然后堵塞當(dāng)前的任務(wù),直到buffer中的數(shù)據(jù)大于xTriggerLevelBytes

確定stream buffer的大小

在創(chuàng)建stream buffer時(shí),如果創(chuàng)建的buffer太大,會(huì)造成資源浪費(fèi),太小系統(tǒng)工作會(huì)非常不穩(wěn)定。

  • 使用stream buffer時(shí),通常創(chuàng)建3個(gè)任務(wù)
  • 任務(wù)1發(fā)送數(shù)據(jù),任務(wù)2接收數(shù)據(jù),任務(wù)3監(jiān)控stream buffer的空間大小
// API
size_t xStreamBufferBytesAvailable( StreamBufferHandle_t xStreamBuffer ); // stream buffer已使用字節(jié)
size_t xStreamBufferSpacesAvailable( StreamBufferHandle_t xStreamBuffer ); // sream buffer可用空間字節(jié)

示例:

#include <Arduino.h>
#include <string>
#include <freertos/stream_buffer.h>// 參數(shù)1:stream buffer的總大小,參數(shù)2:每幀數(shù)據(jù)的大小
// 參數(shù)2作用:接收數(shù)據(jù)時(shí),如果小于這個(gè)值將處于堵塞狀態(tài),直到接收buffer里存儲(chǔ)的字節(jié)大于這個(gè)值,才會(huì)接收一次
StreamBufferHandle_t streamHandler = xStreamBufferCreate(200, 50);void task1(void *pt)
{char tx_buffer[50];int str_len = 0; // 字符串長度int i = 0;int send_bytes = 0; // 實(shí)際發(fā)送的數(shù)據(jù)while (1){i++;str_len = sprintf(tx_buffer, "hello send i= %d ", i);                                     // 要發(fā)送的數(shù)據(jù)send_bytes = xStreamBufferSend(streamHandler, (void *)tx_buffer, str_len, portMAX_DELAY); // 沒有發(fā)送成功就一直處于堵塞狀態(tài)printf("--------------\n");printf("Send: str_len =%d, send_bytes= %d\n", str_len, send_bytes);vTaskDelay(3000);// taskYIELD();}
}void task2(void *pt)
{char rx_buffer[50]; // 存儲(chǔ)接收的數(shù)據(jù)int rec_bytes = 0;  // 接收到多少數(shù)據(jù)while (1){memset(rx_buffer, 0, sizeof(rx_buffer)); // 初始化buffer為0rec_bytes = xStreamBufferReceive(streamHandler, (void *)rx_buffer, sizeof(rx_buffer), portMAX_DELAY);printf("--------------\n");printf("Receive: rec_bytes=%d, rec_data: %s\n", rec_bytes, rx_buffer);}
}void task3(void *pt)
{size_t buf_space = 0; // stream buffer可用空間int min_space = 1000; // buffer的初始值while (1){buf_space = xStreamBufferSpacesAvailable(streamHandler);if (buf_space < min_space){min_space = buf_space;}// 通過觀察min_space的輸出值,當(dāng)接收數(shù)據(jù)可用正常接收時(shí),min_space的值 ,采用1000-min_space得到的結(jié)果就是需要設(shè)置的buffer空間大小printf("buf_space  = %d, min_space = %d\n", buf_space, min_space);vTaskDelay(3000);}
}void setup()
{Serial.begin(115200);xTaskCreatePinnedToCore(task1, "發(fā)送", 1024 * 5, NULL, 1, NULL, 1);xTaskCreatePinnedToCore(task2, "接收", 1024 * 5, NULL, 1, NULL, 1);xTaskCreatePinnedToCore(task3, "監(jiān)控", 1024 * 5, NULL, 1, NULL, 1);vTaskDelete(NULL); // 刪除當(dāng)前任務(wù)
}void loop()
{
}

運(yùn)行結(jié)果:

--------------
Send: str_len =16, send_bytes= 16
--------------
buf_space  = 200, min_space = 200
Receive: rec_bytes=16, rec_data: hello send i= 1 
--------------
Send: str_len =16, send_bytes= 16
buf_space  = 184, min_space = 184
--------------
Send: str_len =16, send_bytes= 16
buf_space  = 168, min_space = 168
--------------
Send: str_len =16, send_bytes= 16
buf_space  = 152, min_space = 152
--------------
Send: str_len =16, send_bytes= 16
--------------
Receive: rec_bytes=50, rec_data: hello send i= 2 hello send i= 3 hello send i= 4 he6	9?
--------------
Receive: rec_bytes=14, rec_data: llo send i= 5 
buf_space  = 186, min_space = 152

6.消息緩存

消息緩存與流媒體緩存的區(qū)別:

  1. 消息緩存一次只能接收一條完整的消息
  2. message buffer在接收buffer信息時(shí),如果定義的buffer空間大小,小于一條消息的長度,則無法正常接收一條完整的消息,返回值為0。而對(duì)于stream buffer,只要buffer中有數(shù)據(jù),就可以獲取對(duì)應(yīng)長度的數(shù)據(jù)。
// API
#include <freertos/message_buffer.h>  // 添加相關(guān)庫文件
MessageBufferHandle_t xMessageBufferCreate( size_t xBufferSizeBytes );  // 創(chuàng)建,參數(shù):buffer的大小// 接收信息
size_t xMessageBufferReceive(MessageBufferHandle_t xMessageBuffer,void *pvRxData,size_t xBufferLengthBytes, TickType_t xTicksToWait);// 發(fā)送信息
size_t xMessageBufferSend(MessageBufferHandle_t xMessageBuffer,const void *pvTxData,size_t xDataLengthBytes,TickType_t xTicksToWait);

示例程序:發(fā)送和接收三條消息

#include <Arduino.h>
#include <string>
#include <freertos/message_buffer.h> // 添加相關(guān)庫文件MessageBufferHandle_t messageHandler = xMessageBufferCreate(1000); // 創(chuàng)建消息緩存buffervoid task1(void *pt)
{char tx_buffer[50];int str_len = 0; // 字符串長度int i = 0;int send_bytes = 0; // 實(shí)際發(fā)送的數(shù)據(jù)// 創(chuàng)建三條消息for (int i = 0; i < 3; i++){str_len = sprintf(tx_buffer, "hello, nomber %d\n", i);send_bytes = xMessageBufferSend(messageHandler, (void *)tx_buffer, str_len, portMAX_DELAY);printf("--------------\n");printf("Send:i=%d, send_bytes = %d\n", i, send_bytes);}vTaskDelete(NULL);
}void task2(void *pt)
{char rx_buffer[200]; // 存儲(chǔ)接收的數(shù)據(jù)int rec_bytes = 0;   // 接收到多少數(shù)據(jù)vTaskDelay(3000);    // 先延時(shí)3s,讓消息發(fā)送到緩存區(qū)while (1){memset(rx_buffer, 0, sizeof(rx_buffer)); // 初始化buffer為0rec_bytes = xMessageBufferReceive(messageHandler, (void *)rx_buffer, sizeof(rx_buffer), portMAX_DELAY);printf("--------------\n");printf("Receive: rec_bytes=%d, rec_data: %s\n", rec_bytes, rx_buffer);}
}
void setup()
{Serial.begin(115200);xTaskCreatePinnedToCore(task1, "發(fā)送", 1024 * 5, NULL, 1, NULL, 1);xTaskCreatePinnedToCore(task2, "接收", 1024 * 5, NULL, 1, NULL, 1);vTaskDelete(NULL); // 刪除當(dāng)前任務(wù)
}void loop() {}

運(yùn)行結(jié)果:

--------------
Send:i=0, send_bytes = 16
--------------
Send:i=1, send_bytes = 16
--------------
Send:i=2, send_bytes = 16
--------------
Receive: rec_bytes=16, rec_data: hello, nomber 0--------------
Receive: rec_bytes=16, rec_data: hello, nomber 1--------------
Receive: rec_bytes=16, rec_data: hello, nomber 2
http://www.risenshineclean.com/news/58225.html

相關(guān)文章:

  • wordpress的favicon網(wǎng)站優(yōu)化排名操作
  • 丹陽網(wǎng)站設(shè)計(jì)網(wǎng)站市場推廣
  • seo短視頻網(wǎng)頁入口引流網(wǎng)站有哪些國家職業(yè)技能培訓(xùn)官網(wǎng)
  • 中文版網(wǎng)站建設(shè)費(fèi)用百度一下百度
  • 網(wǎng)站備案教程海外推廣代理商
  • 坊網(wǎng)站建設(shè)seo推廣公司有哪些
  • 創(chuàng)辦一個(gè)網(wǎng)站要多少錢營業(yè)推廣方式
  • 快速做網(wǎng)站的軟件網(wǎng)站規(guī)劃與設(shè)計(jì)
  • 青島市城鄉(xiāng)建設(shè)局網(wǎng)站西安網(wǎng)絡(luò)推廣公司網(wǎng)絡(luò)推廣
  • 企業(yè)網(wǎng)站優(yōu)化案例論壇優(yōu)化seo
  • 網(wǎng)站 建設(shè)運(yùn)行情況報(bào)告a5站長網(wǎng)
  • 可以上傳自己做的視頻的網(wǎng)站嗎公司怎么做網(wǎng)絡(luò)營銷
  • 自己給網(wǎng)站做優(yōu)化怎么做百度seo高級(jí)優(yōu)化
  • 網(wǎng)站建設(shè)比較好西安seo霸屏
  • 影樓網(wǎng)站源碼php免費(fèi)個(gè)人網(wǎng)站注冊(cè)
  • 山東定制網(wǎng)站建設(shè)公司鄭州網(wǎng)絡(luò)營銷學(xué)校
  • 淺談全球五金網(wǎng)電子商務(wù)網(wǎng)站建設(shè)營銷寶
  • 個(gè)人網(wǎng)站設(shè)計(jì)畢業(yè)設(shè)計(jì)論文百度關(guān)鍵詞推廣費(fèi)用
  • 石景山周邊網(wǎng)站建設(shè)免費(fèi)個(gè)人博客網(wǎng)站
  • wordpress 安裝證書seo引擎
  • 投資網(wǎng)站實(shí)名認(rèn)證可以做嗎深圳網(wǎng)站seo
  • 專門做棋牌廣告廣告的網(wǎng)站查網(wǎng)站排名
  • 做班級(jí)網(wǎng)站的實(shí)訓(xùn)報(bào)告網(wǎng)站怎么才能被百度收錄
  • 商務(wù)部市場體系建設(shè)司網(wǎng)站北京百度網(wǎng)站排名優(yōu)化
  • 以小說名字做網(wǎng)站的小說網(wǎng)關(guān)鍵詞搜索熱度
  • pc網(wǎng)站和app哪個(gè)容易做百度一下搜索
  • 國家企業(yè)信息系統(tǒng)官方seo優(yōu)化多久能上排名
  • 用eclipse編程做網(wǎng)站自動(dòng)app優(yōu)化最新版
  • 網(wǎng)站開發(fā)計(jì)劃書范文怎么創(chuàng)建一個(gè)網(wǎng)址
  • 保定定興網(wǎng)站建設(shè)安卓手機(jī)優(yōu)化神器