第三十一章 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框图
图中
A、
B总线接口,用于连接
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 STM32F4与
SP3485连接电路图
从上图可以看出:
STM32F4的串口
2通过
P9端口设置,连接到
SP3485,通过
STM32F4的
PG8控制
SP3485的收发,当
PG8=0的时候,为接收模式;当
PG8=1的时候,为发送模式。
这里需要注意,
PA2,
PA3和
ETH_MDIO和
PWM_DAC有共用
IO,所以在使用的时候,注意分时复用,不能同时使用。另外
RS485_RE信号,也和
NRF_IRQ共用
PG8,所以他们也不可以同时使用,只能分时复用。
另外,图中的
R38和
R40是两个偏置电阻,用来保证总线空闲时,
A、
B之间的电压差都会大于
200mV(逻辑
1)。从而避免因总线空闲时,
A、
B压差不定,引起逻辑错乱,可能出现的乱码。
然后,我们要设置好开发板上
P9排针的连接,通过跳线帽将
PA2和
PA3分别连接到
485_TX和
485_RX上面,如图
31.2.2所示:
图
31.2.2 硬件连接示意图
最后,我们用
2根导线将两个开发板
RS485端子的
A和
A,
B和
B连接起来。这里注意不要接反了(
A接
B),接反了会导致通讯异常!!
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; //GPIOA2与
GPIOA3
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); //初始化
PA2,
PA3
//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_Data和
RS485_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的波特率为
9600。
cnt是一个累加数,一旦
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,接收到了来自开发板
A的
5个数据。
本章介绍的
485总线时通过串口控制收发的,我们只需要将
P9的跳线帽稍作改变,该实验就变成了一个
RS232串口通信实验了,通过对接两个开发板的
RS232接口,即可得到同样的实验现象,有兴趣的读者可以实验一下。
实验详细手册和源码下载地址:http://www.openedv.com/posts/list/41586.htm
正点原子探索者STM32F407开发板购买地址:http://item.taobao.com/item.htm?id=41855882779
我也以为这样不科学,至少串口的RT作为接收端应该是输入;但12讲的GPIOgongzuo原理“推完复用输出模式”除了能复用输出,也能完成对IO的读取数据。
那我实验一下好了
一周热门 更多>