完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
一些基础串口通信实验(通过串口调试助手与PC机通信)和相关知识点汇总,主要参考MOOC课程《基于STM32CubeMX和HAL驱动库的嵌入式系统设计-漆强》和部分文章,方便新手系统性学习uart串口通信知识
一、实验前准备
USB转TTL、USB转串口、USB转232的区别 二、具体项目代码 根据自己MCU型号在CubeMX中进行相应配置,可参考 【STM32】HAL库 STM32CubeMX教程四—UART串口通信详解 STM32CubeMX串口通信调试避坑(胎教级教程) 项目1:轮询方式通信 轮询方式: 可直接检测标志位(不用中断就算轮询) 中断方式: 在中断服务程序中通过检测不同的中断标志位来判断中断类型 实验内容:发送测试数据,接收指定长度数据并发送给PC(用来显示) 使用的HAL库函数: 在main.c中申明变量 /* Private variables ----------------------------*/ /* USER CODE BEGIN PV */ #define RXBUFFERSIZE 5 uint8_t RxBuffer[RXBUFFERSIZE]; //接收数据 /* USER CODE END PV */ 在while循环中添加发送接收语句 /* USER CODE BEGIN 3 */ if(HAL_UART_Receive(&huart2,RxBuffer,RXBUFFERSIZE,100)==HAL_OK) { HAL_UART_Transmit(&huart2,RxBuffer,RXBUFFERSIZE,100); //把接收的数据发回来 } /* USER CODE END 3 */ 项目2:printf重定向实验 目标:利用串口实现printf函数和scanf函数 内容:利用串口调试助手,发送数据到MCU,MCU调用scanf函数读取数据,然后用printf函数发送对应消息到PC P.S.内容跟轮询模式差不多,只是用printf和scanf去输入输出,为了熟悉重定向这个概念 重定向
具体步骤: 添加头文件:在main.h中添加标准输入输出头文件"stdio.h" /* USER CODE BEGIN Includes */ #include "stdio.h" /* USER CODE END Includes */ 在main.c中的USER CODE BEGIN 4(主函数之后)里添加重定向函数 /* USER CODE BEGIN 4 */ /** *@brief: 重定向printf函数到USART *采用轮询方式发送1字节数据,请参考HAL_UART_Transmit用法 *HAL_MAX_DELAY为无限等待 */ int fputc(int ch, FILE *f) { HAL_UART_Transmit(&huart2, (uint8_t *)&ch,1,HAL_MAX_DELAY);// return ch; } /** *@brief: 重定向scanf函数到USART */ int fgetc(FILE *f) { uint8_t ch; HAL_UART_Receive(&huart2,(uint8_t *)&ch,1,HAL_MAX_DELAY); return ch; } /* USER CODE END 4 */ main.c主函数前,除了基本的初始化代码外,添加申明变量和提示信息 /* Private variables -------------------------*/ /* USER CODE BEGIN PV */ uint8_t aRxBuffer; //接收数据缓冲 /* USER CODE END PV */ /* USER CODE BEGIN 2 */ printf("UART Retarget:\r\n");//用户提示信息 /* USER CODE END 2 */ while循环中添加接收和发送语句:当接收到y时发送Received y!,当接收到其他消息时发送Received others! /* USER CODE BEGIN 3 */ scanf("%c",&aRxBuffer); if(aRxBuffer == 'y') { printf("Received y!\r\n"); } else { printf("Received others!\r\n"); } /* USER CODE END 3 */
串口中断方式特点:
P.S.为了直观,省略了这些函数的参数类型,具体信息请直接看对应函数的官方代码和注释。 [tr]串口中断方式发送函数HAL_UART_Transmit_IT (*huart,*pData,Size)[/tr]
在HAL库中stm32xx_hal_uart.c初始定义了 __weak void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart),(RxCpltCallback也类似)关于weak关键词:C语言之强化,弱化符号weak [tr]串口中断使能函数__HAL_UART_ENABLE_IT ( _ _HANDLE _ _ , _ _ INTERRUP _ _)[/tr]
具体步骤: 参照【STM32】HAL库 STM32CubeMX教程四—UART串口通信详解 实现任意长度字符接收 /* USER CODE BEGIN PD */ #define RXBUFFERSIZE 256 //最大接收字节数 /* USER CODE END PD */ /* USER CODE BEGIN PV */ uint8_t RxBuffer[RXBUFFERSIZE]; //接收数据缓冲 uint8_t aRxBuffer; //接收数据 uint8_t Uart2_Rx_Cnt = 0; //接收缓冲计数 /* USER CODE END PV */ /* USER CODE BEGIN 2 */ HAL_UART_Receive_IT(&huart2, (uint8_t *)&aRxBuffer, 1); //调用串口中断方式接收函数 /* USER CODE END 2 */ 在int main()后补充完整void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) /* USER CODE BEGIN 4 */ /** * @brief Rx Transfer completed callback. * @param huart UART handle. * @retval None */ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { /* Prevent unused argument(s) compilation warning */ UNUSED(huart); /* NOTE: This function Should not be modified, when the callback is needed, the HAL_UART_TxCpltCallback could be implemented in the user file */ if(Uart2_Rx_Cnt >= 255) //溢出判断 { Uart2_Rx_Cnt = 0; memset(RxBuffer,0x00,sizeof(RxBuffer)); HAL_UART_Transmit(&huart2, (uint8_t *)"数据溢出", 10,0xFFFF); } else { RxBuffer[Uart2_Rx_Cnt++] = aRxBuffer; //接收数据转存 if((RxBuffer[Uart2_Rx_Cnt-1] == 0x0A)&&(RxBuffer[Uart2_Rx_Cnt-2] == 0x0D)) //判断结束位 { HAL_UART_Transmit(&huart2, (uint8_t *)&RxBuffer, Uart2_Rx_Cnt,0xFFFF); //将收到的信息发送出去 while(HAL_UART_GetState(&huart2) == HAL_UART_STATE_BUSY_TX);//检测UART发送结束 Uart2_Rx_Cnt = 0; memset(RxBuffer,0x00,sizeof(RxBuffer)); //清空数组 } } HAL_UART_Receive_IT(&huart2, (uint8_t *)&aRxBuffer, 1); //再开启接收中断 } /* USER CODE END 4 */ 实验结果: 项目4:简单的帧格式通信 Modbus消息帧格式: [tr]起始符设备地址功能代码数据校验结束符[/tr]
[tr]帧头设备码功能码帧尾[/tr]
目标:实现简单的帧格式通信 内容:PC按照自定义的帧格式发送指令开启或关闭开发板上的LD2 代码: /* USER CODE BEGIN PV */ #define LENGTH 4 uint8_t RxBuffer[LENGTH]; //接收缓冲区 uint8_t RxFlag =0;//接收完成标志:为0表示接收未完成,为1表示接收完成 uint8_t ErrFlag =0;//指令错误标志:为0表示指令正确,为1表示错误 /* USER CODE END PV */ 1 2 3 4 5 6 /* USER CODE BEGIN 2 */ //发送提示信息 printf("***** Communication Frame *****\r\n"); printf("Please enter instruction:\r\n"); printf("Head->0xaa Device->0x01 Operation->0x00/0x02 Tail->0x55.\r\n"); HAL_UART_Receive_IT(&huart2, (uint8_t *)RxBuffer, LENGTH);//使能接收中断 /* USER CODE END 2 */ 1 2 3 4 5 6 7 main函数中的while(1)循环: while (1) { if(RxFlag == 1) //如果接收完成 { //1、清除接收完成标志位 RxFlag = 0; //2、判断帧格式是否符合规范 if(RxBuffer[0]==0xaa && RxBuffer[3]==0x55) //判断帧头帧尾 { if(RxBuffer[1]==0x01)//判断设备码 { //3、判断功能码,若为0x00则关闭指示灯 if(RxBuffer[2]==0x00) { HAL_GPIO_WritePin(LD2_GPIO_Port,LD2_Pin,GPIO_PIN_RESET); printf("LD2 is close!\r\n"); } else if(RxBuffer[2]==0x01) { HAL_GPIO_WritePin(LD2_GPIO_Port,LD2_Pin,GPIO_PIN_SET); printf("LD2 is open!\r\n"); } else { ErrFlag = 1;//如果功能码错误 } } else { ErrFlag = 1;//如果帧头帧尾错误 } } else { ErrFlag = 1;//如果设备码错误 } if(ErrFlag == 1) { printf("Communication Error! Please send again"); } //4、清除接收缓冲区和错误标志,准备下一次接收 ErrFlag =0; memset(RxBuffer,0x00,sizeof(RxBuffer)); } } /* USER CODE END 3 */ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 /* USER CODE BEGIN 4 */ /** *@brief: 重定向printf函数到USART *@param1: 输出的字符 *@param2: 文件指针 *@retval: 输出的字符 *请参考HAL_UART_Transmit用法,HAL_MAX_DELAY为无限等待 */ int fputc(int ch, FILE *f) { HAL_UART_Transmit(&huart2, (uint8_t *)&ch,1,HAL_MAX_DELAY);// return ch; } /** * @brief Rx Transfer completed callback. * @param huart UART handle. * @retval None */ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart->Instance ==USART2)//判断发生接收中断的串口 { RxFlag = 1;//置位接收完成标志 HAL_UART_Receive_IT(&huart2, (uint8_t *)RxBuffer, LENGTH);//重新使能接收中断,准备下一次数据接收 } } /* USER CODE END 4 */ 实验结果: 串口调试助手发送时选择16进制发送
|
|||||
|
|||||
只有小组成员才能发言,加入小组>>
小黑屋| 手机版| Archiver| 电子发烧友 ( 粤ICP备14022951号 )
GMT+8, 2023-5-23 16:09 , Processed in 1.095260 second(s), Total 119, Slave 103 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 深圳华秋电子有限公司
电子发烧友 (电路图) 粤公网安备 44030402000349 号 电信与信息服务业务经营许可证:粤 B2-20160233 工商网监 粤ICP备 14022951 号