串口和RS485通信

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

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


/*COM Send Data Structure*/
typedef struct
{
	uint8_t  ubs_Index;                    //send index
	uint8_t  ubs_Len;                      //send len
	uint8_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才有驱动能力输出

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值