RS-485接口

RS-485为工业设备常用的传输接口,采用差分信号逻辑,+2V~+6V表示逻辑1,-6V~-2V表示逻辑0。通信方式为半双工,工作模式为主从模式。

在低速、短距离、无干扰的场合可以采用普通的双绞线,反之,在高速、长线传输时,则必须采用阻抗匹配(一般为120欧 )的RS485专用电缆,而在干扰恶劣的环境下还应采用铠装型双绞屏蔽电缆。

差分传输是一种信号传输的技术,区别于传统的一根信号线一根地线的做法,差分传输在这两根线上都传输信号,这两个信号的振幅相同,相位相反。在这两根线上的传输的信号就是差分信号。信号接收端比较这两个电压的差值来判断发送端发送的逻辑状态。在电路板上,差分走线必须是等长、等宽、紧密靠近、且在同一层面的两根线。

实现485通信的SP3485芯片

芯片介绍

SP3481SP3485是一系列+3.3V低功耗半双工收发器,它们完全满足RS-485和 RS-422串行协议的要求;并且,SP3481、SP3485采用单一+3.3V的工作电源。

​ SP3481、SP3485与Sipex的SP481、SP483和SP485的管脚互相兼容,同时兼容工业标准规范。SP3481和SP3485符合RS-485/RS-422串行协议的电气规范,数据传输速率可高达10Mbps(带负载)。SP3481还包含低功耗关断模式。

特点

  1. RS-485和 RS-422收发器
  2. 工作电源为+3.3V
  3. 可与+0.5V的逻辑电路共同工作
  4. 发送器/接收器使能
  5. 低功耗关断模式(SP3481)
  6. -7V~+12V的共模输入电压范围
  7. 允许在同一串行总线上连接32个收发器
  8. 与工业标准75176管脚配置兼容
  9. 发送器输出短路保护

引脚示意图

引脚功能

RO:接收器输出

RE:接收器输出使能(低电平有效)

DE:发送器输出使能(高电平有效)

DI:发送器输入

GND:接地连接

A:发送器输出/接收器输入反相

B:发送器输出/接收器输入反相

Vcc:正极电源(+3.3V<VCC<+3.60V)

发送器

​ SP3481 和SP3485的发送器输出是差分输出,满足RS-485和 RS-422标准。空载时输出电压的大小为0V~+3.3V。即使在差分输出连接了54欧负载的条件下,发送器仍可保证输出电压大于1.5V。SP3481和 SP3485有一根使能控制线(高电平有效)。DE (Pin3)上的逻辑高电平将使能发送器的差分输出。如果 DE (Pin3)为低,则发送器输出呈现三态。
​ SP3481和SP3485收发器的数据传输速率可高达10Mbps。发送器输出最大250mA Isc的限制使SP3481和SP3485可以受-7.0V~+12.0V共模范围内的任何短路情况,保护IC不受到损坏。

接收器

​ SP3481和 SP3485接收器的输入是差分输入,输入灵敏度可低至土200mV。接收器的输入电阻通常为15千欧(最小为 12千欧)。一7V~+12V的宽共模方式范围允许系统之间存在大的零电位偏差。SP3481和SP3485的接收器有一个三态使能控制脚。如果 !RE(Pin2)为低,接收器使能,反之接收器禁止。
​ SP3481和SP3485接收器的数据传输速率可高达10Mbps。两者的接收器都有故障自动保护( fail-safc)特性,该特性可以使得输出在输入悬空时为高电平状态。

SP3481的关断模式

​ SP3481可以工作在关断模式。要使能关断模式,发送器和接收器必须同时禁能。当DE(Pin3)为低且 !RE(Pin2)为高时SP3481进入关断模式。关断模式下,电源电流通常降至1uA,最大为10uA。

发送功能真值表

如果DE为1,则为发送模式

DI为1,则A端电压大于B端电压,使A-B的电压差在+2V ~ +6V之间,输出逻辑1

