[FreeRTOS learning - binary semaphore] serial port interrupt gives semaphore - control LED flip

  • source of demand

Application scenario: the serial port receives a string such as "{led1}", and controls LED1 to flip.
Judging the flag will waste CPU resources. Using binary semaphores can solve this problem

  • Conventional Judgment Flag Bit Mode

The following code needs to loop detection in the taskWhether flag is 1, will waste MCU resources for no reason, even if USART_Task does nothing, it will be scheduled to run by MCU tasks
The following is the task function code

/* 
	FreeRTOS任务:串口数据处理
*/
void USART_Task(void *pvParameters)
{
    
    
	const TickType_t xDelay = 1000 / portTICK_PERIOD_MS;
	while(1)
	{
    
    
	    if(flag == 1)  //判断串口是否接收到一帧数据
	    {
    
    
	    	flag = 0;  //清零flag
	        /* 
	            串口数据处理
	            分割串口接收的字符串 
	            分割出{
    
    }之间的数据赋值给str
	        */
	        /* 判断串口接收到的数据是否为led1 */
	        if(strcmp((const char*)str, "led1") == 0)  //使用strcmp函数需要包含头文件 #include "string.h"
	        {
    
    
	        	printf("LED反转\r\n");
				GPIOC->ODR ^= (1<<13);
			}
		}
		vTaskDelay(xDelay);
	}
}

The following is the serial port code

//串口1中断服务程序
void USART1_IRQHandler(void)                	
{
    
    
	uint8_t r;
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)   //接收中断
	{
    
    
		if(USART1_RX_STA == 0)
		{
    
    
			r = USART1->DR;           //读取接收到的数据
			USART1_RX_BUF[LEN++] = r;
			if(USART1_RX_STA == 0 && r == '}')
			{
    
    
				USART1_RX_STA = 1;    //接受完毕
			}
		}
		USART_ClearFlag(USART1, USART_FLAG_RXNE);
	}
}

  • binary semaphore mode

A binary semaphore can be placed in a specialWhen an interrupt occurs, let the task unblock, which is equivalent to synchronizing the task with the interrupt
. In this way, a large amount of work can be processed in the synchronous task, and only a small part of the work is quickly processed in the interrupt service routine (ISR).

In layman's terms:

  1. Let the task judge in the loopWhether a flag satisfies the condition, if it is not satisfied, it will enter the blocking state and wait for the flag to be satisfied
  2. Let the flag meet the condition in another interrupt function, and thenWake up the task waiting for the flag bit, making the taskenter ready state

According to the programming requirements of the binary semaphore, the following functions (actually macros) need to be used

1. Create a binary semaphore function vSemaphoreCreateBinary( xSemaphore )

A semaphore xSemaphore needs to be passed as a parameter, and the semaphore must be defined in advance

#include "semphr.h"  //注意要包含信号量的头文件哦
xSemaphoreHandle xSemaphore;  //定义信号量(全局)

......
......
//在初始化函数中写入以下代码
/* 初始化二值信号量 */
vSemaphoreCreateBinary(xSemaphore);   //在初始化函数中初始化信号量
/* 经过测试,初始化二值信号量后,默认信号量是满状态,可以先Take一下,把信号量清空 */
xSemaphoreTake(xSemaphore, 0);

2. Get the semaphore function xSemaphoreTake( xSemaphore, xBlockTime )

(1) Obtain the semaphore xSemaphoreTake function in the task function
has two parameters, one is toacquired semaphore,one iswait timeout,
that is, the maximum waiting time when the semaphore is not acquired.

(2) The blocking time isSystem heartbeat period as unitYes, so the absolute time depends on the system heartbeat frequency.
The constant portTICK_RATE_MS (that is, portTICK_PERIOD_MS: RTOS system tick interrupt frequency)
can be used to convert the heartbeat time unit to millisecond time unit.

(3) ifSet xTicksToWait to portMAX_DELAY
, and set INCLUDE_vTaskSuspend to 1 in FreeRTOSConig.h , then the blocking wait will beno timeout limit.

