FreeRTOS learning -- "semaphores"

Posted by bad_goose on Tue, 04 Jan 2022 16:26:03 +0100

In the "message queue" article, we once buried a foreshadowing, that is, the message delivery and synchronization of tasks in FreeRTOS are all through message queue. Can't it be through global variables?
The answer is yes, but this will cause delayed response. If you use global variables, you can only poll, which will inevitably lose a certain amount of time to judge the result

Can we use semaphores?
Of course, it's OK, but the semaphore in FreeRTOS is also realized through message queue

Binary semaphore

Binary semaphores are intuitive, similar to traffic lights. When they are created, they are red by default. Other tasks can't get this semaphore. Only after give can they become green, and other tasks can get semaphores and run. (in essence, semaphores are implemented in queues) the above is an analogy. In the code, when creating, the initial value is 0, give is equivalent to setting the count value to 1, and taking is equivalent to clearing the count value to 0.

demo program

The following program creates a binary semaphore. For example, we operate some hardware and read I2C. At a certain time, only one task should be allowed to operate, whether reading or writing, so we need to use this semaphore to control it. After the operation, we should give up the CPU in time so that other tasks can get the semaphore.

SemaphoreHandle_t xSemaphore = NULL;

// A task that uses the semaphore.
void WriteSomethingTask( void * pvParameters )
{
	long lValueToSend; 

	/* Two instances of the task will be created, so the value written to the queue is passed through the task entry parameter - this way makes each instance use different values. When the queue is created, its data unit is specified as long, so the entry parameter is cast to the type required by the data unit */ 
	lValueToSend = ( long ) pvParameters; 
	for(;;)
	{
		if( xSemaphore != NULL )
		{
			if( xSemaphoreTake(xSemaphore,( TickType_t ) 10 ) == pdTRUE )
			{
				printf("tast:%ld get signal ,keep it 2 seconds\n",lValueToSend);
				{
					//my work
					vTaskDelay(2000 / portTICK_PERIOD_MS); 
				}
				xSemaphoreGive(xSemaphore);
				taskYIELD(); 
			}
			else
			{
			}
		}
	}
}

void app_main(void) 
{ 
	// Semaphore cannot be used before a call to xSemaphoreCreateBinary().
	// This is a macro so pass the variable in directly.
	xSemaphore = xSemaphoreCreateBinary();
	
	if( xSemaphore != NULL )
	{
		xSemaphoreGive(xSemaphore);
		xTaskCreate(WriteSomethingTask, "write1", 1000, ( void * ) 1, 1, NULL ); 
		xTaskCreate(WriteSomethingTask, "write2", 1000, ( void * ) 2, 1, NULL ); 
		xTaskCreate(WriteSomethingTask, "write3", 1000, ( void * ) 3, 1, NULL ); 
	}
}

Create binary semaphore

SemaphoreHandle_t xSemaphoreCreateBinary( void )
parametermeaning
Return valueNULL indicates that there is not enough heap space allocated to semaphores, resulting in creation failure.
A non NULL value indicates that the semaphore was created successfully. This return value should be saved as a handle to manipulate this semaphore.

Binary semaphore minus one

void xSemaphoreTake(SemaphoreHandle_t xSemaphore,TickType_t xBlockTime)
parametermeaning
xSemaphoreHandle to the target semaphore. This handle is the return value when xsemaphotorecreatebinary() is called to create the semaphore.
xBlockTimeBlocking timeout.

Binary semaphore plus one

void xSemaphoreGive( SemaphoreHandle_t xSemaphore )
parametermeaning
xSemaphoreHandle to the target semaphore. This handle is the return value when xsemaphotorecreatebinary() is called to create the semaphore.

Multivalued semaphore

In the system with infrequent interrupts, there is no problem with using binary semaphores, but when interrupts occur frequently, there will be the problem of interrupt loss. Because the task execution is delayed when an interrupt occurs, if there are two more interrupts during the delayed task execution, only the first one will be processed and the second one will be lost. Therefore, multivalued semaphores are introduced to deal with this problem.

demo

This demo simulates two tasks to increase the semaphore. One task processes the signal, which is similar to receiving multiple messages, processing multiple times, and then not losing records.

SemaphoreHandle_t xSemaphore = NULL;

// A task that uses the semaphore.
void WriteSomethingTask1( void * pvParameters )
{
	int countme=0;
	for(;;)
	{
		printf("write1 %d\n",countme++);
		xSemaphoreGive(xSemaphore);
		vTaskDelay(1000 / portTICK_PERIOD_MS); 
		taskYIELD(); 
	}
}
// A task that uses the semaphore.
void WriteSomethingTask2( void * pvParameters )
{
	int countme=0;
	for(;;)
	{
		printf("write2 %d\n",countme++);
		xSemaphoreGive(xSemaphore);
		vTaskDelay(1000 / portTICK_PERIOD_MS); 
		taskYIELD(); 
	}
}

