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

串口和RS485通信

一、 定义串口收发数据结构体

/*COM Received Data Structure*/
typedef struct
{uint8_t	 ubr_EndFlag;				  	//Received data end flag																uint8_t	 ubr_buffer[300];			  	//Received data bufferuint8_t  ubr_bufferTemp[300];           //Received data buffer tempuint16_t ubr_index;				  	    //Received data indexuint16_t ubr_len;                       //Received data len
}ComRevData;
判断ubr_EndFlag标志位就可以得知数据是否接收完成,ubr_bufferTemp这个的作用在于如果接收数据失败就不会把数据存放在buffer数组里面/*COM Send Data Structure*/
typedef struct
{uint8_t  ubs_Index;                    //send indexuint8_t  ubs_Len;                      //send lenuint8_t  ubs_Buffer[255];              //Send data buffer
}ComSendData;

 

二、串口发送的两种方式

第一种是重写fputc函数,使用printf函数打印发送。

第二种是在中断中发送数据

usart_interrupt_enable(USART0, USART_INT_TBE);//使能发送中断即可发送数据void USART0_IRQHandler(void)
{   if(RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_TBE)){usart_interrupt_flag_clear(USART0, USART_INT_FLAG_TBE);usart_data_transmit(USART0, Usart0SendData.ubs_Buffer[Usart0SendData.ubs_Index++]);if(Usart0SendData.ubs_Index >= Usart0SendData.ubs_Len)//发送数据完成{Usart0SendData.ubs_Index = 0;usart_interrupt_disable(USART0, USART_INT_TBE);}}
}

使用中断函数发送数据的好处?

一般我们采用死等发送数据,此时MCU除了发送字节和while等待外没有处理其他任务,严重影响系统实时性。

while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); //等待上一字节发送完成 
USART1->DR=txbuf[cnt]; //要发送的字节存入串口数据寄存器 

而如果采用中断发送函数,他使用的时间是分散的,在等待数据发送完成的期间MCU可以处理其他任务,系统的实时性高

三、串口常见几种中断

USART的各种中断事件被连接到同一个中断向量(见下图),有以下各种中断事件:
● 发送期间:发送完成、清除发送、发送数据寄存器空。
● 接收期间:空闲总线检测、溢出错误、接收数据寄存器非空、校验错误、LIN断开符号检
测、噪音标志(仅在多缓冲器通信)和帧错误(仅在多缓冲器通信)。

 

空闲中断:当一帧数据接收完成,也就是接收中断触发,在下一个周期里面接收中断没有被触发,那么空闲中断被触发。即空闲中断触发条件是:使能控件中断,接收寄存器不为空被触发

四、STM32CubeMX 配置串口通信 HAL库 

4.1 STM32CubeMX 配置串口

 

 

 

每个外设生成独立的 ’.c/.h’ 文件
不勾:所有初始化代码都生成在 main.c
勾选:初始化代码生成在对应的外设文件。 如 GPIO 初始化代码生成在 gpio.c 中。

4.2 重写fputc函数

​
#include <stdio.h>#ifdef __GNUC__#define PUTCHAR_PROTOTYPE int _io_putchar(int ch)#else#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)#endif /* __GNUC__*//*******************************************************************@brief  Retargets the C library printf  function to the USART.*@param  None*@retval None******************************************************************/PUTCHAR_PROTOTYPE{HAL_UART_Transmit(&huart1, (uint8_t *)&ch,1,0xFFFF);return ch;}​

4.3 配置接收中断函数

​
#define RXBUFFERSIZE    1                       /* 缓存大小 */
uint8_t  g_usart_rx_buf[200];  //接收缓冲,最大200个字节
uint16_t g_usart_rx_len;   	   //接收长度
uint8_t g_usart_rx_flag=0;	   //接收完成标志
uint8_t g_rx_buffer[RXBUFFERSIZE];       /* HAL库USART接收Buffer */在串口初始化函数中使能接收中断
/* 该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量 */
HAL_UART_Receive_IT(&huart1, (uint8_t *)g_rx_buffer, RXBUFFERSIZE);void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{  if(huart->Instance == USART1)     {g_usart_rx_buf[g_usart_rx_len] = g_rx_buffer[0] ;g_usart_rx_len++;if(g_rx_buffer[0]==0x0a){g_usart_rx_flag=1;}HAL_UART_Receive_IT(&huart1, (uint8_t *)g_rx_buffer, RXBUFFERSIZE);}
}​

4.4 串口空闲中断接收数据

