常用单片机编程思想及例程6——自定义消息邮箱机制

目录

 前言

信号量

队列

消息邮箱

全局变量有什么问题?

抢占问题

代码规范问题

耦合性问题

安全性问题

解决方案

例程(以串口为例)


 前言

        有深入理解RTOS原理,或阅读过RTOS源码的同学应该知道:RTOS实现任务间通信通常是由一系列指针进行操作实现的。任务间通信的“有效数据”,其实也是由指针指向一个“变量”或“数组”实现的。

信号量

        信号量,本质是传递一个“事件”。比如:任务A完成发送数据,通过信号量通知任务B。

队列

        队列和信号量原理类似有点类似,只是这里是“变量”。比如:串口接收完成一帧数据,通过队列发送给任务B。

消息邮箱

        本质上邮箱是信号量的拓展,相当于把一个指针定义的变量从一个任务传递到另一个或多个任务中去,这个指针是先发到邮箱,然后等待任务从邮箱里提取指针,这也就传递了指针指向的具体变量值。

全局变量有什么问题?

抢占问题

        两个或多个任务,都要去“使用”同一个全局变量,如果不添加任何“互斥”措施,必定会存在抢占的问题。

代码规范问题

        整个项目只有少数几个全局变量没什么问题,如果是整个项目有几十个,甚至几百个全局变量,你觉得这样的代码,后面好维护吗?经过多次迭代,代码只会越来越难理解,越来越难阅读。

耦合性问题

        全局变量会导致分层不合理与模块化编程相违背,你的全局变量没有归属,既不是任务A,也不是模块A,最终可能“任人宰割”导致“夭折”。

安全性问题

        有一句话怎么说的呢,全局变量是项目的“罪魁祸首”,项目做大之后,一旦有小修改,可能就会引发大Bug。

        全局变量还有很多弊端,这里就不一一描述了,总之一点:慎用全局变量

        为什么推荐在裸机开发中,也尽量避免过多的使用全局变量?------考虑到程序的可读性和可移植性。假如你的项目需要从裸机移植到RTOS,这时就需要考虑原来工程中的全局变量是不是存在抢占问题,原来的逻辑可能要做相应的修改。如果一开始就避免了全局变量的使用,那么在后续的移植过程中只需要把现有的接口切换为RTOS的接口即可,其它逻辑不需要改变。

解决方案

      上边已经对消息邮箱做了简单的说明,那么如何实现一个自己的邮箱呢?自定义消息邮箱实现需要配合队列使用(请参考这里: 队列驱动)。 

/******************my_mq.c**************/
#define __MQ_C
#include "mq.h"
void mq_init(msgqueue *mq, void *msg_pool, uint16_t msg_size, uint16_t max_msgs)
{
	mq->msg_pool = msg_pool;
	mq->msg_size = msg_size;
	mq->max_msgs = max_msgs;
	BufferInit(&mq->msg_rb, mq->msg_size, mq->msg_pool, mq->max_msgs);
}

int mq_send(msgqueue *mq, void *in, uint32_t size)
{
	if(size > mq->msg_size)
	{
		return -1;
	}
	BufferWrite(&mq->msg_rb, in, size);
	return 0;
}

int8_t mq_recv(msgqueue *mq, void *out, uint32_t size)
{
	if(BufferNumToRead(&mq->msg_rb))
	{
		BufferRead(&mq->msg_rb, out, size > mq->msg_size ? mq->msg_size : size);
		return 0;
	}
	return -1;
}

/******************my_mq.c**************/

#ifndef __MQ_H
#define __MQ_H

#include "CircleBuffer.h"

#ifdef __MQ_C
	#define MQ_EXT
#else
	#define MQ_EXT extern
#endif

typedef struct 
{
    uint16_t type;
    uint16_t value;
}sys_msg_t;

typedef struct 
{
	void *msg_pool;
	uint32_t msg_size;
	uint32_t max_msgs;
	tCircleBuffer msg_rb;
}msgqueue;

MQ_EXT msgqueue mq_msg;
	
#define MSG_SIZE		sizeof(sys_msg_t)
#define MAX_MSGS		20
MQ_EXT uint8_t msg_buf[MSG_SIZE * MAX_MSGS];	

typedef enum
{
	MSG_NULL = 0x00,		/*have no message*/
	MSG_LPUART1_RX,		//USART1
	MSG_USART1_RX,			//USART1
	MSG_USART2_RX,			//USART2
}MSG_TYPE;

