1.信號量簡介
信號量常用于控制對共享資源的訪問與任務同步。資源共享例如火車票售賣,所有用戶都可以進行買票和退票操作,對于所用用戶來說火車票就是共享資源,當賣出一張票信號量減一、有人退一張票信號量加一,這種案例就屬于計數型信號量。常用的信號量還有二值信號量。例如使用公共電話,同一時間只能一個人使用,此時電話就只有兩種狀態:使用或者未使用,若將電話這兩個狀態作為信號量的話則就是二值信號量。
信號量的另一應用場合就是任務同步。用于任務與任務間或任務與中斷間同步。在執行中斷服務函數時可以向任務發送信號量通知任務該事件發生了,在退出中斷服務函數以后任務調度器的調度下同步的任務就會執行。因為中斷服務函數需要快進快出,代碼簡潔,一般在中斷服務函數中設置標志位,然后在其它地方根據標志位來實現具體功能,在FreeRTOS中就可使用信號量來完成此功能。
2.二值信號量
二值信號量通常用于互斥訪問或同步,二值信號量和互斥信號量非常相似,但還是有細微差別,互斥信號量擁有優先級繼承機制,二值信號沒有。因此二值信量適合于同步(任務與任務、任務與中斷同步),而互斥信號量適合于簡單的互斥訪問。
二值信號量其實就是一個只有一個隊列項的隊列,這個特殊的隊列要么是滿的,要么是空的。使用二值信號量必須包含semphr.h頭文件。
函數名 | 功能 |
vSemaphoreCreateBinary() | 動態創建二值信號量,老版FreeRTOS創建二值信號量函數 |
xSemaphoreCreateBinary() | 動態創建二值信號量,新版FreeRTOS創建二值信號量函數 |
xSemaphoreCreateBinaryStatic() | 靜態創建二值信號量 |
3.二值信號量應用示例
此示例通過創建3個任務,start_task、LED0_task、Semaphore_task。通過任務start_task創建另外兩個任務。
#include "freeRTOS.h"
#include "task.h"http://創建相關頭文件
#include "queue.h"http://消息隊列相關頭文件
#include "semphr.h"http://信號量相關頭文件
#define START_TASK_PRIO 1 //任務優先級
#define START_STK_SIZE 128 //任務堆棧大小
TaskHandle_t StartTask_Handler; //任務句柄
void start_task(void *pvParameters);//任務函數
#define LED0_TASK_PRIO 2 //任務優先級,數字越大優先級越高
#define LED0_STK_SIZE 128 //任務堆棧大小
TaskHandle_t LED0Task_Handler; //任務句柄
void LED0_task(void); //任務函數
#define Semaphore_TASK_PRIO 2 //任務優先級
#define Semaphore_STK_SIZE 128 //任務堆棧大小
TaskHandle_t SemaphoreTask_Handler; //任務句柄
void Semaphore_task(void); //任務函數
主函數
int main()
{
Beep_Init();//蜂鳴器初始化
LED_Init();//LED初始化
KEY_Init();
Usart1_Init(115200);//串口1初始化
TIMx_Init(TIM2,72,20000);//20ms
TIMx_Init(TIM3,7200,5000);//500ms
TIM3->CR1|=1<<0;
/*創建任務*/
xTaskCreate((TaskFunction_t)start_task,//任務函數
(const char *)"start_task",//任務名稱
(uint16_t)START_STK_SIZE,//堆棧大小
NULL, //傳遞給任務函數的參數
(UBaseType_t)START_TASK_PRIO,//任務優先級
(TaskHandle_t *)&StartTask_Handler//任務句柄
);
vTaskStartScheduler(); //開啟任務調度
}
開始任務
SemaphoreHandle_t BinarySemaphore; //二值信號量句柄
/*開始任務函數*/
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //進入臨界區
/*創建二值信號量*/
BinarySemaphore=xSemaphoreCreateBinary();//創建二值信號量
//創建LED0任務
xTaskCreate( (TaskFunction_t )LED0_task,//任務函數
(const char *)"LED0_task",//任務名稱
(uint16_t)LED0_STK_SIZE,//堆棧大小
NULL, //傳遞給任務函數的參數
(UBaseType_t )LED0_TASK_PRIO,//任務優先級
(TaskHandle_t *)&LED0Task_Handler);//任務句柄
xTaskCreate( (TaskFunction_t )Semaphore_task,//任務函數
(const char *)"Semaphore_task",//任務名稱
(uint16_t )Semaphore_STK_SIZE,//堆棧大小
NULL, //傳遞給任務函數的參數
(UBaseType_t )Semaphore_TASK_PRIO,//任務優先級
(TaskHandle_t *)&SemaphoreTask_Handler);//任務句柄
vTaskDelete(StartTask_Handler); //刪除開始任務
taskEXIT_CRITICAL(); //退出臨界區
}
LED0任務,此任務1000ms進行LED翻轉
void LED0_task(void)
{
while(1)
{
LED1=!LED1;
Delay_Ms(1000);
}
}
Semaphore函數,此任務函數獲取信號量,處理串口1中斷接收數據。
void Semaphore_task(void)//任務函數
{
BaseType_t err=pdFALSE;
while(1)
{
if(BinarySemaphore!=NULL)
{
err=xSemaphoreTake(BinarySemaphore,portMAX_DELAY); //獲取信號量
if(err==pdTRUE)
{
usart1_rx_buff[usart1_cnt]='\0';
printf("rx=%s\r\n",usart1_rx_buff);
usart1_cnt=0;
}
}
}
}
串口中斷服務函數
#include "FreeRTOS.h" //FreeRTOS使用
#include "task.h"
#include "queue.h" //消息隊列
#include "semphr.h"http://信號量
u8 usart1_rx_buff[1024];//串口1接收數據緩沖區
u16 usart1_cnt=0;//保存數組下班
u8 usart1_flag;//接收完成標志符
extern SemaphoreHandle_t BinarySemaphore; //二值信號量句柄
void USART1_IRQHandler(void)
{
u8 c;
BaseType_t pxHigherPriorityTaskWoken;//保存任務是否需要切換的值
if(USART1->SR&1<<5)
{
c=USART1->DR;
if(usart1_flag==0)//判斷上一次數據是否處理完成
{
if(usart1_cnt<1024)
{
usart1_rx_buff[usart1_cnt++]=c;
TIM2->CNT=0;//清空計數器值
TIM2->CR1|=1<<0;//開啟定時
}
else usart1_flag=1;
}
}
if( usart1_flag && (BinarySemaphore!=NULL))
{
//釋放信號量
xSemaphoreGiveFromISR(BinarySemaphore,&pxHigherPriorityTaskWoken);
//如果需要的話,進行一次上下文切換
portYIELD_FROM_ISR(pxHigherPriorityTaskWoken);
usart1_flag=0;
}
}
定時器中斷服務函數
#include "FreeRTOS.h" //FreeRTOS使用
#include "semphr.h"
extern SemaphoreHandle_t BinarySemaphore; //二值信號量句柄//消息隊列句柄
void TIM2_IRQHandler(void)
{
BaseType_t pxHigherPriorityTaskWoken;//保存任務是否需要切換的值
if(TIM2->SR&1<<0)//判斷是否為更新中斷
{
TIM2->CR1&=~(1<<0);//關閉定時器2
usart1_flag=1;
if( usart1_flag && (BinarySemaphore!=NULL))
{
//釋放信號量
xSemaphoreGiveFromISR(BinarySemaphore,&pxHigherPriorityTaskWoken);
//如果需要的話,進行一次上下文切換
portYIELD_FROM_ISR(pxHigherPriorityTaskWoken);
usart1_flag=0;
}
}
TIM2->SR=0;//清除標志位
}
實現效果
-
操作系統
+關注
關注
37文章
6801瀏覽量
123283 -
STM32
+關注
關注
2270文章
10895瀏覽量
355729 -
RTOS
+關注
關注
22文章
811瀏覽量
119593 -
FreeRTOS
+關注
關注
12文章
484瀏覽量
62139 -
信號量
+關注
關注
0文章
53瀏覽量
8331
發布評論請先 登錄
相關推薦
評論