天天看點

FreeRTOS-二值信号量

原文位址:http://blog.csdn.net/xukai871105/article/details/43153177

1.前言

    在嵌入式作業系統中二值型信号量是任務間、任務與中斷間同步的重要手段。FreeRTOS的二值型信号量簡單易用,下面結合一個具體例子說明FreeRTOS中的二值型信号量如何使用。

2.特别說明        二值型信号量的使用方法見圖1所示,二值型信号量可以了解為任務與中斷間或者兩個任務間的标志,該标志非“滿”即“空”。Send操作相當把該标志置“滿”,Receive操作相關與把該标志取"空",經過send和receive操作實作任務與中斷間或者兩任務的操作同步。

FreeRTOS-二值信号量
FreeRTOS-二值信号量

圖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 ) ); \
        } \
    }
           

繼續閱讀