OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 9785|回复: 4

【正点原子探索者STM32F407开发板例程连载+教学】第31章 485通信实验

[复制链接]

230

主题

1950

帖子

10

精华

论坛元老

Rank: 8Rank: 8

积分
4562
金钱
4562
注册时间
2010-12-14
在线时间
32 小时
发表于 2014-12-6 17:19:17 | 显示全部楼层 |阅读模式

第三十一章 485 实验

 

[mw_shl_code=c,true]1.硬件平台:正点原子探索者STM32F407开发板 2.软件平台:MDK5.1 3.固件库版本:V1.4.0 [/mw_shl_code]


本章我们将向大家介绍如何使用STM32F4的串口实现485通信(半双工)。在本章中,我们将使用STM32F4的串口2来实现两块开发板之间的485通信,并将结果显示在TFTLCD模块上。本章分为如下几个部分:

31.1 485 简介

31.2 硬件设计

31.3 软件设计

31.4 下载验证

31.1 485 简介

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推荐使用在点对点网络中,线型,总线型,不能是星型,环型网络。理想情况下RS485需要2个终端匹配电阻,其阻值要求等于传输电缆的特性阻抗(一般为120Ω)。没有特性阻抗的话,当所有的设备都静止或者没有能量的时候就会产生噪声,而且线移需要双端的电压差。没有终接电阻的话,会使得较快速的发送端产生多个数据信号的边缘,导致数据传输出错。485推荐的连接方式如图31.1.2所示:


31.1.2 RS485连接

       在上面的连接中,如果需要添加匹配电阻,我们一般在总线的起止端加入,也就是主机和设备4上面各加一个120Ω的匹配电阻。

由于RS485具有传输距离远、传输速度快、支持节点多和抗干扰能力更强等特点,所以RS485有很广泛的应用。

探索者STM32F4开发板采用SP3485作为收发器,该芯片支持3.3V供电,最大传输速度可达10Mbps,支持多达32个节点,并且有输出短路保护。该芯片的框图如图31.1.2所示:

31.1.2 SP3485框图

       图中AB总线接口,用于连接485总线。RO是接收输出端,DI是发送数据收入端,RE是接收使能信号(低电平有效),DE是发送使能信号(高电平有效)。

本章,我们通过该芯片连接STM32F4的串口2,实现两个开发板之间的485通信。本章将实现这样的功能:通过连接两个探索者STM32F4开发板的RS485接口,然后由KEY0控制发送,当按下一个开发板的KEY0的时候,就发送5个数据给另外一个开发板,并在两个开发板上分别显示发送的值和接收到的值。

本章,我们只需要配置好串口2,就可以实现正常的485通信了,串口2的配置和串口1基本类似,只是串口的时钟来自APB1,最大频率为42Mhz

31.2 硬件设计

本章要用到的硬件资源如下:

1)  指示灯DS0

2)  KEY0按键

3) TFTLCD模块

4) 串口2

5) RS485收发芯片SP3485

前面3个之前都已经详细介绍过了,这里我们介绍SP3485和串口2的连接关系,如图31.2.1所示:

31.2.1 STM32F4SP3485连接电路图

       从上图可以看出:STM32F4的串口2通过P9端口设置,连接到SP3485,通过STM32F4PG8控制SP3485的收发,当PG8=0的时候,为接收模式;当PG8=1的时候,为发送模式。

这里需要注意,PA2PA3ETH_MDIOPWM_DAC有共用IO,所以在使用的时候,注意分时复用,不能同时使用。另外RS485_RE信号,也和NRF_IRQ共用PG8,所以他们也不可以同时使用,只能分时复用。

       另外,图中的R38R40是两个偏置电阻,用来保证总线空闲时,AB之间的电压差都会大于200mV(逻辑1)。从而避免因总线空闲时,AB压差不定,引起逻辑错乱,可能出现的乱码。

然后,我们要设置好开发板上P9排针的连接,通过跳线帽将PA2PA3分别连接到485_TX485_RX上面,如图31.2.2所示:

31.2.2 硬件连接示意图

       最后,我们用2根导线将两个开发板RS485端子的AABB连接起来。这里注意不要接反了(AB),接反了会导致通讯异常!!

31.3 软件设计

打开我们的485实验例程,可以发现项目中加入了一个rs485.c文件以及其头文件rs485文件,同时485通信因为底层用的是串口2,所以需要引入库函数stm32f4xx_usart.c文件和对应的头文件stm32f4xx_usart.h

打开rs485.c文件,代码如下:

#if EN_USART2_RX                //如果使能了接收        

//接收缓存区       

u8 RS485_RX_BUF[64];     //接收缓冲,最大64个字节.

//接收到的数据长度

u8 RS485_RX_CNT=0;  

