FreeRTOS semaphore (1) ------ binary semaphore


1. Introduction to Semaphore

Semaphores are often used to control access to shared resources and to synchronize tasks. To give a very common example, there are 100 parking spaces in a certain parking lot, and everyone can use these 100 parking spaces. For everyone, these 100 parking spaces are shared resources. Assuming that the parking lot is operating normally now, if you want to park your car in this parking lot, you must first check how many cars are parked now? Is there any parking space? The current parking number is a semaphore, and the specific parking number is the semaphore value. When the value reaches 100, it means that the parking lot is full. When the parking lot is full, you can wait for a while to see if there are other cars driving out of the parking lot. When a car leaves the parking lot, the number of parking lots will be reduced by one, that is to say, the semaphore will be reduced by one. You can park the car in, and the parking number will increase by one after you park the car in, that is, the semaphore will increase by one. This is a typical use

A case of semaphore for shared resource management. In this case, a counting semaphore is used. Let’s look at another case: using a public phone. We know that only one person can use the phone at a time. At this time, the public phone can only have two states: used or unused. If these two states of the phone are used as semaphores, Then this is a binary semaphore. The scenario where the semaphore is used to control access to shared resources is equivalent to a locking mechanism, and the code can only be executed if the key of the lock is obtained.

The above describes the use of semaphores in shared resource access. Another important application of semaphores is task synchronization, which is used for synchronization between tasks or between interrupts and tasks. When the interrupt service function is executed, the task can be notified that the expected event has occurred by sending a semaphore to the task. After exiting the interrupt service function, the task that is synchronized under the scheduling of the task scheduler will be executed. When writing the interrupt service function, we all know that we must fast in and fast out, and we cannot put too much code in the interrupt service function, otherwise it will affect the real-time performance of the interrupt. When writing the interrupt service function on bare metal, it is generally just to mark the interrupt service function, and then do specific processing in other places according to the mark. When using the RTOS system, we can use the semaphore to complete this function. When the interrupt occurs, the semaphore is released, and the interrupt service function does not do specific processing. The specific processing process is made into a task. This task will obtain the semaphore. If the semaphore is obtained, it means that an interrupt has occurred, and then the corresponding processing will be completed. The advantage of this is that the interrupt execution time is very short. This example is the use of semaphores to complete synchronization between interrupts and tasks. Of course, semaphores can also be used to complete synchronization between tasks.

There are some other special types of semaphores in FreeRTOS, such as mutex semaphores and recursive mutex semaphores, which will be explained when they are encountered. The knowledge about semaphores is explained in detail on the official website of FreeRTOS, including binary semaphores, counting semaphores, mutual exclusion semaphores and recursive mutual exclusion semaphores. The following we will explain these involve theoretical knowledge They are all translated from FreeRTOS official information. If you are interested, you can go to the official website to see the original English information.

Two, binary semaphore

1. Introduction to binary semaphores

Binary semaphores are usually used for mutual exclusion access or synchronization. Binary semaphores and mutex semaphores are very similar, but there are still some subtle differences. Mutual exclusion semaphores have a priority inheritance mechanism, and binary semaphores have no priority. inherit. Therefore, binary signals are more suitable for synchronization (synchronization between tasks and tasks or between tasks and interrupts), while mutual exclusion semaphores are suitable for simple mutual exclusion access. The content of mutual exclusion semaphores will be specifically explained later, in this section Only the application of binary semaphores in synchronization is explained.

Like the queue, the semaphore API function allows setting a blocking time, which is the maximum number of clock ticks for the task to enter the blocking state due to the invalid semaphore when the task acquires the semaphore. If multiple tasks are blocked on the same semaphore at the same time, the task with the highest priority will get the semaphore first, so that the high-priority task will unblock the state when the semaphore is valid.

A binary semaphore is actually a queue with only one queue item. This special queue is either full or empty. Isn't this just binary? Tasks and interrupts using this special queue don't care what messages are stored in the queue, they only need to know whether the queue is full or empty. This mechanism can be used to complete the synchronization between tasks and interrupts.

