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

常州城乡和住房建设厅网站怎么注销建设银行网站用户

常州城乡和住房建设厅网站,怎么注销建设银行网站用户,建立网站的作用,做微网站的第三方登录2023.5.11 FreeRTOS中文数据手册#xff1a;https://www.freertos.org/zh-cn-cmn-s/RTOS.html 感谢以下两位B站UP主的教程#xff1a;孤独的二进制、Michael_ee 1.Task 创建任务常用API#xff1a; 任务函数描述xTaskCreate()使用动态的方法创建一个任务xTaskCreatePinne…2023.5.11 FreeRTOS中文数据手册https://www.freertos.org/zh-cn-cmn-s/RTOS.html 感谢以下两位B站UP主的教程孤独的二进制、Michael_ee 1.Task 创建任务常用API 任务函数描述xTaskCreate()使用动态的方法创建一个任务xTaskCreatePinnedToCore指定任务的运行核心(最后一个参数)vTaskDelete(NULL)删除当前任务 BaseType_t xTaskCreate(TaskFunction_t pxTaskCode, // 任务函数名const char *const pcName, // 任务备注const configSTACK_DEPTH_TYPE usStackDepth, // 栈大小void *const pvParameters, // 传入的参数UBaseType_t uxPriority, // 任务优先级TaskHandle_t *const pxCreatedTask); // 任务句柄任务间传参 任务间传参可以使用多种方式常见的为 使用全局变量需要注意并发读写的问题当有两个任务及以上对全局变量进行读写时需要使用信号量或互斥量进行保护。使用队列需要注意队列的大小和数据类型的一致性不需要使用信号量或互斥量进行保护。队列的读写效率相比全局变量慢一些 使用全局变量进行传参时 传入参数传递的为指针且必须进行强制类型转换为空指针(void *)pt接收参数把传递过来的空指针进行强制类型转换转换为对应传输的类型指针 传递整数 #include Arduino.hint 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() {}输出结果为1 传递数组 #include Arduino.hint arr[] {1, 2, 3};void mytask(void *pt) {int *b (int *)pt;int len sizeof(arr) / sizeof(int); // 数组的长度,注意这里指针占4个字节要用原数组名Serial.println(len);for (int i 0; i len; i){Serial.print(*(b i)); // 输出数组元素Serial.print(,);}while (1){} }void setup() {Serial.begin(115200);// 数组名代表数组元素的首地址所以不需要xTaskCreatePinnedToCore(mytask, , 1024 * 3, (void *)arr, 1, NULL, 1);vTaskDelete(NULL); }void loop() {}传递结构体 #include Arduino.htypedef struct {int a;int b; } Mystruct;Mystruct test1 {1, 2};void mytask(void *pt) {Mystruct *test2 (Mystruct *)pt; // 强制类型转换为结构体指针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.hconst 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() {}任务的优先级 注意中断任务的优先级永远高于任何任务的优先级。 在ESP32中默认一共有25个优先级别最低为0最高为24。(可修改相关的配置函数进行修改优先级的数目超过25但是不建议级别越高越占内存)。 同优先级的任务FreeRTOS将采用循环调度算法来运行他们也就是交替执行同优先级的任务。每个任务执行一个时间片然后将CPU时间片分配给另一个任务。优先级别高的任务先被创建和运行。 任务的调度在FreeRTOS中vTaskDelay()和vTaskDelayUntil()函数可以暂停当前任务的执行等待一段时间后再继续执行。(让其他任务有机会执行)taskYIELD()函数立即将CPU时间片退让给同等级或更高优先级的任务如果没有其他任务等待执行则当前任务会立即继续执行。(简单的说就是让其他任务执行) 任务的挂起和恢复 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PQOP5J54-1684032842836)(images/1.png)] 任务的状态running、ready、blocked、suspended(挂起,暂停) running运行状态如果MCU只有一个内核那么在任何给定时间内只能有一个任务处于运行状态。ready准备状态(任务刚被创建时准备执行)不处于堵塞或挂起状态(没有获得CPU执行权限等待执行状态)因为同等级或更高优先级的任务正在执行blocked使用了vTaskDelay()或delay()函数suspended挂起状态挂起之后任务被恢复才能继续执行 // API: TaskHandle_t pxtask NULL; // 创建任务的句柄 xTaskCreatePinnedToCore(task1, , 1024 * 2, NULL, 1, pxtask, 1);vTaskSuspend(pxtask); // 挂起任务任务不再执行 vTaskResume(pxtask); // 恢复被挂起的任务继续执行 vTaskSuspendAll(); // 挂起所有函数挂起后不可以执行 vTaskResumeAll(); // 恢复所有挂起函数任务的堆栈设置和调试 创建任务时如果给任务分配的内存空间过小会导致程序不断重启。如果分配的内存空间过多会造成资源浪费。 // API: ESP.getHeapSize() // 本程序Heap最大尺寸空间总大小 ESP.getFreeHeap() // 当前Free Heap最大尺寸当前可用剩余空间大小 uxTaskGetStackHighWaterMark(taskHandle) // 计算当前任务剩余多少内存示例程序 TaskHandle_t taskHandle; // 创建任务的句柄 void setup() {Serial.begin(115200);xTaskCreatePinnedToCore(mytask, , 1024*3 , NULL, 1, taskHandle, 1);int waterMark uxTaskGetStackHighWaterMark(taskHandle);Serial.print(Task Free Memory: ); // 任务剩余空间Serial.print(waterMark);vTaskDelete(NULL); }vTaskDelay()和delay() 一个tick的时间是由FreeRTOS的时钟节拍周期和时钟频率决定的可以通过配置文件进行设置。默认情况下1 tick 1ms vTaskDelay()函数以系统时钟节拍(tick)为单位进行延时例如vTaskDelay(100)表示让任务暂停100个系统时钟节拍的时间。delay()函数是一个简单的延时函数它通常在不需要多任务处理和系统保护的应用中使用。使用后会后边的程序都会被延迟执行。 vTaskDelayUntil() vTaskDelayUntil函数比vTaskDelay函数定时精准。 // API TickType_t xLastWakeTime xTaskGetTickCount(); // 获取当前时间 const TickType_t xFrequency 3000; // 需要的时间间隔 vTaskDelayUntil(xLastWakeTime, xFrequency);while(1){vTaskDelayUntil(xLastWakeTime, xFrequency);// 下边为需要运行的函数 } // 示例程序 #include Arduino.h void mytask(void *pt) {TickType_t xLastWakeTime xTaskGetTickCount(); // 获取当前时间const TickType_t xFrequency 1000; // 需要的时间间隔while (1){vTaskDelayUntil(xLastWakeTime, xFrequency);Serial.println(xTaskGetTickCount()); // 输出当前时间进行验证} }void setup() {Serial.begin(115200);xTaskCreatePinnedToCore(mytask, , 1024 * 3, NULL, 1, NULL, 1);vTaskDelete(NULL); }void loop() {}2.Queue 2023.5.12 队列先入先出(FIFO,first in first out) 使用方法 创建队列长度尺寸(每个内存空间存储的数据大小)发送数据到队列中从队列中取数据 // portMAX_DELAY - 无限Block // TickType_t timeOut portMAX_DELAY; // 无限等待直到队列中有数据或者等待数据有空位置可以存储新数据 TickType_t timeOut 10; xStatus xQueueSend(Qhandle, i, timeOut); // 往队列里发送数据如果队列里内容是满的就等待10ms再次尝试发送API描述xQueueCreate()创建一个队列xQueueSend()往队列里写数据xQueueReceive从队列里读数据uxQueueMessagesWaiting(队列句柄)返回值为队列中参数的个数可用于接收数据时先判断一下队列里是否有数据 // 创建一个队列 QueueHandle_t Qhandle xQueueCreate(5, sizeof(int)); // 创建一个队列长度为5每个空间的大小为int队列存储int数据 #include Arduino.h// 创建队列的句柄 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(队列数据发送失败));}else{Serial.print(F(发送成功));Serial.println(i);}i;if (i 8)i 0;vTaskDelay(1000);} }void receive(void *pt) {int j 0; // 存储接收的队列数据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); // 发送数据xTaskCreatePinnedToCore(receive, , 1024 * 5, NULL, 1, NULL, 1); // 接收数据vTaskDelete(NULL); }void loop() {}运行结果 发送成功0 接收成功:0 发送成功1 接收成功:1 发送成功2 接收成功:2 发送成功3 接收成功:3队列传递结构体(重点) 跟上面的案例类似只是队列中每个元素类型为struct并且发送和接收的数据存储也要设置为struct类型 #include Arduino.h// 创建一个结构体 typedef struct {int a;int b; } Mystruct;// 创建队列的句柄 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(队列数据发送失败));}else{Serial.print(F(发送成功));struct1.a;Serial.println(struct1.a);}vTaskDelay(1000);} }void receive(void *pt) {Mystruct struct2; // 接收结构体数据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); // 发送数据xTaskCreatePinnedToCore(receive, , 1024 * 5, NULL, 1, NULL, 1); // 接收数据vTaskDelete(NULL); }void loop() {}运行结果按照FIFO的规则进行数据的发送和接收 发送成功2 接收成功:1 发送成功3 接收成功:2 发送成功4 接收成功:3队列传递大型数据时 例如传递字符串。传递大型数据时把指针对应的数据进行传递。 malloc()函数在使用malloc开辟空间时使用完一定要释放空间如果不释放会造成内存泄漏。malloc()函数返回的实际是一个无类型指针必须在其前面加上指针类型强制转换才可以使用。指针自身 (指针类型*)malloc(sizeof(指针类型)*数据数量) int *p NULL; p (int *)malloc(sizeof(int)*10);// 使用完之后采用free()进行释放 free(p); p NULL; // 让其重新指向NULL队列的多进单出多个任务写一个任务读 多个任务把数据写入一个队列一个任务进行读。设置写入的任务级别为同级别读任务的优先级别要比写任务高一级别。 不推荐这种方式容易造成系统工作混乱。最好的工作方式是一个队列只有一个写操作可以有多个读操作但是写操作只能有一个。 队列集合(常用) [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qgLZATfT-1684032842837)(images/2.png)] 多个队列但是每个队列只有一个写操作一个读操作(读取所有队列) 实现步骤 创建队列集合的句柄同时指定队列集合的总长度将已创建的队列添加到集合中创建一个句柄从队列集合中获取有数据的队列 QueueHandle_t Qhandle1 xQueueCreate(5, sizeof(int)); // 队列1 QueueHandle_t Qhandle2 xQueueCreate(5, sizeof(int)); // 队列2QueueSetHandle_t QueueSet xQueueCreateSet(10); // 队列集合句柄10为队列的总长度xQueueAddToSet(Qhandle1, QueueSet); // 把队列1加入到队列集合中 xQueueAddToSet(Qhandle2, QueueSet); // 把队列2加入到队列集合中QueueSetMemberHandle_t QueueData xQueueSelectFromSet(QueueSet, portMAX_DELAY); // 从队列集合中获取有数据的队列 QueueData为句柄示例程序这个程序编译不成功还没有解决 #include Arduino.hQueueHandle_t Qhandle1 xQueueCreate(5, sizeof(int)); // 队列1 QueueHandle_t Qhandle2 xQueueCreate(5, sizeof(int)); // 队列2QueueSetHandle_t QueueSet xQueueCreateSet(10); // 队列集合句柄xQueueAddToSet(Qhandle1, QueueSet); // 把队列1加入到队列集合中 xQueueAddToSet(Qhandle2, QueueSet); // 把队列2加入到队列集合中QueueSetMemberHandle_t QueueData xQueueSelectFromSet(QueueSet, portMAX_DELAY); // 从队列集合中获取有数据的队列void send1(void *pt) {int i 1; // 任务1要发送的数据while (1){if (xQueueSend(Qhandle1, i, portMAX_DELAY) ! pdPASS){Serial.println(发送失败);}else{Serial.println(发送成功);}vTaskDelay(1000);} }void send2(void *pt) {int i 2; // 任务2要发送的数据while (1){if (xQueueSend(Qhandle2, i, portMAX_DELAY) ! pdPASS){Serial.println(发送失败);}else{Serial.println(发送成功);}vTaskDelay(1000);} }void receive(void *pt) {int i; // 存储接收数据while (1){if (xQueueReceive(QueueData, i, portMAX_DELAY) ! pdPASS) // portMAX_DELAY一直等待直到队列中有数据{Serial.println(接收失败);}else{Serial.print(接收成功);Serial.println(i);}// vTaskDelay(1000); // 采用了portMAX_DELAY这里就不需要delay了} }void setup() {Serial.begin(9600);Serial.println(队列创建成功);xTaskCreatePinnedToCore(send1, , 1024 * 5, NULL, 1, NULL, 1); // 两个相同的优先级别轮流发送数据xTaskCreatePinnedToCore(send2, , 1024 * 5, NULL, 1, NULL, 1);xTaskCreatePinnedToCore(receive, , 1024 * 5, NULL, 2, NULL, 1); // 优先级别2只要队列中有数据就读 }void loop() { }队列邮箱(常用) [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CUCtItFd-1684032842837)(images/3.png)] 只有一个队列一个任务写多个任务读 // API QueueHandle_t Mailbox xQueueCreate(5, sizeof(int)); // 创建一个队列邮箱 xQueueOverwrite(); // 往队列中写数据 xQueuePeek(); // 从队列中读数据示例程序运行不成功 #include Arduino.hQueueHandle_t Mailbox xQueueCreate(5, sizeof(int));void send(void *pt) {int i 1; // 任务1要发送的数据while (1){if (xQueueOverwrite(Mailbox, i) ! pdPASS){Serial.println(发送失败);}else{Serial.println(发送成功);i;}vTaskDelay(1000);} }void receive1(void *pt) {int i; // 存储接收数据while (1){if (xQueuePeek(Mailbox, i, 1000) ! pdPASS) // portMAX_DELAY一直等待直到队列中有数据{Serial.println(接收失败);}else{Serial.print(接收成功);Serial.println(i);}} } void receive2(void *pt) {int i; // 存储接收数据while (1){if (xQueuePeek(Mailbox, i,1000) ! pdPASS) // portMAX_DELAY一直等待直到队列中有数据{Serial.println(接收失败);}else{Serial.print(接收成功);Serial.println(i);}} }void setup() {Serial.begin(9600);Serial.println(队列创建成功);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.信号量 信号量分类二进制信号量、计数信号量、互斥信号量。 信号量就像红绿灯一样控制车辆的通行。 信号量常用于控制对共享资源的访问和任务同步。信号量对于控制共享资源访问的场景相当于一个上锁机制代码只有获得这个锁的钥匙才能执行。 使用队列、信号量都可以实现互斥访问。 二进制信号量(常用) 二值信号量常用于互斥访问或同步二值信号量和互斥信号量非常类似但是互斥信号量拥有优先级继承机制二值信号量没有优先级继承。 二进制信号量可以用于一个任务控制另一个任务的运行与堵塞。二进制信号量表示只有两个值0和1二值信号量相当于长度为1的队列二进制信号量只有两种状态已触发和未触发类似于一个开关。当一个任务等待一个已经触发的二进制信号量是它会立即获得信号量如果信号量未被触发任务将被堵塞直到信号量被触发。可以避免资源冲突和死锁问题提高系统的可靠性和效率 // API SemaphoreHandle_t xHandler xSemaphoreCreateBinary(); // 创建二进制信号量 xSemaphoreGive(xHandler); // 释放信号量 xSemaphoreTake(xHanlder, timeout); // 在指定时间内获取信号量返回值为pdPASS, 或者pdFAIL示例程序1按键控制LED的亮灭(已验证) #include Arduino.hSemaphoreHandle_t xHandler xSemaphoreCreateBinary(); // 创建二进制信号量 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); // 两个相同的优先级别轮流发送数据xTaskCreatePinnedToCore(task2, , 1024 * 5, NULL, 1, NULL, 1); }void loop() { }示例2采用二进制信号量对任务进行管理对全局变量进行读写 该示例验证了使用二进制信号量可以很好的控制任务的执行顺序。 #include Arduino.h int a 0; SemaphoreHandle_t xHandler xSemaphoreCreateBinary(); // 创建二进制信号量void task1(void *pt) {while (1){xSemaphoreTake(xHandler, portMAX_DELAY); // 无限等待直到获取信号量for (int i 0; i 10; i){a;printf(mytask1 a %d\n, a);}xSemaphoreGive(xHandler); // 执行完之后需要再次释放信号量vTaskDelay(1000);} }void task2(void *pt) {while (1){xSemaphoreTake(xHandler, portMAX_DELAY); // 无限等待直到获取信号量for (int i 0; i 10; i){a;printf(mytask2 a %d\n, a);}xSemaphoreGive(xHandler); // 执行完之后需要再次释放信号量vTaskDelay(1000);} }void setup() {Serial.begin(115200);xSemaphoreGive(xHandler); // 首先释放一次信号量不然运行不了xTaskCreatePinnedToCore(task1, , 1024 * 5, NULL, 1, NULL, 1); // task1先获取信号量执行一次xTaskCreatePinnedToCore(task2, , 1024 * 5, NULL, 1, NULL, 1); // 然后task2获取信号量执行一次task11再执行 }void loop() { }运行结果 采用二进制信号量 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不采用二进制信号量运行结果 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计数信号量 与二进制不同的是计数信号量可以有更多的状态。 计数信号量相当于长度大于1的队列同二值信号量一样不需要关系队列中存储了什么数据只需要关心队列是否为空即可。例如在一个停车场中有10个车位车辆进入时车位被占用(计数器减1)车开出去后(计数器加1)为0时表示没有可用的车位。 // API uxSemaphoreGetCount( semphrHandle); // 获得计数型信号量的值 SemaphoreHandle_t semphrHandle xSemaphoreCreateCounting(10,0);// 创建计数型信号量参数1最大值参数2初始值 xSemaphoreGive(semphrHandle); // 释放信号量 xSemaphoreTake(semphrHandle); // 获取信号量使用场合事件计数、资源管理 共享资源的访问控制当多个任务需要共享同一个资源时可以使用计数信号量来控制资源的访问。每个任务需要访问资源时都需要获取一个计数信号量如果计数信号量的值为0则任务会被堵塞直到其他任务释放资源并增加计数信号量的值。这种方式可以避免资源冲突和死锁等问题。控制任务的执行顺序有些情况下需要控制任务的执行顺序例如任务A必须在任务B执行完成之后才能执行。可以使用计数信号量来实现这种控制。任务B执行完成后可以增加计数信号量的值任务A等待计数信号量的值为1时可以获取信号量并开始执行。 示例1模拟停车场的停车位 #include Arduino.h// 创建计数型信号量参数1最大值参数2初始值 SemaphoreHandle_t semphrHandle xSemaphoreCreateCounting(5, 5); // 初值为5代表初始有5个空车位void carintask(void *pt) {int emptySpace 0; // 空的停车位BaseType_t iResult;while (1){emptySpace uxSemaphoreGetCount(semphrHandle);printf(emptySpace %d\n, emptySpace);iResult xSemaphoreTake(semphrHandle, 0); // 获取信号量if (iResult pdPASS)printf(One car in\n);elseprintf(No Space\n);vTaskDelay(1000);} }void caroutTask(void *pt) {while (1){vTaskDelay(6000);xSemaphoreGive(semphrHandle); // 释放信号量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() { }互斥信号量(常用) 与二进制信号量十分相似。Mutex的工作原理可以想象成共享的资源被锁在一个箱子里只有一把钥匙有钥匙的任务才能对共享资源进行访问。 互斥量与二进制信号量的区别优先级继承在FreeRTOS中当一个任务持有一个互斥量时该任务对共享资源的访问是独占的其他试图获取该互斥量的任务将被堵塞。当一个优先级更高的任务试图获取已经被持有的互斥量时FreeRTOS会自动暂时提高持有互斥量任务的优先级别使其具有与试图获取互斥量的任务相同的优先级别。这样可以确保高优先级别的任务在获取共享资源时能够及时执行并避免低优先级别任务长时间持有共享资源。当持有互斥量的任务释放互斥量时其优先级别将恢复到原始值而不是保持被继承的优先级。这样可以确保任务在不需要共享资源时恢复其原始优先级以避免低优先级任务一直持有高优先级任务的优先级导致高优先级任务无法及时执行其他任务。也就是对于不同优先级别的任务采用mutex对共享资源进行保护如果任务均为同优先级别可以采用二进制信号量进行共享资源保护可以理解为互斥量是二进制信号量的升级版注意使用完立即释放钥匙 SemaphoreHandle_t mutexHandler xSemaphoreCreateMutex(); // 创建一个Mutex互斥量 xSemaphoreGive(mutexHandler); // 释放信号量 xSemaphoreTake(mutexHandler, timeout); // 在指定时间内获取信号量,返回值为pdPASS, 或者pdFAIL示例程序 // 对于互斥量通常创建3个任务 #include Arduino.hSemaphoreHandle_t mutexHandler xSemaphoreCreateMutex(); // 创建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); // 让低优先级别的任务有机会执行while (1){;} } void task3(void *pt) {printf(task3 begin\n);vTaskDelay(1000); // 让低优先级别的任务有机会执行while (1){xSemaphoreTake(mutexHandler, portMAX_DELAY); // 获取信号量printf(tsak3 take\n);for (size_t i 0; i 10; i){printf(task3 i %d\n, i);vTaskDelay(1000);}xSemaphoreGive(mutexHandler); // 释放信号量printf(tsak3 give\n);} }void setup() {Serial.begin(115200);xTaskCreatePinnedToCore(task1, , 1024 * 5, NULL, 1, NULL, 1); // task1获取到信号量时如果task3高优先级的任务也尝试获取该信号量会将task1的优先级暂时升级为3xTaskCreatePinnedToCore(task2, , 1024 * 5, NULL, 2, NULL, 1);xTaskCreatePinnedToCore(task3, , 1024 * 5, NULL, 3, NULL, 1); // 优先级别最高最先执行vTaskDelete(NULL); // 删除当前任务 }void loop() { }递归互斥量 允许同一任务在持有互斥量的情况下再次获取该互斥量而不会导致死锁。 递归互斥量可以用于需要对同一资源进行多层保护的情况例如嵌套调用的函数。 SemaphoreHandle_t mutexHandler xSemaphoreCreateRecursiveMutex(); // 创建递归互斥量 xSemaphoreTakeRecursive(mutexHandler); // 获取信号量 xSemaphoreGiveRecursive(mutexHandler); // 释放信号量注意使用递归互斥量时获取和释放的次数要相等以避免死锁的情况。 #include Arduino.hSemaphoreHandle_t mutexHandler xSemaphoreCreateRecursiveMutex(); // 创建mutex句柄void task1(void *pt) {while (1){printf(task1 begin\n);xSemaphoreTakeRecursive(mutexHandler, portMAX_DELAY); // 第一次取得信号量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); // 第二次取得信号量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); // 删除当前任务 }void loop() { }运行结果 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 take4.任务通知(重要) 从FreeRTOS V8.2.0版本新增了任务通知(task notify)这个功能可用使用任务通知来代替信号量、消息队列、事件标志组这些东西。使用任务通知可用提高系统的工作效率。 FreeRTOS的每个任务都有一个32位的通知值任务控制块中的成员变量ulNotifiedValue就是这个通知值。 使用任务通知可以控制任务的流向执行顺序。 任务通知虽然可用提高速度并且减少RAM的使用但是任务通知也是有使用限制的 FreeRTOS的任务通知只能有一个接收任务大多数的应用都是这种情况接收任务可以因为接收任务通知而进入阻塞态但是发送任务不会因为任务通知发送失败而阻塞。 通知同步 示例程序任务2通知任务1执行如果任务1没有接收到任务通知就一直处于阻塞状态。更详细的内容参考FreRTOS中文数据手册2.18 xTaskNotifyGive() #include Arduino.hstatic TaskHandle_t xTask1 NULL, xTask2 NULL; // 创建任务的句柄void task1(void *pt) {while (1){printf(task1 wait notification!\n);ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // 阻塞自身等待通知执行下面的程序printf(task1 got notification\n);} }void task2(void *pt) {while (1){vTaskDelay(1000); // 1s发送1次通知printf(task2 notify task1!\n);xTaskNotifyGive(xTask1); // 通知任务1解锁阻塞状态} }void setup() {Serial.begin(115200);xTaskCreatePinnedToCore(task1, , 1024 * 5, NULL, 1, xTask1, 1);xTaskCreatePinnedToCore(task2, , 1024 * 5, NULL, 1, xTask2, 1);vTaskDelete(NULL); // 删除当前任务 }void loop() {}运行结果 task1 wait notification! task2 notify task1! task1 got notification task1 wait notification!任务通知值 通过通知不同的值可以控制任务进入不同的处理流程。 BaseType_t xTaskNotifyWait(uint32_t ulBitsToClearOnEntry, // 在进入函数时清除所有函数的通知值uint32_t ulBitsToClearOnExit, // 在退出的时候清楚uint32_t *pulNotificationValue, // 取得当前任务通知的值TickType_t xTicksToWait); // 等待时间BaseType_t xTaskNotify(TaskHandle_t xTaskToNotify, // 任务通知的句柄uint32_t ulValue, // 需要发送的任务通知值eNotifyAction eAction); // 常用eSetValueWithOverwrite 示例程序通过设置不同的通知值执行不同的事件参考数据手册2.14 xTaskNotify() #include Arduino.hstatic TaskHandle_t xTask1 NULL, xTask2 NULL; // 创建任务的句柄void task1(void *pt) {uint32_t ulNotifiedValue;while (1){printf(task1 wait notification!\n);xTaskNotifyWait(0x00, /* Dont 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发送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); // 删除当前任务 }void loop() {}运行结果 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!任务通知取代信号量 使用直接任务通知取代二进制信号量由于没有了二进制信号量这个中间媒介不仅节省了内存而且速度也会快45%。 注意设置任务的阻塞和通知顺序要注意先后顺序。 直接任务通知不能取代二进制信号量的场景 直接任务通知相当于严格指定任务的执行顺序而采用二进制信号量可以做到在等待事件内各任务随机抢占CPU执行权因此当有2个及以上需要接收信号量时最好采用二进制信号量而不是直接任务通知 #include Arduino.hstatic TaskHandle_t xTask1 NULL, xTask2 NULL; // 创建任务的句柄void task1(void *pt) {while (1){xTaskNotifyGive(xTask2); // 通知任务2执行/* 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); // 通知任务1执行} }void setup() {Serial.begin(115200);xTaskCreatePinnedToCore(task1, , 1024 * 5, NULL, 1, xTask1, 1);xTaskCreatePinnedToCore(task2, , 1024 * 5, NULL, 1, xTask2, 1);vTaskDelete(NULL); // 删除当前任务 }void loop() {}直接任务当作邮箱 可以通过设置任务通知值的方式达到想要的效果。 5.流媒体缓存 流媒体音频、视频 适合一个任务读一个任务写不适合多任务读写与队列的不同stream buffer读写的大小没有限制而队列是预先设置好的固定值 #include freertos/stream_buffer.h // 首先添加流媒体相关的头文件// 创建streambuffer StreamBufferHandle_t xStreamBufferCreate(size_t xBufferSizeBytes, // 参数1buffer的大小size_t xTriggerLevelBytes); // 参数2最小一帧数据的大小例如一个声音最少8个字节则设置为8,// Stream Buffer内数据超过这个数值才会被读取否则一直接收进行存储达到这个值进行一次读取// 发送流媒体数据 size_t xStreamBufferSend(StreamBufferHandle_t xStreamBuffer, // 句柄const void *pvTxData, // 需要发送数据的指针需要进行强制类型转换size_t xDataLengthBytes, // 需要发送数据的长度可以用sizeof()计算TickType_t xTicksToWait); // 堵塞时间// 接收流媒体数据 size_t xStreamBufferReceive(StreamBufferHandle_t xStreamBuffer,void *pvRxData, // 接收数据需要进行强制类型转换size_t xBufferLengthBytes, // 存储接收数据的buffer长度TickType_t xTicksToWait);注意创建stream buffer时xTriggerLevelBytes的设置非常重要。在接收函数中如果buffer中有数据首先会将能够接收的数据接收下来然后堵塞当前的任务直到buffer中的数据大于xTriggerLevelBytes 确定stream buffer的大小 在创建stream buffer时如果创建的buffer太大会造成资源浪费太小系统工作会非常不稳定。 使用stream buffer时通常创建3个任务任务1发送数据任务2接收数据任务3监控stream buffer的空间大小 // API size_t xStreamBufferBytesAvailable( StreamBufferHandle_t xStreamBuffer ); // stream buffer已使用字节 size_t xStreamBufferSpacesAvailable( StreamBufferHandle_t xStreamBuffer ); // sream buffer可用空间字节示例 #include Arduino.h #include string #include freertos/stream_buffer.h// 参数1stream buffer的总大小参数2每帧数据的大小 // 参数2作用接收数据时如果小于这个值将处于堵塞状态直到接收buffer里存储的字节大于这个值才会接收一次 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; // 实际发送的数据while (1){i;str_len sprintf(tx_buffer, hello send i %d , i); // 要发送的数据send_bytes xStreamBufferSend(streamHandler, (void *)tx_buffer, str_len, portMAX_DELAY); // 没有发送成功就一直处于堵塞状态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]; // 存储接收的数据int rec_bytes 0; // 接收到多少数据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的输出值当接收数据可用正常接收时min_space的值 采用1000-min_space得到的结果就是需要设置的buffer空间大小printf(buf_space %d, min_space %d\n, buf_space, min_space);vTaskDelay(3000);} }void setup() {Serial.begin(115200);xTaskCreatePinnedToCore(task1, 发送, 1024 * 5, NULL, 1, NULL, 1);xTaskCreatePinnedToCore(task2, 接收, 1024 * 5, NULL, 1, NULL, 1);xTaskCreatePinnedToCore(task3, 监控, 1024 * 5, NULL, 1, NULL, 1);vTaskDelete(NULL); // 删除当前任务 }void loop() { } 运行结果 -------------- Send: str_len 16, send_bytes 16 -------------- buf_space 200, min_space 200 Receive: rec_bytes16, 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_bytes50, rec_data: hello send i 2 hello send i 3 hello send i 4 he6 9ô -------------- Receive: rec_bytes14, rec_data: llo send i 5 buf_space 186, min_space 1526.消息缓存 消息缓存与流媒体缓存的区别 消息缓存一次只能接收一条完整的消息message buffer在接收buffer信息时如果定义的buffer空间大小小于一条消息的长度则无法正常接收一条完整的消息返回值为0。而对于stream buffer只要buffer中有数据就可以获取对应长度的数据。 // API #include freertos/message_buffer.h // 添加相关库文件 MessageBufferHandle_t xMessageBufferCreate( size_t xBufferSizeBytes ); // 创建参数buffer的大小// 接收信息 size_t xMessageBufferReceive(MessageBufferHandle_t xMessageBuffer,void *pvRxData,size_t xBufferLengthBytes, TickType_t xTicksToWait);// 发送信息 size_t xMessageBufferSend(MessageBufferHandle_t xMessageBuffer,const void *pvTxData,size_t xDataLengthBytes,TickType_t xTicksToWait);示例程序发送和接收三条消息 #include Arduino.h #include string #include freertos/message_buffer.h // 添加相关库文件MessageBufferHandle_t messageHandler xMessageBufferCreate(1000); // 创建消息缓存buffervoid task1(void *pt) {char tx_buffer[50];int str_len 0; // 字符串长度int i 0;int send_bytes 0; // 实际发送的数据// 创建三条消息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]; // 存储接收的数据int rec_bytes 0; // 接收到多少数据vTaskDelay(3000); // 先延时3s让消息发送到缓存区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, 发送, 1024 * 5, NULL, 1, NULL, 1);xTaskCreatePinnedToCore(task2, 接收, 1024 * 5, NULL, 1, NULL, 1);vTaskDelete(NULL); // 删除当前任务 }void loop() {}运行结果 -------------- Send:i0, send_bytes 16 -------------- Send:i1, send_bytes 16 -------------- Send:i2, send_bytes 16 -------------- Receive: rec_bytes16, rec_data: hello, nomber 0-------------- Receive: rec_bytes16, rec_data: hello, nomber 1-------------- Receive: rec_bytes16, rec_data: hello, nomber 2
http://www.hkea.cn/news/14485119/