void USART2_IRQHandler(void)

{

       u8 res;        

       if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)//接收到数据

       {         

         res =USART_ReceiveData(USART2);//;读取接收到的数据USART2->DR

              if(RS485_RX_CNT<64)

              {

                     RS485_RX_BUF[RS485_RX_CNT]=res;            //记录接收到的值

                     RS485_RX_CNT++;                                         //接收数据增加1

              }

       }                                                                           

}

#endif                                                                   

//初始化IO 串口2

//bound:波特率       

void RS485_Init(u32 bound)

{     

  GPIO_InitTypeDef GPIO_InitStructure;

USART_InitTypeDef USART_InitStructure;

NVIC_InitTypeDef NVIC_InitStructure;

 

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能PA时钟

RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);//使能USART2时钟

      

  //串口2引脚复用映射

GPIO_PinAFConfig(GPIOA,GPIO_PinSource2,GPIO_AF_USART2); //PA2复用为USART2

GPIO_PinAFConfig(GPIOA,GPIO_PinSource3,GPIO_AF_USART2);//PA3复用为USART2

      

//USART2   

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3; //GPIOA2GPIOA3

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;   //速度100MHz

GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出

GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉

GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA2PA3

      

//PG8推挽输出,485模式控制 

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //GPIOG8

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//输出

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;   //速度100MHz

GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽输出

GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉

GPIO_Init(GPIOG,&GPIO_InitStructure); //初始化PG8

      

  //USART2 初始化设置

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); //初始化串口2

      

  USART_Cmd(USART2, ENABLE);  //使能串口2

USART_ClearFlag(USART2, USART_FLAG_TC);

      

#if EN_USART2_RX   

USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启接受中断

 

       //Usart2 NVIC 配置

    NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;

       NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级3

       NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;            //响应优先级3

       NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                //IRQ通道使能

       NVIC_Init(&NVIC_InitStructure);      //根据指定的参数初始化VIC寄存器、

#endif    

       RS485_TX_EN=0;               //默认为接收模式 

}

 

//RS485发送len个字节.

//buf:发送区首地址

//len:发送的字节数(为了和本代码的接收匹配,这里建议不要超过64个字节)

void RS485_Send_Data(u8 *buf,u8 len)

{

       u8 t;

       RS485_TX_EN=1;               //设置为发送模式

      for(t=0;t<len;t++)         //循环发送数据

       {

        while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET); //等待发送结束

     USART_SendData(USART2,buf[t]); //发送数据

       }    

       while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET); //等待发送结束

       RS485_RX_CNT=0;       

       RS485_TX_EN=0;                      //设置为接收模式 

}

//RS485查询接收到的数据

//buf:接收缓存首地址

//len:读到的数据长度

void RS485_Receive_Data(u8 *buf,u8 *len)

{

       u8 rxlen=RS485_RX_CNT;

       u8 i=0;

       *len=0;                        //默认为0

       delay_ms(10);        //等待10ms,连续超过10ms没有接收到一个数据,则认为接收结束

       if(rxlen==RS485_RX_CNT&&rxlen)//接收到了数据,且接收完成了

       {

              for(i=0;i<rxlen;i++)

              {

                     buf=RS485_RX_BUF;  

              }           

              *len=RS485_RX_CNT;  //记录本次数据长度

              RS485_RX_CNT=0;             //清零

       }

}

此部分代码总共4个函数,其中RS485_Init函数为485通信初始化函数,其实基本上就是在配置串口2,只是把PG8也顺带配置了,用于控制SP3485的收发。同时如果使能中断接收的话,会执行串口2的中断接收配置。USART2_IRQHandler函数用于中断接收来自485总线的数据,将其存放在RS485_RX_BUF里面。最后RS485_Send_DataRS485_Receive_Data这两个函数用来发送数据到485总线和读取从485总线收到的数据,都比较简单。

头文件rs485.h文件中,我们通过下面一行代码打开了接受中断:

#define EN_USART2_RX    1            //0,不接收;1,接收.

其他内容就是一些函数什么,所以这里我们不细说。

接下来,我们来看看主函数代码:

int main(void)

