当前位置: 首页 > news >正文

RS485+DMA+空闲中断+HAL库收发数据

一、概念讲解

1.1 RS485       

        485(一般称作 RS485/EIA-485)是隶属于 OSI 模型物理层的电气特性规定为 2 线,半双工,多点通信的标准。它的电气特性和 RS-232 大不一样。用缆线两端的电压差值来表示传递信号。RS485 仅仅规定了接受端和发送端的电气特性。它没有规定或推荐任何数据协议

RS485 的特点包括:
        1) 接口电平低,不易损坏芯片。RS485 的电气特性:逻辑“1”以两线间的电压差为+(2~6)V表示;逻辑“0”以两线间的电压差为-(2~6)V 表示。接口信号电平比 RS232 降低了,不易损坏接口电路的芯片,且该电平与 TTL 电平兼容,可方便与 TTL 电路连接。
        2) 传输速率高。10 米时,RS485 的数据最高传输速率可达 35Mbps,在 1200m 时,传输速度可达 100Kbps。
        3) 抗干扰能力强。RS485 接口是采用平衡驱动器和差分接收器的组合,抗共模干扰能力增强,即抗噪声干扰性好。
        4) 传输距离远,支持节点多。RS485 总线最长可以传输 1200m 以上(速率≤100Kbps)一般最大支持 32 个节点,如果使用特制的 485 芯片,可以达到 128 个或者 256 个节点,最大的可以支持到 400 个节点。

1.2 RS485电气特性

        差分信号逻辑1(正)电压为+2~+6V,而逻辑0(负)电压为-2~-6V.接口信号电平比RS-232-C降低了,就不易损坏接口电路的芯片,且该电平与TTL电平兼容,可方便与TTL电路连接。

        SP3485 作为收发器,该芯片支持 3.3V 供电,最大传输速度可达10Mbps,支持多达 32 个节点,并且有输出短路保护。图中 A、B 总线接口,用于连接 485 总线。RO 是接收输出端,DI 是发送数据收入端,RE是接收使能信号(低电平有效),DE 是发送使能信号(高电平有效)。可以将RE和DE用同一个线连接,然后控制该脚的电平信号来确定是发送还是接收模式

1.3 RS485切换模式需要时间

因为RS485通信是采用半双工通信,有一个引脚作用是使能接收还是发送,但是MCU切换引脚电平需要一定的时间,在这段时间里面MCU的引脚是高阻态。

RS485芯片从接收模式切换到发送模式需要经过3.5us才有驱动能力输出

1.4 DMA介绍

(1)DMA是单片机集成在芯片内部的一个数据搬运工,它可以代替单片机对数据进行传输、存储,节约CPU资源。一般应用场景,ADC多通道采集,串口收发(频繁进入接收中断),SPI和IIC通信等

(2)STM32F2系列的DMA控制器最多有2个,每个控制器有8个数据流,每个数据流可以映射到不同的通道。例如,DMA2的数据流7可能用于某个特定外设,比如USART1的TX。(每个数据流同一时间只能服务一个外设。例如,若USART1_TX占用了DMA2_Stream7,则该流不可用于其他外设)

(3)DMA配置参数:

DMA请求源与通道选择:DMA1通道1

数据传输方向:外设 ↔ 存储器,存储器 ↔ 存储器

地址递增配置:外设地址递增和存储器地址递增(如果外设是ADC数据寄存器,关闭递增,如果存储器地址是接收数组,则开启递增)

数据宽度与对齐:外设/存储器数据宽度(串口一般是8位,ADC一般是16位)

传输模式:单次模式(串口接收)和循环模式(ADC采集数据)

(4)配置DMA发送必须要使能DMA中断,否则会出现发送一次成功后,后续串口始终处于“busy”状态(DMA传输完成后需通过中断通知CPU,否则无法判断数据是否发送完毕)

更详细的可以查看下面的博客

DMA伟大的数据搬运工_lpc1768定时器匹配触发dma-CSDN博客

