STM32F103 485通信开发实例

目录

一、背景介绍

二、主机代码

1、串口初始化配置

2、发送函数定义

3、串口接收中断函数定义

4、定时中断(用于主机发送指令)

5、.h文件 

三、从机代码

1、串口初始化配置

2、发送函数定义

3、串口接收中断函数定义

4、.h文件

四、测试结果

五、注意事项


一、背景介绍

        项目开发需要用到stm32的串口实现485通信,整个调试过程花了一天半,比预想中的长,期间陆续解决了几个小问题,有些是硬件上的问题,最后总算是把整套代码调试通顺。整理了一下,放在这里供有需要的人参考。

        因为需要实现多个stm32f103芯片之间的数据交互,485通信为半双工模式,因此代码包含了主机和从机两个部分。为了便于多装置组网,整体上采用主机问询-从机应答的模式,保证同一时间网络中只有一个装置发数据,避免发生通信冲突。

        具体的规约设计需根据实际需求而定,本文尽量采用简单实例,便于清晰展示485通信功能的整体架构和逻辑。

二、主机代码

1、串口初始化配置

void usart2_init(u32 baud)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE);

	//TX
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure); 
	
    //RX  	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure); 
	
	//RN
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOD, &GPIO_InitStructure);
	
	USART_InitStructure.USART_BaudRate = baud;//串口波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//长度为8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//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); //初始化串口2
    USART_Cmd(USART2, ENABLE);                //使能串口2
    USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启串口接收中断
    
    //中断优先级配置
	NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);

	RS485_RN = 0;//初始化时默认为接收模式
}

2、发送函数定义

u8 RS485_Send(u8 *buf,u8 len)
{
	u8 i;
	
	for(i=0;i<len;i++)
	{
		USART_SendData(USART2,buf[i]);
	}

	while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET);
	
	return 1;
}

3、串口接收中断函数定义

void USART2_IRQHandler(void)
{
	u8 readd;
	u8 error;

	if(USART_GetITStatus(USART2,USART_IT_RXNE)!=RESET)
	{
		//检测噪音、帧错误或校验位错误
        if(USART_GetFlagStatus(USART2,USART_FLAG_NE|USART_FLAG_FE|USART_FLAG_PE))
        {
            error = 1;
        }
			
		else
        {
            error = 0;
        }			
		
		readd = USART_ReceiveData(USART2); //读取接收到的字节
		
		if((RS485_RX_CNT < 8)&&(error == 0))
		{
			//按照规约设置,一帧数据包含8字节,逐个接收
            RS485_RX_BUFF[RS485_RX_CNT]=res;
			RS485_RX_CNT++;
		}
		
        //一帧数据接收完毕,按照规约,进行数据整理,根据实际需求设计规约
		if(RS485_RX_CNT == 8)
		{
			RS485_RX_CNT = 0;
			
			/*接收数据整理*/
			
			//发送标志位为1,表示主机数据接收完毕,可以准备发送新指令
			RS485_TX_EN = 1;
		}
	}
}

4、定时中断(用于主机发送指令)

void TIM3_IRQHandler(void)
{
	u8 i;

	if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
	{
		if(RS485_TX_EN)//如果接收中断结束,表示可以发送新的指令
		{		
			RS485_TX_EN = 0;//置0,因为发送完毕后需要等待从机的返回数据,避免通信冲突
			
            //发送数据赋值,这里仅以简单数组表示
			RS485_TX_BUFF[0] = 0x01;
			RS485_TX_BUFF[1] = 0x01;
			RS485_TX_BUFF[2] = 0x01;
			RS485_TX_BUFF[3] = 0x01;
			RS485_TX_BUFF[4] = 0x01;
			RS485_TX_BUFF[5] = 0x01;
			RS485_TX_BUFF[6] = 0x01;

			//最后一个字节设置为校验位,生成校验值
			RS485_TX_BUFF[7] = 0x00;
			for(i=0;i<7;i++)
			{
				RS485_TX_BUFF[7] += RS485_TX_BUFF[i];		
			}
			
            //数据发送
			RS485_RN = 1;
			RS485_Send(RS485_TX_BUFF,8);
			RS485_RN = 0;
		}
		
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update);  //Çå³ýTIM3µÄÖжϴý´¦Àíλ
	}
}

5、.h文件 

#ifndef __USART2_H
#define __USART2_H

#include "all.h"

#define RS485_RN PDout(7)

void usart2_init(u32 baud);

u8 RS485_Send(u8 *buf,u8 len);
#endif

三、从机代码

1、串口初始化配置

        与主机相同

2、发送函数定义

        与主机相同

3、串口接收中断函数定义

        基本流程是先接收,然后校验,最后生成返回值并发送。作为从机,不会主动向外发送信息,仅根据接收到的数据按照规约发送相应数据返回给主机。