3. Give the semaphore function xSemaphoreGive / xSemaphoreGiveFromISR ( xSemaphore )

(1) Give the semaphore usage in the interrupt functionxSemaphoreGiveFromISR function
You can use the xSemaphoreGive function outside the interrupt, and there is only one parameter, that is, the semaphore

(2) When a semaphore is given, the task waiting for the semaphore will be woken up

(3) If anyMore than one task is waiting on this semaphore,butOnly the first task in the waiting queue will be woken up

The following is the writing method of the task function (responsible for obtaining the semaphore, if it is not obtained, it will enter the blocking state and wait for wake-up)

/* 
	FreeRTOS任务:串口数据处理
*/
void USART_Task(void *pvParameters)
{
    
    
	const TickType_t xDelay = 1000 / portTICK_PERIOD_MS;
//	const TickType_t xBlockTime = 6000 / portTICK_PERIOD_MS;  //等待信号量时间
	BaseType_t result;  //获取信号量结果变量
	char str[10];       //解析串口数据缓冲区
	while(1)
	{
    
    
		/*
		 * portMAX_DELAY:表示没有超时限制,一直等,等到海枯石烂......
		 * 尝试获取信号量,如果没有获取到则进入阻塞态
		 * 如果设置了超时时间,超时时间内获取到了信号量,则返回pdPASS,如果没有获取到,则返回pdFALSE
		*/
		result = xSemaphoreTake(xSemaphore, portMAX_DELAY);   
		printf("***********信号量测试***********\r\n");
		if(result == pdPASS)
		{
    
    
			printf("获取到二值信号量!!!\r\n");
			
			//截取串口数据
			strncpy(str, (const char*)&USART1_RX_BUF[1], LEN-2);
			printf("接收到 : %s\r\n",str);
			
			//判断是否是led1命令
			if(strcmp((const char*)str, "led1") == 0)
			{
    
    
				printf("LED反转\r\n");
				GPIOC->ODR ^= (1<<13);
			}
			
			//清空串口配置变量
			memset(USART1_RX_BUF, 0, USART1_REC_LEN);
			USART1_RX_STA = 0;
			LEN = 0;
		}
		vTaskDelay(xDelay);
	}
}

The following is the serial port interrupt code (responsible for the semaphore)

//串口1中断服务程序
void USART1_IRQHandler(void)                	
{
    
    
	uint8_t r;
	BaseType_t xHigherPriorityTaskWoken;
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)   //接收中断
	{
    
    
		//如果没有接收完毕
		if(USART1_RX_STA == 0)
		{
    
    
			r = USART1->DR;           //读取接收到的数据
			USART1_RX_BUF[LEN++] = r;
			//如果接收到帧尾 '}'
			if(USART1_RX_STA == 0 && r == '}')
				USART1_RX_STA = 1;    //接收完毕
		}
		USART_ClearFlag(USART1, USART_FLAG_RXNE);   //清除接收标志位
	}
	
	if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)//空闲中断
	{
    
    
		printf("空闲中断,接收完毕\r\n");
		r = USART1->SR;  //串口空闲中断的中断标志只能通过先读SR寄存器,再读DR寄存器清除!
		r = USART1->DR;
		
		//给出二值信号量,唤醒串口数据处理任务
		xSemaphoreGiveFromISR(xSemaphore, &xHigherPriorityTaskWoken);  
		portYIELD_FROM_ISR(xHigherPriorityTaskWoken);  //如果需要的话进行一次任务切换
	}

Added: If the priority of the awakened blocked task is higher than the priority of the current task - force a task switch to ensure that the interrupt returns directly to the unblocked task (higher priority)

  • Experimental results

insert image description here
It can be seen that after the serial port assistant sends {led1}, the serial port data processing task is awakened and the task is executed, causing the LED to flip

Guess you like

Origin blog.csdn.net/HuangChen666/article/details/129690934