最近打算学习RT-Thread,但是由于手上没有现成的开发套件,也懒得去买(有条件的最好还是买一块),于是在公司随手找了一块好用的板子,只要仿真器好用+有一个可用的串口,就可以玩起来了,有兴趣的可以试试,高手勿喷。
首先使用STM32Cubemx ,配合原理图,配置好引脚功能
配置好要用的功能,本次实验可以只配置串口,如果有其他外设可以自己扩展,生成一个附带RT-Thread操作系统的基础工程,接下来开始扩展代码。
本次实验要实现的功能是,在串口中断中,以单个字节的方式接收串口数据,释放信号量,消息队列发送线程获取信号量,以单字节的方式,解析串口数据,当确认收到一包完整的数据后,将数据发送到消息队列,接收消息队列取出数据,打印信息。先看主函数:
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART2_UART_Init();
/*开启串口中断*/
HAL_UART_Receive_IT(&huart2, (uint8_t *)&rDataBuffer, 1);
/* 初始化信号量*/
rt_sem_init(&sem_lock, "sig_lock", 0 , RT_IPC_FLAG_PRIO);
/*创建一个消息队列*/
test_mq = rt_mq_create("test_mq",/*消息队列名字*/
50, /*消息最大长度*/
20, /*消息队列最大容量*/
RT_IPC_FLAG_FIFO);/*队列模式*/
if (test_mq != RT_NULL)
rt_kprintf("消息队列创建成功!\n\n");
receive_thread = /* 线程控制块指针 */
rt_thread_create( "receive", /* 线程名字 */
receive_thread_entry, /* 线程入口函数 */
RT_NULL, /* 线程入口函数参数 */
512, /* 线程栈大小 */
3, /* 线程的优先级 */
20); /* 线程时间片 */
/* 启动线程,开启调度 */
if (receive_thread != RT_NULL)
rt_thread_startup(receive_thread);
else
return -1;
send_thread = /* 线程控制块指针 */
rt_thread_create( "send", /* 线程名字 */
send_thread_entry, /* 线程入口函数 */
RT_NULL, /* 线程入口函数参数 */
512, /* 线程栈大小 */
2, /* 线程的优先级 */
20); /* 线程时间片 */
/* 启动线程,开启调度 */
if (send_thread != RT_NULL)
rt_thread_startup(send_thread);
else
return -1;
}
启动串口中断,初始化信号量,并创建消息队列,创建两个线程,一个接收,一个发送。在串口接收中断回调函数里:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
UNUSED(huart);
rData[rDataCount]=rDataBuffer;
rDataCount++;
//数据缓冲区满
if(rDataCount >= RecvBuffSize)
{
rDataCount = 0;
}
/*重新设置中断*/
HAL_UART_Receive_IT(&huart2, (uint8_t *)&rDataBuffer, 1);
/*释放锁信号*/
rt_sem_release(&sem_lock);
}
发送线程用来解析数据,将每次收到的字节发送到解析程序,让线程程序实现快进快出,增加实时性,当确认收到完整的数据后,将数据发送到消息队列:
static void send_thread_entry(void* parameter)
{
rt_err_t uwRet = RT_EOK;
while (1)
{
/* 获取信号量 */
rt_sem_take(&sem_lock, RT_WAITING_FOREVER);
/* 解析数据--并判断是否为完整包数据*/
CmdReceive(rData[rSendDataCount]);
rSendDataCount++;
if(rSendDataCount >= RecvBuffSize)
{
rSendDataCount = 0;
}
if(Msg_Deal.msg_finish == 1)
{
/* 收到完整包,将数据写入(发送)到队列中 */
uwRet = rt_mq_send(test_mq,/* 写入(发送)队列的ID(句柄) */
/* 写入(发送)的数据 */
&Msg_Deal.msg_Data[Msg_Deal.msg_packsend],
/* 数据的长度 */
sizeof(Msg_Deal.msg_Data[Msg_Deal.msg_packsend]));
Msg_Deal.msg_finish = 0;
if (RT_EOK != uwRet)
{
rt_kprintf("数据不能发送到消息队列!错误代码: %lx\n",uwRet);
}
}
rt_thread_delay(50);
}
}
接收线程里,取出消息队列中的数据,并打印信息,这里还可以进一步扩展为命令解析后的执行操作,待后续完善:
static void receive_thread_entry(void* parameter)
{
rt_err_t uwRet = RT_EOK;
Uart_MessageInfo r_queue;
/* 线程都是一个无限循环,不能返回 */
while (1)
{
/* 队列读取(接收),等待时间为一直等待 */
uwRet = rt_mq_recv( test_mq, /* 读取(接收)队列的ID(句柄) */
&r_queue, /* 读取(接收)的数据保存位置 */
sizeof(r_queue), /* 读取(接收)的数据的长度 */
RT_WAITING_FOREVER); /* 等待时间:一直等 */
if (RT_EOK == uwRet)
{
rt_kprintf("接收的数据:Len=%d Devid=%d SrcAddr=%d DstAddr=%d Cmd=%d
DateLen=%d\n" ,r_queue.Len,r_queue.DevId,r_queue.SrcAddr
,r_queue.DstAddr,r_queue.Cmd,r_queue.DateLen);
}
else
{
rt_kprintf("数据接收出错,错误代码: 0x%lx\n",uwRet);
}
rt_thread_delay(100);
}
}
记录一下实现效果: