UCOS learning (7) - detailed explanation of semaphore

Introduction to semaphores

The semaphore is like a locking mechanism. The code must obtain the corresponding key before it can continue to execute. Once the key is obtained, it means that the task has the authority to enter the locked part of the code. Once the locked code segment is executed, the task waits until the key corresponding to the locked part of the code is released again before continuing to execute.
Semaphores are used to control the protection of shared resources, but now they are basically used for task synchronization.
Semaphores are usually divided into two types: binary semaphores and counting semaphores.
The binary semaphore can only take two values ​​of 0 and 1. The semaphore value of the counting semaphore is greater than 1. The range of the counting semaphore is determined by OS_SEM_CTR. OS_SEM_CTR can be 8 bits, 16 bits and 32 bits, and the value ranges are respectively : 0 255, 0 65535 and 0~4294967295.
Two-level semaphores are used for resources that can only be used by one task at a time, such as I/O devices, printers, and digital semaphores are used for certain resources that can be used by several tasks at the same time. For example, a buffer pool has 10 cache block, then it can support up to 10 tasks to use the memory pool at the same time. If you
don’t understand it at the first time, it’s a small problem. As long as you understand how it is used, you will know what it is.

Semaphores protect shared resources

Give a chestnut: if you don't use semaphores

First, let’s take a look at my two tasks. We define a global array soure_arrayas a public resource. We use this resource in both tasks: task1Task 1, copy data to the array and print it out.task2soure_array
soure_array

//公共资源数组
char soure_array[40];
//led0任务函数
void led0_task(void *p_arg)
{
    
    
	OS_ERR err;
	p_arg = p_arg;
	char task1_array[] = "task1_suning!";
	while(1)
	{
    
    
	    printf("任务一:\r\n");
		LED0 = !LED0;
		memcpy(soure_array,task1_array,sizeof(task1_array));
		delay_ms(200);
		printf("%s\r\n\r\n",soure_array);
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err);
	}
}

//led1任务函数
void led1_task(void *p_arg)
{
    
    
	OS_ERR err;
	p_arg = p_arg;
	char task2_array[] = "task2_runing!";
	while(1)
	{
    
    
		printf("任务二:\r\n");
		LED1 = !LED1;
		memcpy(soure_array,task2_array,sizeof(task2_array));
		delay_ms(200);
		printf("%s\r\n\r\n",soure_array);
        OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err);
	}
}

Running result:
Please add a picture description
The running result we expect is that task 1 outputs once, and task 2 outputs once.
We will find that this is not the running result we want, because task 2 and task 1 occupy soure_arraythis resource at the same time, how to solve it? Just use a semaphore.

Semaphore solves common resource problem

First of all, we have to understand the functions:
insert image description here
the three functions we commonly use are the three functions marked in red.

Create a semaphore:

1. Create a semaphore structure

//1.创建信号量结构体
OS_SEM my_sem;

2. Use to create OSSemCreate()a semaphore Created
in the start task:

void  OSSemCreate (OS_SEM      *p_sem,  //全局信号量,就是刚才定义的信号量结构体
                   CPU_CHAR    *p_name, //信号量的名字
                   OS_SEM_CTR   cnt,    //cnt=1,二级制信号量,cnt>1,计数型信号量
                   OS_ERR      *p_err)  //错误码

2. The use of semaphores
mainly uses the following two OSSemPend()functions OSSemPost():

//等待一个信号量
OS_SEM_CTR  OSSemPend (OS_SEM   *p_sem,  //信号量结构体指针
                       OS_TICK   timeout,//等待信号量的时间(时钟节拍数), 为0会一直等待信号量
                       OS_OPT    opt,    /*用于设置是否阻塞模式:
                                            OS_OPT_PEND_BLOCKING     指定信号量无效时,任务挂起等待信号量
                                            OS_OPT_PEND_NON_BLOCKING   信号量无效直接返回
                                         */
                       CPU_TS   *p_ts,   //时间戳,记录信号量的时刻,p_ts=NULL时无时间戳
                       OS_ERR   *p_err)  //错误码
