51单片机毕设-消息队列os_queue.c


1> 消息队列结构体

// 消息队列结构体
#define MsgQ_SIZE 4  

typedef struct                           // 定义一个队列结构体
{                                                       
    ushort16_t  * Start;                // 指向队列起始地址 
    ushort16_t  * End;                  // 指向队列结束地址
    ushort16_t  * In;                   // 指向插入一条消息的地址
    ushort16_t  * Out;                  // 指向被取出的消息地址
    uchar8_t      Size;                 // 队列数组长度
    uchar8_t      Entries;          	// 已存放消息条数
    ushort16_t    Buffer[MsgQ_SIZE];	// 队列存储数组
} QUEUE_TypeDef;

在这里插入图片描述

如何实现循环队列


2> 消息队列初始化


2.1> 实例化

// 消息队列实例化
static QUEUE_TypeDef idata MsgQ;

void QUEUE_Init(void)
{
    MsgQ.Start   = MsgQ.Buffer;					// 队列头
    MsgQ.End     = MsgQ.Buffer + MsgQ_SIZE - 1; // 队列尾
    MsgQ.In      = MsgQ.Start;					// 插入点
    MsgQ.Out     = MsgQ.Start;					// 取出点
    MsgQ.Size    = MsgQ_SIZE;					// 队列长度,4
    MsgQ.Entries = 0;							// 消息条数
}

图形表示:
2

换个图像表示:
4
初始化后:

指针变量Start,In,Out都指向Buffer[0];
指针变量End指向Buffer[3]
队列长度Size,固定为4;
队列消息条数,为0;
消息内容Buffer,都为0;


2.2> 发挥想象-制作信箱

把消息队列(MsgQ)想成只能放4封信的收件箱;
2.2


3> 消息格式

3


3.1> 消息类型

typedef enum
{
    MessageKey      = 0xFF,         // 按键消息类型
    MessageUsart    = 0xFE,         // 串口消息类型
    MessageInt0     = 0xFD,         // 外部中断0消息类型
    MessageInt1     = 0xFC,         // 外部中断1消息类型
   
    MessageTimer    = 0xF0          // 最小值,不允许其他类型小于它
} MSG_TypeDef;

不同的消息类型,处理方式不同
MessgeTimer,软件定时器Timer消息类型;
软件定时器Timer消息类型需要16bit,【?】


4> 发送消息

/** 
 * @brief	发送消息
 * @message	消息类型
 * @Value   消息值, MessageTimer类型为16bit地址,其他类型下都是8bit数据
 * @retval 	无
 */
void QUEUE_Post(MSG_TypeDef message, ushort16_t value)
{
    EnterCritical();         							// 进入临界态
    Assert(MsgQ.Entries <= MsgQ.Size);	                // 断言,判断是否满

    if(message == MessageTimer)					        // 软件定时器器类型
        *MsgQ.In = value;      					        // 直接赋16bit地址
    else                               					// 常规消息
    {
        GetBit_UshortH8(*MsgQ.In) = message;		    // 把类型赋给高地址
        GetBit_UshortL8(*MsgQ.In) = value & 0x00FF;	    // 把值赋值给低地址
    }
    
	MsgQ.In++;                               			// 消息入口+1       
    
    if(MsgQ.In > MsgQ.End)        		                // 入口越界循环
        MsgQ.In = MsgQ.Start;
    
    MsgQ.Entries++;                        		        // 消息数量+1
    
	ExitCritical();                                  	// 退出临界态
}

发送1条消息好比,向邮箱里投了1封信

4


4.2> 发挥想象-投递信件

投递信件:
在这里插入图片描述


5> 读取消息


5.1> 程序代码

/** 
 * @brief	读取消息
 * @param	无
 * @retval  无
 */
ushort16_t QUEUE_Pend(void)
{    
    short16_t message;
	
    while(MsgQ.Entries == 0)    		    // 等待消息
    { /* 推荐在这儿喂狗 */ };										
    
    message = *MsgQ.Out++;				    // 消息出口+1
    MsgQ.Entries--;						    // 消息数量-1
    
    if (MsgQ.Out > MsgQ.End) 	            // 出口越界循环
        MsgQ.Out = MsgQ.Start;
    
    return(message);					    // 返回消息
}

5

指针变量【In】指向可以插入信息的地址;
指针变量【Out】指向要取的信息的地址;


5.2> 发挥想象-读取信件

5


6> 解析消息-main函数

int main(void)
{
	ushort16_t idata msg;
    uchar8_t   idata msg_type;
    uchar8_t   idata msg_value;
	
	// 填充初始化程序
	while (1) {
        msg      = QUEUE_Pend(); 	// 读取信息
        msg_type = GetMsgType(msg);
        msg_value = GetMsgData(msg);
		
        switch (msg_type) {
			
		case MessageKey: 
				// 按键处理
				break;
		
		case MessageUart:
				// 串口信息处理
			    break// 填充消息处理函数
		
        default:   
				Function(msg);	// 软件定时器消息处理
                break;
        }
    }
}

整个流程:

Step 1> 先初始化系统;
Step 2> 进入While大循环;
Step 3> QUEUE_Pend()函数读取信息;
Step 4> switch-case根据信息类型,执行对应的处理函数;

这段代码是一个关于时间片轮法的实现,但是需要注意以下事项: 1. 代码中使用了 `State` 类型,但是没有给出其定义,需要确认其定义是否正确,否则代码会报错。 2. `ready_queue = processes.copy()` 将 `processes` 列表复制一遍,但是如果 `processes` 中的元素是对象,复制得到的只是对象的引用,需要确认是否符合需要。 3. `any(p.state != State.TERMINATED for p in ready_queue)` 用于判断是否还有进程未完成,需要确认代码中是否正确地修改了进程的状态。 4. `processes_in_queue = len(ready_queue)` 用于记录当前队列中的进程数,需要确认代码中是否正确地维护了进程队列。 5. `current_process = ready_queue.pop(0)` 用于从队列中取出下一个进程,需要确认代码中是否正确地维护了队列的顺序。 6. `current_process.run(time_slice)` 用于模拟进程运行,需要确认 `run()` 方法的实现是否正确。 7. `current_process.end_time = current_time` 用于记录进程结束时间,需要确认代码中是否正确地记录了时间信息。 8. `ready_queue.append(current_process)` 用于将进程重新加入队列,需要确认代码中是否正确地维护了队列的顺序。 9. `return [p.end_time - p.start_time for p in processes],[(p.end_time - p.start_time) / p.cpu_time for p in processes],time_slice` 返回了进程的运行时间和 CPU 利用率,需要确认代码中的计算是否正确。 以上是需要注意的事项,希望对你有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值