使用消息邮箱处理串口消息
邮箱用于线程间通信,特点是开销比较低,效率较高
邮箱中的每一封邮件只能容纳固定的 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
通知线程处理