STC15单片机-RS-485通信

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位清0
  AUXR1 |=  S1_S0;        //AUXR1第6位置1

  SCON = 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);    //等待前面的数据发送完,串口中断中发送数据后标志位会置FALSE
  UART1.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
        //长按 亮度 0
        if(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,芯片处于接收模式,也能通过上下拉将高电平发送出去

  • 7
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STC89C52单片机RS485通信串口,需要使用单片机的串口通信模块和RS485收发芯片来实现。以下是实现步骤: 1. 连接RS485芯片 将RS485芯片的DI和RO分别连接到STC89C52单片机的TXD和RXD引脚上,RE和DE引脚接到单片机的任意一个IO口上,RE和DE引脚的电平由单片机控制。接线示意图如下: <img src="https://img-blog.csdn.net/20180411104023624?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d5b25nMTIz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/q/80" alt="RS485接线示意图" style="display:block;margin:0 auto;"> 2. 配置串口 STC89C52单片机有一个UART串口通信模块,可以通过SBUF寄存器进行数据的发送和接收。需要配置串口的波特率、数据位、停止位和校验位等参数。具体的配置方式可以参考STC89C52单片机的数据手册。 3. 发送数据 在发送数据时,需要先将RE和DE引脚的电平设置为高电平,表示进入发送模式。然后将要发送的数据写入SBUF寄存器,等待发送完成。 4. 接收数据 在接收数据时,需要先将RE和DE引脚的电平设置为低电平,表示进入接收模式。然后通过中断或轮询的方式,检查SBUF寄存器是否有数据接收到。 5. 处理数据 接收到数据后,需要对数据进行处理。可以通过串口中断或者定时器中断来处理数据,具体的处理方式可以根据实际情况进行选择。 以上就是STC89C52单片机RS485通信串口的实现步骤,希望对你有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值