任务间通信==ucosii

背景
  • 在主程序里面做i++(i=10),在某个中断服务函数里面做i–,会出现主程序刚取出i的值得时候被中断打断,在中断里面做了i—(写回去的值为9),返回到断点后,主程会对i(已经取出的值)做加1操作,然后写回去(最终i=11;).------在主程序里面在对i操作之前先把中断关了,等操作完后再打开中断。 出现以上情况的原因是—打断。在Ucos中不可避免的会出现以上打断问题。因为ucos是以优先级作为调度原则,所以也存在打断问题。所以任务间间交换信息如果用全局变量,就存在以上问题。所以ucos任务间交换信息尽量的不要用全局变量。

  • Ucosii的任务间通信机制:信号量、互斥信号量、消息邮箱、消息队列、事件标志组

  • 任务控制块:存放当前任务的相关信息(任务函数地址、任务优先级、任务栈、任务状态)

  • 事件控制块:当成功创建一个事件(信号量、互斥信号量、消息邮箱、消息队列、事件标志组)后,系统就分配一段内存空间,这段空间就是事件控制块,存放这该事件的相关信息。

信号量

可以把信号量看成是一个计数器,表示当前资源的占用情况,当释放一个资源时信号量+1,如果占用一个资源,信号量-1。且信号量不可删除,而且只有任务才允许使用信号量,ISR是不允许的。

  • 1.要想使用信号量,必须先创建一个信号量,并且可以对这个信号量赋予一个初始值。
    函数原型:OS_EVENT *OSSemCreate(INT16U value);
    参数为信号量计数器,
    函数作用:建立并初始化一个信号量
    参数说明:建立的信号量的初始值,可以取 0 到 65535 之间的任何值。
    返回值:指向成功创建的信号量的事件控制块地址

  • 2.释放信号量(发送),信号量就会+1
    函数原型:INT8U OSSemPost(OS_EVENT *pevent);
    函数作用:释放一个信号量,把信号值加 1

  • 3.要想得到一个信号量,要先查看信号量是否为0,如果大于0表示当前可以去占用一个信号量,如果为 0表示当前信号量被用光了,可以死等其他任务释放信号量,也可以不等。
    函数原型:void OSSemPend (OS_EVENT *pevent,INT32U timeout,INT8U *perr);
    pevent 是被请求信号量的指针,
    timeout 为等待时限(设置为0表示无限等待),
    err 为错误信息。
    函数作用:申请一个信号量(挂起任务等待信号量)。

  • 之外有,
    函数原型:void OSSemSet(OS_EVENT *pevent,INT16U cnt,INT8U *perr);
    函数作用:改变当前信号量的计数值,应用于信号量集

    函数原型:INT16U OSSemAccept(OS_EVENT *pevent);
    函数作用:无等待查看信号量是否为0。

    OS_EVENT *sem_led1=OSSemCreate(0);  //创建信号量
    void led_task()
    {
      While(1)
     {
          OSSemPend(sem_led1,0,&err);//请求信号
          Led =1;//点亮
      }
    }
    void main_task()
    {
       While(1)
    	{
    	   if(confidition)
    	      OSSemPost(sem_led1);//发送信号      
    	}
    }
    //这样就可以通过主任务函数来控制各个子任务了
    
消息邮箱
  • 顾名思义,可以往邮箱里存放消息,这个消息的内容比信号量要更加丰富。
  • 这个邮箱只能存放一则消息。
  • 如果这个邮箱里存在消息没有被读取,然后再往里面存放消息,就会失败。
  • 在消息邮箱里面,如果发送方能力比较强(发送速度快),接受方能力比较弱(接受得慢),那么就会丢失新的消息。
  • 使用邮箱可以在多任务间传递数据,而不仅仅只是一个标志位。主要也是用到了三个函数
    • 创建邮箱:OS_EVENT *OSMboxCreate (void *msg); 参数为消息即数据指针,返回值是邮箱指针
    • 请求邮箱:void *OSMboxPend (OS_EVENT *pevent, INT16U timeout, INT8U *err);其中pevent 是邮箱指针,timeout 为等待时限(设置为0表示无限等待),err 为错误信息。
    • 发送邮箱:INT8U OSMboxPost (OS_EVENT *pevent,void *msg);其中 pevent 为消息邮箱的指针,msg 为消息指针。
    OS_EVENT * msg_key=OSMboxCreate((void*)0); //创建邮箱
    Void key_task()
    {     
    	u8 key;             
    	while(1)
    	{
    		key=KEY_Scan(0);   //获取按键值
    		if(key)OSMboxPost(msg_key,(void*)key);//发送消息
    	}
    }
    Void main_task()
    {
    	u8 key;
    	While(1)
    	{
    	    //接收
    		key=(u32)OSMboxPend(msg_key,10,&err);        
    	}
    }
    //子任务可以发送不同的按键值给主任务
    
