原文位址:http://blog.csdn.net/xukai871105/article/details/43153177
1.前言
在嵌入式作業系統中二值型信号量是任務間、任務與中斷間同步的重要手段。FreeRTOS的二值型信号量簡單易用,下面結合一個具體例子說明FreeRTOS中的二值型信号量如何使用。
2.特别說明 二值型信号量的使用方法見圖1所示,二值型信号量可以了解為任務與中斷間或者兩個任務間的标志,該标志非“滿”即“空”。Send操作相當把該标志置“滿”,Receive操作相關與把該标志取"空",經過send和receive操作實作任務與中斷間或者兩任務的操作同步。
圖1 二值型信号量使用示意圖
【特别說明】 V7.X版本和V8.X的信号量操作存在少許差別 V7.X版本中使用 vSemaphoreCreateBinary函數,使用該函數建立的信号量初始值為“滿”,receive操作立刻有傳回。相關代碼見文章末尾補充代碼1,從補充代碼1中可以發現,建立信号量之後立刻調用xSemaphoreGive函數,使得信号量由“空”變“滿”。 V8.X版本中使用xSemaphoreCreateBinary函數,使用該函數建立的信号量初始值為“空”,receive操作不會立刻傳回。
3.參考代碼 示例代碼具有一個128位元組的序列槽接收緩沖區,在序列槽中斷中把接收到的字元存入緩沖區中,一旦接收到回車換行符(\r\n),便通過xSemaphoreGiveFromISR把信号量置“滿”,列印任務中使用xSemaphoreTake實作于中斷接收函數的同步, xSemaphoreTake把任務挂起,一旦查詢到信号量為“滿”,通過序列槽列印結束到的内容,并清空緩沖區。 【示例代碼】
/* Standard includes. */
#include <stdio.h>
#include <string.h>
/* Scheduler includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
/* Library includes. */
#include "stm32f10x.h"
#define LED0_ON() GPIO_SetBits(GPIOB,GPIO_Pin_5);
#define LED0_OFF() GPIO_ResetBits(GPIOB,GPIO_Pin_5);
static void Setup(void);
static void PrintTask(void *pvParameters);
void LedInit(void);
void UART1Init(void);
uint8_t RxBuffer[128];
__IO uint8_t RxCounter = 0;
SemaphoreHandle_t xSemaphore;
int main(void)
{
/* 初始化硬體平台 */
Setup();
/* 建立信号量 */
xSemaphore = xSemaphoreCreateBinary();
/* 建立Print任務 */
xTaskCreate(PrintTask, "Print Task", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY+4, NULL);
/* 啟動OS */
vTaskStartScheduler();
return 0;
}
void PrintTask(void *pvParameters)
{
for(;;)
{
if( xSemaphoreTake( xSemaphore, portMAX_DELAY ) == pdTRUE )
{
printf("receive:%s", RxBuffer);
memset(RxBuffer, 0x00, 128);
RxCounter = 0;
}
}
}
static void Setup( void )
{
LedInit();
UART1Init();
}
void LedInit( void )
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );
/*LED0 @ GPIOB.5*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init( GPIOB, &GPIO_InitStructure );
}
void UART1Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
/* 第1步:打開GPIO和USART時鐘 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
/* 第2步:将USART1 [email protected]的GPIO配置為推挽複用模式 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* 第3步:将USART1 [email protected]的GPIO配置為浮空輸入模式 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* 第4步:配置USART1參數
波特率 = 9600
資料長度 = 8
停止位 = 1
校驗位 = No
禁止硬體流控(即禁止RTS和CTS)
使能接收和發送
*/
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
/* 第5步:使能 USART1, 配置完畢 */
USART_Cmd(USART1, ENABLE);
/* 清除發送完成标志 */
USART_ClearFlag(USART1, USART_FLAG_TC);
/* 使能USART1發送中斷和接收中斷,并設定優先級 */
NVIC_InitTypeDef NVIC_InitStructure;
/* 設定USART1 中斷優先級 */
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = configLIBRARY_KERNEL_INTERRUPT_PRIORITY;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* 使能接收中斷 */
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
}
int fputc(int ch, FILE *f)
{
/* 寫一個位元組到USART1 */
USART_SendData(USART1, (uint8_t) ch);
/* 等待發送結束 */
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET)
{}
return ch;
}
void USART1_IRQHandler(void)
{
static BaseType_t xHigherPriorityTaskWoken;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
RxBuffer[RxCounter++] = USART_ReceiveData(USART1);
if (RxCounter > 2 && RxBuffer[RxCounter-2] == '\r' && RxBuffer[RxCounter-1] == '\n') {
// 在中斷中發送信号量
xSemaphoreGiveFromISR( xSemaphore, &xHigherPriorityTaskWoken );
}
}
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
4.簡單說明 SemaphoreHandle_t xSemaphore; 信号量句柄,二值型信号量、數值型信号量和互斥型信号量均使用 SemaphoreHandle_t類型聲明 xSemaphore = xSemaphoreCreateBinary(); 建立信号量,V8.X版本中新增加函數,建立信号量時初值為“空”。 xSemaphoreGiveFromISR( xSemaphore, &xHigherPriorityTaskWoken ); 在中斷中發送信号量,以FromISR結尾的函數具有保護功能,如果在任務中發送信号量可使用xSemaphoreGive。 xSemaphoreTake( xSemaphore, portMAX_DELAY ); 等待信号量,等待時間為最大等待時間,如果信号量為“空”任務會處于挂起狀态。
(1)二值信号量的建立 新函數:xSemaphoreCreateBinary() 信号量所需要的RAM是由FreeRTOS的記憶體管理部分動态配置設定的,此函數建立好的二值信号量預設是空的,剛建立好的二值信号量使用函數xSemaogoreTake()是擷取不到的
老版本的二值信号量建立完成後就會使用xSemaphoreGive()釋放二值信号量,新版本的函數在成功建立二值信号量以後不會立即釋放,也就是說新版本的函數建立二值信号量預設是無效的,而老版本的是有效的.
(2)二值信号量的釋放 xSemaphoreGive()任務級信号量釋放函數 xSemaphoreGiveFromISR()中斷級信号量釋放函數
(3)二值信号量的擷取 xSemaphoreTake()任務級擷取信号量的函數 Basetype_t xSemaphoreTake(SemaphoreHandle_t xsemaphore, TickType_t xBlockTime); //xsemaphore要擷取的信号量;xBlockTime阻塞時間
xSemaphoreTakeFromISR()中斷級擷取信号量的函數
5.在中斷中使用RTOS API注意點 【FromISR】 應使用xSemaphoreGiveFromISR,而不是 xSemaphoreGive。 【中斷優先級設定】 序列槽中斷的優先級應該低于configMAX_SYSCALL_INTERRUPT_PRIORITY(191,從另一個角度可以了解為11)設定的最高優先級,本文UART的響應優先級為configLIBRARY_KERNEL_INTERRUPT_PRIORITY(該宏的具體值為15,數值越大優先級越低)。 【main.c】 NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = configLIBRARY_KERNEL_INTERRUPT_PRIORITY; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure);
【FreeRTOSConfig.h】 #define configKERNEL_INTERRUPT_PRIORITY 255 #define configMAX_SYSCALL_INTERRUPT_PRIORITY 191 #define configLIBRARY_KERNEL_INTERRUPT_PRIORITY 15
鑒于Cortex M3的NVIC的特性,請詳細參考——【 在Cortex M3平台上運作FreeRTOS】
5.總結 【1】V8.X中使用 xSemaphoreCreateBinary() 建立的信号量初始值為"空"。 【2】中斷中發送信号量盡量使用XXXXFromISR。 【3】某中斷的優先級數值應大于configMAX_SYSCALL_INTERRUPT_PRIORITY。
【 補充代碼1 】——vSemaphoreCreateBinary函數實作代碼
#define vSemaphoreCreateBinary( xSemaphore ) \
{ \
( xSemaphore ) = xQueueGenericCreate( ( unsigned portBASE_TYPE ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE ); \
if( ( xSemaphore ) != NULL ) \
{ \
( void ) xSemaphoreGive( ( xSemaphore ) ); \
} \
}