In practical applications, a task is usually used to process a certain peripheral of the MCU. For example, in network applications, the easiest way is to use a task to poll the ETH of the MCU (network-related peripherals, such as the Ethernet of STM32 Network MAC) Whether there is data in the peripheral device, and when there is data, the network data is processed. This way of using polling is a waste of CPU resources, and it also prevents other tasks from running. The most ideal method is to enter the blocking state when there is no network data, let the CPU to other tasks, and execute the network task only when there is data. Now use the binary semaphore to realize such a function. The task judges whether there is network data by obtaining the semaphore. STM32's MAC-specific DMA interrupt, which can be used to determine whether data is received) is notified by releasing the semaphore that the Ethernet peripheral has received network data, and the network task can be extracted and processed. The network task just keeps getting the binary semaphore, it won't release the semaphore, but the interrupt service function keeps releasing the semaphore, it won't get the semaphore. The function xSemaphoreGiveFromISR() can be used to send the semaphore in the interrupt service function, or the task notification function can be used to replace the binary semaphore, and the speed of using the task notification is faster and the amount of code is less.

In the mechanism of using binary semaphore to complete interrupt and task synchronization, task priority ensures that peripherals can be processed in a timely manner, which is equivalent to postponing the interrupt processing process. It is also possible to use a queue instead of a binary semaphore, obtain relevant data in the interrupt service function of the peripheral event, and send the relevant data to the task through the queue. If the queue is invalid, the task will enter the blocking state until there is data in the queue, and the task will start related processing after receiving the data. The following steps demonstrate how a binary semaphore works.

1. The binary semaphore is invalid
insert image description here
In the figure above, the task Task obtains the semaphore through the function xSemaphoreTake(), but the binary semaphore is invalid at this time
, so the task enters the blocking state.

2. Interrupt release semaphore

insert image description here
At this time, an interrupt occurs, and the semaphore is released through the function xSemaphoreGiveFromISR() in the interrupt service function, so the semaphore becomes valid.

3. The task acquires the semaphore successfully
insert image description here
Since the semaphore is already valid, the task Task acquires the semaphore successfully, the task is released from the blocking state, and starts to execute the relevant
processing.

4. The task enters the blocking state again
. Since the task function is generally a large loop, the function xSemaphoreTake() will be called again to obtain the semaphore after the task completes the relevant processing. After executing the third step, the binary semaphore has become invalid, so the task will enter the blocking state again, the same as the first step, until the interrupt occurs again and the function xSemaphoreGiveFromISR() is called to release the semaphore.

2. Create a binary semaphore

Like a queue, if you want to use a binary semaphore, you must first create a binary semaphore. The binary semaphore creation function is shown in the following table:
insert image description here

①Function vSemaphoreCreateBinary ()

This function is the function of creating a binary semaphore in the old version of FreeRTOS. The new version is no longer used. The new version of FreeRTOS uses xSemaphoreCreateBinary() to replace this function. This function is retained here to be compatible with those based on the old version of FreeRTOS. Do the application layer code. This function is a macro, and the specific creation process is completed by the function xQueueGenericCreate(), which is defined in the file semphr.h as follows:

void vSemaphoreCreateBinary( SemaphoreHandle_t xSemaphore )

Parameters:
xSemaphore: save the successfully created binary semaphore handle.

Return value:
NULL: failed to create binary semaphore.
Other values: The binary semaphore was created successfully.

②Function xSemaphoreCreateBinary()

This function is a new version of vSemaphoreCreateBinary(), which is uniformly used in the new version of FreeRTOS to create binary semaphores. If this function is used to create a binary semaphore, the RAM required by the semaphore is dynamically allocated by the memory management part of FreeRTOS. The binary semaphore created by this function is empty by default, which means that the newly created binary semaphore cannot be obtained using the function xSemaphoreTake(). This function is also a macro. The specific creation process is performed by the function xQueueGenericCreate( ) to complete, the function prototype is as follows:

SemaphoreHandle_t xSemaphoreCreateBinary( void )

Parameters:
None .

Return value:
NULL: failed to create binary semaphore.
Other values: Handle to the successfully created binary semaphore.

③Function xSemaphoreCreateBinaryStatic()

This function also creates a binary semaphore, but the RAM required by the semaphore needs to be
allocated by the user if this function is used to create a binary semaphore. This function is a macro, and the specific creation process is completed through the function xQueueGenericCreateStatic() , the function prototype is as follows:

SemaphoreHandle_t xSemaphoreCreateBinaryStatic( StaticSemaphore_t *pxSemaphoreBuffer )

Parameters:
pxSemaphoreBuffer: This parameter points to a variable of type StaticSemaphore_t, which is used to save the semaphore structure.

Return value:
NULL: failed to create binary semaphore.
Other values: Handle to a successfully created binary semaphore.

3. Analysis of binary semaphore creation process