DI为0,则A端电压小于B端电压,使A-B的电压差在-6V ~ -2V之间,输出逻辑0

接收功能真值表

如果DE为0,则为接收模式

A和B的电压差大于0.2V(+200mv)时,则RO口输出1

A和B的电压差小于-0.2V(-200mv)时,则RO口输出0

RS-485原理图

该原理图设置时,模式控制位RE和DE接在了一起,用单片机的一个引脚控制,单片机该引脚输出高电平,则为发送模式,单片机输出低电平,则为接收模式

单片机发送数据:要发送的数据通过单片机的串口MCU_TXD1发送到DI口,若485芯片处于发送模式,则会将会这些数据再从右边的A、B端口发送出去

单片机接收数据:从A、B端口接收到的电压差信号经过转换,再从RO口输入到单片机的串口接收口MCU_RXD1

程序

文件结构

与串口通信的文件结构相同,485相关的函数写到了UART1.c源文件中

main.c ->主函数文件,包含main函数等;

Public.c ->公共函数文件,包含Delay延时函数等;

Sys_init ->系统初始化函数,包含GPIO初始化函数等;

LED.c->LED外设函数,包含LED打开、关闭函数等;

Timer0.c ->定时器函数,包含定时器初始化,中断函数等;

KEY1.c->按键1函数,包含按键检测,中断函数等;

KEY2.c ->按键⒉函数,包含按键状态机检测函数等;

PWM. c ->PWM初始化、亮度调节、占空比储存与恢复函数等;

IAP.c->字节读、字节写、扇区擦除等函数。

UART1.c->串口1初始化、发送、中断等函数。

说明:RS-485接口位于串口1。

程序运行结果

系统上电,会通过RS-485发送系统初始化信息,然后不断检测按键2的状态,按键2按下后会调节PWM灯的亮度,在调节亮度的同时,按键的状态会转为RS-485差分信号发送出去,可以通过电脑串口助手查看发送出去的数据,需要用到RS-485转串口工具

如何查看RS-485数据

那通过RS-485 A、B端发送的差分信号如何查看呢?除非有另一台设备可以接收该485信号,并做出相应的回应;在没有多余设备的情况下,可以用RS-485转串口工具,将接收到的485信号再转换为串口信号,将工具插到电脑上后,可以通过串口助手查看原本单片机发送的数据

因为开发板上已经集成了SP3485芯片,所以可以直接将开发板的A、B端子和工具的A、B端子用杜邦线连接起来,然后将工具的另一头插入到电脑USB接口中,在串口助手中查看MCU串口的数据,当然,在程序的串口中断中如果写了接收处理,那电脑串口助手也是可以通过RS-485转串口工具把数据发送到单片机上的

其数据传输原理如图

UART1.c:

UART.h和UART1.h和串口的一样,不变

在编写串口通信的过程中已经把RS-485的函数也定义了,这次只需把函数体实现就行

在串口通信的基础上,先把串口1从P30和P31切换到P36和P37,把AUXR1第7位清0,第6位置1即可;

添加RS-485的宏定义,分别实现RS-485发送模式和RS-485接收模式的函数,UART1_MAX485_DE_nRE为宏名,实质是P20口,将P20口置为1则是发送模式,置0则是接收模式;

然后在串口发送字符、发送数组、发送字符串函数中添加RS-485设置发送模式函数和RS-485设置接收模式函数,先将RS-485设置为发送模式,然后进行串口发送,发送完毕后,再将RS-485设置为接收模式;这个可以看原理图进行理解,SP3485芯片与单片机的通信是通过串口实现的,将SP3485设置为发送模式后,单片机将数据通过串口发送给SP3485,SP3485再将数据转换为差分信号从A、B端发送出去

