打印

【连载】STM32开发指南--第二十九章 485实验

[复制链接]
3541|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
正点原子|  楼主 | 2013-3-17 23:07 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
第二十九章 485 实验
       本章我们将向大家介绍如何使用STM32的串口实现485通信(半双工)。在本章中,我们将使用STM32的串口2来实现两块开发板之间的485通信,并将结果显示在TFTLCD模块上。本章分为如下几个部分:
29.1 485 简介
29.2 硬件设计
29.3 软件设计
29.4 下载验证

29.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推荐的连接方式如图29.1.2所示:
图29.1.2 RS485连接
       在上面的连接中,如果需要添加匹配电阻,我们一般在总线的起止端加入,也就是主机和设备4上面各加一个120Ω的匹配电阻。
由于RS485具有传输距离远、传输速度快、支持节点多和抗干扰能力更强等特点,所以RS485有很广泛的应用。
战舰STM32开发板采用SP3485作为收发器,该芯片支持3.3V供电,最大传输速度可达10Mbps,支持多达32个节点,并且有输出短路保护。该芯片的框图如图29.1.2所示:

图29.1.2 SP3485框图
       图中A、B总线接口,用于连接485总线。RO是接收输出端,DI是发送数据收入端,RE是接收使能信号(低电平有效),DE是发送使能信号(高电平有效)。
本章,我们通过该芯片连接STM32的串口2,实现两个开发板之间的485通信。本章将实现这样的功能:通过连接两个战舰STM32开发板的RS485接口,然后由KEY0控制发送,当按下一个开发板的KEY0的时候,就发送5个数据给另外一个开发板,并在两个开发板上分别显示发送的值和接收到的值。
本章,我们只需要配置好串口2,就可以实现正常的485通信了,串口2的配置和串口1基本类似,只是串口的时钟来自APB1,最大频率为36Mhz。
29.2 硬件设计
本章要用到的硬件资源如下:
1)  指示灯DS0
2)  KEY0按键
3) TFTLCD模块
4) 串口2
5) RS485收发芯片SP3485
前面3个之前都已经详细介绍过了,这里我们介绍SP3485和串口2的连接关系,如图29.2.1所示:
图29.2.1 STM32与SP3485连接电路图
       从上图可以看出:STM32的串口2通过P9端口设置,连接到SP3485,通过STM32的PG9控制SP3485的收发,当PG9=0的时候,为接收模式;当PG9=1的时候,为发送模式。这里注意,我们要设置好开发板上P9排针的连接,通过跳线帽将PA2和PA3分别连接到485T和485R上面,如图29.2.2所示:
图29.2.2 硬件连接示意图
       最后,我们用2根导线将两个开发板RS485端子的A和A,B和B连接起来。这里注意不要接反了(A接B),接反了会导致通讯异常!!
29.3 软件设计
打开上一章的工程,首先在HARDWARE文件夹下新建一个RS485的文件夹,然后新建一个rs485.c和rs485.h的文件保存在RS485文件夹下,并将RS485文件夹加入头文件包含路径。
打开rs485.c文件,输入如下代码:
#include "sys.h"               
#include "rs485.h"  
#include "delay.h"
#ifdef EN_USART2_RX     //如果使能了接收        
//接收缓存区        
u8 RS485_RX_BUF[64];     //接收缓冲,最大64个字节.
//接收到的数据长度
u8 RS485_RX_CNT=0;   
void USART2_IRQHandler(void)
{
       u8 res;         
       if(USART2->SR&(1<<5))//接收到数据
       {     
              res=USART2->DR;                     
              if(RS485_RX_CNT<64)
              {
                     RS485_RX_BUF[RS485_RX_CNT]=res;            //记录接收到的值
                     RS485_RX_CNT++;                                         //接收数据增加1
              }
       }                                                                           
}
#endif                                                                    
//初始化IO 串口2
//pclk1:PCLK1时钟频率(Mhz)
//bound:波特率        
void RS485_Init(u32 pclk1,u32 bound)
{     
       float temp;
       u16 mantissa;
       u16 fraction;      
       temp=(float)(pclk1*1000000)/(bound*16);  //得到USARTDIV
       mantissa=temp;                                       //得到整数部分
       fraction=(temp-mantissa)*16;                    //得到小数部分     
    mantissa<<=4;
       mantissa+=fraction;
       RCC->APB2ENR|=1<<8;                        //使能PORTG口时钟  
      GPIOG->CRH&=0XFFFFFF0F;                //IO状态设置
       GPIOG->CRH|=0X00000030;                    //IO状态设置   
       RCC->APB2ENR|=1<<2;                        //使能PORTA口时钟  
       GPIOA->CRL&=0XFFFF00FF;                 //IO状态设置
       GPIOA->CRL|=0X00008B00;                    //IO状态设置   
       RCC->APB1ENR|=1<<17;                       //使能串口时钟            
       RCC->APB1RSTR|=1<<17;                     //复位串口2
       RCC->APB1RSTR&=~(1<<17);                 //停止复位            
       //波特率设置
      USART2->BRR=mantissa;                         // 波特率设置      
       USART2->CR1|=0X200C;                        //1位停止,无校验位.
#ifdef EN_USART2_RX                                  //如果使能了接收
       //使能接收中断
       USART2->CR1|=1<<8;                           //PE中断使能
       USART2->CR1|=1<<5;                           //接收缓冲区非空中断使能            
       MY_NVIC_Init(3,3,USART2_IRQChannel,2);//组2,最低优先级
#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((USART2->SR&0X40)==0);      //等待发送结束              
              USART2->DR=buf[t];
       }     
       while((USART2->SR&0X40)==0);             //等待发送结束     
       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,只是把PG9也顺带配置了,用于控制SP3485的收发。同时如果使能中断接收的话,会执行串口2的中断接收配置。USART2_IRQHandler函数用于中断接收来自485总线的数据,将其存放在RS485_RX_BUF里面。最后RS485_Send_Data和RS485_Receive_Data这两个函数用来发送数据到485总线和读取从485总线收到的数据,都比较简单。