void USART2_IRQHandler(void)
{
	u8 readd;
	u8 error;
	u8 check_temp = 0;
	u8 i;

	if(USART_GetITStatus(USART2,USART_IT_RXNE)!=RESET)
	{
        //检测噪音、帧错误或校验错误		
        if(USART_GetFlagStatus(USART2,USART_FLAG_NE|USART_FLAG_FE|USART_FLAG_PE))
        {
            error = 1;
        }

		else
        {
            error = 0;
        }
			
		//读取接收字节
		readd = USART_ReceiveData(USART2); 
		
        //逐个读取各字节
		if((RS485_RX_CNT < 8)&&(error == 0))
		{
			RS485_RX_BUFF[RS485_RX_CNT]=res;
			RS485_RX_CNT++;			
		}
		
        //8字节读取完毕,进行数据整理,及返回数据发送
		if(RS485_RX_CNT == 8)
		{
			RS485_RX_CNT = 0;

			//首先进行数据校验
			for(i=0;i<7;i++)
			{
				check_temp += RS485_RX_BUFF[i];
			}

            //若校验通过,返回一组数据
			if(check_temp == RS485_RX_BUFF[7])
			{
				//组织返回数据
                RS485_TX_BUFF[0] = 0x10;
				RS485_TX_BUFF[1] = 0x10;
				RS485_TX_BUFF[2] = 0x10;
				RS485_TX_BUFF[3] = 0x10;
				RS485_TX_BUFF[4] = 0x10;
				RS485_TX_BUFF[5] = 0x10;
				RS485_TX_BUFF[6] = 0x10;

				//生成返回数据的校验值
				RS485_TX_BUFF[7] = 0x00;
				for(i=0;i<7;i++)
				{
					RS485_TX_BUFF[7] += RS485_TX_BUFF[i];		
				}

				//数据发送
				RS485_RN = 1;
				RS485_Send(RS485_TX_BUFF,8);
				RS485_RN = 0;
			}
            
            //若校验不通过,返回另一组数据
			else
			{
                //组织返回数据
				RS485_TX_BUFF[0] = 0x11;
				RS485_TX_BUFF[1] = 0x00;
				RS485_TX_BUFF[2] = 0x00;
				RS485_TX_BUFF[3] = 0x00;
				RS485_TX_BUFF[4] = 0x00;
				RS485_TX_BUFF[5] = 0x00;
				RS485_TX_BUFF[6] = 0x00;
				
                //生成返回数据的校验值
				RS485_TX_BUFF[7] = 0x00;
				for(i=0;i<7;i++)
				{
					RS485_TX_BUFF[7] += RS485_TX_BUFF[i];		
				}
				
                //数据发送
				RS485_RN = 1;
				RS485_Send(RS485_TX_BUFF,8);
				RS485_RN = 0;
			}
		}
	}
}

4、.h文件

        与主机相同

四、测试结果

        利用两块带有485接口的开发板进行测试,主机采用调试模式,测试结果如图:

         结论:发送数据和接收数据符合预期,通信正确。

五、注意事项

        1)本设计中的485采用半双工,因此现实中通信规约和主从机的发送接收机制需要重点设计,尤其要考虑发送与接收之间的时延,避免出现通信冲突;

        2)本文仅验证了包含一台主机和一台从机的简单系统,其在复杂系统中的应用效果有待进一步测试。

  • 15
    点赞
  • 149
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
STM32F103是一款常用的ARM Cortex-M3内核微控制器,具有强大的计算能力和丰富的外设资源。在485通信开发中,可以使用STM32F103来实现485通信功能。 首先,需要连接485收发器模块和STM32F103微控制器。485收发器模块起到将TTL电平转换为RS485电平的作用。 其次,需要配置STM32F103的相应外设模块,如串口模块USART作为485通信的物理接口。可以使用STM32CubeMX软件进行配置,设置串口工作模式为RS485模式,配置相应的波特率、数据位、停止位等参数。 接下来,需要编写程序来实现485通信的具体逻辑。可以使用STM32 HAL库提供的相关函数来方便地进行串口通信。 首先,在发送数据时,利用HAL库的函数可以直接发送数据到串口缓冲区,并等待发送完成。对于RS485通信,需要将发送使能引脚控制为逻辑高电平,使得485收发器进入发送模式。发送数据后,再将发送使能引脚控制为逻辑低电平,使得485收发器返回接收模式。 在接收数据时,可以设置中断接收方式,利用HAL库的函数来接收串口数据,并进行处理。对于RS485通信,需要在接收使能引脚控制为逻辑高电平时才接收数据,并在接收完成后将接收使能引脚控制为逻辑低电平。 此外,还可以使用STM32F103的其他功能来优化485通信,如使用DMA提高数据传输效率,使用定时器来定时发送或接收数据等。 总结来说,实现STM32F103485通信开发需要进行硬件连接、外设配置和编写相应的程序。通过合理的配置和程序编写,可以实现稳定、高效的485通信功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Forster-C

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值