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

2019-07-20 20:42发布

第三十一章 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;//设置字体为红 {MOD}        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;//设置字体为蓝 {MOD}          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
  

友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
4条回答
hackathos
1楼-- · 2019-07-20 21:49
 精彩回答 2  元偷偷看……
zouri456
2楼-- · 2019-07-21 03:00
 精彩回答 2  元偷偷看……
不忘初心713
3楼-- · 2019-07-21 07:09
zouri456 发表于 2016-8-17 09:23
不科学,为什么串口的两个IO口都配置成输出状态啊

我也以为这样不科学,至少串口的RT作为接收端应该是输入;但12讲的GPIOgongzuo原理“推完复用输出模式”除了能复用输出,也能完成对IO的读取数据。
zouri456
4楼-- · 2019-07-21 12:01


那我实验一下好了

一周热门 更多>