消息队列
  • 消息邮箱只能存放一则消息,而消息队列则可以存放一队(多则)消息,相当于增强了接受方的能力消息队列存放消息的方式是先进先出
  • 创建一个消息队列:
    OS_EVENT *OSQCreate (void **start,INT16U size)
    start:指向用户创建一个存储区(数组),这个存储空间(数组)存放的是消息的地址。成功创建了消息队列后,创建的这个存储区(数组)就交由UCOSII去管理。
    size:消息内存区的大小
  • 发送一则消息:
    INT8U OSQPost (OS_EVENT *pevent,void *pmsg);
    pmsg:消息的地址
    发送多则不同消息需要发送不同的消息地址。
  • 删除消息队列;
    OS_EVENT *OSQDel(OS_EVENT *pevent, INT8U opt, INT8U *perr);
    删除后会将pevent->OSEventType置为无使用类型,所以再用OSQPend和OSQPost操作消息队列都无效。
    返回值,如果成功则返回0,如果失败则返回pevent;
    pevent是消息队列句柄;
    opt可以是两个参数,OS_DEL_NO_PEND表示没有任务在等待该消息时才会删除消息,OS_DEL_ALWAYS即表示不管有没有任务在等待,都删除消息队列,如果有任务在等待该消息,会强制让任务标志为已经接收到该消息,然后进入就绪态;
  • 刷新消息队列的内容;实际是复位消息队列,即把消息队列的内容清空;
    INT8U OSQFlush(OS_EVENT *pevent);
  • 等待消息;
    void *OSQPend(OS_EVENT *pevent, INT32U timeout, INT8U *perr);
    返回值,返回最先入队的消息的指针;消息的类型是自定义的,所以用的void型指针;
    pevnet,消息队列句柄;
    timeout,等待消息的超时时间,如果为0,则表示一直等待有消息为止,其它值则表示经过timeout个tick(一个tick就是一个systick的中断周期)后,就不在等待消息,往下执行;
    perr,异常返回值;
    OSQCreate(&g_queue_usart1,"g_queue_usart1",16,&err);//创建消息队列
    
    void Task1_task(void *parg)
    {
    	OS_ERR err;
    	u32 cnt =0;
    	char buf[64]={0};//消息存储
    	while(1)
    	{
    		cnt++;
    		sprintf(buf,"task1 run %d times\r\n",cnt);
    		OSQPost(&g_queue_usart1,buf,strlen(buf),OS_OPT_POST_FIFO,&err);//发送消息buf
    		printf("task1 is running ...\r\n");
    		delay_ms(1000);
    	}
    }
    void Task2_task(void *parg)
    {
    	OS_ERR err;		
    	OS_MSG_SIZE msg_size;//消息大小
    	u32 i =0;
    	char *p = NULL;
    	printf("task2 is create ok\r\n");
    	while(1){
    		p=OSQPend(&g_queue_usart1,0,OS_OPT_PEND_BLOCKING,&msg_size,NULL,&err);//消息请求接收
    		if(p && msg_size)
    		{
    			printf("OSQPend msg:");
    			for(i=0;i<msg_size;i++)
    			{
    				printf("%c",p[i]);
    			}
    			printf("\r\n");
    		}
    		printf("task2 is running ...\r\n");
    		delay_ms(1000);
    	}
    }
    

实际项目三者配合使用,可发挥最大功效。

参考链接:https://blog.csdn.net/qq_40860986/article/details/93669196
参考链接:https://blog.csdn.net/weixin_42542969/article/details/100042565
参考链接:https://blog.csdn.net/mangofu/article/details/123033290
参考链接:https://blog.csdn.net/qq_25355591/article/details/112300802

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

栋哥爱做饭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值