RT-Thread消息邮箱和队列在STM32串口中的应用

使用消息邮箱处理串口消息

邮箱用于线程间通信,特点是开销比较低,效率较高
邮箱中的每一封邮件只能容纳固定的 4 字节内容(针对 32 位处理系统,指针为 4 个字节大小,一封邮件恰好能够容纳一个指针

代码:

rt_thread_app.c:

#include "rtthread.h"
#include "rt_thread_app.h"
#include "bsp_usart.h"

static rt_thread_t usart1_thread = RT_NULL;
rt_mailbox_t usart1_mail = RT_NULL;

static void usart_receive_thread_entry(void* parameter);

int rt_thread_app_init(void)
{
  /* 创建一个邮箱 */
	usart1_mail = rt_mb_create("usart1_mail", /* 邮箱名字 */
                              10,         /* 邮箱大小 */
                             RT_IPC_FLAG_FIFO);/* 信号量模式 FIFO(0x00)*/
	if (usart1_mail != RT_NULL)
		rt_kprintf("邮箱创建成功!\n\n");
    
	usart1_thread =                          /* 线程控制块指针 */
		rt_thread_create( "receive",              /* 线程名字 */
                          usart_receive_thread_entry,   /* 线程入口函数 */
                          RT_NULL,             /* 线程入口函数参数 */
                          512,                 /* 线程栈大小 */
                          3,                   /* 线程的优先级 */
                          20);                 /* 线程时间片 */
                   
    /* 启动线程,开启调度 */
   if (usart1_thread != RT_NULL)
        rt_thread_startup(usart1_thread);
    else
       return -1;
}

//串口消息处理线程
static void usart_receive_thread_entry(void* parameter)
{		
	rt_err_t uwRet = RT_EOK;
	stUsartMsgBuffer *usart1Msg;
  /* 任务都是一个无限循环,不能返回 */
	while(1)
	{
		/* 等待接邮箱消息 */
		uwRet = rt_mb_recv(usart1_mail,              /* 串口消息的邮箱对象句柄 */
						  (rt_uint32_t*)&usart1Msg,  /* 接收串口的邮箱消息 */
						  RT_WAITING_FOREVER);       /* 指定超时事件,一直等 */
		
		if(RT_EOK == uwRet) /* 如果接收完成并且正确 */
		{
			rt_kprintf ( "邮箱的内容是:%s\r\n", usart1Msg->rxBuf);		
		}
		else
			rt_kprintf ( "邮箱接收错误!错误码是0x%x\n", uwRet);
		
		UsartBuffer_Reset(usart1Msg);		
  }
}

bsp_usart.c:

#define USART1_RX_BUFFER_SIZE    256
#define EN_USART1_RX  1

static u8 usart1RxBuffer[USART1_RX_BUFFER_SIZE];
stUsartMsgBuffer usart1MsgBuffer;

void UsartBuffer_Init(stUsartMsgBuffer *usartBuf, USART_TypeDef *USARTx, u8 *rxbuf, u32 rxbufSize)
{
	usartBuf->rxIndex   = 0;
	usartBuf->status    = 0;
	usartBuf->rxBuf     = rxbuf;
	usartBuf->txBufSize = 0;
	usartBuf->USARTx    = USARTx;
	memset(usartBuf->rxBuf, 0, rxbufSize);
}

void UsartBuffer_Reset(stUsartMsgBuffer *usartBuf)
{
	usartBuf->rxIndex = 0;
	memset(usartBuf->rxBuf, 0, usartBuf->rxBufSize);
}

void Usart_Send(stUsartMsgBuffer *usartBuf, u8 *txbuf, u32 txbufSize)
{
	u16 i, ucErrTime;

    for (i = 0; i < txbufSize; i++)
    {
        ucErrTime = 0;
        while (USART_GetFlagStatus(usartBuf->USARTx, USART_FLAG_TC) == RESET)
        {
            if (ucErrTime++ > 2000)    //这个值更具CPU的频率不同而有所改变
            {
                printf("USART_Write error: Write timeout\r\n");
                return;
            }
        }
        USART_SendData(usartBuf->USARTx, (uint8_t)txbuf[i]);
    }
}

void UsartIRQHandler(stUsartMsgBuffer *usartBuf)
{
    u8 retval;
	
	if (!usartBuf)
		return;
	
    if (USART_GetITStatus(usartBuf->USARTx, USART_IT_RXNE) == SET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
    {
        retval = USART_ReceiveData(usartBuf->USARTx);
        if (usartBuf->rxIndex > usartBuf->rxBufSize - 1) //长度超出,位找下面的头做准备
        {
            usartBuf->rxBuf[0] = usartBuf->rxBuf[usartBuf->rxIndex - 1]; //把最后一个搬到第一个
            usartBuf->rxIndex = 1;                                           //指向第二个
        }
        usartBuf->rxBuf[usartBuf->rxIndex++] = retval;         //(USART1->DR);	//读取接收到的数据
    }
    if (USART_GetITStatus(usartBuf->USARTx, USART_IT_IDLE) == SET)   //idle interrupt
    {
		extern rt_mailbox_t usart1_mail;
        USART_ReceiveData(usartBuf->USARTx);                         //clear idle interrupt
        usartBuf->status = 1;       
		rt_mb_send(usart1_mail, (rt_uint32_t)usartBuf);
//		USART_ITConfig(usartBuf->USARTx, USART_IT_RXNE, DISABLE); 
	}
}

void USART1_IRQHandler(void) //串口1中断服务程序
{
    UsartIRQHandler(&usart1MsgBuffer);
}

使用消息队列处理串口消息

消息队列是另一种常用的线程间通讯方式,是邮箱的扩展。可以应用在多种场合:线程间的消息交换、使用串口接收不定长数据等;
消息队列是一种异步的通信方式
消息队列是直接的数据内容复制,所以不需要动态分配内存作为缓存,只需用了局部变量作为缓存保存消息,这样也就免去动态内存分配的烦恼。

代码:

#include "rtthread.h"
#include "rt_thread_app.h"
#include "bsp_usart.h"

static rt_thread_t usart1_thread = RT_NULL;

rt_mq_t usart1_mq = RT_NULL;

static void usart_receive_thread_entry(void* parameter);

int rt_thread_app_init(void)
{
   /* 创建一个消息队列 */
	usart1_mq = rt_mq_create("usart1_mq",/* 消息队列名字 */
                     40,     /* 消息的最大长度 */
                     20,    /* 消息队列的最大容量 */
                     RT_IPC_FLAG_FIFO);/* 队列模式 FIFO(0x00)*/
  if (usart1_mq != RT_NULL)
    rt_kprintf("消息队列创建成功!\n\n");

	usart1_thread =                          /* 线程控制块指针 */
		rt_thread_create( "receive",              /* 线程名字 */
                          usart_receive_thread_entry,   /* 线程入口函数 */
                          RT_NULL,             /* 线程入口函数参数 */
                          512,                 /* 线程栈大小 */
                          3,                   /* 线程的优先级 */
                          20);                 /* 线程时间片 */
                   
    /* 启动线程,开启调度 */
   if (usart1_thread != RT_NULL)
        rt_thread_startup(usart1_thread);
    else
       return -1;
}

static void usart_receive_thread_entry(void* parameter)
{		
	rt_err_t uwRet = RT_EOK;
	stUsartMsgBuffer usart1Msg;
  /* 任务都是一个无限循环,不能返回 */
	while(1)
	{
		/* 等待接邮箱消息 */
		uwRet = rt_mq_recv(usart1_mq,	/* 读取(接收)队列的ID(句柄) */
							&usart1Msg,			/* 读取(接收)的数据保存位置 */
							sizeof(usart1Msg),		/* 读取(接收)的数据的长度 */
							RT_WAITING_FOREVER); 	/* 等待时间:一直等 */
		
		if(RT_EOK == uwRet) /* 如果接收完成并且正确 */
		{
			rt_kprintf ( "消息队列的内容是: %s\n\n", usart1Msg.rxBuf);		
		}
		else
			rt_kprintf ( "消息队列接收错误!错误码是0x%x\n", uwRet);
	}
}
void UsartIRQHandler(stUsartMsgBuffer *usartBuf)
{
    u8 retval;
	
	if (!usartBuf)
		return;
	
    if (USART_GetITStatus(usartBuf->USARTx, USART_IT_RXNE) == SET) //接收中断
    {
        retval = USART_ReceiveData(usartBuf->USARTx);
        if (usartBuf->rxIndex > usartBuf->rxBufSize - 1)
        {
            usartBuf->rxBuf[0] = usartBuf->rxBuf[usartBuf->rxIndex - 1]; 
            usartBuf->rxIndex = 1;                                          
        }
        usartBuf->rxBuf[usartBuf->rxIndex++] = retval;        
    }
    if (USART_GetITStatus(usartBuf->USARTx, USART_IT_IDLE) == SET)   //idle interrupt
    {
		extern rt_mq_t usart1_mq;
        USART_ReceiveData(usartBuf->USARTx);                         //clear idle interrupt
       // usartBuf->status = 1;       
		rt_mq_send(	usart1_mq,	/* 写入(发送)队列的ID(句柄) */
                    usartBuf,			/* 写入(发送)的数据 */
                    sizeof(stUsartMsgBuffer));			/* 数据的长度 */
                    
		UsartBuffer_Reset(usartBuf);
//		USART_ITConfig(usartBuf->USARTx, USART_IT_RXNE, DISABLE); 
	}
}

void USART1_IRQHandler(void) //串口1中断服务程序
{
    UsartIRQHandler(&usart1MsgBuffer);
}

后记:

两种方式都是在中断中接收数据,在线程中处理处理数据,处理数据是比较耗时的工作。这样的操作是将中断的处理分为上半部底半部。在Linux中对于中断的处理也是这类似这样处理的。
上半部——耗时短的工作放在中断中处理
底半部——耗时长的工作通过IPC通知线程处理

上一篇:Linux记录-集群时间同步解决方案


下一篇:STM32F4移植FreeRTOS V10.3.1