Let’s analyze these two dynamic creation functions here. The static creation functions are similar to the dynamic ones, so we won’t analyze them here.
First look at the old version of the binary semaphore dynamic creation function vSemaphoreCreateBinary(), the function code is as follows:

#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
#define vSemaphoreCreateBinary( xSemaphore ) \
{
      
       \
	( xSemaphore ) = xQueueGenericCreate( ( UBaseType_t ) 1, \ (1)
	semSEMAPHORE_QUEUE_ITEM_LENGTH, \
	queueQUEUE_TYPE_BINARY_SEMAPHORE ); \
	if( ( xSemaphore ) != NULL ) \
	{
    
     \
		( void ) xSemaphoreGive( ( xSemaphore ) ); \ (2)
	} \
}
#endif

(1) As mentioned above, binary semaphores are implemented on the basis of queues, so creating binary semaphores is
the process of creating queues. Here the function xQueueGenericCreate() is used to create a queue with a queue length of 1, a queue item length of 0, and a queue type of queueQUEUE_TYPE_BINARY_SEMAPHORE, which is a binary semaphore.

(2) When the binary semaphore is successfully created, immediately call the function xSemaphoreGive() to release the binary semaphore, and the newly
created binary semaphore is valid at this time. Let's take a look at the new version of the binary semaphore creation function xSemaphoreCreateBinary(), the function code is as follows:

#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
#define xSemaphoreCreateBinary() \
xQueueGenericCreate( ( UBaseType_t ) 1, \
semSEMAPHORE_QUEUE_ITEM_LENGTH, \
 queueQUEUE_TYPE_BINARY_SEMAPHORE ) \
#endif

It can be seen that the new version of the binary semaphore creation function also uses the function xQueueGenericCreate() to create a queue
of type queueQUEUE_TYPE_BINARY_SEMAPHORE, length 1, and queue item length 0. This step is the same as the old version of the binary semaphore creation function, the only difference is that the new version of the function will not immediately call the function xSemaphoreGive() to release the binary semaphore after successfully creating the binary semaphore. That is to say, the binary semaphore created by the new version of the function is invalid by default, while the old version is valid.

Please note that the created queue is a queue without a storage area. As mentioned above, whether the queue is empty is used to represent the binary
semaphore, and whether the queue is empty can be judged by the member variable uxMessagesWaiting of the queue structure.

4. Release the semaphore

There are two functions to release the semaphore, as shown in the above table:
insert image description here
Like the queue, the release of the semaphore is also divided into task level and interrupt level. besides! Regardless of binary semaphore, counting
semaphore or mutex semaphore, they all use the functions in the above table to release the semaphore, and the recursive mutex semaphore has a dedicated release function.

①Function xSemaphoreGive()

This function is used to release a binary semaphore, counting semaphore or mutual exclusion semaphore. This function is a macro. The actual
process of releasing the semaphore is completed by the function xQueueGenericSend(). The function prototype is as follows:

BaseType_t xSemaphoreGive( xSemaphore )

Parameters:
xSemaphore: The semaphore handle to be released.

Return value:
pdPASS: Release the semaphore successfully.
errQUEUE_FULL: Failed to release the semaphore.

Let's take a look at the specific content of the function xSemaphoreGive(). This function is defined as follows in the file semphr.h:

#define xSemaphoreGive( xSemaphore ) \
xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), \
NULL, \
semGIVE_BLOCK_TIME, \
queueSEND_TO_BACK ) \

It can be seen that releasing the semaphore at the task level is the process of sending a message to the queue, but no specific message is sent here, the
blocking time is 0 (the macro semGIVE_BLOCK_TIME is 0), and the enqueue method adopts backward enqueue. When entering the queue, the member variable uxMessagesWaiting of the queue structure will be increased by one. For binary semaphores, you can know whether the semaphore is valid by judging uxMessagesWaiting. If the queue is full, the error value errQUEUE_FULL will be returned, indicating that the queue is full and enqueue failed.

②Function xSemaphoreGiveFromISR()

This function is used to release the semaphore in the interrupt. This function can only be used to release the binary semaphore and the counting semaphore. It must not be used to release the mutex semaphore in the interrupt service function! This function is a macro, and what is actually executed is the function xQueueGiveFromISR(). The prototype of this function is as follows:

BaseType_t xSemaphoreGiveFromISR( SemaphoreHandle_t xSemaphore,
 								  BaseType_t * pxHigherPriorityTaskWoken)

