记录一下,方便以后翻阅~
主要内容:
1) 485接口原理;
2) 相关实验代码解读。
实验功能:两个开发板的485接口以A连接A,B连接B的方式直连。两个开发版的代码启动后,主开发版上,按键KEY0控制W25Q128的写入数据并传至次开发版,且在串口调试助手上面显示相关信息。同时,主开发版实时采集从次开发板传来的数据,也显示在串口调试助手上,LED0闪烁提示程序正在运行;另一个开发版,按键SW4按下后,写入数据并传至主开发板。
硬件连接:
主开发板
注意:
1)R19和R22是两个偏置电阻,用来保证总线空闲时,AB之间的电压差都会大约200mV,避免总线空闲时压差不定逻辑混乱;
2)两个485接口连接,A连接A,B连接B。
其它原理图如下:
另一个开发板芯片采用STM32F103C8T6,原理图如下所示:
备注:该开发板采用的MAX485收发器。
1. 485接口原理
1.1 串口
串口是一种接口标准,它规定了接口的电气标准,简单说只是物理层的一个标准。没有规定接口插件电缆以及使用的协议,所以只要我们使用的接口插件电缆符合串口标准就可以在实际中灵活使用,在串口接口标准上使用各种协议进行通讯及设备控制。
典型的串行通讯标准是RS232和RS485,它们定义了电压,阻抗等,但不对软件协议给予定义。
1.2 RS232接口缺陷
缺点:
1.2.1 接口的信号电平值较高(+/-12V),易损坏接口电路的芯片;
1.2.2 传输速率较低,在异步传输时,波特率为20Kbps;
1.2.3 接口使用一根信号线和一根信号返回线而构成共地的传输形式,这种共地传输容易产生共模干扰,所以抗噪声干扰性弱;
1.2.4 传输距离有限,最大传输距离标准值为50英尺,实际上也只能用在50米左右。
备注:早期设备大都采用RS232接口,因为设备与设备之间的传输距离和速率要求都不高。
1.3 485接口简述
485(一般称作RS485/EIA-485)是隶属于OSI模型物理层的电气特性规定为2线,半双工,多点通信的标准。它的电气特性和RS-232大不一样。用缆线两端的电压差值来表示传递信号。RS485仅仅规定了接受端和发送端的电气特性。它没有规定或推荐任何数据协议。
1.4 485特点
1.4.1 接口电平低,不易损坏芯片。RS485的电气特性:逻辑“1”以两线间的电压差为+(26)V表示;逻辑“0”以两线间的电压差为-(26)V表示。接口信号电平比RS232降低了,不易损坏接口电路的芯片;
1.4.2 传输速率高。10米时,RS485的数据最高传输速率可达35Mbps,在1200m时,传输速度可达100Kbps;
1.4.3 抗干扰能力强。RS485接口是采用平衡驱动器和差分接收器的组合,抗共模干扰能力增强,即抗噪声干扰性好;
1.4.4 传输距离远,支持节点多。RS485总线最长可以传输1200m以上(速率≤100Kbps)一般最大支持32个节点,如果使用特制的485芯片,可以达到128个或者256个节点,最大的可以支持到400个节点。
1.5 485连接方式
RS485推荐使用在点对点网络中,线型,总线型,不能是星型,环型网络。理想情况下RS485需要2个匹配电阻,其阻值要求等于传输电缆的特性阻抗(一般为120Ω)。没有特性阻抗的话,当所有的设备都静止或者没有能量的时候就会产生噪声,而且线移需要双端的电压差。没有终接电阻的话,会使得较快速的发送端产生多个数据信号的边缘,导致数据传输出错。485推荐的连接方式:
在上面的连接中,如果需要添加匹配电阻,我们一般在总线的起止端加入,也就是主机和设备4上面各加一个120Ω的匹配电阻。
2. 收发器SP3485
图中A、B总线接口,用于连接485总线。RO是接收输出端,DI是发送数据收入端,RE是接收使能信号(低电平有效),DE是发送使能信号(高电平有效)。
3. 相关实验代码解读
这里仅给出主开发板的实验代码,次开发板的实验代码跟主开发板的代码基本差不多。
3.1 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 PDout(7) //485模式控制.0,接收;1,发送//
#define EN_USART2_RX 1 //设0时不接收;设1时接收//
//三个函数申明//
void RS485_Init(u32 bound);
void RS485_Send_Data(u8 *buf,u8 len);
void RS485_Receive_Data(u8 *buf,u8 *len);
#endif
3.2 rs485.c文件代码解读
#include "sys.h"
#include "rs485.h"
#include "delay.h"
#include "usart.h"
#ifdef EN_USART2_RX //这里EN_USART2_RX设1,接收//
u8 RS485_RX_BUF[64]; //接收缓存区,最大64个字节.
u8 RS485_RX_CNT=0;
void USART2_IRQHandler(void) //串口2中断服务函数//
{
u8 res;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //判断USART2_SR寄存器的RXNE位是否不为0,即是否接收到数据//
{
res =USART_ReceiveData(USART2); //如果不为0,则接收到数据,读取接收到的数据//
if(RS485_RX_CNT<64)
{
RS485_RX_BUF[RS485_RX_CNT]=res; //记录接收到的值,放置在RS485_RX_BUF[]中//
RS485_RX_CNT++; //RS485_RX_CNT加1//
}
}
}
#endif
//初始化IO 串口2,pclk1:PCLK1时钟频率(Mhz),bound:波特率//
void RS485_Init(u32 bound)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOD, ENABLE); //使能GPIOA和D的时钟//
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE); //使能USART2时钟//
//初始化PD7,推挽输出,用来控制SP3485的接受/发送使能//
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //PD7端口配置//
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出//
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD, &GPIO_InitStructure);
//配置PA2为复用推挽输出//
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PA2//
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽//
GPIO_Init(GPIOA, &GPIO_InitStructure);
//配置PA3为浮空输入//
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; //PA3//
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入//
GPIO_Init(GPIOA, &GPIO_InitStructure);
RCC_APB1PeriphResetCmd(RCC_APB1Periph_USART2,ENABLE); //复位串口2//
RCC_APB1PeriphResetCmd(RCC_APB1Periph_USART2,DISABLE); //停止复位//
#ifdef EN_USART2_RX //如果使能了接收//
//如果EN_USART2_RX设1,为接收,还要初始化USART//
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中断优先级//
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; //使能串口2中断//
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; //抢占优先级2级//
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级2级//
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道//
NVIC_Init(&NVIC_InitStructure);
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); //开启接收中断//
USART_Cmd(USART2, ENABLE); //使能串口//
#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[i]=RS485_RX_BUF[i];
}
*len=RS485_RX_CNT; //记录本次数据长度//
RS485_RX_CNT=0; //清零//
}
}
3.3 main.c文件代码解读
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
#include "rs485.h"
int main(void)
{
u8 key;
u8 i=0,t=0;
u8 cnt=0;
u8 rs485buf[5];
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置中断优先级分组为组2:2位抢占优先级,2位响应优先级//
uart_init(115200); //串口初始化为115200//
LED_Init(); //初始化与LED连接的硬件接口//
KEY_Init(); //按键初始化//
RS485_Init(9600); //初始化RS485//
while(1)
{
key=KEY_Scan(0); //有键按下时,key就不为0//
if(key==KEY0_PRES) //KEY0按下,发送一次数据//
{
for(i=0;i<5;i++)
{
rs485buf[i]=cnt+i; //填充发送缓冲区//
printf("\n发送数据为:%d\r\n",rs485buf[i]);
}
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++)
printf("\n接收数据为:%d\r\n",rs485buf[i]);
}
t++;
delay_ms(10);
if(t==20)
{
LED0=!LED0; //提示系统正在运行//
t=0;
cnt++;
}
}
}
4. 实验结果
旧知识点
1)复习如何新建工程模板,可参考STM32学习心得二:新建工程模板;
2)复习基于库函数的初始化函数的一般格式,可参考STM32学习心得三:GPIO实验-基于库函数;
3)复习寄存器地址,可参考STM32学习心得四:GPIO实验-基于寄存器;
4)复习位操作,可参考STM32学习心得五:GPIO实验-基于位操作;
5)复习寄存器地址名称映射,可参考STM32学习心得六:相关C语言学习及寄存器地址名称映射解读;
6)复习时钟系统框图,可参考STM32学习心得七:STM32时钟系统框图解读及相关函数;
7)复习延迟函数,可参考STM32学习心得九:Systick滴答定时器和延时函数解读;
8)复习ST-LINK仿真器的参数配置,可参考STM32学习心得十:在Keil MDK软件中配置ST-LINK仿真器;
9)复习ST-LINK调试方法,可参考STM32学习心得十一:ST-LINK调试原理+软硬件仿真调试方法;
10)复习串口通信相关知识,可参考STM32学习心得十四:串口通信相关知识及配置方法。