二、配置过程

1、配置时钟,烧入方式,配置RS485的接收使能脚

2、配置串口,DMA

查看芯片参考手册

STMCU中文官网

3、串口收发缓存结构体

#define USART_TX_MAX_LEN 100
#define USART_RX_MAX_LEN 100
typedef struct
{unsigned char tx_buf[USART_TX_MAX_LEN]; unsigned char rx_buf[USART_RX_MAX_LEN];unsigned char rx_flag;unsigned char rx_len;
}usart_data_t;extern usart_data_t stUsart1Data;

 4、 串口初始化代码加入  开启IDLE中断、开始DMA接收

__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断
HAL_UART_Receive_DMA(&huart1,stUsart1Data.rx_buf,USART_RX_MAX_LEN);

5、串口中断函数中加入 接收数据代码

void USART1_IRQHandler(void)
{/* USER CODE BEGIN USART1_IRQn 0 *//* USER CODE END USART1_IRQn 0 */HAL_UART_IRQHandler(&huart1);/* USER CODE BEGIN USART1_IRQn 1 */if((__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE) != RESET))  {__HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位HAL_UART_AbortReceive(&huart1);	//已经接收完一帧数据,所以这里要停止接收,然后再重新接收	uint32_t   temp =  __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);// 获取DMA中未传输的数据个数 ,stUsart1Data.rx_len =  USART_RX_MAX_LEN - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数stUsart1Data.rx_flag = 1;	// 接受完成标志位置1	HAL_UART_Receive_DMA(&huart1,stUsart1Data.rx_buf,USART_RX_MAX_LEN);	//开始DMA接收}/* USER CODE END USART1_IRQn 1 */
}

6、配置串口printf打印函数

void Usart1Printf(const char *format,...)
{uint16_t len;va_list args;	va_start(args,format);len = vsnprintf((char*)stUsart1Data.tx_buf,sizeof(stUsart1Data.tx_buf)+1,(char*)format,args);va_end(args);if(HAL_UART_Transmit_DMA(&huart1, stUsart1Data.tx_buf, len)!= HAL_OK)   //判断是否发送正常,如果出现异常则进入异常中断函数{Error_Handler();}}

7、RS485通信加入使能发送和接收

/* USER CODE BEGIN 1 */
void USART1_RS485_Send_Enable(void)
{HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
}void USART1_RS485_Receive_Enable(void)
{HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);
}

8、测试代码

USART1_RS485_Receive_Enable(); //默认为接收使能while (1){/* USER CODE END WHILE */if(stUsart1Data.rx_flag  == 1)  //接收完成标志{USART1_RS485_Send_Enable();Usart1Printf("%s\r\n",stUsart1Data.rx_buf);HAL_Delay(100);//等待串口DMA发送完成,如果不加入此行代码,会出现发送失败,因为还没有发送完就切换成接收模式,导致不能发送数据 USART1_RS485_Receive_Enable();stUsart1Data.rx_len = 0;//清除计数stUsart1Data.rx_flag = 0;//清除接收结束标志位memset(stUsart1Data.rx_buf,0,USART_RX_MAX_LEN);}}

9、测试结果

三、优化RS485发送

当发送数据后,我们会等待发送完成后在开启串口接收,一般要么是延时一定时间,要么就是判断发送完成标志位,如果没有这个过程,会造成发送数据不完全,因此资源遭到了一定的浪费

优化思路:等待触发串口发送完成中断后,使能串口接收中断

