FreeRTOS study notes - binary semaphore

1 Introduction

    In embedded operating system, binary semaphore is an important means of synchronization between tasks and between tasks and interrupts. The binary semaphore of FreeRTOS is simple and easy to use. The following is a specific example to illustrate how to use the binary semaphore in FreeRTOS.
    【Related blog posts】

2. Special instructions    
    The usage of the binary semaphore is shown in Figure 1. The binary semaphore can be understood as a flag between a task and an interrupt or between two tasks. The flag is either "full" or "empty". The Send operation is equivalent to setting the flag to "full", and the Receive operation is related to setting the flag to "empty", and the operation synchronization between the task and the interrupt or between the two tasks is achieved through the send and receive operations.


Figure 1 Schematic diagram of the use of binary semaphores
    【Special Note】
    There is a little difference between the semaphore operation of V7.X version and V8.X
    The vSemaphoreCreateBinary function is used in the V7.X version. The initial value of the semaphore created by this function is "full", and the receive operation returns immediately. For the relevant code, see Supplementary Code 1 at the end of the article. From Supplementary Code 1, it can be found that the xSemaphoreGive function is called immediately after the semaphore is created, so that the semaphore changes from "empty" to "full".
    The xSemaphoreCreateBinary function is used in the V8.X version. The initial value of the semaphore created by this function is "null", and the receive operation will not return immediately.

3. Reference code
    The sample code has a serial port receiving buffer of 128 bytes, and stores the received characters in the buffer in the serial port interrupt. Once the carriage return line feed (\r\n) is received, the semaphore is set to " through xSemaphoreGiveFromISR. "full", use xSemaphoreTake in the print task to achieve synchronization in the interrupt receiving function, xSemaphoreTake suspends the task, and once the semaphore is "full", print the finished content through the serial port and clear the buffer.
    【Sample code】
/* 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 Tx@PA9的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 Rx@PA10的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 );
    等待信号量,等待时间为最大等待时间,如果信号量为“空”任务会处于挂起状态。

5.在中断中使用RTOS API注意点
    【FromISR】
    应使用xSemaphoreGiveFromISR,而不是 xSemaphoreGive。
    【中断优先级设置】
    串口中断的优先级应该低于configMAX_SYSCALL_INTERRUPT_PRIORITY(191,从另一个角度可以理解为11)设置的最高优先级,本文UART的响应优先级为configLIBRARY_KERNEL_INTERRUPT_PRIORITY(该宏的具体值为15,数值越大优先级越低)。
    【main.c】
/* 使能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);

    【FreeRTOSConfig.h】
/* This is the raw value as per the Cortex-M3 NVIC.  Values can be 255
(lowest) to 0 (1?) (highest). */
#define configKERNEL_INTERRUPT_PRIORITY     255
#define configMAX_SYSCALL_INTERRUPT_PRIORITY  191 /* equivalent to 0xb0, or priority 11. */
/* This is the value being used as per the ST library which permits 16
priority values, 0 to 15.  This must correspond to the
configKERNEL_INTERRUPT_PRIORITY setting.  Here 15 corresponds to the lowest
NVIC value of 255. */
#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 ) ); \
        } \
    }

6.参考资料



Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325162072&siteId=291194637