STM32上使用UCOSII--消息队列和信号量集

有关UCOS任务的介绍:
STM32上使用UCOSII–任务
有关UCOS信号量和邮箱的介绍:
STM32上使用UCOSII–信号量和邮箱

一、 消息队列

使用消息队列可以在任务之间传递多条消息。消息队列由三个部分组成:事件控制块、消息队列和消息。当把事件控制块成员 OSEventType 的值置为 OS_EVENT_TYPE_Q 时,该事件控制块描述的就是一个消息队列。

消息队列的数据结构如图:

duilie

消息队列相当于一个共用一个任务等待列表的消息邮箱数组,事件控制块成员OSEventPtr指向了一个叫做队列控制块(OS_Q)的结构,该结构管理了一个数组 MsgTbl[],该数组中的元素都是一些指向消息的指针

队列控制块( OS_Q)的结构定义如下:

typedef struct os_q
{
struct os_q *OSQPtr;
void **OSQStart;
void **OSQEnd;
void **OSQIn;
void **OSQOut;
INT16U OSQSize;
INT16U OSQEntries;
} OS_Q;
参数说明
OSQPtr指向下一个空的队列控制块
OSQSize数组的长度
OSQEntres已存放消息指针的元素数目
OSQStart指向消息指针数组的起始地址
OSQEnd指向消息指针数组结束单元的下一个单元。它使得数组构成了一个循环的缓冲区
OSQIn指向插入一条消息的位置。当它移动到与 OSQEnd 相等时,被调整到指向数组的起始单元
OSQOut指向被取出消息的位置。当它移动到与 OSQEnd 相等时,被调整到指向数组的起始单元

其中,可以移动的指针为 OSQIn 和 OSQOut,而指针 OSQStart 和 OSQEnd 只是一个标志(常指针)。当可移动的指针 OSQIn 或 OSQOut 移动到数组末尾,也就是与OSQEnd相等时,可移动的指针将会被调整到数组的起始位置OSQStart。也就是说,从效果上来看,指针 OSQEnd与 OSQStart 等值。于是,这个由消息指针构成的数组就头尾衔接起来形成了一个循环的队列

消息队列相关函数

1. 创建消息队列函数

OS_EVENT *OSQCreate(void**start,INT16U size);

start 为存放消息缓冲区指针数组的地址

size 为该数组大小

2. 请求消息队列函数

void*OSQPend(OS_EVENT*pevent,INT16U timeout,INT8U *err);

pevent 为所请求的消息队列的指针

timeout 为任务等待时限

err 为错误信息

3. 向消息队列发送消息函数

INT8U OSQPost(OS_EVENT *pevent,void *msg);//先进先出
INT8U OSQPostFront(OS_EVENT *pevent,void*msg); //后进先出

pevent 为消息队列的指针

msg 为待发消息的指针

二、 信号量集

UCOSII 为了实现多个信号量组合的功能定义了一种特殊的数据结构——信号量集

不同于信号量、消息邮箱、消息队列等事件, UCOSII 不使用事件控制块来描述信号量集,而使用了一个叫做标志组的结构 OS_FLAG_GRP 来描述。 OS_FLAG_GRP 结构如下:

typedef struct
{
INT8U OSFlagType; //识别是否为信号量集的标志
void *OSFlagWaitList; //指向等待任务链表的指针
OS_FLAGS OSFlagFlags; //所有信号列表
}OS_FLAG_GRP;

成员 OSFlagWaitList 是一个指针,当一个信号量集被创建后,这个指针指向了这个信号量集的等待任务链表

与其他前面介绍过的事件不同,信号量集用一个双向链表来组织等待任务,每一个等待任务都是该链表中的一个节点( Node)。标志组 OS_FLAG_GRP 的成员 OSFlagWaitList 就指向了信号量集的这个等待任务链表。等待任务链表节点 OS_FLAG_NODE 的结构如下:

typedef struct
{
    void *OSFlagNodeNext; //指向下一个节点的指针
    void *OSFlagNodePrev; //指向前一个节点的指针
    void *OSFlagNodeTCB; //指向对应任务控制块的指针
    void *OSFlagNodeFlagGrp; //反向指向信号量集的指针
    OS_FLAGS OSFlagNodeFlags; //信号过滤器
    INT8U OSFlagNodeWaitType; //定义逻辑运算关系的数据
} OS_FLAG_NODE;

其中 OSFlagNodeWaitType 是定义逻辑运算关系的一个常数(根据需要设置),其可选值和对应的逻辑关系如表

常数信号有效状态等待任务的就绪条件
WAIT_CLR_ALL 或WAIT_CLR_AND0信号全部有效(全 0)
WAIT_CLR_ANY 或WAIT_CLR_OR0信号有一个或一个以上有效(有 0)
WAIT_SET_ALL 或WAIT_SET_AND1信号全部有效(全 1)
WAIT_SET_ANY 或WAIT_SET_OR1信号有一个或一个以上有效(有 1)

OSFlagFlags、 OSFlagNodeFlags、 OSFlagNodeWaitType 三者的关系如图
duilie

信号量相关函数

1. 创建信号量集函数

OS_FLAG_GRP *OSFlagCreate (OS_FLAGS flags,INT8U *err );

