一、stm32串口通信

stm32串口通信一般是指通过UART(Universal Asynchronous Receiver/Transmitter)通用异步收发传输器传输数据,UART 作为异步串口通信协议的一种,工作原理是将传输数据的每个字符一位接一位地传输,其在应用程序开发过程中使用频率最高的数据总线。而MCU的UART 通信实际上是基于GPIO引脚电平信号实现的,因此,在预制UART外设不够时,也会可以采用GPIO引脚自行模拟UART串口通信。

【1】硬件流控

UART 串口的特点是将数据一位一位地顺序传送,只要 2 根传输线就可以实现双向通信,一根线发送数据的同时用另一根线接收数据。但是,数据在两个串口之间进行通讯的时候常常会出现丢失数据的现象,主要原因是数据处理及数据接收效率不一致引起的冲突,为了解决这种问题,引入了流控概念。流控的概念源于 RS232 这个标准,用来解决这个速度匹配的问题,通过新增传输线来控制数据传输数据线的流向、流速等,这就是简单的三线串口的通讯方式,如下图:

在两条数据线基础上,增加两根控制线,一根叫 CTS(Clear To Send 为输入信号,一根叫 RTS(Require To Send 为输出信号),一个是接收控制,一个是发送控制。可以看到,数据线方向与流控线数据方向相反,因为流控的主要是协调接收数据和处理数据一致性,所以需要让发送端等待,接收端发出来的信号叫 RTS 信号,发送端检测管脚叫 CTS。

数据接收与 RTS 信号标记如下图,接收端缓存没数据时,RTS是低电平状态,发送端可以发送数据;当接收端接收到数据后,RTS切换为高电平,就是告诉发送端,数据还没有被拿走,请发送端等待;RTS 信号在数据没有被读取之前都是保持在高电平状态,一旦数据被 DMA 或者 CPU 从 DR 寄存器读取之后,RTS 就释放高电平,变为低电平,这时候发送端又可以发送数据的了。另外,如果 USART 是FIFO 模式,即缓冲模式开启的时候,在 FIFO 满的时候才会去拉高 RTS 信号。

相应的,发送数据和CTS信号类似,发送端在发送数据时,要实时监测 CTS 的电平状态,如果发现是高电平,就不会再发送新的数据,直到 CTS 检测发现已经没有高电平信号才会再次发送。

我们在cubeIDE中,对应上述的就是勾选RS232流控制支持,就可以选择CTS/RTS引脚配置。

如上面所述,由设想的两根数据线传输多出两根流控制线,这无形中增加硬件成本,因此,RS485出现后,RS232的CTS/RT合二为一DE ,STM32 上有一个 DE 管脚和 RS485 的接收器芯片直接相连,控制数据的收发,主要就是数据的方向的控制。因为 RS485 是一个半双工的通讯模式,它的数据收的时候就不能发,发的时候不能收。

在cubeMX中,需要支持DE引脚开始,就需要勾选RS485流控制功能

【2】软件流控

上述的增加流控制线的做法都是属于硬件流控的实现,在实际项目开发过程,我们很多时候为了方便以及节省引脚,往往采用软流控。软件流控是以特殊的字符来代表从机已经不能再接收新的数据了,基本的流程就是从机在接收数据很多的时候或主动给发送端发送一个特殊字符,当发送端接收到这个特殊字符后就不能再发送数据了。

  软件流控很方便,不需要增加新的硬件,还是以前的TX、RX两根数据线实现,但是使用了软件流控,它本身的字符也是数据,这个数据只不过是说在软件里把它设置了一个特殊的含义。如果它是一个全双工的通讯,在给另一个串口发送数据的时候如果也包含了这样一个特殊字符,对方就会误以为我让它不要再发送数据了,会有一定的概率出现错误,而硬件流控就不需要考虑这方面,只需要使用 CTS 和 RTS,所有的数据都是由硬件来操作的,因此硬件流控在稳定性上更可靠。

【3】串口通信参数

UART 串口通信有几个重要的参数,分别是起始位、波特率、数据位、停止位和奇偶检验位(流控,按需),对于两个使用 UART 串口通信的端口,这些参数必须匹配,否则通信将无法正常完成。

起始位:表示数据传输的开始,默认电平逻辑为 “0” 。

波特率:串口通信时的速率,它用单位时间内传输的二进制代码的有效位(bit)数来表示,其单位为每秒比特数 bit/s(bps)。常见的波特率值有 4800、9600、14400、38400、115200等,数值越大数据传输的越快,波特率为 115200 表示每秒钟传输 115200 位数据。

数据位:可能值有 5、6、7、8、9,表示传输这几个 bit 位数据。一般取值为 8,因为一个 ASCII 字符值为 8 位。

停止位: 表示一帧数据的结束。默认电平逻辑为 “1”。

奇偶校验位:用于接收方对接收到的数据进行校验,校验 “1” 的位数为偶数(偶校验)或奇数(奇校验),以此来校验数据传送的正确性,使用时不需要此位也可以。

二、新建RS485通信工程

【1】项目实现背景信息

本文采用的STM32L496VGTX-ali开发板,该开发板预留了扩展接口支持USART4通信,本文将USART4与USB转RS485(CH340)连接,进而与电脑通信。

实物图:

扩展接口原理框图如下:

另外本人开发板标注背后扩展口标注的是PA0、PA1,查看其他描述,确定就是PA0、PA1引脚。

现在基于本专栏前面已近实现的串口lpusart通信和及lcd亮屏工程为基础创建新工程,并移植了相关代码。

cubeIDE开发, stm32调试信息串口通信输出显示_py_free的博客-CSDN博客

cubeIDE开发, stm32的OLED点亮及字符显示设计(基于SPI通信)_py_free的博客-CSDN博客

现将在该工程基础上,实现串口Usart4和lpusart通信,并lcd屏打印其发送数据;Usart4发送'A'/'B'可以点亮及熄灭LED0。

【2】项目配置

双击(.ioc)打开cubeMX配置界面,开启USART4串口功能,配置串口参数

并开启usart4的中断支持

点击保存生成输出代码。

【3】在ICore目录下,新建rs485文件夹,并在该文件目录下,创建rs485.h和rs485.c源文件,其实现代码如下:

rs485.h

#ifndef RS485_RS485_H_
#define RS485_RS485_H_#include "stm32l4xx_hal.h" //HAL库文件声明extern UART_HandleTypeDef huart4;//声明USART4的HAL库结构体
void RS485_printf (char *fmt, ...);  //RS485发送#endif /* RS485_RS485_H_ */

rs485.c

#include "../usart/usart.h"
#include "main.h"
#include <stdarg.h>
/*
RS485总线通信,使用UART4,这是RS485专用的printf函数
*/
void RS485_printf (char *fmt, ...)
{char buff[USART4_REC_LEN+1];  //用于存放转换后的数据 [长度]uint16_t i=0;va_list arg_ptr;va_start(arg_ptr,fmt);vsnprintf(buff, USART4_REC_LEN+1,fmt,arg_ptr);//数据转换i=strlen(buff);//得出数据长度if(strlen(buff)>USART4_REC_LEN)i=USART4_REC_LEN;//如果长度大于最大值,则长度等于最大值(多出部分忽略)HAL_UART_Transmit(&huart4,(uint8_t *)buff,i,0Xffff);//串口发送函数(串口号,内容,数量,溢出时间)va_end(arg_ptr);
}
//所有USART串口的中断回调函数HAL_UART_RxCpltCallback,统一存放在【USART.C】文件中。

调整usart.h和usart.c文件,重新实现串口USART4的中断回调函数

usart.h

#ifndef _USART_H_
#define _USART_H_#include "stm32l4xx_hal.h" //HAL库文件声明
#include <string.h>//用于字符串处理的库
#include "../print/print.h"//用于printf函数串口重映射extern UART_HandleTypeDef hlpuart1;//声明LPUSART的HAL库结构体
extern UART_HandleTypeDef huart4;//声明USART4的HAL库结构体#define HLPUSART_REC_LEN  256//定义LPUSART最大接收字节数
#define USART4_REC_LEN 256extern uint8_t  HLPUSART_RX_BUF[HLPUSART_REC_LEN];//接收缓冲,最大HLPUSART_REC_LEN个字节.末字节为换行符
extern uint16_t HLPUSART_RX_STA;//接收状态标记
extern uint8_t HLPUSART_NewData;//当前串口中断接收的1个字节数据的缓存extern uint8_t USART4_RX_BUF[USART4_REC_LEN];
extern uint16_t USART4_RX_STA;
extern uint8_t USART4_NewData;void  HAL_UART_RxCpltCallback(UART_HandleTypeDef  *huart);//串口中断回调函数声明#endif /* _USART_H_ */

usart.c

#include "usart.h"uint8_t  HLPUSART_RX_BUF[HLPUSART_REC_LEN];//接收缓冲,最大HLPUSART_REC_LEN个字节.末字节为换行符
/** bit15:接收到回车(0x0d)时设置HLPUSART_RX_STA|=0x8000;* bit14:接收溢出标志,数据超出缓存长度时,设置HLPUSART_RX_STA|=0x4000;* bit13:预留* bit12:预留* bit11~0:接收到的有效字节数目(0~4095)*/
uint16_t HLPUSART_RX_STA=0;接收状态标记//bit15:接收完成标志,bit14:接收到回车(0x0d),bit13~0:接收到的有效字节数目
uint8_t HLPUSART_NewData;//当前串口中断接收的1个字节数据的缓存uint8_t USART4_RX_BUF[USART4_REC_LEN];
uint16_t USART4_RX_STA=0;
uint8_t USART4_NewData;void  HAL_UART_RxCpltCallback(UART_HandleTypeDef  *huart)//串口中断回调函数
{if(huart ==&hlpuart1)//判断中断来源(串口1:USB转串口){if(HLPUSART_NewData==0x0d){//回车标记HLPUSART_RX_STA|=0x8000;//标记接到回车}else{if((HLPUSART_RX_STA&0X0FFF)<HLPUSART_REC_LEN){HLPUSART_RX_BUF[HLPUSART_RX_STA&0X0FFF]=HLPUSART_NewData; //将收到的数据放入数组HLPUSART_RX_STA++;  //数据长度计数加1}else{HLPUSART_RX_STA|=0x4000;//数据超出缓存长度,标记溢出}}HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData,1); //再开启接收中断}if(huart ==&huart4)//判断中断来源(串口4:USB转串口){
//      printf("%c",USART4_NewData);if(USART4_NewData==0x0d){//回车标记USART4_RX_STA|=0x8000;//标记接到回车}else{if((USART4_RX_STA&0X0FFF)<USART4_REC_LEN){USART4_RX_BUF[USART4_RX_STA&0X0FFF]=USART4_NewData; //将收到的数据放入数组USART4_RX_STA++;  //数据长度计数加1}else{USART4_RX_STA|=0x4000;//数据超出缓存长度,标记溢出}}HAL_UART_Receive_IT(&huart4,(uint8_t *)&USART4_NewData,1); //再开启接收中断}
}

三、RS485驱动调用及测试

在main.c文件中加入rs485.h头文件支持

/* USER CODE BEGIN Includes */
#include "../../ICore/key/key.h"
#include "../../ICore/led/led.h"
#include "../../ICore/print/print.h"
#include "../../ICore/usart/usart.h"
#include "../../ICore/rs485/rs485.h"
#include "../../ICore/oled/oled.h"
/* USER CODE END Includes */

在主函数初始化中调整加入usart4初始化设定

  /* USER CODE BEGIN 2 */ResetPrintInit(&hlpuart1);HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断HLPUSART_RX_STA = 0;HAL_UART_Receive_IT(&huart4,(uint8_t *)&USART4_NewData,1); //开启串口4接收中断USART4_RX_STA = 0;//OLED_init();//设置OLED蓝色背景显示BSP_LCD_Clear_DMA(LCD_DISP_BLUE);printf("OLED_Clear_DMA\r\n");/* USER CODE END 2 */

在主函数循环体中,修改代码实现对usart4接收数据处理

  /* USER CODE BEGIN WHILE */while (1){if(HLPUSART_RX_STA&0xC000){//溢出或换行,重新开始RS485_printf("%.*s\r\n",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);OLED_printf(10,10,"%.*s",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);HLPUSART_RX_STA=0;//接收错误,重新开始HAL_Delay(100);//等待}if(USART4_RX_STA&0xC000){//溢出或换行,重新开始if(1==(USART4_RX_STA&0x0FFF)){switch (USART4_RX_BUF[0]){case 'A':set_led0_val(1);break;case 'B':set_led0_val(0);break;default:break;}}printf("%.*s\r\n",USART4_RX_STA&0X0FFF, USART4_RX_BUF);OLED_printf(10,42,"%.*s",USART4_RX_STA&0X0FFF, USART4_RX_BUF);USART4_RX_STA=0;//接收错误,重新开始HAL_Delay(100);//等待}/* USER CODE END WHILE */

编译及下载及测试

LED0在发送'A'字符是关闭(LED灯是低位有效)

STM32CubeIDE开发(二十二), stm32的RS485/232串口通信开发要点相关推荐

  1. (RS485 232串口通信数据解析实用干货(1)

    文章目录 (RS485 232串口通信数据解析实用干货(1) 新的改变 功能快捷键 合理的创建标题,有助于目录的生成 如何改变文本的样式 插入链接与图片 如何插入一段漂亮的代码片 生成一个适合你的列表 ...

  2. iOS 11开发教程(二十二)iOS11应用视图实现按钮的响应(2)

    iOS 11开发教程(二十二)iOS11应用视图实现按钮的响应(2) 此时,当用户轻拍按钮后,一个叫tapButton()的方法就会被触发. 注意:以上这一种方式是动作声明和关联一起进行的,还有一种先 ...

  3. Android开发笔记(一百二十二)循环器视图RecyclerView

    RecyclerView RecyclerView是Android在support-v7库中新推出控件,中文别名为循环器视图,它的功能非常强大,可分别实现ListView.GridView,以及瀑布流 ...

  4. 陈力:传智播客古代 珍宝币 泡泡龙游戏开发第十二讲:盒子的定位方式

    陈力:传智播客古代 珍宝币 泡泡龙游戏开发第十二讲:盒子的定位方式 摘要:通过前节<第十一讲:浮动>学习了贵阳网站建设中的DIV+CSS中盒子模型和浮动进行介绍.框模型是CSS的基础,本文 ...

  5. Simulink代码生成(二十二)——TSP开发之创建外部设备模块

    Simulink代码生成(二十二)--TSP开发之创建外部设备模块 文章目录 Simulink代码生成(二十二)--TSP开发之创建外部设备模块 一.C Mex Sfunction文件解读 一.C M ...

  6. 【Visual C++】游戏开发五十五 浅墨DirectX教程二十二 水乳交融的美学:alpha混合技术

    本系列文章由zhmxy555(毛星云)编写,转载请注明出处. 文章链接: http://blog.csdn.net/poem_qianmo/article/details/15026917 作者:毛星 ...

  7. 数字音频总线A2B开发详解二十二:ADAU1701做的功放板的音效(26个例程)(第二部分)

    六. A2B-ADAU1701音效开发:直通分频 混音讲完了,我们再来说说分频,同一个音源,如何才能同时推送到S板的2个黑色输出口来播放音乐呢? 先做S板的Sigma工程,最简单的分频输出,就是用T: ...

  8. Senparc.Weixin.MP SDK 微信公众平台开发教程(二十二):如何安装 Nuget(dll) 后使用项目源代码调试...

    最近碰到开发者问:我使用 nuget 安装了 Senparc.Weixin SDK,但是有一些已经封装好的过程想要调试,我又不想直接附加源代码项目,这样就没有办法同步更新了,我应该怎么办? 这其实是一 ...

  9. 读后感: 波、波、波---走出软件作坊:三五个人十来条枪 如何成为开发正规军(二十二)

    波.波.波---走出软件作坊:三五个人十来条枪 如何成为开发正规军(二十二) http://blog.csdn.net/david_lv/archive/2008/06/26/2589809.aspx ...

最新文章

  1. SQLite第八课 auth.c授权文件解析
  2. ext3文件系统反删除利器-ext3grep
  3. Boost:基于boost::asio模块引用计数程序
  4. Python爬虫(十三)_案例:使用XPath的爬虫
  5. android组建之间通信_Android各组件/控件间通信利器之EventBus
  6. 基于Jenkins + Tomcat 的安卓客户端可持续化构建及发布下载(loltube.cn)
  7. linux数据向量化指令,不充分SIMD向量化技术研究.PDF
  8. delphi读取xml中的内容property name传递参数_python3 Json和XML数据解析
  9. Oracle_11g_R2数据库在Windows_Server_2008_R2企业版中安装
  10. 调度算法——时间片轮转、优先级、多级反馈队列(例题详细!!!)
  11. 医疗行业容灾备份建设迫在眉睫
  12. 软件测试的类型有哪些?
  13. 笔记本电脑连接无线网总是突然断网
  14. 看完数学概念背后的故事,让孩子的数学兴趣激增1000倍!
  15. 微信小程序-音视频剪辑
  16. 计算机怎么截音乐,电脑怎么剪辑音乐 怎样在电脑上剪辑歌曲
  17. [杂言] 祭奠与真正的告别
  18. 又来爬小说了,纯原创
  19. nginx 同一个端口同时 支持 http 和 https
  20. UE4轮廓描边【非后处理】

热门文章

  1. 优化算法:人工蜂鸟算法AHA
  2. 微信5.0 Android版飞机大战破解无敌模式手记
  3. 绿盟科技软件测试招聘,绿盟科技防火墙产品团队招聘——来加入我们把~
  4. C语言求最大公约数(四种)
  5. 带来高效照片扫描体验 佳能Lide400扫描仪试用
  6. 计算机设备耗材管理,计算机耗材管理办法
  7. Laravel8 分页样式问题
  8. 什么叫爬虫python_网络爬虫是什么?
  9. 发现一款APP可以看到哪些区域是禁飞区,还可以申报飞行,这样方便自己选地方test自己的无人机了。
  10. 如何删除pagefile.sys