在之前的教程中,我们介绍了带Arduino的FreeRTOS的基础知识以及FreeRTOS Arduino中的Queue内核对象。现在,在第三篇FreeRTOS教程中,我们将学习有关FreeRTOS及其高级API的更多信息,这可以使您更深入地了解多任务平台。
信号量和互斥量(互斥)是用于同步,资源管理和保护资源免遭破坏的内核对象。在本教程的前半部分,我们将了解Semaphore背后的思想,如何以及在何处使用它。在下半年,我们将继续使用Mutex。
什么是信号量?
在先前的教程中,我们讨论了任务优先级,并且还了解了高优先级任务会抢占低优先级任务,因此在执行高优先级任务时,低优先级任务可能会发生数据损坏尚未执行,并且数据不断从传感器传到该任务,这会导致数据丢失和整个应用程序故障。
因此,需要保护资源免受数据丢失的影响,而信号量在这里起着重要的作用。
信号量是一种信令机制,其中处于等待状态的任务由另一个任务发出信号以执行。换句话说,当task1完成工作时,它将显示一个标志或将标志增加1,然后另一个任务(task2)接收到该标志,表明它现在可以执行其工作。当task2完成工作后,该标志将减少1。
因此,从根本上讲,它是一种“授予”和“接受”机制,而信号量是一个整数变量,用于同步对资源的访问。
FreeRTOS中的信号量类型:
信号量有两种类型。
- 二进制信号量
- 计数信号量
1.二进制信号量:它具有两个整数值0和1。它与长度为1的队列有些相似。例如,我们有两个任务task1和task2。Task1将数据发送到task2,因此task2继续检查队列项是否为1,然后它可以读取数据,否则必须等待直到变为1。在获取数据之后,task2将队列减1并使其变为0,这意味着task1再次可以将数据发送到task2。
从上面的示例可以说,二进制信号量用于任务之间或任务与中断之间的同步。
2.计数信号量:它的值大于0,并且可以认为长度大于1的队列。该信号量用于计数事件。在这种使用情况下,事件处理程序将在事件发生时“给予”信号量(增加信号量计数值),而处理程序任务将在每次处理事件时“获取”信号量(减少信号量计数值)。 。
因此,计数值为发生的事件数与已处理的事件数之差。
现在,让我们看看如何在FreeRTOS代码中使用信号量。
如何在FreeRTOS中使用信号量?
FreeRTOS支持不同的API,用于创建信号量,获取信号量和提供信号量。
现在,同一内核对象可以有两种类型的API。如果必须从ISR提供信号量,则不能使用正常的信号量API。您应该使用受中断保护的API。
在本教程中,我们将使用二进制信号量,因为它易于理解和实现。由于此处使用了中断功能,因此您需要在ISR函数中使用受中断保护的API。当我们说用中断同步任务时,这意味着在ISR之后立即将任务置于“运行”状态。
创建信号量:
要使用任何内核对象,我们必须首先创建它。要创建二进制信号量,请使用vSemaphoreCreateBinary()。
此API不带任何参数,并返回SemaphoreHandle_t类型的变量。创建一个全局变量名称sema_v来存储信号量。
SemaphoreHandle_t sema_v; sema_v = xSemaphoreCreateBinary();
提供信号量:
为了提供信号量,有两种版本-一种用于中断,另一种用于常规任务。
- xSemaphoreGive():此API在创建信号量时仅接受一个参数,即上述信号量的变量名称,如sema_v。可以从要同步的任何常规任务中调用它。
- xSemaphoreGiveFromISR():这是xSemaphoreGive()的受中断保护的API版本。当需要同步ISR和正常任务时,应从ISR函数中使用xSemaphoreGiveFromISR()。
采取信号量:
要获取信号量,请使用API函数xSemaphoreTake()。该API带有两个参数。
xSemaphoreTake(SemaphoreHandle_t xSemaphore,TickType_t xTicksToWait);
xSemaphore:在本例中为sema_v的信号量的名称。
xTicksToWait:这是任务在“阻塞”状态下等待信号量变为可用的最长时间。在我们的项目中,我们将 xTicksToWait 设置为 portMAX_DELAY, 以使task_1在Blocked状态下无限期等待,直到sema_v可用。
现在,让我们使用这些API并编写代码来执行一些任务。
这里一个按钮和两个LED相连。该按钮将充当中断按钮,该按钮连接到Arduino Uno的引脚2。当按下此按钮时,将产生一个中断,并且连接到引脚8的LED指示灯将亮起,再次按下该指示灯将熄灭。
所以,当按钮被按下 xSemaphoreGiveFromISR() 将从ISR功能,并呼吁 xSemaphoreTake() 函数将TaskLED函数被调用。
为了使系统看起来具有多任务处理能力,请将其他LED与引脚7连接,该引脚将始终处于闪烁状态。
信号量代码说明
让我们通过打开Arduino IDE开始编写代码
1.首先,包括 Arduino_FreeRTOS.h 头文件。现在,如果使用任何内核对象(例如队列信号量),那么还必须为其包含头文件。
#include #include
2.声明一个SemaphoreHandle_t类型的变量来存储信号量的值。
SemaphoreHandle_t interruptSemaphore;
3.在void setup()中,使用xTaskCreate()API创建两个任务(TaskLED和TaskBlink),然后使用xSemaphoreCreateBinary()创建一个信号量。创建一个具有相同优先级的任务,稍后尝试使用此数字。另外,将引脚2配置为输入,并启用内部上拉电阻并连接中断引脚。最后,如下所示启动调度程序。
void setup(){ pinMode(2,INPUT_PULLUP); xTaskCreate(TaskLed,“ Led”,128,NULL,0,NULL); xTaskCreate(TaskBlink,“ LedBlink”,128,NULL,0,NULL); interruptSemaphore = xSemaphoreCreateBinary(); 如果(interruptSemaphore!= NULL){ attachInterrupt(digitalPinToInterrupt(2),debounceInterrupt,LOW); } }
4.现在,实现ISR功能。创建一个函数,并将其命名为与 attachInterrupt() 函数的第二个参数相同的名称。为了使中断正常工作,您需要使用毫秒或微米功能并通过调整反跳时间来消除按钮的反跳问题。从该函数中,如下所示调用 interruptHandler() 函数。
长时间的debouncing_time = 150; volatile无符号长last_micros; void debounceInterrupt(){ if((long)(micros()-last_micros)> = debouncing_time * 1000){ interruptHandler(); last_micros = micros(); } }
在 interruptHandler() 函数中,调用 xSemaphoreGiveFromISR() API。
void interruptHandler(){ xSemaphoreGiveFromISR(interruptSemaphore,NULL); }
此功能将向TaskLed提供信号灯以打开LED。
5.创建一个 TaskLed 函数,并在 while 循环内,调用xSemaphoreTake() API并检查信号量是否成功获取。如果等于pdPASS(即1),则如下图所示使LED切换。
void TaskLed(void * pvParameters) { (void)pvParameters; pinMode(8,输出); while(1){ if(xSemaphoreTake(interruptSemaphore,portMAX_DELAY)== pdPASS){ digitalWrite(8,!digitalRead(8)); } } }
6.此外,创建一个功能来使连接到引脚7的其他LED闪烁。
void TaskLed1(void * pvParameters) { (void)pvParameters; pinMode(7,输出); while(1){ digitalWrite(7,HIGH); vTaskDelay(200 / portTICK_PERIOD_MS); digitalWrite(7,LOW); vTaskDelay(200 / portTICK_PERIOD_MS); } }
7. void循环功能将保持为空。别忘了
无效循环(){}
就这样,完整的代码可以在本教程的结尾处找到。现在,上传此代码,并根据电路图将LED和按钮与Arduino UNO连接。
电路原理图
上载代码后,您将看到一个LED在200毫秒后闪烁,并且当按下按钮时,第二个LED将立即发光,如末尾的视频所示。
通过这种方式,信号量可以在带有Arduino的FreeRTOS中使用,它需要将数据从一个任务传递到另一个任务而不会造成任何损失。
现在,让我们看看什么是Mutex以及如何使用它FreeRTOS。
什么是互斥体?
如上文所述,信号量是一种信号传递机制,与互斥量不同,互斥量是一种锁定机制,与具有独立的增量和递减功能的信号量不同,但在互斥量中,该功能本身具有并给出。这是一种避免共享资源损坏的技术。
为了保护共享资源,可以为资源分配一个令牌卡(互斥体)。拥有此卡的人都可以访问其他资源。其他人应该等到卡归还。这样,只有一种资源可以访问任务,而其他资源则等待机会。
让我们借助示例来了解FreeRTOS中的Mutex。
这里我们有三个任务,一个任务是在LCD上打印数据,第二个任务是将LDR数据发送到LCD任务,最后一个任务是在LCD上发送温度数据。因此,这里有两个任务共享同一个资源,即LCD。如果LDR任务和温度任务同时发送数据,则数据之一可能已损坏或丢失。
因此,为了保护数据丢失,我们需要为任务1锁定LCD资源,直到它完成显示任务为止。然后LCD任务将解锁,然后task2可以执行其工作。
您可以在下图中观察Mutex和信号量的工作。
如何在FreeRTOS中使用Mutex?
互斥体也以与信号量相同的方式使用。首先,创建它,然后使用相应的API进行使用。
创建互斥体:
要创建Mutex,请使用 xSemaphoreCreateMutex()API 。顾名思义,互斥体是一种二进制信号量。它们用于不同的上下文和目的。二进制信号量用于同步任务,而Mutex用于保护共享资源。
该API不接受任何参数,并返回 SemaphoreHandle_t 类型的变量。如果无法创建互斥锁,则 xSemaphoreCreateMutex() 返回NULL。
SemaphoreHandle_t互斥体v; Muttex_V = xSemaphoreCreateMutex();
使用互斥体:
当任务想要访问资源时,它将通过使用 xSemaphoreTake() API获取Mutex 。它与二进制信号量相同。它还需要两个参数。
xSemaphore:在本例中为 Mutex_v 采取的互斥对象的名称。
xTicksToWait:这是任务在“阻止”状态下等待Mutex可用的最长时间。在我们的项目中,我们将 xTicksToWait 设置为 portMAX_DELAY, 以使task_1在Blocked状态下无限期等待,直到 mutex_v 可用为止。
提供互斥体:
访问共享资源后,任务应返回Mutex,以便其他任务可以访问它。 xSemaphoreGive() API用于将Mutex返回。
xSemaphoreGive()函数仅采用一个参数,即在本例中为mutex_v给出的互斥量。
使用上述API,让我们使用Arduino IDE在FreeRTOS代码中实现Mutex。
互斥代码解释
在这里,此部分的目标是使用串行监视器作为共享资源,并执行两个不同的任务来访问串行监视器以打印一些消息。
1.头文件将保持与信号灯相同。
#include #include
2.声明一个 SemaphoreHandle_t 类型的变量来存储Mutex的值。
SemaphoreHandle_t互斥体v;
3.在 void setup()中, 以9600波特率初始化串行监视器,并使用 xTaskCreate() API创建两个任务(Task1和Task2)。然后使用 xSemaphoreCreateMutex() 创建Mutex 。 创建具有相同优先级的任务,然后尝试使用此数字。
void setup(){ Serial.begin(9600); Muttex_V = xSemaphoreCreateMutex(); 如果(mutex_v == NULL){ Serial.println(“无法创建Mutex”); } xTaskCreate(Task1,“ Task 1”,128,NULL,1,NULL); xTaskCreate(Task2,“ Task 2”,128,NULL,1,NULL); }
4.现在,为Task1和Task2创建任务功能。在任务功能的 while 循环中,在串行监视器上打印消息之前,我们必须使用 xSemaphoreTake() 获取Mutex,然后打印消息,然后使用 xSemaphoreGive() 返回Mutex 。 然后延迟一下。
无效Task1(void * pvParameters){ while(1){ xSemaphoreTake(mutex_v,portMAX_DELAY); Serial.println(“任务1的嗨”); xSemaphoreGive(mutex_v); vTaskDelay(pdMS_TO_TICKS(1000)); } }
同样,以500ms的延迟实现Task2功能。
5.空循环() 将保持为空。
现在,将此代码上传到Arduino UNO并打开串行监视器。
您将看到正在从task1和task2打印的消息。
要测试Mutex的工作,只需注释 xSemaphoreGive(mutex_v);即可。 从任何任务。您会看到该程序挂在最后一条打印消息上。
这就是使用Arduino在FreeRTOS中实现Semaphore和Mutex的方式。有关Semaphore和Mutex的更多信息,您可以访问FreeRTOS的官方文档。
下面给出了信号量和静音的完整代码和视频。