// A task that uses the semaphore.
void ReadSomethingTask( void * pvParameters )
{
	int countme=0;
	for(;;)
	{
		if(xSemaphoreTake(xSemaphore,( TickType_t ) 0 ) == pdTRUE )
		{
			printf("read %d\n",countme++);
		}
		else
		{
		}
		vTaskDelay(10 / portTICK_PERIOD_MS); 
		taskYIELD(); 
	}
}


void app_main(void) 
{ 

    xSemaphore = xSemaphoreCreateCounting( 10, 0 );
	
	if( xSemaphore != NULL )
	{
		xTaskCreate(WriteSomethingTask1, "write1", 1000, NULL, 1, NULL ); 
		xTaskCreate(WriteSomethingTask2, "write2", 1000, NULL, 1, NULL ); 
		xTaskCreate(ReadSomethingTask, "read", 1000, NULL, 1, NULL ); 
	}
}

Multivalued semaphore creation

SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount, UBaseType_t uxInitialCount )
parametermeaning
uxMaxCountMaximum value of multivalued semaphore.
uxInitialCountInitial value of multivalued semaphore.
Return valueNULL indicates that there is not enough heap space allocated to semaphores, resulting in creation failure.
A non NULL value indicates that the semaphore was created successfully. This return value should be saved as a handle to manipulate this semaphore.

Other operations are the same as binary semaphore functions.

Mutually exclusive semaphore

If a resource can be accessed by only one task at the same time, the resource can be protected with mutex. This resource must exist, so when creating a mutex, a mutex will be released first, indicating that this resource can be used. When a task wants to access a resource, it first obtains the mutex, and then releases it after using the resource. That is, once the mutex is created, it should be obtained first and then released. It should be obtained and released in the same task. This is also an important difference between mutually exclusive semaphores and binary semaphores. Binary semaphores can be obtained or released in any task, and then released or obtained in any task. The mutex is different from the binary semaphore: the mutex has a priority inheritance mechanism, the binary semaphore does not, the mutex cannot be used to interrupt the service program, and the binary semaphore can.

In short, who uses who releases.

demo

Only one of the two write tasks can write critical resources, so it is controlled by mutually exclusive semaphores. Rob yourself and release yourself. This is mutual exclusion.

SemaphoreHandle_t xSemaphore = NULL;

void WriteSomethingTask1( void * pvParameters )
{
	for(;;)
	{
		if(xSemaphoreTake(xSemaphore,( TickType_t ) 10 ) == pdTRUE )
		{
			printf("WriteSomethingTask1 work\n");
			vTaskDelay(1000 / portTICK_PERIOD_MS); 
			xSemaphoreGive(xSemaphore);
		}
		else
		{
			vTaskDelay(10 / portTICK_PERIOD_MS); 
		}
		taskYIELD(); 
	}

}
void WriteSomethingTask2( void * pvParameters )
{
	for(;;)
	{
		if(xSemaphoreTake(xSemaphore,( TickType_t ) 10 ) == pdTRUE )
		{
			printf("WriteSomethingTask2 work\n");
			vTaskDelay(1000 / portTICK_PERIOD_MS); 
			xSemaphoreGive(xSemaphore);
		}
	else
		{
			vTaskDelay(10 / portTICK_PERIOD_MS); 
		}
		taskYIELD(); 
	}

}


void app_main(void) 
{ 

    xSemaphore = xSemaphoreCreateMutex();
	
	if( xSemaphore != NULL )
	{
		xSemaphoreGive(xSemaphore);
		xTaskCreate(WriteSomethingTask1, "write1", 1000, NULL, 1, NULL ); 
		xTaskCreate(WriteSomethingTask2, "write2", 1000, NULL, 1, NULL ); 
	}
}

Mutex semaphore creation

SemaphoreHandle_t xSemaphoreCreateMutex( void )
parametermeaning
Return valueNULL indicates that there is not enough heap space allocated to semaphores, resulting in creation failure.
A non NULL value indicates that the semaphore was created successfully. This return value should be saved as a handle to manipulate this semaphore.

Abnormal error

In a test, it was found that the watchdog reported an error and the program was restarted randomly. This is because there are tasks running all the time, resulting in the watchdog not feeding. Therefore, it is necessary to ensure that each task has an appropriate rest time.

Conclusion

It's a pity to hear that someone fell off a building and died in the back community. There was no family at the scene during the Chinese New Year. It felt like an accident. Some people said it was depression. The name of depression always sounds very slight. Why not call it a disease? At least it can make people pay attention to it. Hey, so did Tencent employees a few days ago.

The long-term manifestations of mild depression are depression, unsociable, outlier, physical discomfort, loss of appetite and sleep disorder.
Self mitigation methods include:
At ordinary times, learn more skills, exercise and try to communicate more outside:
Observe more, understand more, sort out your ideas, write them, and read more books.

See, learn more technology and put it on the first line. Lao Wang, I've been asking you to learn more to save you

Topics: Single-Chip Microcomputer FreeRTOS