Parameters:
xSemaphore: The semaphore handle to be released.
pxHigherPriorityTaskWoken: Mark whether to switch tasks after exiting this function. The value of this variable is set by these three functions. The user does not need to set it. The user only needs to provide a variable to save this value. When this value is pdTRUE, a task switch must be performed before exiting the interrupt service function.

Return value:
pdPASS: Release the semaphore successfully.
errQUEUE_FULL: Failed to release the semaphore.


The function xQueueGiveFromISR() is actually used to release the semaphore in the interrupt, which is very similar to the interrupt-level general enqueue function xQueueGenericSendFromISR()! Only minor changes were made for semaphores. The function
xSemaphoreGiveFromISR() cannot be used to release the mutex semaphore in the interrupt, because the mutex semaphore involves the issue of priority inheritance, and the interrupt does not belong to the task, so it cannot handle the interrupt priority inheritance.

5. Get the semaphore

There are also two functions for obtaining the semaphore, as shown in the following table:
insert image description here
same as the API function for releasing the semaphore, whether it is a binary semaphore, a counting semaphore or a mutual exclusion semaphore, they all
use the functions in the above table to obtain the semaphore

①Function xSemaphoreTake()

This function is used to obtain a binary semaphore, a counting semaphore or a mutual exclusion semaphore. This function is a macro. The actual
process of obtaining a semaphore is completed by the function xQueueGenericReceive (). The function prototype is as follows:

BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore,
						  TickType_t xBlockTime)

Parameters:
xSemaphore: The semaphore handle to be acquired.
xBlockTime: block time.

Return value:
pdTRUE: The semaphore is acquired successfully.
pdFALSE: timeout, failed to acquire semaphore.

Let's take a look at the specific content of the function xSemaphoreTake (), this function is defined as follows in the file semphr.h:

#define xSemaphoreTake( xSemaphore, xBlockTime ) \
xQueueGenericReceive( ( QueueHandle_t ) ( xSemaphore ), \
 NULL, \
( xBlockTime ), \
pdFALSE ) \

The process of obtaining the semaphore is actually the process of reading the queue, but this is not for reading the messages in the queue. If the queue is empty and the blocking time is 0, it will return errQUEUE_EMPTY immediately, indicating that the queue is full. If the queue is empty and the blocking time is not 0, add the task to the delay list. If the queue is not empty, read the data from the queue (this step is not performed to obtain the semaphore). After the data is read, it is necessary to reduce the member variable uxMessagesWaiting of the queue structure by one, and then release some blocking due to entering the queue. task, and finally returns pdPASS to indicate success. The mutual exclusion semaphore involves priority inheritance, and the processing method is different. When explaining the mutual exclusion semaphore, I will explain it in detail later.

②Function xSemaphoreTakeFromISR ()

This function is used to obtain the semaphore in the interrupt service function. This function is used to obtain the binary semaphore and counting semaphore.
This function must not be used to obtain the mutex semaphore! This function is a macro, and what actually executes is the function xQueueReceiveFromISR (), the prototype of this function is as follows:

BaseType_t xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore,
 								 BaseType_t * pxHigherPriorityTaskWoken)

Parameters:
xSemaphore: The semaphore handle to be acquired.
pxHigherPriorityTaskWoken: Mark whether to switch tasks after exiting this function. The value of this variable is set by these three functions. The user does not need to set it. The user only needs to provide a variable to save this value. When this value is pdTRUE, a task switch must be performed before exiting the interrupt service function.

Return value:
pdPASS: Obtain semaphore successfully.
pdFALSE: Failed to acquire semaphore.

The function xQueueReceiveFromISR () is actually used to obtain the semaphore in the interrupt, and this function is the interrupt-level
dequeuing function! When the queue is not empty, copy the data in the queue (this step is not required for semaphores), and then reduce the member variable uxMessagesWaiting in the queue structure by one, and release it if any task is blocked due to enqueueing In blocking state, when the unblocked task has a higher priority, set the parameter pxHigherPriorityTaskWoken to pdTRUE, and finally return pdPASS to indicate the success of dequeue. If the queue is empty, it will directly return pdFAIL to indicate failure to leave the queue!

3. Binary semaphore operation experiment

1. The purpose of the experiment
The mission of the binary semaphore is to synchronize, to complete the synchronization between tasks and tasks or between interrupts and tasks. In most cases it is the synchronization between interrupts and tasks. In this section, we will learn how to use binary semaphores to complete the synchronization between interrupts and tasks.

2. Experiment design
Design an experiment to control the LED1 and BEEP switch on the development board by sending specified instructions through the serial port. The instructions are as follows (case insensitive):

