Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
用消息队列方式实现串口2数据接收
发布于 2020-03-28 16:11:08 浏览:6876
订阅该版
**作业题目2:用消息队列方式实现串口2数据接收** 大家提交作业时,直接在本贴下方跟贴即可
查看更多
58
个回答
默认排序
按发布时间排序
ddllxxrr
2020-03-31
这家伙很懒,什么也没写!
```c #include
#include
#include
#include
#include "uart_app.h" #define FKB_UART_NAME "uart2" /* 用于接收消息的信号量 */ static struct rt_semaphore rx2_sem; static struct rt_semaphore rx2_timeout_sem; static rt_device_t serial2; //串口3消息队列接收缓存 #define THREAD_PRIORITY 25 #define THREAD_TIMESLICE 5 /* 消息队列控制块 */ static struct rt_messagequeue rx2_mq; static rt_uint8_t rx2_msg_pool[2048]; static char rx2data[256]; static rt_int16_t index2; static void uart2cmd_thread_entry(void *parameter) { char buf[256]; rt_uint16_t len=0; int result; while(1) { /* 从消息队列中接收消息 */ if (rt_mq_recv(&rx2_mq, &buf, sizeof(buf), 0) == RT_EOK) { rt_kprintf("R23data: "); for(len=0;len<255;len++) { rt_kprintf(" %x",buf[len]); } rt_kprintf(" \n "); rt_kprintf(" rt_mq_recv\n "); } rt_kprintf("scan uart2cmd\n "); rt_thread_mdelay(5000); } } static void uart2fkbapp_thread_entry(void *parameter) { char buf[256]; rt_uint8_t cnt = 0; rt_uint16_t len=0; int result; static rt_uint16_t index2_old,index2_last; index2_old=0; index2_last=0; while(1) { if(index2!=0)index2_old=index2; rt_thread_mdelay(50); if(index2!=0)index2_last=index2; if((index2_old==index2_last)&&(index2_old>0)&&(index2_last>0))//超时收完一帧数据 { //发送消息队列 //====可把长度放置在内存数组后面或者前面,把长度作为数据发送到消息队列里面=== result = rt_mq_send(&rx2_mq, rx2data, index2); if (result != RT_EOK) { rt_kprintf(" send rx2_mq ERR\n "); } index2 =0; index2_old =0; index2_last=0; rt_kprintf(" rx2data is one packet\n"); } } } /* 接收数据回调函数 */ static rt_err_t uart_input(rt_device_t dev, rt_size_t size) { /* 串口接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */ rt_sem_release(&rx2_sem); return RT_EOK; } //串口3---单字节接收 static void serial2_RX_thread_entry(void *parameter) { char ch; index2=0; while (1) { /* 从串口读取一个字节的数据,没有读取到则等待接收信号量 */ while (rt_device_read(serial2, -1, &ch, 1) != 1) { /* 阻塞等待接收信号量,等到信号量后再次读取数据 */ rt_sem_take(&rx2_sem, RT_WAITING_FOREVER); } rx2data[index3++] = ch; } } int uart2_init_app(void) { rt_err_t ret = RT_EOK; // char uart_name[RT_NAME_MAX]; rt_err_t result; rt_kprintf(" uart2_init_app\n"); /* 查找系统中的串口设备 */ serial2 = rt_device_find(FKB_UART_NAME); if (!serial2) { rt_kprintf("find uart2 failed!\n"); return RT_ERROR; } /* 初始化消息队列 */ result = rt_mq_init(&rx2_mq, "rx2_mq", &rx2_msg_pool[0], /* 内存池指向msg_pool */ 256, /* 每个消息的大小是 256 字节 */ sizeof(rx2_msg_pool), /* 内存池的大小是msg_pool的大小 */ RT_IPC_FLAG_FIFO); /* 如果有多个线程等待,按照先来先得到的方法分配消息 */ if (result != RT_EOK) { rt_kprintf("init rx2_mq message queue failed.\n"); return -1; } /* 初始化信号量 */ rt_sem_init(&rx2_sem, "rx2_sem", 0, RT_IPC_FLAG_FIFO); rt_sem_init(&rx2_timeout_sem, "rx2_timeout_sem", 0, RT_IPC_FLAG_FIFO); /* 以中断接收及轮询发送模式打开串口设备 */ rt_device_open(serial2, RT_DEVICE_FLAG_INT_RX); /* 设置接收回调函数 */ rt_device_set_rx_indicate(serial2, uart_input); /* 发送字符串 */ // rt_device_write(serial2, 0, str, (sizeof(str) - 1)); /* 创建 serial 线程 */ rt_thread_t thread = rt_thread_create("serial2", serial3_RX_thread_entry, RT_NULL, 1024, 25, 10); /* 创建成功则启动线程 */ if (thread != RT_NULL) { rt_thread_startup(thread); rt_kprintf(" uart2 rx thread\n"); } rt_thread_t uart2app_thread = rt_thread_create("uart2app", uart2fkbapp_thread_entry, RT_NULL, 1024, RT_THREAD_PRIORITY_MAX / 2, 20); if (uart2app_thread != RT_NULL) { rt_thread_startup(uart2app_thread); } rt_thread_t uart2cmd_thread = rt_thread_create("uart2cmd", uart2cmd_thread_entry, RT_NULL, 1024, RT_THREAD_PRIORITY_MAX / 2, 20); if (uart2cmd_thread != RT_NULL) { rt_thread_startup(uart2cmd_thread); } return ret; } /* 导出到 msh 命令列表中 */ MSH_CMD_EXPORT(uart2_init_app, uart2 fkb); ```
风雨潇潇
2020-03-29
这家伙很懒,什么也没写!
在上个课程移植好RTThread_Nano工程的基础上进行修改。 限制于硬件,此次作业使用USART3进行实验。 以下作业根据个人理解作答,如有偏题请指正。 使用STM32CUBEMX配置USART3,生成工程。建议先备份以前代码,CUBE新生成的工程会覆盖一些代码,如果生成工程后出现问题或者不清楚的话可以对比以前的代码先改回来,参考上一课题的和官方文档。 [attach]14285[/attach] 此时我们需要用到消息队列,需要先在rtconfig.h文件中打开它的配置 [attach]14286[/attach] 接着实现信号量和串口线程的创建 ```c /* 消息队列中用到的放置消息的内存池 */ static rt_uint8_t msg_pool[1024]; struct rt_messagequeue mq; void uasrt_Task_Init(void) { rt_err_t result; /* 初始化消息队列 */ result = rt_mq_init(&mq, "mqt", &msg_pool[0], /* 内存池指向 msg_pool */ 1, /* 每个消息的大小是 1 字节 */ sizeof(msg_pool), /* 内存池的大小是 msg_pool 的大小 */ RT_IPC_FLAG_FIFO); /* 如果有多个线程等待,按照先来先得到的方法分配消息 */ if (result != RT_EOK) { rt_kprintf("init message queue failed.\n"); return; } usart_thread = rt_thread_create("uasrt_thread", usart_thread_entry, RT_NULL, 512, 3, 10); if(usart_thread != RT_NULL) { rt_thread_startup(usart_thread); } __HAL_UART_ENABLE_IT(&huart3, UART_IT_RXNE); } ``` 在创建的串口线程中,接收消息队列,使用帧间间隔的方式判断一包不定长数据的接收。 在接受完成后判断接收的消息,是否是控制LED的指令,LedOff/LedOn;从而进行对LED的控制。 ```c void usart_thread_entry(void *parameter) { static FlagStatus RecFlag = RESET; static uint8_t recBuf; while(1) { if (rt_mq_recv(&mq, &recBuf, 1, RT_WAITING_NO) == RT_EOK) { RecFlag = 1; RxBuf[Pos++] = recBuf; } else { if(RecFlag) { rt_thread_mdelay(20); //帧间隔 if (rt_mq_recv(&mq, &recBuf, 1, RT_WAITING_NO) != RT_EOK) { //接收完成 rt_kprintf("Usart3 Receive OK!\n"); if(strstr((char*)RxBuf,"LedOn")!=NULL) { HAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_RESET); } if(strstr((char*)RxBuf,"LedOff")!=NULL) { HAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_SET); } Pos = 0; RecFlag = RESET; } else { RxBuf[Pos++] = recBuf; } } } } } ``` 在串口中断里发送消息 ```c void USART3_IRQHandler(void) { /* USER CODE BEGIN USART3_IRQn 0 */ uint8_t recByte; if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_RXNE) && __HAL_UART_GET_IT_SOURCE(&huart3,UART_IT_RXNE)) //接收中断 { if(HAL_UART_Receive(&huart3,&recByte,1,10) == HAL_OK) { rt_mq_send(&mq, &recByte, 1); } } /* USER CODE END USART3_IRQn 0 */ HAL_UART_IRQHandler(&huart3); /* USER CODE BEGIN USART3_IRQn 1 */ __HAL_UART_ENABLE_IT(&huart3, UART_IT_RXNE); /* USER CODE END USART3_IRQn 1 */ } ``` 在board.c中要初始化USART3, ```c void rt_hw_board_init() { #if 0 /* System Clock Update */ SystemCoreClockUpdate(); /* System Tick Configuration */ _SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND); #endif HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); MX_USART3_UART_Init(); MX_IWDG_Init(); //MX_TIM1_Init(); /* Call components board initial (use INIT_BOARD_EXPORT()) */ #ifdef RT_USING_COMPONENTS_INIT rt_components_board_init(); #endif #if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP) rt_system_heap_init(rt_heap_begin_get(), rt_heap_end_get()); #endif } ``` [attach]14287[/attach]
风雨潇潇
2020-03-29
这家伙很懒,什么也没写!
实现方法二: 使用串口的空闲中断来判断是否接收完成一包数据,完成后触发中断,发送消息。 创建一个新的线程。 ```c /* 消息队列中用到的放置消息的内存池 */ rt_uint8_t msg_pool[100] = {0}; rt_uint8_t RxBuf[50] = {0};//串口接收缓冲区 void usidle_thread_entry(void *parameter); void uasrtidle_Task_Init(void) { rt_err_t result; /* 初始化消息队列 */ result = rt_mq_init(&mq, "mqt", &msg_pool[0], /* 内存池指向 msg_pool */ 50, /* 每个消息的大小是 50 字节 */ sizeof(msg_pool), /* 内存池的大小是 msg_pool 的大小 */ RT_IPC_FLAG_FIFO); /* 如果有多个线程等待,按照先来先得到的方法分配消息 */ if (result != RT_EOK) { rt_kprintf("init message queue failed.\n"); return; } usidle_thread = rt_thread_create("usidle_thread", usidle_thread_entry, RT_NULL, 1024, 3, 10); if(usidle_thread != RT_NULL) { rt_thread_startup(usidle_thread); } __HAL_UART_ENABLE_IT(&huart3, UART_IT_RXNE); __HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE); } void usidle_thread_entry(void *parameter) { rt_err_t uwRet = RT_EOK; uint8_t recBuf[50] = {0}; while(1) { uwRet = rt_mq_recv(&mq, recBuf, sizeof(recBuf), RT_WAITING_FOREVER); if (uwRet == RT_EOK) { //接收完成 rt_kprintf("Usart3 Receive OK!\n"); if(strstr((char*)recBuf,"LedOn")!=NULL) { HAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_RESET); } if(strstr((char*)recBuf,"LedOff")!=NULL) { HAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_SET); } memset(RxBuf,0,Pos);//清零 Pos = 0; } else { rt_kprintf("Usart3 Receive Faile!\n"); } } } ``` 在中断函数里接收数据,和发送消息 ```c void USART3_IRQHandler(void) { /* USER CODE BEGIN USART3_IRQn 0 */ uint8_t recByte; //uint8_t recBuf[50]; if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_RXNE) && __HAL_UART_GET_IT_SOURCE(&huart3,UART_IT_RXNE)) //接收中断 { if(HAL_UART_Receive(&huart3,&recByte,1,10) == HAL_OK) { RxBuf[Pos++] = recByte; } } else if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE)) //空闲中断 { rt_mq_send(&mq, RxBuf, sizeof(RxBuf)); __HAL_UART_CLEAR_IDLEFLAG(&huart3); } /* USER CODE END USART3_IRQn 0 */ HAL_UART_IRQHandler(&huart3); /* USER CODE BEGIN USART3_IRQn 1 */ __HAL_UART_ENABLE_IT(&huart3, UART_IT_RXNE); /* USER CODE END USART3_IRQn 1 */ } ``` 实现效果和第一中方式一样
xinmeng_wit
2020-03-29
这家伙很懒,什么也没写!
用uart3进行实验。 主要逻辑为: 在串口3的接收中断里判断有没有收到回车符'\n',若收到了回车符,则将收到的字符串通过消息队列发送出去。 另外专门有一个线程获取消息队列的消息,若获取到消息,则用rt_kprint将消息打印出来。 代码如下: 串口接收中断: ```c void UART3_IRQHandler(void) { rt_err_t uwRwet=RT_EOK; //UART2_SendByte('t'); switch( UART3_GetITFlag() ) { case UART_II_LINE_STAT: // 线路状态错误 UART3_GetLinSTA(); UART3_SendByte('x'); break; case UART_II_RECV_RDY: // 数据达到设置触发点 uart_buf[i++]=UART3_RecvByte(); if(uart_buf[i-1]=='\n') { uwRwet=rt_mq_send(uart_mq, uart_buf, sizeof(uart_buf)); if(uwRwet!=RT_EOK) { rt_kprintf("消息队列发送消息失败,%x\r\n",uwRwet); } i=0; memset(uart_buf,0,sizeof(uart_buf)); } //rt_kprintf("uart2接收完成\r\n"); //UART3_SendByte('w'); break; case UART_II_RECV_TOUT: // 接收超时,暂时一帧数据接收完成 uart_buf[i]='\0'; i=0; UART3_SendByte('f'); //rt_kprintf("uart2接收完成\r\n"); break; case UART_II_THR_EMPTY: // 发送缓存区空,可继续发送 UART3_SendByte('a'); break; case UART_II_MODEM_CHG: // 只支持串口0 break; default: break; } } ``` 接收线程: ```c static void uart_rec_thread_entry(void *parameter) { rt_err_t uwRet=RT_EOK; uint8_t rec_buf[32]; while(1) { uwRet=rt_mq_recv(uart_mq, rec_buf, 32, RT_WAITING_FOREVER); if(uwRet==RT_EOK) { rt_kprintf("收到的消息为:%s",rec_buf); } } } ``` 效果: [attach]14310[/attach]
瑞尧
2020-03-30
这家伙很懒,什么也没写!
上次看门狗的作业用studio做了之后感觉学习效果不太好,所以这次就重新用keil移植了新的工程(尝试了一下,HAL和标准库都是可以的,大家有兴趣的可以都试一遍) 开发环境:硬件(自制STM32F407ZG开发板)+软件(keil V5.26) 需求:使用消息队列接收串口4(由于板子上没有引出串口2接口,故此使用串口4代替)数据 需要用到的资源:串口,消息队列 ## 实现过程 配置串口在上一次的finsh控制台实现时已经配置了,直接复制一份即可使用,不过多叙述 串口接收中断,采用缓存接收形式,等待串口数据接收完成,在串口处于空闲状态时,通过消息队列发送数据,代码如下: ```c #ifdef RT_USING_UART4 void UART4_IRQHandler(void) { u8 recv; if( USART_GetFlagStatus(UART4,USART_FLAG_RXNE)!=RESET ) // 串口接收数据 { /* 数据接收至缓存中 */ recv = (u8)USART_ReceiveData(UART4); if (UART4_RX_STA < UART4_RECV_LEN) { UART4_RX_BUF[UART4_RX_STA++] = recv; } USART_ClearFlag(UART4, USART_IT_RXNE); } if( USART_GetFlagStatus(UART4, USART_FLAG_IDLE)==SET) { /* 等待接收完成,集中发送数据 */ UART4_RX_BUF[UART4_RX_STA] = '\0'; rt_mq_send(&mq, UART4_RX_BUF, UART4_RX_STA); UART4_RX_STA = 0; /* * 此处一定使用接收函数来消除标志 * 用标志清除函数基本无效 */ USART_ReceiveData(UART4); } } #endif ``` 消息队列接收线程配置,使用官网文档示例代码即可,无需做其他修改 ```c #ifdef RT_USING_UART4 static rt_thread_t usart4_recv_thread = RT_NULL; static rt_uint8_t msg_pool[2048]; static struct rt_messagequeue mq; void uart4_recv_thread_entry(void *parameter) { rt_err_t recv_status = RT_EOK; uint8_t recvBuf[30] = {0}; while(1) { rt_thread_mdelay(50); recv_status = rt_mq_recv(&mq, recvBuf, sizeof(recvBuf), RT_WAITING_FOREVER); if (recv_status == RT_EOK) { rt_kprintf("Receive message from UART4:"); rt_kprintf("%s\n",recvBuf); if(strstr((char*)recvBuf, "LEDON")) { LED1 = 0; } if(strstr((char*)recvBuf, "LEDOFF")) { LED1 = 1; } } } } int recv_sample(void) { rt_err_t result; /* 初始化消息队列 */ result = rt_mq_init(&mq, "mqt", &msg_pool[0], /* 内存池指向 msg_pool */ 30, /* 每个消息的大小是30字节 */ sizeof(msg_pool), /* 内存池的大小是 msg_pool 的大小 */ RT_IPC_FLAG_FIFO); /* 如果有多个线程等待,按照先来先得到的方法分配消息 */ if (result != RT_EOK) { rt_kprintf("init message queue failed.\n"); return -RT_ERROR; } /* 创建线程 */ usart4_recv_thread = rt_thread_create("u4_recv", uart4_recv_thread_entry, RT_NULL, 512, 10, 10); if(usart4_recv_thread != RT_NULL) { /* 启动线程 */ rt_thread_startup(usart4_recv_thread); rt_kprintf("usart4_recv_thread start!\n"); } } INIT_APP_EXPORT(recv_sample); #endif ``` 实现效果如下: [attach]14328[/attach]
小半仙
2020-03-30
这家伙很懒,什么也没写!
建议尝试 信号量+DMA+回调 实现
BruceTan
2020-03-30
这家伙很懒,什么也没写!
一、开发环境: 1、硬件环境:野火霸道F103开发板 2、软件环境:RT-Thread Nano 源码、ST 3.5固件库、MDK5.25 二、实验目标: 使用消息队列的方式实现串口2的数据接收 三、实验思路: 思路1、使用串口接收中断,将一个一个的字节数据发送给消息队列。开一个线程专门用来取出消息队列的数据(通过超时的方式)。如果获取消息队列的数据时,返回超时,说明串口空闲了,判断下有没有收到数据,有的话就进行数据解析。 PS:这种方式下,如果发送方发送的数据间隔小于超时间隔,会导致接收消息队列永远不会超时,也就是说数据会得不到处理。 思路2、使用串口接收中断和空闲中断,当串口有数据过来的时候,将数据存到全局缓冲区中。当空闲的时候,触发空闲中断,这时候可以将数组的首地址和接受到的数据长度发送给消息队列。开一个线程专门用来接收消息队列的数据(永久等待的方式),如果消息队列获取到消息,就将消息拷贝出来,清空全局缓冲区,然后进行数据解析。 PS:这种方式下,如果数据在处理线程中还没有拷贝完成,串口又来数据了,就可能导致数据被破坏。 两种方式都各有利弊,这是我自己的思路,感觉应该还有更好的处理方式的。 我使用的是第一种方式来进行实现。 四、实验步骤: 1、添加串口驱动,初始化串口,设置串口为接收中断 ``` void USART_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); //GPIO USART2-Tx GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); //GPIO USART2-Rx GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); //USART2-Config USART_InitStructure.USART_BaudRate = 115200; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No ; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART2, &USART_InitStructure); //NVIC-Config NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); //INT-Select USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); //USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);//添加串口空闲中断使能,请不要使用USART_IT_RXNE|USART_IT_IDLE,记住分开写两条语句 USART_Cmd(USART2, ENABLE); USART_ClearFlag(USART2, USART_FLAG_TC);//清发送完成标志位 } ``` ``` void USART2_IRQHandler(void) { u8 data; if( USART_GetFlagStatus(USART2, USART_FLAG_RXNE) == SET ) { data = (uint8_t)USART_ReceiveData(USART2); rt_mq_send(usart2_recv_msg_mq, (void*)&data, 1);/* 将接收到的数据发送线程 */ USART_ClearFlag(USART2, USART_FLAG_RXNE); } if( USART_GetFlagStatus(USART2, USART_FLAG_ORE) == SET ) { USART_ClearFlag(USART2, USART_FLAG_ORE); } } ``` 2、创建一个消息队列,每一个消息的大小为1字节,消息队列总大小为256字节。 ``` usart2_recv_msg_mq = rt_mq_create("usart2_recv_msg_mq", /* 创建消息队列用于串口2数据接收 */ 1, /* 消息队列最大长度 byte */ 256, /* 消息的数量 */ RT_IPC_FLAG_FIFO); /* 如果有多个线程等待此消息队列,采用先进先出的方式进行线程切换 */ if( usart2_recv_msg_mq != RT_NULL ) rt_kprintf("usart2_recv_msg_mq 消息队列创建成功!\n"); else rt_kprintf("usart2_recv_msg_mq 消息队列创建失败!\n"); ``` 3、创建一个线程,专门用来接收来自串口消息队列的数据。 ``` void usart2_recv_thread_entry(void *parameter) { uint8_t *buffer = RT_NULL;/* 用于分配存储消息的内存指针 */ uint8_t data; uint16_t index = 0; rt_err_t res; buffer = rt_malloc(256);/* 分配一个256字节的缓冲区 */ if( buffer == RT_NULL ) { rt_kprintf("usart2_recv_thread_entry 内存分配失败!\n"); return ; } while(1) { res = rt_mq_recv(usart2_recv_msg_mq, (void*)&data, 1, 40);/* 从消息队列中接收消息到 data */ if( RT_EOK == res )/* 有数据过来,将数据进行保存 */ { index = index >= 254 ? 254 : index; buffer[index++] = data; } else if( -RT_ETIMEOUT == res )/* 超出40ms没有接收到数据,说明串口空闲了 */ { if( index > 0 )/* 如果缓冲区中有数据,进行数据处理 */ { buffer[index] = '\0'; rt_kprintf("Usart2 Recv Buffer : %s \n", buffer); if( strstr((char *)buffer, "BeepOn") != NULL ) { BeepOn(); } if( strstr((char *)buffer, "BeepOff") != NULL ) { BeepOff(); } index = 0; } } else if( -RT_ERROR == res ) { rt_kprintf("接收消息错误!"); } } } ``` 五、实验结果: [attach]14356[/attach]
赵撵猪
2020-03-30
这家伙很懒,什么也没写!
[i=s] 本帖最后由 赵撵猪 于 2020-3-30 21:52 编辑 [/i] [list=1] [*]配置cubemx uart2作为本次实验的串口 ![110,0](https://www.rt-thread.org/qa/forum.php?mod=image&aid=14357&size=300x300&key=521d1a3ae7d08250&nocache=yes&type=fixnone) ,配置RTT打开消息队列 ![110,0](https://www.rt-thread.org/qa/forum.php?mod=image&aid=14358&size=300x300&key=fc04de07202f46c8&nocache=yes&type=fixnone) [*]新建文件callback c和h文件,作为本次编码的文件保存位置 [*]逻辑关系是uart2_rt_create函数先创建消息队列,再打开接收中断,当串口收到数据时进入接受中断回调函数,中断再开启接受中断同时将数据send给消息队列,实现了不定长数据的接收。 ``` #include "callback.h" #include "usart.h" #include "stdio.h" #include "string.h" #include
#define UART_TASK_PRIORITY 25 #define UART_TASK_STACK_SIZE 1024 #define UART_TASK_TIMESLICE 20 static uint8_t uart_rx_byte; //tem buf byte rt_mq_t uart2_mq; //uart2 mq id rt_thread_t uart_thread_tid = RT_NULL; void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { /* Prevent unused argument(s) compilation warning */ if (huart->Instance == USART2) { HAL_UART_Receive_IT(&huart2, &uart_rx_byte, 1); rt_mq_send (uart2_mq, &uart_rx_byte, 1); HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin); } } static void uart2_thread_entry(void *param) { static uint8_t uart2_buf[UINT8_MAX]; uint16_t index = 0; while (1) { memset(uart2_buf,0,UINT8_MAX); index = 0; while (1) { if (rt_mq_recv (uart2_mq, &uart2_buf[(index)%UINT8_MAX],1, 0) != RT_EOK) { if(index) rt_kprintf("接受消息为:%s\r\n",uart2_buf); break ; } index = (index+1)%UINT8_MAX; } rt_thread_mdelay(5000); } } int uart2_rt_create(void) { uart2_mq = rt_mq_create("uart2_mq", 1,UINT8_MAX, RT_IPC_FLAG_FIFO); HAL_UART_Receive_IT(&huart2, &uart_rx_byte, 1); uart_thread_tid = rt_thread_create("uart2_rx", uart2_thread_entry, RT_NULL, UART_TASK_STACK_SIZE, UART_TASK_PRIORITY, UART_TASK_TIMESLICE); if (uart_thread_tid != RT_NULL) rt_thread_startup(uart_thread_tid); return 0; } INIT_DEVICE_EXPORT(uart2_rt_create); MSH_CMD_EXPORT(uart2_rt_create, uart2 thread create); ``` [attach]14359[/attach] [*]实验结果 FINSH_THREAD_STACK_SIZE需要改大一些[attach]14360[/attach] [/list]
MrZhou
2020-03-30
这家伙很懒,什么也没写!
一、准备工作 1.硬件准备: STM32F103RCT6(本人使用正点原子的MINI板) ST-LINK USB转TTL模块 2.软件准备: KEIL5 MDK STM32CubeMX 二、使用STM32CubeMX生成MDK工程 使用STM32CubeMX创建STM32F103RCT6的对应工程,配置时钟源,使能GPIO、USART,配置时钟树,开启USART3中断。 1.配置预览效果,配置PA8引脚为推挽输出,用于LED灯闪烁。 [attach]14362[/attach] 2.使能USART引脚,并开启USART3中断。 [attach]14363[/attach] 3.STM32F103RCT6的最大主频为72M,配置PLL选择外部时钟。 [attach]14364[/attach] 4.生成KEIL MDK工程。 [attach]14365[/attach] 三、编写代码 对STM32CubeMX生成的工程稍作修改,注释main.c中的HAL_Init()和SystemClock_Config()。 1.在STM32CubeMX生成的usart.c中添加以下两句代码,将USART初始化函数使用自动初始化的宏添加到RTT中进行初始化,当然你也可以直接在main.c调用初始化,看个人习惯。 ``` INIT_BOARD_EXPORT(MX_USART1_UART_Init); INIT_BOARD_EXPORT(MX_USART3_UART_Init); ``` 2.新建一个usart_test文件,在里面编写串口接收的相关的代码,创建串口接收线程、消息队列,并使用MSH_CMD_EXPORT()将函数注册到FinSH中。 ``` static int usart_test_task(void) { rt_err_t ret = RT_EOK; tid1 = rt_thread_create("uart_task", uart_thread_entry, RT_NULL, THREAD_STACK_SIZE, THREAD_PRIORITY - 1, THREAD_TIMESLICE); if(tid1 != RT_NULL) { rt_thread_startup(tid1); } mq = rt_mq_create("uart_rev_mq", 32, 10, RT_IPC_FLAG_FIFO); if(mq != RT_NULL) { rt_kprintf("message create sucess\n"); } __HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE); //使能空闲中断 // __HAL_UART_ENABLE_IT(&huart3, UART_IT_TXE); //使能发送中断 __HAL_UART_ENABLE_IT(&huart3, UART_IT_RXNE); //使能接收中断 return ret; } MSH_CMD_EXPORT(usart_test_task, uart task); ``` 3.USART线程 从消息队列中将接收到的数据发送到上位机。 ``` static void uart_thread_entry(void *param) { while(1) { if(rt_mq_recv(mq, recv_buf, sizeof(recv_buf), RT_WAITING_FOREVER) == RT_EOK) { rt_kprintf("uart thread receive msg: %s\n", recv_buf); memset(recv_buf, 0, sizeof(recv_buf)); } rt_thread_mdelay(50); } } ``` 4.USART中断服务函数 函数分为两个部分,我们前面开启了两个USART中断,接收中断与空闲中断,分别对接收到的数据做不同处理,接收中断将数据存入recv_buf,空闲中断等待上位机数据发送结束后触发,往recv_buf中插入结束字符,并将接收到的数据插入到消息队列。 ``` void HAL_UART_RxCallback(UART_HandleTypeDef *huart) { /* 判断是哪个串口触发的中断 */ if(huart ->Instance == USART3) { if(HAL_UART_Receive(&huart3,&recByte,1,10) == HAL_OK) { recv_buf[usart_rx_pos++] = recByte; } } } void UART_IDLECallBack(UART_HandleTypeDef *huart) { int result; /* 判断是哪个串口触发的中断 */ if(huart ->Instance == USART3) { __HAL_UART_CLEAR_IDLEFLAG(&huart3); recv_buf[usart_rx_pos] = '\0'; result = rt_mq_send(mq, &recv_buf, sizeof(recv_buf)); if (result != RT_EOK) { rt_kprintf("rt_mq_send ERR\n"); } usart_rx_pos = 0; } } /** * @brief This function handles USART1 global interrupt. */ void USART3_IRQHandler(void) { /* USER CODE BEGIN USART1_IRQn 0 */ if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_RXNE) != RESET) { HAL_UART_RxCallback(&huart3); } if(__HAL_UART_GET_FLAG(&huart3, UART_FLAG_IDLE) != RESET) { UART_IDLECallBack(&huart3); } /* USER CODE END USART1_IRQn 0 */ HAL_UART_IRQHandler(&huart3); ``` } 四、下载验证 使用串口助手发送一个字符串到MCU,MCU将收到的内容返回到串口助手,完成一次数据收发流程。 [attach]14366[/attach] 五、小知识点 1.串口空闲中断产生条件 当接收数据后出现一个byte的高电平(空闲)状态,就会触发空闲中断。并不是空闲就会一直中断,准确的说应该是上升沿(停止位)后一个byte,如果一直是低电平是不会触发空闲中断的(会触发break中断)。经常在接收不定长的数据时与DMA配合使用。 2.中断清除方式 STM32固件库,USART_ClearITPendingBit( USARTx, USART_IT_IDLE )是不能清除中断的,需要再读取USARTx->DR寄存器的值才能清除。 HAL库,使用__HAL_UART_CLEAR_IDLEFLAG(&huartx)即可清除中断。
。。。
2020-03-31
这家伙很懒,什么也没写!
[i=s] 本帖最后由 。。。 于 2020-3-31 11:26 编辑 [/i] 观看学习何老师的RT-Thread Nano-串口接收(信号量使用)编写与RT-Thread Nano-外部中断(消息队列使用)的教学视频,我先将消息队列框架编写,先在“config.h”的头文件中定义一个消息队列控制块message_mq代码如下: ``` #ifdef MAIN_CONFIG //头文件被多个C调用时,避免变量冲突问题 #define EXT #else #define EXT extern #endif EXT rt_mq_t message_mq;//定义一个消息队列控制块 ``` 然后要创建消息队列和专门处理串口接收数据的线程程序如下: ``` TaskStruct TaskThread[]={ {"IWDG_thread", IWDG_thread_entry, RT_NULL, 256, 5, 10}, {"Usart2_thread", Usart2_thread_entry, RT_NULL, 768, 2, 20}, {"", RT_NULL, RT_NULL, RT_NULL, RT_NULL, RT_NULL}, }; void TaskInit(void) { uint8_t TaskThreadTndex = 0; message_mq = rt_mq_create("message_mq",/*消息队列的名称*/ 256, /*消息队列中一条消息的最大长度,单位:字节*/ 10, /*消息队列的最大个数*/ RT_IPC_FLAG_FIFO); /*消息队列采用的等待方式*/ if (message_mq != RT_NULL) rt_kprintf("\n消息队列message_mq创建成功\n"); while(1) { if(strcmp(TaskThread[TaskThreadTndex].name,"") != 0 ) { message_thread = rt_thread_create(TaskThread[TaskThreadTndex].name, /*线程名称*/ TaskThread[TaskThreadTndex].entry, /*线程入口函数名*/ TaskThread[TaskThreadTndex].parameter, /*线程入口函数参数*/ TaskThread[TaskThreadTndex].stack_size, /*线程栈大小*/ TaskThread[TaskThreadTndex].priority, /*线程的优先级*/ TaskThread[TaskThreadTndex].tick); /*线程时间片*/ if (message_thread != RT_NULL) rt_thread_startup(message_thread); TaskThreadTndex ++; } else { break; } } } ``` 在消息队列方式实现串口2数据接收的线程函数中进行接收消息,并从串口1打印接收到的数据信息,程序如下: ``` static void Usart2_thread_entry(void *parameter) { uint8_t message_queue[256]; /*消息队列接收数据缓冲区*/ rt_err_t uwRet = RT_EOK; while(1) { uwRet = rt_mq_recv(message_mq, /*消息队列对象的句柄*/ message_queue, /*消息内容*/ sizeof(message_queue), /*消息大小*/ RT_WAITING_FOREVER); /*指定的超时时间*/ if(uwRet == RT_EOK) { rt_kprintf("Usart2 Receive Data: %s\n", message_queue);//将接收到的消息在串口1打印 } else { rt_kprintf("数据接收错误"); } } } ``` 接下来编写串口2的驱动以及发送消息程序如下: ``` #if USART2_EN == 1 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 ; //TX GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 ; //RX GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); USART_InitStructure.USART_BaudRate = USART2_BAUD; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_Mode = USART_Mode_Rx |USART_Mode_Tx; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_Init(USART2, &USART_InitStructure);//初始化串口2 //串口2中断初始化 NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); USART_ITConfig(USART2, USART_IT_IDLE, ENABLE); USART_Cmd(USART2, ENABLE);//使能串口2 USART_ClearFlag(USART2, USART_FLAG_TC );//清发送完成标志位 #endif ``` ``` //USART2_IRQHandler,串口2中断回调函数 void USART2_IRQHandler(void) { #if USART2_EN == 1 uint8 ch; #endif if( USART_GetFlagStatus(USART2,USART_FLAG_RXNE)!=RESET ) // 串口接收数据 { #if USART2_EN == 1 ch = (uint8) USART_ReceiveData(USART2); g_USART2_RxBuf[g_USART2_RecPos ++ ] = ch; #endif USART_ClearFlag(USART2, USART_FLAG_RXNE); } if( USART_GetFlagStatus(USART2,USART_FLAG_ORE)==SET ) // 串口溢出错误 { USART_ClearFlag(USART2, USART_FLAG_ORE); } if( USART_GetFlagStatus(USART2,USART_FLAG_IDLE)==SET ) { #if USART2_EN == 1 g_USART2_RxBuf[g_USART2_RecPos] = '\0';//串口接收一帧数据完 rt_mq_send( message_mq, g_USART2_RxBuf, sizeof(g_USART2_RxBuf));//发送数据消息 memset( g_USART2_RxBuf, 0, USART2_RX_BUF_SIZE);//串口接收缓冲区清除 g_USART2_RecPos = 0;//存放当前串口接收数据存放的位置归零 USART_ReceiveData(USART2); #endif } } ``` 调试采用硬件调试,串口1是连接COM15的串口号,串口2是连接COM12的串口号。 消息队列创建成功的显示与线程的信息如下图: [attach]14387[/attach] 在串口2的串口调试助手中依次发送了“Hello”,“Good”,“Excellent”的数据,串口1的串口调试助手显示结果如下图: [attach]14388[/attach]
撰写答案
登录
注册新账号
关注者
1
被浏览
6.9k
关于作者
RT-Thread小喇叭
这家伙很懒,什么也没写!
提问
29
回答
54
被采纳
1
关注TA
发私信
相关问题
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
最新文章
1
《原子操作:程序世界里的“最小魔法单位”解析》
2
《C++设计模式:重塑游戏角色系统类结构的秘籍》
3
rt-thread官方usb驱动之虚拟串口
4
RTduino物联网应用零基础入门
5
TinyUSB Demo运行教程
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
ART-Pi
FinSH
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
WIZnet_W5500
ota在线升级
UART
cubemx
freemodbus
PWM
flash
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
Debug
编译报错
msh
SFUD
rt_mq_消息队列_msg_queue
keil_MDK
at_device
ulog
C++_cpp
本月问答贡献
踩姑娘的小蘑菇
6
个答案
3
次被采纳
张世争
8
个答案
2
次被采纳
rv666
5
个答案
2
次被采纳
用户名由3_15位
11
个答案
1
次被采纳
KunYi
6
个答案
1
次被采纳
本月文章贡献
出出啊
1
篇文章
2
次点赞
小小李sunny
1
篇文章
1
次点赞
张世争
1
篇文章
1
次点赞
crystal266
2
篇文章
2
次点赞
whj467467222
2
篇文章
1
次点赞
回到
顶部
发布
问题
分享
好友
手机
浏览
扫码手机浏览
投诉
建议
回到
底部