flags 为信号量的初始值(即 OSFlagFlags 的值)

err 为错误信息

返回值为该信号量集的标志组的指针,应用程序根据这个指针对信号量集进行相应的操作

2. 请求信号量集函数

OS_FLAGS OSFlagPend(OS_FLAG_GRP*pgrp, OS_FLAGS flags,INT8U wait_type, INT16U timeout, INT8U *err);

pgrp 为所请求的信号量集指针

flags 为滤波器(即 OSFlagNodeFlags 的值)

wait_type 为逻辑运算类型(即 OSFlagNodeWaitType 的值)

timeout 为等待时限

err 为错误信息

3. 向信号量集发送信号函数

OS_FLAGS OSFlagPost (OS_FLAG_GRP *pgrp, OS_FLAGS flags, INT8U opt, INT8U *err);

pgrp 为所请求的信号量集指针

flags 为选择所要发送的信号

opt 为信号有效选项

err 为错误信息

三、 stm32上使用uucos信号量集

#include "led.h"
#include "delay.h"
#include "sys.h"
#include "includes.h" 
#include "key.h"
#include "oled.h"

/////////////////////////UCOSII任务设置///////////////////////////////////
//START 任务
//设置任务优先级
#define START_TASK_PRIO                 10 //开始任务的优先级设置为最低
//设置任务堆栈大小
#define START_STK_SIZE                  64
//任务堆栈  
OS_STK START_TASK_STK[START_STK_SIZE];
//任务函数
void start_task(void *pdata);   



//信号量处理任务
//设置任务优先级
#define FLAG_TASK_PRIO                  5
//设置任务堆栈大小
#define FLAG_STK_SIZE                       64
//任务堆栈
OS_STK FLAG_TASK_STK[FLAG_STK_SIZE];
//任务函数
void flag_task(void *pdata);


//按键扫描任务
//设置任务优先级
#define SCAN_TASK_PRIO                  4
//设置任务堆栈大小
#define SCAN_STK_SIZE                   64
//任务堆栈
OS_STK SCAN_TASK_STK[SCAN_STK_SIZE];
//任务函数
void scan_task(void *pdata);    

///////////////////////////////////////////////////////////////////////////////////////////////
OS_FLAG_GRP *flags_key;//按键信号量集

 int main(void)
 {  
    delay_init();            //延时函数初始化  
  NVIC_Configuration();  
    LED_Init();         //初始化与LED连接的硬件接口
    KEY_Init();
    OLED_Init(); 
    OSInit();   
    OSTaskCreate(start_task,(void *)0,(OS_STK *)&START_TASK_STK[START_STK_SIZE-1],START_TASK_PRIO );//创建起始任务
    OSStart();  
 }


//开始任务
void start_task(void *pdata)
{
    u8 err;
  OS_CPU_SR cpu_sr=0;
    pdata = pdata; 
//  msg_key=OSMboxCreate((void *)0);//创建消息邮箱
    flags_key=OSFlagCreate(0,&err);     //创建信号量集    
    OS_ENTER_CRITICAL();            //进入临界区(无法被中断打断)    
//  OSTaskCreate(led0_task,(void *)0,(OS_STK*)&LED0_TASK_STK[LED0_STK_SIZE-1],LED0_TASK_PRIO);                         
//  OSTaskCreate(led1_task,(void *)0,(OS_STK*)&LED1_TASK_STK[LED1_STK_SIZE-1],LED1_TASK_PRIO);
    OSTaskCreate(flag_task,(void *)0,(OS_STK*)&FLAG_TASK_STK[FLAG_STK_SIZE-1],FLAG_TASK_PRIO);
    OSTaskCreate(scan_task,(void *)0,(OS_STK*)&SCAN_TASK_STK[SCAN_STK_SIZE-1],SCAN_TASK_PRIO);
    OSTaskSuspend(START_TASK_PRIO); //挂起起始任务.
    OS_EXIT_CRITICAL();             //退出临界区(可以被中断打断)
}





void flag_task(void *pdata)
{
    int flags=0;
    u8 err;
    while(1)
    {
        flags=OSFlagPend(flags_key,0X001F,OS_FLAG_WAIT_SET_ANY,0,&err);//等待信号量
        OLED_Clear();
        if(flags&0X0001) {OLED_ShowString(0,0,"LED0",12);LED0=0;delay_ms(500);LED0=1;delay_ms(500);}
        if(flags&0X0002) {OLED_ShowString(0,0,"LED1",12);LED1=0;delay_ms(500);LED1=1;delay_ms(500);}
        if(flags&0X0004) {OLED_ShowString(0,0,"LED0 and LED1",12);LED0=0;LED1=0;delay_ms(500);LED0=1;LED1=1;delay_ms(500);}
        OLED_Refresh_Gram();
        OSFlagPost(flags_key,0X0007,OS_FLAG_CLR,&err);//全部信号量清零
    }
}

//按键扫描任务
void scan_task(void *pdata)
{
    u8 key;
    u8 err;
    while(1)
    {
        key=KEY_Scan(0);
        if(key)OSFlagPost(flags_key,1<<(key-1),OS_FLAG_SET,&err);//发送消息
        delay_ms(10);
    }
}

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值