/* Includes ------------------------------------------------------------------*/
#include <main.h>/* Private define-------------------------------------------------------------*/
#define S1_S0 BIT6      //定义串口切换寄存器AUXR1的第6位
#define S1_S1 BIT7      //定义串口切换寄存器AUXR1的第7位#define UART1_MAX485_DE_nRE   P20           //定义模式控制引脚
#define UART1_MAX485_SendMode   (bit)1      //RS-485设置为发送模式
#define UART1_MAX485_RecMode    (bit)0      //RS-485设置为接收模式#define UART1_Send_LENGTH 20
#define UART1_Rec_LENGTH  10
/* Private variables----------------------------------------------------------*/
static uint8_t idata ucSend_Buffer[UART1_Rec_LENGTH]  = {0};
static uint8_t idata ucRec_Buffer[UART1_Rec_LENGTH]   = {0x00};
/* Private function prototypes------------------------------------------------*/
static void Init();                                 //串口初始化
static void SendData(uint8_t dat);                  //串口发送字符
static void SendArray(uint8_t *p_Arr,uint16_t LEN); //串口发送数组
static void SendString(uint8_t *p_Str);             //串口发送字符串
static void Protocol();                             //串口协议static void RS485_Set_SendMode();                   //RS-485设置为发送模式
static void RS485_Set_RecMode();                    //RS-485设置为接收模式/* Public variables-----------------------------------------------------------*/
UART_t idata UART1 =
{Band_115200,FALSE,FALSE,0,ucSend_Buffer,ucRec_Buffer,Init,SendData,SendArray,SendString,Protocol,RS_485,RS485_Set_SendMode,RS485_Set_RecMode
};/*
* @name   Init
* @brief  串口1初始化
* @param  None
* @retval None
*/
static void Init()
{//把串口1映射到RS-485连接到的P37和P36引脚,AUXR1 &= ~S1_S1;        //AUXR1第7位清0AUXR1 |=  S1_S0;        //AUXR1第6位置1SCON = 0x50;       //8位数据,可变波特率,REN位置1,开启中断//辅助寄存器AUXR的第6位T1x12置1,设置定时器1的速度是传统8051的12倍,不分频AUXR |= 0x40;//1111 1110 最低位S1ST2清0,选择定时器1作为串口1的波特率发生器AUXR &= 0xFE;        //串口1选择定时器1为波特率发生器TMOD &= 0x0F;        //设定定时器1为16位自动重装方式switch (UART1.ucBandRate){case Band_4800:   TL1 = 0xCD; TH1 = 0xFD; break;case Band_9600:   TL1 = 0xE0; TH1 = 0xFE; break;case Band_19200:  TL1 = 0x70; TH1 = 0xFF; break;case Band_115200: TL1 = 0xE8; TH1 = 0xFF; break;default:          TL1 = 0xCD; TH1 = 0xFD; break;}ET1 = 0;        //禁止定时器1中断TR1 = 1;     //启动定时器1
}/*
* @name   SendData
* @brief  发送字符
* @param  dat:待发送的数据
* @retval None
*/
static void SendData(uint8_t dat)
{while(UART1.ucTX_Busy_Flag);    //等待前面的数据发送完,串口中断中发送数据后标志位会置FALSEUART1.ucTX_Busy_Flag = TRUE;    //置为忙碌标志位SBUF = dat;                     //写数据到UART寄存器
}/*
* @name   SendArray
* @brief  发送数组
* @param  p_Arr:数组首地址,LEN:发送长度
* @retval None
*/
static void SendArray(uint8_t *p_Arr,uint16_t LEN)
{uint16_t i = 0;UART1.RS485_Set_SendMode();   //RS-485设置为发送模式for(i = 0;i<LEN;i++){UART1.UART_SendData(*(p_Arr+i));}while(UART1.ucTX_Busy_Flag);   //等待数据发送完Public.Delay_ms(1);            //等待RS-485数据传输完UART1.RS485_Set_RecMode();    //让RS-485切回到接收模式,默认处于接收模式
}/*
* @name   SendString
* @brief  发送字符串
* @param  p_Arr:字符串首地址
* @retval None
*/
static void SendString(uint8_t *p_Str)
{UART1.RS485_Set_SendMode();     //RS-485设置为发送模式while(*(p_Str) != '\0'){UART1.UART_SendData(*(p_Str++));}while(UART1.ucTX_Busy_Flag);Public.Delay_ms(1);             //等待RS-485数据传输完UART1.RS485_Set_RecMode();      //RS-485设置为接收模式
}/*
* @name   RS485_Set_SendMode
* @brief  RS_485接口设置为发送模式
* @param  None
* @retval None
*/
static void RS485_Set_SendMode()
{//485模式控制引脚置1,设置为发送模式UART1_MAX485_DE_nRE = UART1_MAX485_SendMode;//延时一会,待硬件稳定Public.Delay_ms(1);
}/*
* @name   RS485_Set_RecMode
* @brief  RS_485接口设置为接收模式
* @param  None
* @retval None
*/
static void RS485_Set_RecMode()
{//485模式控制引脚清0,设置为接收模式UART1_MAX485_DE_nRE = UART1_MAX485_RecMode;//延时一会,待硬件稳定Public.Delay_ms(1);
}/*
* @name   putchar
* @brief  字符发送函数重定向
* @param  c:发送的字符
* @retval char
*/
extern char putchar(char ch)
{UART1.UART_SendData((uint8_t)ch);   //在putchar函数内直接调用串口发送字符函数return ch;
}/*
* @name   UART1_isr
* @brief  串口1中断处理函数
* @param  None
* @retval None
*/
void UART1_isr() interrupt 4
{if(RI){RI = (bit)0;                   //清除接收中断标志/*UART1_Rec_LENGTH宏定义为10,所以接收的数据不能超过10个字节UART1.ucRec_Cnt表示数组下标,初始化为0*/if(UART1.ucRec_Cnt < UART1_Rec_LENGTH){ucRec_Buffer[UART1.ucRec_Cnt++] = SBUF;}UART1.ucRec_Flag = TRUE;      //接收完成标志位}if(TI){TI = (bit)0;                    //清除发送中断标志UART1.ucTX_Busy_Flag = FALSE;   //清除忙碌标志}
}/*
* @name   Protocol
* @brief  串口协议
* @param  None
* @retval None
*/
static void Protocol()
{}
/********************************************************End Of File
********************************************************/

PWM.c:

只放被改变的PWM灯调整亮度的函数,其他如占空比设置函数、IAP备份和恢复函数不变

在区分按键单击、双击或长按后,分别开启RS-485发送模式,将按键状态用重定向的printf函数发送出去,再将RS-485切回接收模式

/*
* @name   PWM_LED_Adjust_Brightness
* @brief  PWM灯调整亮度
* @param  None
* @retval None
*/
static void PWM_LED_Adjust_Brightness()
{if(KEY2.KEY_Flag == TRUE){//单击 亮度 0-20-40-60-80-100-0 循环调节//双击 亮度 100//长按 亮度 0if(KEY2.Click == TRUE){switch (PWM.Duty){case Duty_0:    PWM.Duty = Duty_20; break;case Duty_20:   PWM.Duty = Duty_40; break;case Duty_40:   PWM.Duty = Duty_60; break;case Duty_60:   PWM.Duty = Duty_80; break;case Duty_80:   PWM.Duty = Duty_100;break;case Duty_100:  PWM.Duty = Duty_0;  break;default: PWM.Duty = Duty_0; break;}UART1.RS485_Set_SendMode();           //RS-485设置为发送模式printf("KEY2 click detected\r\n\r\n");    //打印单击信息UART1.RS485_Set_RecMode();            //RS-485设置为接收模式}//检测双击else if(KEY2.Double_Click == TRUE){PWM.Duty = 100;UART1.RS485_Set_SendMode();                   //RS-485设置为发送模式printf("KEY2 double click detected\r\n\r\n");     //打印双击信息UART1.RS485_Set_RecMode();                    //RS-485设置为接收模式}//检测长按else if(KEY2.Press == TRUE){PWM.Duty = 0;UART1.RS485_Set_SendMode();                   //RS-485设置为发送模式printf("KEY2 press detected\r\n\r\n");            //打印长按信息UART1.RS485_Set_RecMode();                    //RS-485设置为接收模式}//设置占空比,调整亮度PWM_Duty_Set(PWM.Duty);//标志位清零KEY2.KEY_Flag     = FALSE;KEY2.Click        = FALSE;KEY2.Double_Click = FALSE;KEY2.Press        = FALSE;//备份占空比PWM.IAP_Duty_Backup(IAP_PWM_DUTY_ADDR,PWM.Duty);}
}

main.c:

主函数中调用按键2检测函数和PWM灯调整亮度函数即可

/******************************************************************************* @file    main.c * @author  * @version V1.0* @date    2022-xx-xx* @Conpany * @project STC15实战项目
*******************************************************************************//* Includes ------------------------------------------------------------------*/
#include <main.h>/* Private define-------------------------------------------------------------*//* Private variables----------------------------------------------------------*/
// uint16_t Cnt = 0;   //初始化自动加1的变量
// float f = 3.14;     //浮点数/* Public variables-----------------------------------------------------------*//* Private function prototypes------------------------------------------------*//*
* @name   main
* @brief  主函数
* @param  void
* @retval int
*/
int main(void)
{//系统初始化Hradware.Sys_Init();//串口1发送初始化信息,实际会转为RS-485信号传输UART1.UART_SendString("Initialization completed,system startup!\r\n");//系统主循环while(1){//按键检测//KEY1.KEY_Detect();KEY2.KEY_Detect();PWM.PWM_LED_Adjust_Brightness();//RS-485发送字符串//UART1.UART_SendString("hello RS-485\r\n");//Public.Delay_ms(1000);}
}
/********************************************************End Of File
********************************************************/

485自动收发

除了用单片机控制SP3485E芯片完成接收发送之外,还有一种方式是485自动收发的,不怎么用单片机控制,这主要由硬件来实现

如下图

1.假如UART1_TX发送1,(其实在空闲模式下,单片机引脚一般也是输出高电平)三极管导通,此时RE,DE接地,是低电平,所以芯片处于接收模式,DI已经接地,这个低电平默认不会发送到右边,但右边A和B分别接了上下拉电阻,A为高电平,B为低电平,所以就发送了一个1即高电平出去

2.假如UART1_TX发送0,则三极管截止,上拉电阻R19接到了VCC,所以DE是高电平,此时处于发送模式,而DI是低电平,所以便将低电平发送了出去

总的来说,是巧妙地利用了A和B的上下拉电阻,UART1_TX为1,芯片处于接收模式,也能通过上下拉将高电平发送出去

STC15单片机-RS-485通信相关推荐

  1. 51单片机c语言485通讯案例,485通讯协议程序怎么写(51单片机的485通信程序案例)...

    RS-485总线接口是一种常用的串口,具有网络连接方便.抗干扰性能好.传输距离远等优点.RS-485收发器采用平衡发送和差分接收,因此具有抑制共模干扰的能力,加上收发器具有高的灵敏度,能检测到低达20 ...

  2. 基于新塘51单片机的485通信

    基于新塘51单片机的485通信 序言 最近有一个项目用新塘的51FB9A单片机做时控开关(上位机控制)控制继电器,但是我原来也没整过485啊,本来以为这玩意很难.没想到才了一些坑之后还觉得挺简单的.这 ...

  3. 毕设测试问题记录1-LCD初始化了但是不工作、初始化顺序、两个单片机用485通信异常、串口标志位TXE和TC

    1.首先是上一个文章提到的问题. (传送门:https://blog.csdn.net/qq_45563820/article/details/124344360?spm=1001.2014.3001 ...

  4. 485通信原理_上位机开发之单片机通信实践

    经常会有一些学员会问到上位机与单片机之间通信的问题,而我们经常会讲上位机与PLC之间通信,那么其实对上位机开发来说,不管是和PLC通信,还是和单片机通信,通信原理都是一样的.PLC的本质就是单片机,在 ...

  5. 485通信原理_上位机开发之单片机通信实践(一)

    微信号 :thinger_swj微博:@新阁程序园扫码关注 经常会有一些学员会问到上位机与单片机之间通信的问题,而我们经常会讲上位机与PLC之间通信,那么其实对上位机开发来说,不管是和PLC通信,还是 ...

  6. 调试stc8a8k64d4单片机485通信总结

    一开始感觉很简单,没想到几经折腾,还好,最后基本找到原因,暂时先记录下来. 先说明一下:单片机型号stc8a8k64d,485串口使用UART3(P5.10,P5.1),485使用网上找的一种方案,自 ...

  7. Step7-Mricro/win S7-200 485轮询 西门子485 modbus RTU 200 ModbusRTU通信S7-200与最大32个从站RS 485主站程序

    Step7-Mricro/win S7-200 485轮询 西门子485 modbus RTU 200 ModbusRTU通信S7-200与最大32个从站RS 485主站程序,程序块自动轮询,无需编写 ...

  8. 485通信自动收发电路,历史上最详细的解释

    作者:瑞生,来源:科技老顽童 微信公众号:芯片之家(ID:chiphome-dy) 上边的485通信自动收发电路,实测波特率9600不会有问题,但是,波特率115200的话,曾经出现过问题. 我们先看 ...

  9. 485通信自动收发数据实现

    485通信是工业控制中很常用的一种通信方式,但是编写通信程序的时候需要在代码中手动控制接收和发送,如果接收和发送逻辑没处理好往往会造成通信故障,实现起来比较麻烦.那能不能让485通信是接收和发送数据可 ...

  10. 超声波引导系统开源(三)485通信原理

    本菜鸡以个人理解的白话文讲解485通信,如果想要全面专业资料请去度娘 以该项目为例:我使用的是某信公司的max487系列的芯片 那max487和485通信有什么关系呢? 一般来说,网上最常见的是max ...

最新文章

  1. 功能整合(二):轮播图(可控)、事件流
  2. 11、流程控制语句详解,IF,CASE,LOOP,LEAVE, ITERATE,REPEAT,WHILE
  3. 详解联邦学习Federated Learning
  4. oracle取消备份存放本地,Oracle自动备份,压缩打包,删除原文件
  5. 中芯国际发布2021Q1财报:55/65纳米工艺依旧为营收主力
  6. 禅道 mysql 远程连接_远程访问禅道开源版数据库(基于docker)
  7. Jxl实现Excel的导入与导出
  8. 趣图:嫁人就嫁程序员,大妈都懂的!
  9. MQ_ActiveMQ环境部署+C#推送和接收消息
  10. supermap javascript 点聚合
  11. Microsoft Teams管理(一)
  12. Unity升级到URP渲染管线,
  13. 这本书献给所有铸就开源世界的人们
  14. 用Altium Designer绘制stm32最小系统的电路原理图并完成STM32+SD卡 的系统原理图设计
  15. 普通u盘linux不识别,Linux识别不了u盘怎么办
  16. 各大公司数据结构与算法面试题解答(一)
  17. 计算湖泊(岛屿)数量问题
  18. ssh登录或者scp传文件给远程主机起别名
  19. Swift——仿微信发起群聊悬浮框实现
  20. ios 内购服务器验票(漏单处理)

热门文章

  1. matlab读取yuv420文件,基于matlab的YUV420播放器程序
  2. Hexo添加可控制网易云音乐播放器
  3. 如何解决微图不能在虚拟机上运行的问题
  4. HDOJ试水心酸总结
  5. fba4droid android,fba4droid模拟器
  6. paraview远程模式
  7. S5p4418平台AP6212 WIFI稳定性的解决过程
  8. 基于MATLAB的有源三相滤波器的设计,基于MATLAB的电力系统有源滤波器设计
  9. DSP SRIO接口认识
  10. 栈的输出_TAOCP|基本算法|栈、队列与双端队列