保存rs485.c,并把该文件加入HARDWARE组下面,然后我们打开rs485.h在里面输入如下代码:
#ifndef __RS485_H
#define __RS485_H                    
#include "sys.h"                                                            
extern u8 RS485_RX_BUF[64];                 //接收缓冲,最大64个字节
extern u8 RS485_RX_CNT;                     //接收到的数据长度
//模式控制
#define RS485_TX_EN        PGout(9)        //485模式控制.0,接收;1,发送.
//如果想串口中断接收,请不要注释以下宏定义
#define EN_USART2_RX    1                   //0,不接收;1,接收.                                          
void RS485_Init(u32 pclk2,u32 bound);
void RS485_Send_Data(u8 *buf,u8 len);
void RS485_Receive_Data(u8 *buf,u8 *len);            
#endif        
在这里,我们开启了串口2的中断接收,保存rs485.h。最后,我们在test.c里面,修改main函数如下:
int main(void)
{            
       u8 key; u8 cnt=0;
       u8 i=0,t=0;     
       u8 rs485buf[5];
      Stm32_Clock_Init(9);    //系统时钟设置
       uart_init(72,9600);      //串口初始化为9600
       delay_init(72);                  //延时初始化
       LED_Init();                 //初始化与LED连接的硬件接口
       LCD_Init();                  //初始化LCD
       usmart_dev.init(72);      //初始化USMART        
       KEY_Init();                  //按键初始化              
       RS485_Init(36,9600);    //初始化RS485
      POINT_COLOR=RED;  //设置字体为红色
       LCD_ShowString(60,50,200,16,16,"WarShip STM32");   
       LCD_ShowString(60,70,200,16,16,"RS485 TEST");  
       LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");
       LCD_ShowString(60,110,200,16,16,"2012/9/9");      
       LCD_ShowString(60,130,200,16,16,"KEY0:Send");  //显示提示信息     
      POINT_COLOR=BLUE;                                          //设置字体为蓝色   
       LCD_ShowString(60,150,200,16,16,"Count:");          //显示当前计数值  
       LCD_ShowString(60,170,200,16,16,"Send Data:");    //提示发送的数据  
       LCD_ShowString(60,210,200,16,16,"Receive Data:");       //提示接收到的数据
       while(1)
       {
              key=KEY_Scan(0);
              if(key==KEY_RIGHT)//KEY0按下,发送一次数据
              {
                     for(i=0;i<5;i++)
                     {
                            rs485buf=cnt+i;//填充发送缓冲区
                            LCD_ShowxNum(60+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(60+i*32,230,rs485buf,3,16,0X80);
                     //显示数据
             }
              t++;
              delay_ms(10);
              if(t==20)
              {
                     LED0=!LED0;//提示系统正在运行     
                     t=0; cnt++;                  
                     LCD_ShowxNum(60+48,150,cnt,3,16,0X80);     //显示数据
              }               
       }
}
此部分代码,我们主要关注下RS485_Init(36,9600),这里用的是36,而不是72,是因为APB1的时钟是36Mhz,故是36,而串口1的时钟来自APB2,是72Mhz的时钟,所以这里和串口1的设置是有点区别的。cnt是一个累加数,一旦KEY_RIGHT(KEY0)按下,就以这个数位基准连续发送5个数据。当485总线收到数据的时候,就将收到的数据直接显示在LCD屏幕上。
29.4 下载验证
在代码编译成功之后,我们通过下载代码到ALIENTEK战舰STM32开发板上(注意要2个开发板都下载这个代码哦),得到如图29.4.1所示:


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

图29.4.2 RS485实验测试图片
       上图中,左侧的图片来自开发板A,发送了5个数据,右侧的图片来自开发板B,接收到了来自开发板A的5个数据。
本章介绍的485总线时通过串口控制收发的,我们只需要将P9的跳线帽稍作改变,该实验就变成了一个RS232串口通信实验了,通过对接两个开发板的RS232接口,即可得到同样的实验现象,有兴趣的读者可以实验一下。

   实验24 485实验.rar (126.04 KB)

《STM32开发指南》第二十九章 485实验.rar (643.29 KB)


评分
参与人数 1威望 +1 收起 理由
电子小虾 + 1 赞一个!
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:我的STM32开发板店铺:http://openedv.taobao.com 我的技术论坛论坛:www.openedv.com

91

主题

264

帖子

71

粉丝