__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);//空闲中断
#define USART1_MAX_RECV_LEN		1000				//最大接收缓存字节数
char USART1_RX_BUF[USART1_MAX_RECV_LEN]; 				//接收缓冲,最大USART3_MAX_RECV_LEN个字节
unsigned short USART1_RX_STA=0;   	/* USER CODE BEGIN 1 */
void USART1_IRQHandler(void)
{uint8_t res = 0;//接收中断if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_RXNE) != RESET){HAL_UART_Receive(&huart1,&res,1,1000);//将数据放入缓冲区if( (USART1_RX_STA&0x7fff) < USART1_MAX_RECV_LEN){USART1_RX_BUF[USART1_RX_STA] = res;USART1_RX_STA++;}__HAL_UART_CLEAR_FLAG(&huart1,UART_FLAG_RXNE);}//空闲中断if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE) != RESET){//一帧数据接收完成//USART1_IdleCallback(USART1_RX_BUF,USART1_RX_STA&0x7fff);USART1_RX_BUF[ USART1_RX_STA &0x7fff] = 0;USART1_RX_STA |= 1 << 15;  __HAL_UART_CLEAR_IDLEFLAG(&huart1);}
}
	if(USART1_RX_STA& 0x8000){printf("%s\r\n",USART1_RX_BUF);USART1_RX_STA=0;}

五、串口溢出中断如何处理ORE

5.1 出现的问题和现象

        当数据接收区或者FIFO区有数据或者满时,又有新数据进来,会导致发生溢出错误,一旦发生溢出错误,RX 移位寄存区虽然能有新数据不断的覆盖,但是数据不会到达RXR或FIFO(现象是:RXNE在ORE置位时不会被置位),导致程序中不能读到新的数据。只有通过ICR清除ORE才能使得RXNE在接收到新数据时置位。或者增大接收数组的容量,一般采用前者解决。

5.2 什么是ORE中断?为什么会产生?
这里写图片描述

产生原因如上所述。

ORE标志位在USART_SR寄存器,但值得注意的是

USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);使能了接收中断,那么ORE中断也同时被开启了。

USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);使能了接收中断,那么ORE中断也同时被开启了。

USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);使能了接收中断,那么ORE中断也同时被开启了。

5.3 解决办法

 stm32

if(USART_GetFlagStatus(USART1,USART_FLAG_ORE)== SET)//程序中断过多,主机的发送速度又快,很容易会造成溢出错误
{USART_ClearFlag(USART1, USART_FLAG_ORE); //清除溢出中断USART_ReceiveData(USART1);//必须要读,不然溢出中断清除不了
}

兆易创新

	/*Erro flag*/if(RESET != usart_interrupt_flag_get(UART7, USART_INT_FLAG_RBNE_ORERR)){usart_data_receive(UART7);	usart_interrupt_flag_clear(UART7,USART_INT_FLAG_RBNE_ORERR);}if(RESET != usart_interrupt_flag_get(UART7, USART_INT_FLAG_ERR_NERR)){usart_data_receive(UART7);	usart_interrupt_flag_clear(UART7,USART_INT_FLAG_ERR_NERR);}if(RESET != usart_interrupt_flag_get(UART7, USART_INT_FLAG_ERR_FERR)){usart_data_receive(UART7);	usart_interrupt_flag_clear(UART7,USART_INT_FLAG_ERR_FERR);}

六、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 个节点。

七、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用同一个线连接,然后控制该脚的电平信号来确定是发送还是接收模式

八、RS485切换模式需要时间

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

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


http://www.mrgr.cn/news/10444.html

相关文章:

  • 2024Android面试题与答案(1)
  • Shader 中的渲染顺序
  • 数学基础 -- 线性代数之增广矩阵
  • 快速排序与其例题
  • stm32智能颜色送餐小车(红外光管避障)
  • python如何调用另一个文件中的函数
  • 使用kafka改造分布式事务
  • 一文搞定MybatisPlus
  • asio之task_io_service多线程
  • [Linux]如何在虚拟机安装Ubuntu?(小白向)
  • Java获取List实体交集差集
  • docker inspect输出内容详解,推测容器运行命令
  • 【案例59】WebSphere类加载跟踪开启方法
  • 吴恩达谈AI未来:Agentic Workflow、推理成本下降与开源的优势
  • 【Kotlin设计模式】Kotlin实现工厂模式
  • Python-断点续传的方式下载GPM降水数据
  • 企业高性能web服务器知识点合集
  • [指南]微软发布Windows-Linux双系统无法启动的完整修复方案
  • 可变参数模板(C++11)
  • 深度学习设计模式之策略模式