利用RS485通信、串口收发数据的硬件连接及部分代码

文章讲述了如何通过STM32F103C8T6单片机A检测单片机B的485通信功能,包括设计思路、引脚连接、XL3485芯片使用、遇到的问题解决方案、串口与RS485通信原理以及代码实现。重点介绍了如何通过半双工通信协议确保数据的正确传输和接收。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、设计思路

1.1、总体设计思路

设计思路:单片机A检测单片机B的功能,单片机B待检测的地方全部引出,然后再将一些功能信息发送到屏幕。单片机A和单片机B之间、单片机A和屏幕(一种可视化编辑的屏幕,规定好通信协议,利用485通信将字符串发送过去就能显示相应的内容,屏幕:TK607ID) 。

注:本文只对单片机B的485通信功能进行检测,所用的单片机芯片皆为STM32F103C8T6,用到的485通信芯片为XL3485。

1.2、软件设计思路

软件设计思路:相当于单片机B的功能是否正常要通过屏幕显示出来,而单片机A是中间的桥梁,由于485是半双工,单片机B只有收到询问指令的数据才会发送响应的数据(单片机B程序不可更改,被动);屏幕是不断的询问单片机A,单片机A收到询问后再发送响应的数据给屏幕,屏幕作出指令(主动)。由单片机A发送一个数据后等待回应,收到单片机B的数据后解析相应的功能,处理好后再发送给屏幕,中间有个时间差。

2、单片机引脚定义及连接

下面是单片机A和单片机B之间的通信:通过查找手册串口收发引脚。

黄色高亮的是串口通信收发引脚。 

3、XL3485芯片

3.1引脚分布

 3.2引脚定义

3.3接线方式

A、B总线接口,用于连接485总线。RO是接收输出端DI是发送数据收入端,RE是接收使能信号 (低电平有效) DE是发送使能信号(高电平有效)。所以直接将RE和DE连接一块,接PA6引脚,当输出高电平时发送数据,输出低电平时接受数据。

仅供参考,具体详见XL3485规格书。

4、遇到的问题及解决方法

 遇到的问题:遇到了第一个问题,接受不到单片机B发送的数据,不知道单片机A是否发送数据。我参考了致远电子的RS485通信的详细说明,在XL3485的A总线加上一个上拉电阻、B总线加上一个下拉电阻,如下:

 飞线接了上拉电阻和下拉电阻后,调试发现发送数组和接受数组有相应的值,问题解决。

我查找相关数据手册和资料发现,在485通信中,连接一个120Ω的电阻是为了实现信号的匹配和防止信号反射。这个电阻被称为终端电阻或者终端电阻器,120R电阻加在信号线末端,原因是:高频信号传输时,信号波长相对传输线较短,信号在传输线终端会形成反射波,干扰原信号,所以需要在传输线末端加终端电阻,使信号到达传输线末端后不反射,对于低频信号则不用,如下图:

 5、串口通信和485通信

5.1串口通信

关于串口通信和RS485通信:串口通信很简单,有三个端口即可收发数据,发送端TX,接收端RX,以及公共地GND。发送和接受的数据实际上是一串高低电平的信号,所以要公共地GND来区别是高电平还是低电平。

5.2RS485通信

RS485通信是串口通信的一种,通常是由单片机的串口连接485通信芯片,再由485芯片和485芯片之间通信。另外RS485采用差分信号的方式,由A端和B端接受到信号后进行差值运算来识别是高电平还是低电平,这样不仅可以减少外界对信号传输的干扰,而且只需要俩根线就可以完成传输。在多设备通信,远距离通信上应用广泛。

6、代码部分

6.1串口初始化

RS485通信也是利用串口通信,第一步要对串口进行初始化,以串口2为例。

void Uart2_Init(u32 bound)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);	//使能 GPIOA 时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);   //使能 USART2 
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO , ENABLE);

	USART_DeInit(USART2);										//复位串口1

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;					//USART2_TX PA.2
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;				//复用推挽输出
	GPIO_Init(GPIOA, &GPIO_InitStructure);						

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;					//USART2_RX PA.3
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; 		//浮空输入
	GPIO_Init(GPIOA, &GPIO_InitStructure);						

	USART_InitStructure.USART_BaudRate = bound;					//波特率设置
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;	//字长为 8 位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;		//一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;			//无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件数据流控制	
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//收发模式
	USART_Init(USART2, &USART_InitStructure);					//初始化串口
	
	USART_Cmd(USART2, ENABLE);  								//使能串口2 
	
	
	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);				//开启接收中断
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;             //485芯片使能
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;			//复用推挽输出
	GPIO_Init(GPIOA, &GPIO_InitStructure);						//初始化 GPIOA.1

	NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;		//抢占优先级 0
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;			//子优先级 0
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//IRQ 通道使能
	NVIC_Init(&NVIC_InitStructure);								//中断优先级初始化
}