void mq_init(msgqueue *mq, void *msg_pool, uint16_t msg_size, uint16_t max_msgs);
int mq_send(msgqueue *mq, void *in, uint32_t size);
int8_t mq_recv(msgqueue *mq, void *out, uint32_t size);

#endif

例程(以串口为例)

/**********************usart.c****************/
//低功耗串口1中断函数
void LPUART1_IRQHandler(void)
{
	sys_msg_t send_msg;
	if(LL_USART_IsActiveFlag_IDLE(LPUART1))	//接收空闲中断
	{
		LL_USART_ClearFlag_IDLE(LPUART1);
		send_msg.value = USARTx_Recvice_Data(LPUART1, &lpuart1_ringbuf.rx);
		if(send_msg.value > 0)
		{
			send_msg.type = MSG_LPUART1_RX;
			mq_send(&mq_msg, &send_msg, sizeof(send_msg));
		}
	}
}

//串口1中断函数
void USART1_IRQHandler(void)
{
	sys_msg_t send_msg;
	if(LL_USART_IsActiveFlag_IDLE(USART1))	//接收空闲中断
	{
		LL_USART_ClearFlag_IDLE(USART1);
		send_msg.value = USARTx_Recvice_Data(USART1, &usart1_ringbuf.rx);
		if(send_msg.value > 0)
		{
			send_msg.type = MSG_USART1_RX;
			mq_send(&mq_msg, &send_msg, sizeof(send_msg));
		}
	}
}

//串口2中断函数
void USART2_IRQHandler(void)
{
	sys_msg_t send_msg;
	if(LL_USART_IsActiveFlag_IDLE(USART2))	//接收空闲中断
	{
		LL_USART_ClearFlag_IDLE(USART2);
		send_msg.value = USARTx_Recvice_Data(USART2, &usart2_ringbuf.rx);
		if(send_msg.value > 0)
		{
			send_msg.type = MSG_USART2_RX;
			mq_send(&mq_msg, &send_msg, sizeof(send_msg));
		}
	}
}

/*****************main.c**************/
void main(void)
{
    mq_init(&mq_msg, msg_buf, MSG_SIZE, MAX_MSGS);
	sys_msg_t recv_msg;

	while(1)
	{
		if(mq_recv(&mq_msg, &recv_msg, sizeof(recv_msg)) == 0)
		{
			switch(recv_msg.type)
			{
				case MSG_LPUART1_RX:
                    lpusart1_pro();
					break;
				case MSG_USART1_RX:
					usart1_pro();
					break;
				case MSG_USART2_RX:
					usart2_pro();
					break;
				default:
					break;
			}
		}
	}
}

         当然,这里只是以串口接收处理为例,事实上,该邮箱机制可以在任意地方使用,具体使用方法各位可以自行摸索,这里就不再赘述了。

  • 22
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
混合编程是指在一个程序中同时使用汇编语言和C语言编写代码。这样做的好处是可以充分发挥汇编语言的高效性能,同时又可以利用C语言的易读性和可移植性。 下面是一个简单的程,展示了如何在C语言中调用汇编语言函数: ```c #include <stdio.h> extern int asm_add(int a, int b); int main() { int a = 10, b = 20, sum; sum = asm_add(a, b); printf("sum = %d\n", sum); return 0; } ``` 上面的代码中,`asm_add`是一个汇编语言函数,在C语言中通过`extern`关键字声明。在`main`函数中,我们调用了`asm_add`函数,并将结果打印出来。 下面是`asm_add`函数的汇编语言实现: ```asm section .text global asm_add asm_add: mov eax, edi add eax, esi ret ``` 上面的代码中,`asm_add`函数接收两个参数,分别存放在寄存器`edi`和`esi`中。函数实现将这两个参数相加,并将结果返回。 对于上面的程,我们需要将C语言和汇编语言代码分别保存为`.c`和`.asm`文件,并使用汇编器和编译器编译链接: ```bash nasm -f elf64 -o asm_add.o asm_add.asm gcc -c main.c gcc -o main main.o asm_add.o ``` 上述命令中,`-f elf64`参数指定了汇编语言程序的目标平台为x86-64,`-c`参数表示只编译不链接,最后一个命令将编译好的目标文件链接成可执行文件。 以上就是一个简单的混合编程程,通过在C语言中调用汇编语言函数,实现了高效的计算。实际应用中,混合编程可以用于底层的系统编程和优化性能要求较高的算法实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

陈大本事er

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

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

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

打赏作者

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

抵扣说明:

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

余额充值