一个用于嵌入式开发的简单可靠的消息系统-STOpen消息系统

在一般中低端的嵌入式系统里面,程序要么是跑大循环,要么是跑一个简单的操作系统(比如ucos,rtthread,freertos等等),简单和效率是第一要素,毕竟系统硬件资源有限。
要在这方寸之间,施展十八般武艺,实属具有一定的挑战性。
我们实现了一个简单的类windows的消息系统,让所有的操作都采用消息的机制来传递信息,把模块之间的耦合度降低到最低,并且让程序事务处理起来特别简单,层次分明,逻辑清晰。
可以说这个消息系统是核心中的核心,充分理解和灵活应用消息系统,是我们STOpen项目的必要基础。
该消息系统采用先进先出的方式,后来的覆盖最早的没有来得及处理的消息,所以系统在响应消息的时候不可长时间阻塞,否则会导致消息丢失(当然可以通过加大消息队列的长度来一定程度克服)。该消息系统一般不适合放入密集发生的事件(比如1ms的定时),通常用于响应按键,定时器,通讯接收完成,状态转移消息等事件。
在任何系统设计和程序开发过程中,不适用delay(500)类型的软件延时是一个基本的原则。
该消息队列其本质就是一个环形队列,队列的内容就是一个一个的消息,核心就在消息的结构和使用技巧上面。使用了两个位置计数来记录读取和放入消息的位置。该消息系统支持从中断函数里面发送消息,为中断发送通知打下了足够的基础。略微改动(添加一个信号量)也能支持操作系统的多任务。
消息结构示意图:
在这里插入图片描述

首先我们定义了一个消息类型的结构,两个变量
一个用于保存消息类型:不同的事件用不同的消息通知
一个用于保存消息参数:参数根据不同消息具有不同的意义,可以传递足够的信息

#define MAX_MSG			16	//最大消息数量
typedef	struct __tagMessage {
	UINT32	type;				//消息类型
	UINT32	wParam;				//参数
}MSGTYPE; 
然后,我们定义了一个保存消息的队列,其大小可以配置:
typedef	struct __tagMsgQueue {
	MSGTYPE		Msg_Queue[MAX_MSG];			//消息队列
	UINT16	In;								//消息入队位置
	UINT16	Out;							//消息出队位置
}MSGQUEUE;
消息相关的宏定义如下,根据系统需要增加:
#define		MSG_NULL		0x00000000
#define		MSG_KEYDOWN		0x00000001			//按键按下
#define		MSG_KEYUP		0x00000002			//按键谈起 
#define		MSG_SECOND		0x00000003			//秒钟中断
#define		MSG_TIMER		0x00000004			//定时器中断
#define		MSG_CREATE		0x00000006			//初始化窗口消息
#define		MSG_DESTROY		0x00000007			//销毁窗口消息
#define		MSG_PAINT		0x00000008			//重画窗口消息
#define		MSG_DUMMY		0x0FFFFFFF			//虚假信息,占位

消息系统的初始化
将所有位置初始化为空消息(这一点很重要,后面放入消息和取消息都需要判断这个状态),出队和入队位置指向第一个消息。

void MessageQueueClear(void)
{
	UINT32 i;
	DISABLE_INTERRUPT();    //要关闭中断,防止在清除过程中有中断在放消息,结果导致又被清除
	Msg.In = 0;
	Msg.Out = 0;
	for(i = 0;i < MAX_MSG;i++)
		Msg.Msg_Queue[i].type = MSG_NULL; //注意,不是清除为0,是MSG_NULL
	ENABLE_INTERRUPT();
}

发送一条消息
将新的消息放入队列,如果和要取走的消息位置重复,则覆盖旧的消息。仔细理解第一个条件判断:如果消息入队和出队的地方重合了,并且原来位置有消息,则调整出队消息到下一个位置,该位置放入新的消息。
所以:我们在系统任意两条消息之间,要快速的处理完成任务,返回继续处理下一条消息,这一点很重要。

void SendMessage(MSGTYPE *msg)
{
	DISABLE_INTERRUPT();
	if((Msg.In == Msg.Out) && (Msg.Msg_Queue[Msg.Out].type != MSG_NULL))
		Msg.Out = (Msg.Out + 1) % MAX_MSG;		//覆盖要出队的消息,调整为下一条消息
	Msg.Msg_Queue[Msg.In] = *msg;
	Msg.In = (Msg.In + 1) % MAX_MSG;		//下一次消息入队的地方
	ENABLE_INTERRUPT();
}

获取一条消息
获取消息比较简单,首先判断要获取消息的位置是否有消息,有就取出来,然后调整下一次取消息的位置。

UINT32 GetMessage(MSGTYPE *msg)
{
	DISABLE_INTERRUPT();
	if(Msg.Msg_Queue[Msg.Out].type != MSG_NULL)
	{
		*msg = Msg.Msg_Queue[Msg.Out];
		Msg.Msg_Queue[Msg.Out].type = MSG_NULL;		//清空原来消息
		Msg.Out = (Msg.Out + 1) % MAX_MSG;			//调整下一个位置
		ENABLE_INTERRUPT();
		return	TRUE;
	}
	ENABLE_INTERRUPT();
	return FALSE;
}

一个简单但是在STOpen项目中可以担起重要作用的消息系统构建完成,它使我们的系统开始向一个完备的消息驱动系统迈进,并能提供易于使用和健壮的消息服务,使后面的应用开发起来及其简单和实用。

原创文章,欢迎转载,请注明来源,未经书面允许,请勿用于商业用途。

发布了20 篇原创文章 · 获赞 7 · 访问量 5686

猜你喜欢

转载自blog.csdn.net/huangbinvip/article/details/105018359