信号量常用于控制对共享资源的访问与任务同步。资源共享例如火车票售卖,所有用户都可以进行买票和退票操作,对于所用用户来说火车票就是共享资源,当卖出一张票信号量减一、有人退一张票信号量加一,这种案例就属于计数型信号量。常用的信号量还有二值信号量。例如使用公共电话,同一时间只能一个人使用,此时电话就只有两种状态:使用或者未使用,若将电话这两个状态作为信号量的话则就是二值信号量。
信号量的另一应用场合就是任务同步。用于任务与任务间或任务与中断间同步。在执行中断服务函数时可以向任务发送信号量通知任务该事件发生了,在退出中断服务函数以后任务调度器的调度下同步的任务就会执行。因为中断服务函数需要快进快出,代码简洁,一般在中断服务函数中设置标志位,然后在其它地方根据标志位来实现具体功能,在FreeRTOS中就可使用信号量来完成此功能。
二值信号量通常用于互斥访问或同步,二值信号量和互斥信号量非常相似,但还是有细微差别,互斥信号量拥有优先级继承机制,二值信号没有。因此二值信量适合于同步(任务与任务、任务与中断同步),而互斥信号量适合于简单的互斥访问。
二值信号量其实就是一个只有一个队列项的队列,这个特殊的队列要么是满的,要么是空的。使用二值信号量必须包含semphr.h头文件。
函数名 | 功能 |
vSemaphoreCreateBinary() | 动态创建二值信号量,老版FreeRTOS创建二值信号量函数 |
xSemaphoreCreateBinary() | 动态创建二值信号量,新版FreeRTOS创建二值信号量函数 |
xSemaphoreCreateBinaryStatic() | 静态创建二值信号量 |
此示例通过创建3个任务,start_task、LED0_task、Semaphore_task。通过任务start_task创建另外两个任务。
#include "freeRTOS.h"
#include "task.h"//创建相关头文件
#include "queue.h"//消息队列相关头文件
#include "semphr.h"//信号量相关头文件
#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"//信号量
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;//清除标志位
}
实现效果
全部0条评论
快来发表一下你的评论吧 !