{

       u8 key, i=0,t=0, cnt=0, rs485buf[5];

       NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2

       delay_init(168);  //初始化延时函数

       uart_init(115200);  //初始化串口波特率为115200

       LED_Init();                                //初始化LED

      LCD_Init();                                //LCD初始化

       KEY_Init();                        //按键初始化 

       RS485_Init(9600);        //初始化RS485串口2

      POINT_COLOR=RED;//设置字体为红色

       LCD_ShowString(30,50,200,16,16,"Explorer STM32F4");      

       LCD_ShowString(30,70,200,16,16,"RS485 TEST"); 

       LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");

       LCD_ShowString(30,110,200,16,16,"2014/5/7");      

       LCD_ShowString(30,130,200,16,16,"KEY0:Send");  //显示提示信息           

      POINT_COLOR=BLUE;//设置字体为蓝色  

       LCD_ShowString(30,150,200,16,16,"Count:");                 //显示当前计数值 

       LCD_ShowString(30,170,200,16,16,"Send Data:");           //提示发送的数据 

       LCD_ShowString(30,210,200,16,16,"Receive Data:");       //提示接收到的数据           

       while(1)

       {

              key=KEY_Scan(0);

              if(key==KEY0_PRES)//KEY0按下,发送一次数据

              {

                     for(i=0;i<5;i++)

                     {

                            rs485buf=cnt+i;//填充发送缓冲区

                            LCD_ShowxNum(30+i*32,190,rs485buf,3,16,0X80);    //显示数据

                    }

                     RS485_Send_Data(rs485buf,5);//发送5个字节                                                                   

              }           

              RS485_Receive_Data(rs485buf,&key);

              if(key)//接收到有数据

              {

                     if(key>5)key=5;//最大是5个数据.

             for(i=0;i<key;i++)LCD_ShowxNum(30+i*32,230,rs485buf,3,16,0X80);      //显示数据

             }

              t++;

              delay_ms(10);

              if(t==20)

              {

                     LED0=!LED0;//提示系统正在运行    

                     t=0;cnt++;

                     LCD_ShowxNum(30+48,150,cnt,3,16,0X80);     //显示数据

              }              

       }  

}

此部分代码,我们通过函数RS485_Init(9600),初始化串口2的波特率为9600cnt是一个累加数,一旦KEY0按下,就以这个数位基准连续发送5个数据。当485总线收到数据的时候,就将收到的数据直接显示在LCD屏幕上。

31.4 下载验证

在代码编译成功之后,我们通过下载代码到ALIENTEK探索者STM32F4开发板上(注意要2个开发板都下载这个代码哦),得到如图31.4.1所示:

31.4.1 程序运行效果图

伴随DS0的不停闪烁,提示程序在运行。此时,我们按下KEY0就可以在另外一个开发板上面收到这个开发板发送的数据了。如图31.4.2和图31.4.3所示:

31.4.2 RS485发送数据

31.4.3 RS485接收数据

       31.4.2来自开发板A,发送了5个数据,图31.4.3来自开发板B,接收到了来自开发板A5个数据。

本章介绍的485总线时通过串口控制收发的,我们只需要将P9的跳线帽稍作改变,该实验就变成了一个RS232串口通信实验了,通过对接两个开发板的RS232接口,即可得到同样的实验现象,有兴趣的读者可以实验一下。

 


 

 实验详细手册和源码下载地址:http://www.openedv.com/posts/list/41586.htm 

正点原子探索者STM32F407开发板购买地址http://item.taobao.com/item.htm?id=41855882779

  


实验26 485通信实验.zip

520.84 KB, 下载次数: 1664

第三十一章 485 实验-STM32F4开发指南-正点原子探索者STM32开发板.pdf

957.81 KB, 下载次数: 1505

我是开源电子网?网站管理员,对网站有任何问题,请与我联系!QQ:389063473Email:389063473@qq.com
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

5

主题

21

帖子

0

精华

初级会员

Rank: 2

积分
174
金钱
174
注册时间
2016-3-26
在线时间
45 小时
发表于 2016-4-9 09:11:25 | 显示全部楼层
程序中怎么都没看明白发送的是084到088这5个数据,是怎么设置的?别的数据行吗
回复 支持 反对

使用道具 举报

4

主题

20

帖子

0

精华

初级会员

Rank: 2

积分
57
金钱
57
注册时间
2016-7-18
在线时间
13 小时
发表于 2016-8-17 09:23:08 | 显示全部楼层
不科学,为什么串口的两个IO口都配置成输出状态啊
回复 支持 反对

使用道具 举报

13

主题

75

帖子

0

精华

高级会员

Rank: 4

积分
659
金钱
659
注册时间
2016-6-23
在线时间
72 小时
发表于 2016-8-17 15:40:08 | 显示全部楼层
zouri456 发表于 2016-8-17 09:23
不科学,为什么串口的两个IO口都配置成输出状态啊

我也以为这样不科学,至少串口的RT作为接收端应该是输入;但12讲的GPIOgongzuo原理“推完复用输出模式”除了能复用输出,也能完成对IO的读取数据。
回复 支持 反对

使用道具 举报

4

主题

20

帖子

0

精华

初级会员

Rank: 2

积分
57
金钱
57
注册时间
2016-7-18
在线时间
13 小时
发表于 2016-8-18 17:09:46 | 显示全部楼层


那我实验一下好了
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-2-28 03:38

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表