void USART1_IRQHandler(void)
{HAL_UART_IRQHandler(&huart1);if((__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE) != RESET))  {__HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位HAL_UART_AbortReceive(&huart1);	//已经接收完一帧数据,所以这里要停止接收,然后再重新接收	uint32_t   temp =  __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);// 获取DMA中未传输的数据个数 ,stUsart1Data.rx_len =  USART_RX_MAX_LEN - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数stUsart1Data.rx_flag = 1;	// 接受完成标志位置1	HAL_UART_Receive_DMA(&huart1,stUsart1Data.rx_buf,USART_RX_MAX_LEN);	//开始DMA接收}if(hdma_usart1_tx.State==HAL_DMA_STATE_READY){USART1_RS485_Receive_Enable();//传输完成,在此开启下一次传输}
}USART1_RS485_Receive_Enable();while (1){/* USER CODE END WHILE */if(stUsart1Data.rx_flag  == 1)  //接收完成标志{USART1_RS485_Send_Enable();Usart1Printf("%s\r\n",stUsart1Data.rx_buf);
//		HAL_Delay(100);//等待串口DMA发送完成 
//		USART1_RS485_Receive_Enable();stUsart1Data.rx_len = 0;//清除计数stUsart1Data.rx_flag = 0;//清除接收结束标志位memset(stUsart1Data.rx_buf,0,USART_RX_MAX_LEN);}}

四、调试问题记录

第一步:控制串口调试助手和单片机的波特率相同,可以正常接收数据

第二步:控制串口调试助手和单片机的波特率不同,单片机接收数据不正确,乱码,但此时还是正常现象

第三步:控制串口调试助手切换正确波特率后,单片机接收数据还是不正确,并且DMA状态会变为HAL_DMA_STATE_ABORT,此时说明DMA配置存在Bug

上述问题根本原因是 波特率不匹配导致DMA传输超时或硬件错误,进而触发DMA中止,但切换回正确波特率后DMA未正确恢复

解决办法一:当DMA状态变为 HAL_DMA_STATE_ABORT 时,需重新初始化DMA

//重新初始化串口if (hdma_usart1_rx.State == HAL_DMA_STATE_ABORT){MX_USART1_UART_Init();LOG("DMA Abort\r\n");}

解决办法二:(玄学解决)

hdma_usart1_rx.Instance = DMA2_Stream2;
//DMA2_Stream5改为DMA2_Stream2

http://www.dtcms.com/a/328197.html

相关文章:

  • 无人机智能返航模块技术分析
  • element-table的合并行的使用-指定某些字段允许相邻数据能进行合并,通过传递的key键进行判断-公共方法
  • LaTeX 教程:从入门到专业的排版模板
  • UGUI源码剖析(6):遮罩的“魔法”与“算法”——从C#到Shader,彻底揭示Mask与RectMask2D的原理
  • 13.深度学习——Minst手写数字识别
  • git config的配置全局或局部仓库的参数: local, global, system
  • java面试题储备4: 谈谈对es的理解
  • 【银行测试】外贸信托项目与电子资金项目(面试项目讲解)
  • Java面试题储备11: mysql优化全面讲一下,及你遇到的对应业务场景
  • 不废话,UE5极速云渲染操作方法
  • B.10.02.3-分布式一致性:电商业务场景下的理论与工程实践
  • 使用 RealSense D435 获取红外图像:完整 Python 脚本解析
  • 扣子空间深度解析
  • 堆排序以及实现
  • 飞算 JavaAI -智慧城市项目实践:从交通协同到应急响应的全链路技术革新
  • 【Go】Gin 超时中间件的坑:fatal error: concurrent map writes
  • FPGA即插即用Verilog驱动系列——UART串口接收
  • 医疗智慧大屏系统 - Flask + Vue实现
  • nextTick和setTimeout的区别
  • Docker概述与安装Dockerfile文件
  • k8s-scheduler 解析
  • 1小时 MySQL 数据库基础速通
  • log4cplus的功能是什么,我们如何来使用它?
  • 调整UOS在VMware中的分辨率
  • Linux系统启动过程详解
  • CTO 如何从“干活的人”转变成“带方向的人”?
  • 需求沟通会议如何组织
  • 云手机在电商行业中的作用
  • 知名车企门户漏洞或致攻击者远程解锁汽车并窃取数据
  • C++ 学习与 CLion 使用:(二)using namespace std 语句详解,以及 std 空间的标识符罗列