配置好响应的引脚以及参数。

6.2数据发送

u8 USART_Send_Data(u8 *buf,u16 len,USART_TypeDef* USARTx)
{
   u16 t;
	for(t=0;t<len;t++)
	{																		//循环发送数据
		while(USART_GetFlagStatus(USARTx,USART_FLAG_TC)==RESET);			//等待发送结束
		USART_SendData(USARTx,buf[t]);
	}	 
	while(USART_GetFlagStatus(USARTx,USART_FLAG_TC)==RESET);
	return 0;
}

调用该函数,使能XL3485芯片,实现发送。

void Usart2_Printf(u8 *buf,u16 len)
{
	 GPIO_SetBits(GPIOA, GPIO_Pin_6);     										//使能发送
	 if(!USART_Send_Data(buf,len,USART2))   					//发送完成转变成接受模式
	 {  
		 GPIO_ResetBits(GPIOA, GPIO_Pin_6);
	 }
}

在实际发送数据时,可通过定义一个数组USART_TX_BUF_Two,再定义上述函数发送:

		USART_TX_BUF_Two[0] = 1;
		USART_TX_BUF_Two[1] = 0x04;
		USART_TX_BUF_Two[2] = 0x00;
		USART_TX_BUF_Two[3] = 0x00;
		USART_TX_BUF_Two[4] = 0x00;
		USART_TX_BUF_Two[5] = 0x02;
		CRC16_Usart_Send = CRC16(USART_TX_BUF_Two,6);				//计算CRC16校验
		USART_TX_BUF_Two[6] = CRC16_Usart_Send;
		USART_TX_BUF_Two[7] = CRC16_Usart_Send>>8;
		Usart2_Printf(USART_TX_BUF_Two,8);	//发送数据

本设计RS485通信用的是MODBUS通信协议,需要CRC检验来判断发送的数据是否正确,可采用其他不同的通信协议,也可以自己定义一种通信的协议。这样通过对函数的调用即可实现发送数据。

6.3数据的接收

数据在接收时同样定义一个数组USART_RX_BUF_Two,通过中断来接收数据(和串口通信一样):

void USART2_IRQHandler(void)
{
	u8 Data;
	if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)	//接受中断
	{
		USART_ClearITPendingBit(USART2,USART_IT_RXNE);
		Data = USART_ReceiveData(USART2);					//(USART2->DR); //读取接收到的数据
		USART_RX_BUF_Two[RxTwoNum++] = Data;
		Cnt_Two = 0;									   //计时清零
	}
}

再定义一个定时器,每隔一段时间读取寄存器的值。

void TIM3_IRQHandler(void)      //TIM3中断 
{
	 if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
	 {
		 TIM_ClearITPendingBit(TIM3, TIM_IT_Update);	     //清除TIM3更新中断标志
		 
		 if(RxTwoNum)			                                //串口2定时接收,RxTwoNum不等于0时即为接收到了数据
		 {
			if(++ Cnt_Two >= 4)									//接收210/4ms
			{
				Cnt_Two = 0;
				RxTwoNum = 0;									//接收计数清零
				PreRevTwoLong = RxTwoNum;
				ReveceFinishTwo = 1;							//串口1接收成功标志位
			}
		 }

	 }
}

RS485是半双工的通信,可以根据定时结束后视为接受一次数据对标志位进行操作,接收到数据后才进行发送或者发送后才接受数据,将收发数据分开,以免数据紊乱。收到数据的时间根据需求设置中断函数。

这样每隔一段时间就可以自动接受到一次数据了。

RS485通信的收发数据和串口收发数据大同小异,要注意到RS485通信方式是半双工收发要间隔开。另外RS485在收发数据时要注意芯片的RE是接收使能信号 (低电平有效), DE是发送使能信号(高电平有效),通常可接在一起使用。

注:-2到-6电平代表逻辑0;2到6电平代表逻辑1.

评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值