//释放或者发送一个信号量
OS_SEM_CTR  OSSemPost (OS_SEM  *p_sem, //信号量结构体指针
                       OS_OPT   opt,   /*选择信号量发送的方式
                                         OS_OPT_POST_1 仅向等待该任务量的优先级最高的任务发送
                                         OS_OPT_POST_ALL 向等待该信号量的所有任务发送
                                         OS_OPT_POST_NO_SCHED 该选项禁止在本函数中执行任务调度
                                         */
                       OS_ERR  *p_err)//错误码

3. The changed task code is as follows:

//led0任务函数
void led0_task(void *p_arg)
{
    
    
	OS_ERR err;
	p_arg = p_arg;
	char task1_array[] = "task1_suning!";
	while(1)
	{
    
    
	  printf("任务一:\r\n");
		LED0 = !LED0;
	OSSemPend(&my_sem,0,OS_OPT_PEND_BLOCKING,NULL,&err);//等待信号量直到不为0
		memcpy(soure_array,task1_array,sizeof(task1_array));
		delay_ms(200);
		printf("%s\r\n\r\n",soure_array);
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err);//延时1s
	OSSemPost(&my_sem,OS_OPT_POST_1,&err);//释放信号量
	}
}

//led1任务函数
void led1_task(void *p_arg)
{
    
    
	OS_ERR err;
	p_arg = p_arg;
	char task2_array[] = "task2_runing!";
	
	while(1)
	{
    
    
		printf("任务二:\r\n");
		LED1 = !LED1;
	OSSemPend(&my_sem,0,OS_OPT_PEND_BLOCKING,NULL,&err);//等待信号量不为0
		memcpy(soure_array,task2_array,sizeof(task2_array));
		delay_ms(200);
		printf("%s\r\n\r\n",soure_array);
        OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err);//延时1s
     OSSemPost(&my_sem,OS_OPT_POST_1,&err);//释放一个信号量
	}
}

4. Running results
This achieves the effect we want:
Please add a picture description
we can analyze its execution process:

  1. Task 2 waits for the semaphore, prints, task 2 sends the semaphore
  2. Task one waits for the semaphore, prints, task one sends the semaphore
  3. Task 2 waits for the semaphore...

Such a cycle can solve the problem of errors when two or more tasks use the same resource.
Below we are introducing a semaphore application:

Semaphore to achieve task synchronization

First of all, we need to know what task synchronization is, that is, every time a task is executed, another task is also executed. Even if these two tasks are synchronized, it is very simple to use semaphores:
the same as before, that is, create tasks and create semaphores

//KEY任务函数
void key_task(void *p_arg)
{
    
    
	OS_ERR err;
	u8 key = 0;
	p_arg = p_arg;
	while(1)
	{
    
    
		key = Key_Scan(0);
		if(key == Short_Press)//按下按键
		{
    
    
			LED0 = !LED0;
			printf("任务一:\r\n");
            OSSemPost(&my_sem,OS_OPT_POST_1,&err);//发送一个信号量
			printf("当前信号量:%d\r\n\r\n",my_sem.Ctr); //打印信号量数值
		}
		OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err);//延时10ms
	}
}

//led1任务函数
void led1_task(void *p_arg)
{
    
    
	OS_ERR err;
	p_arg = p_arg;
	while(1)
	{
    
    
		OSSemPend(&my_sem,0,OS_OPT_PEND_BLOCKING,NULL,&err);
		LED1 = !LED1;
		printf("任务二:\r\n");
		printf("当前信号量:%d\r\n\r\n",my_sem.Ctr);
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err);//延时1s
	}
}

Let's take a look at the running results:
Please add a picture description
when I press the button twice in a row, Task 1 sends the semaphore twice, and we find that the semaphore value changes from 0 to 2; then Task 2 waits for the semaphore twice, and changes the semaphore value from 2 Change to 0; the final effect is that task one is executed several times, and task two is executed several times.

Summarize

After learning this, I still have a lot of questions, and I hope someone can help me out:
1. In the semaphore realization resource protection experiment, if OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err);//延时1sthe delay is deleted, the task execution is still normal, and task 1 and task 2 are executed sequentially , this is not very understandable.
2. The main difference between the binary semaphore and the counting semaphore and the circumstances in which they are used are not very clear.
I hope my article can let you learn semaphores, see you next time!

Guess you like

Origin blog.csdn.net/qq_52608074/article/details/122448910