LED1ON:打开 LED1。
LED1OFF:关闭 LED1。
BEEPON:打开蜂鸣器。
BEEPOFF:关闭蜂鸣器。

These instructions are sent to the development board through the serial port, and the instructions are not case-sensitive!
The development board uses interrupt reception, and releases the binary semaphore when data is received . The task DataProcess_task() is used to process these instructions. The task will always try to obtain the binary semaphore. When the semaphore is obtained, it will extract these instructions from the serial port receiving buffer, and then control the corresponding peripherals according to the instructions.

This experiment designs three tasks: start_task, task1_task, DataProcess_task The task functions of these three tasks are as follows:
start_task: used to create the other two tasks.
task1_task : Control LED0 to blink, indicating that the system is running.
DataProcess_task : Instruction processing tasks to control different peripherals according to the received instructions.
In the experiment, a binary semaphore BinarySemaphore is also created to complete
the synchronization between the serial interrupt and the task DataProcess_task.

3. Experimental procedure and analysis

●Task settings

#define START_TASK_PRIO 1 //任务优先级
#define START_STK_SIZE 256 //任务堆栈大小
TaskHandle_t StartTask_Handler; //任务句柄
void start_task(void *pvParameters); //任务函数
#define TASK1_TASK_PRIO 2 //任务优先级
#define TASK1_STK_SIZE 256 //任务堆栈大小
TaskHandle_t Task1Task_Handler; //任务句柄
void task1_task(void *pvParameters); //任务函数
#define DATAPROCESS_TASK_PRIO 3 //任务优先级
#define DATAPROCESS_STK_SIZE 256 //任务堆栈大小
TaskHandle_t DataProcess_Handler; //任务句柄
void DataProcess_task(void *pvParameters); //任务函数
//二值信号量句柄
SemaphoreHandle_t BinarySemaphore;//二值信号量句柄
//用于命令解析用的命令值
#define LED1ON 1
#define LED1OFF 2
#define BEEPON 3
#define BEEPOFF 4
#define COMMANDERR 0XFF

● Other applied functions

//将字符串中的小写字母转换为大写
//str:要转换的字符串
//len:字符串长度
void LowerToCap(u8 *str,u8 len)
{
    
    
	u8 i;
	for(i=0;i<len;i++)
	{
    
    
		if((96<str[i])&&(str[i]<123)) //小写字母
		str[i]=str[i]-32; //转换为大写
	}
}

//命令处理函数,将字符串命令转换成命令值
//str:命令
//返回值: 0XFF,命令错误;其他值,命令值
u8 CommandProcess(u8 *str)
{
    
    
	u8 CommandValue=COMMANDERR;
	if(strcmp((char*)str,"LED1ON")==0) CommandValue=LED1ON;
	else if(strcmp((char*)str,"LED1OFF")==0) CommandValue=LED1OFF;
	else if(strcmp((char*)str,"BEEPON")==0) CommandValue=BEEPON;
	else if(strcmp((char*)str,"BEEPOFF")==0) CommandValue=BEEPOFF;
	return CommandValue;
}

The function LowerToCap() is used to uniformly convert the lowercase letters in the commands sent by the serial port into uppercase letters, so that
you can send commands without distinguishing between case and case, because the development board will uniformly convert them into uppercase letters. The function CommandProcess() is used to convert the received command string into a command value, for example, the command "LED1ON" converted into a command value is 0 (macro LED1ON is 0).

● main() function

int main(void)
{
    
    
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组 4
	delay_init(); //延时函数初始化
	uart_init(115200); //初始化串口
	LED_Init(); //初始化 LED
	KEY_Init(); //初始化按键
	BEEP_Init(); //初始化蜂鸣器
	LCD_Init(); //初始化 LCD
	my_mem_init(SRAMIN); //初始化内部内存池
	 
	POINT_COLOR=RED;
	 LCD_ShowString(10,10,200,16,16,"ATK STM32F103/407");
	LCD_ShowString(10,30,200,16,16,"FreeRTOS Examp 14-1");
	LCD_ShowString(10,50,200,16,16,"Binary Semap");
	LCD_ShowString(10,70,200,16,16,"Command data:");
	 //创建开始任务
	 xTaskCreate((TaskFunction_t )start_task, //任务函数
	 (const char* )"start_task", //任务名称
	 (uint16_t )START_STK_SIZE, //任务堆栈大小
	 (void* )NULL, //传递给任务函数的参数
	 (UBaseType_t )START_TASK_PRIO, //任务优先级
	 (TaskHandle_t* )&StartTask_Handler); //任务句柄 
	 vTaskStartScheduler(); //开启任务调度
}