相关文章:

  • 个人网站 费用做网站的得花多少钱
  • 网站需要什么服务器潍坊seo推广
  • 个人网站前置审批项重庆免费发布信息网站
  • 企业网站事例wordpress小程序音频插件
  • 做网站的编程语言网站未来发展规划
  • 手机网站整站源码下载千图网素材免费
  • 快速建设一个网站免费的商城网站
  • 沃尔玛官方网站查余额手机网站翻页底时自动链接
  • phpcms做双语网站微商代理
  • 网站不备案会怎样常州市经开区建设局网站
  • 愿意做cps的网站建设科技处网站
  • 开源的网站管理系统广州专业网站设计定制
  • 潍坊潍城姓王的做网站长沙建网站设计
  • 做废品回收在什么网站推广龙炎电商小程序
  • 微信里的商家链接网站怎么做的wordpress分类目录没有内容
  • 建立公司网站步骤云工厂网站建设
  • 对网站建设的意见建议图书馆网站建设好处
  • 江苏军民融合网站建设进入淘宝网官网首页 淘宝
  • 做网站vpn多大内存外贸行业网站推广
  • 做外贸没有网站需要注意什么问题手机怎么浏览国外网站
  • 可商用的图片素材网站网站移动适配
  • 阐述电子商务网站的建设要求做响应网站的素材网站有哪些
  • 女性手表网站网上做环评立项的网站是哪个
  • 空间服务 网站被黑万维网如何建设网站
  • 网站设计所用到的技术app推广拉新一手渠道代理
  • 长沙机械网站建设南昌广告公司
  • 广州网站app制作公司微信公众号官网登录入口手机版
  • 神州顺利办深一做网站crm系统排名
  • php 移动网站开发举例一个成功的网络营销案例
  • 描述建设一个网站的基本步骤新闻聚合网站开发