● task function

//开始任务任务函数
void start_task(void *pvParameters)
{
    
    
	 taskENTER_CRITICAL(); //进入临界区
	//创建二值信号量
	BinarySemaphore=xSemaphoreCreateBinary();
	 //创建 TASK1 任务
	 xTaskCreate((TaskFunction_t )task1_task, 
	 (const char* )"task1_task", 
	 (uint16_t )TASK1_STK_SIZE, 
	 (void* )NULL, 
	 (UBaseType_t )TASK1_TASK_PRIO, 
	 (TaskHandle_t* )&Task1Task_Handler); 
	 //创建 TASK2 任务
	 xTaskCreate((TaskFunction_t )DataProcess_task,
	 (const char* )"keyprocess_task", 
	 (uint16_t )DATAPROCESS_STK_SIZE,
	 (void* )NULL,
	 (UBaseType_t )DATAPROCESS_TASK_PRIO,
	 (TaskHandle_t* )&DataProcess_Handler); 
	 vTaskDelete(StartTask_Handler); //删除开始任务
	 taskEXIT_CRITICAL(); //退出临界区
}

//task1 任务函数
void task1_task(void *pvParameters)
{
    
    
	while(1)
	{
    
    
		LED0=!LED0;
	    vTaskDelay(500); //延时 500ms,也就是 500 个时钟节拍
	}
}

//DataProcess_task 函数
void DataProcess_task(void *pvParameters)
{
    
    
	u8 len=0;
	u8 CommandValue=COMMANDERR;
	BaseType_t err=pdFALSE;
	u8 *CommandStr;
	POINT_COLOR=BLUE;
	while(1)
	{
    
    
		if(BinarySemaphore!=NULL)
		{
    
    
			err=xSemaphoreTake(BinarySemaphore,portMAX_DELAY);//获取信号量 (1)
			if(err==pdTRUE) //获取信号量成功
			{
    
    
				len=USART_RX_STA&0x3fff; //得到此次接收到的数据长度
				CommandStr=mymalloc(SRAMIN,len+1); //申请内存
				sprintf((char*)CommandStr,"%s",USART_RX_BUF);
				CommandStr[len]='\0'; //加上字符串结尾符号
				LowerToCap(CommandStr,len); //将字符串转换为大写 (2)
				CommandValue=CommandProcess(CommandStr); //命令解析 (3)
				if(CommandValue!=COMMANDERR) //接收到正确的命令
				{
    
    
					LCD_Fill(10,90,210,110,WHITE); //清除显示区域
					LCD_ShowString(10,90,200,16,16,CommandStr);//在 LCD 上显示命令
					printf("命令为:%s\r\n",CommandStr);
					switch(CommandValue) //处理命令 (4)
					{
    
    
					case LED1ON: 
						LED1=0;
						break;
					case LED1OFF:
						LED1=1;
						break;
					case BEEPON:
						BEEP=1;
						break;
					case BEEPOFF:
						BEEP=0;
						break;
					}
			}
			else
			{
    
    
				printf("无效的命令,请重新输入!!\r\n");
			}
				USART_RX_STA=0;
				memset(USART_RX_BUF,0,USART_REC_LEN);//串口接收缓冲区清零
				myfree(SRAMIN,CommandStr); //释放内存
			}
		}
		else if(err==pdFALSE)
		{
    
    
			vTaskDelay(10); //延时 10ms,也就是 10 个时钟节拍
		}
	}
}
	 

(1) Use the function xSemaphoreTake() to obtain the binary semaphore BinarySemaphore, and the delay time is portMAX_DELAY.

(2) Call the function LowerToCap() to convert the lowercase letters in the command string to uppercase.

(3) Calling the function CommandProcess() to process the command string is actually to convert the command string into a command value.

(4) Perform different operations according to different command values, such as switching LED1 and switching BEEP.

●Interrupt initialization and processing process
In this experiment, serial port 1 receives data through interrupts, so it is necessary to initialize serial port 1. The initialization of serial port is very
simple, and it has been mentioned many times before. But pay attention to the interrupt priority of serial port 1! Because we want to use the FeeRTOS API function in the interrupt service function of serial port 1, this experiment sets the preemptive priority of serial port 1 to 7, and the sub-priority to 0.

Guess you like

Origin blog.csdn.net/Dustinthewine/article/details/130312126