在学习单片机的过程中,我在网络上参考了不少同学的毕业设计。其中最令我觉得有意思的就是智能家居系统,特别是我看着那些小巧的温湿度传感器和角度传感器,我就在想,如果真的应用到实际的场景中,那些传感器的线还能用杜邦线吗?起码I2C的传感器肯定是不能用了。那他们怎么扩大传输距离呢?结合在网上零零碎碎的看到什么工业控制用485,我就想着试一试,最后结果还行。哥们找遍优快云都没找到几个免费的有用信息,实验成功后想想还是放上来给大家参考下。所以就引来了本文的主角——485通信。
RS485是电气层面上的规则,真要看起来理论一大堆。一堆人说485协议,又有一些人说485就不是协议而是标准,爱咋咋地吧,本文不谈理论,就告诉大家如何去用。
前期准备
单片机使用的是STC89C516RD+,引脚与89C52一样,就是程序存储较大。资料来源于STC。

在淘宝上购买了TTL转485模块,以下是接线示意图。有了这个模块,我们就可以按照配置串口的方式来实现485通讯。有基础的可以直接偷掉电路图画到自己板子上哈哈

485通讯在应用上使用的是双绞线,既可以去淘宝上购买485专用线,也可以在实验阶段使用网线验证。网线中包含着四对双绞线,对个人而言容易获得。在实验无干扰的情况下地线可以不接。这次我使用了60M的网线,实验结果非常OK。
串口配置
首先,要配置串口控制寄存器SCON和电源控制寄存器PCON。

SM0、SM1: 根据手册上的说明,如果我们想要实现波特率可变的通信,就要使用方式1和方式3。其不同点在于,如果发送端要8位UART只发送8位数据,而9位UART会发送8位数据和上图SCON中的TB8。在方式2和方式3中规定发送端如果令TB8 = 1,串口通讯后,接受端的RB8就会变成1。这个功能对于多机通信发送的数据类型有很大帮助,具体会在文章后面详细介绍。

为方便多机通讯,我们选择方式3。 接下来介绍SCON中的其它位。
REN:REN = 1,允许接受数据。REN = 0,禁止接受数据。
TI:发送完成后TI由硬件置1,若不及时用软件置0,则程序会跳到中断函数。
RI:接受完成后RI由硬件置1,执行一次中断函数,若在中断函数不用置0则会一直循环中断函数。
SM2:最关键的就是SM2位,芯片手册中规定:在方式2和方式3中。若SM2 = 1 && REN = 1时,还需要接收到的RB8 == 1才能使得硬件置RI为1,进入中断函数。简单来说SM2 = 1时,只有RB8 = 1进入中断。SM2 = 0时,RB8 无论为0或1 都能进入中断。
综上,初始化主机端SCON = 1101 0000 = 0xD0 ,初始化副机端SCON = 1111 0000 = 0xF0 。

PCON的配置在应用中只需要配置SMOD和SMOD0,其它位是跟电源低功耗相关的,暂切不用。SMOD的功能是让波特率加倍,在此次实验中用不到。SMOD0会影响SM0选择工作方式的功能,取0即可。所以PCON = 0000 0000 = 0x00 。
其次,要设置通讯的波特率。在本芯片中,要配置的是定时器1的工作方式。

本套系统用的是11.0592MHz的晶振,根据波特率计算公式,串口通信在9600波特率的设置为TH1 = 0xFD。根据芯片手册“TL1作为八位定时器产生溢出,溢出时会产生中断,并且将TH1的值重新装入TL1中,TH1内容由软件预置,重装时TH1内容不变。”,在定时器工作模式寄存器TMOD中配置定时器1的工作模式为八位自动重载。由于我们只是利用定时器1作为波特率发生器,所以不在需要给定时器1配置中断函数。其它位取0即可,即TMOD = 0010 0000 = 0x20 。

最后,根据单片机的中断结构示意图即可写出串口初始化函数。

//主机配置函数
void UART_Init(unsigned char baud)
{
TMOD |= 0x20; //定时器1配置为8位重载,不影响其他口配置所以用“|”
SCON |= 0xD0; //主机串口配置
PCON = 0x00; //电源配置
TH1 = baud;
TH0 = baud;
ES = 1; //打开串口中断
EA = 1; //打开总中断
TR1 = 1; //定时器开始
}
//从机配置函数
void UART_Init(unsigned char baud)
{
TMOD |= 0x20; //定时器1配置为8位重载,不影响其他口配置所以用“|”
SCON |= 0xF0; //从机串口配置,使SCON中的SM2为1
PCON = 0x00; //电源配置
TH1 = baud;
TH0 = baud;
ES = 1; //打开串口中断
EA = 1; //打开总中断
TR1 = 1; //定时器开始
}
发送函数
上方串口初始化代码分为主机和从机。可以看出主机的SCON |= 0xD0而从机的SCON |= 0xF0。也就是主机的SM2 = 0,从机的SM2 = 1。根据前文蓝色字体所述,从机这样配置后,只有主机发送TB8 = 1的数据,从机才会进入中断函数。而从机发送任何数据,主机都会进入中断函数。
这样就可以简单实现一种多机通讯的协议,假如现在有三个单片机ABC,单片机A发送“单片机B的地址ADDRESS+TB8=1”,单片机B收到地址后在中断函数将SM2取反,并且发送应答。单片机A再发送“八位数据DATA+TB8=0”,此时由于单片机B的SM2为0所以能够直接进入到中断接受数据。而单片机C接收不到DATA这个数据。
发送地址函数和发送数据函数如下:
//发送地址函数
void Send_Address(unsigned char temp)
{
ES = 0; //关闭串口中断开关
TB8 = 1;
SBUF = temp; //temp装入SBUF寄存器就会发送,此时发送完成后TI会硬件置1
while(!TI); //发送完成
TI = 0; //TI硬件置1后立马软件置0,避免进入中断函数
ES = 1; //打开串口中断开关
}
//发送数据函数
void Send_Data(unsigned char temp)
{
ES = 0;
TB8 = 0;
SBUF = temp;
while(!TI);
TI = 0;
ES = 1;
}
有了这些,我们就能够实现485多机通讯啦~
代码模板
以下是主从main函数的模板,可以按照个人需求修改。
//主机main函数
#define SLAVE1_ADDRESS 0XC1 //从机1的地址
#define SLAVE2_ADDRESS 0XC2 //从机2的地址
unsigned char flag;
typedef union
{
float dat;
unsigned char tmp_buf[4];
}Data;
Data resA;
Data resB;
void main()
{
UART_Init(0xFD);
LCD_Init();
while (1)
{
flag = 0;
Send_Address(SLAVE1_ADDRESS); //发送从机1地址
LCD_ShowSignedNum(1,1,resA.dat*10,4);//在LCD1602上显示从机1读取的数据的10倍
flag = 1;
Send_Address(SLAVE2_ADDRESS); //发送从机2地址
LCD_ShowSignedNum(2,1,resB.dat*10,4);//在LCD1602上显示从机2读取的数据的10倍
}
}
void UART_Routine() interrupt 4 //中断函数
{
static unsigned char i = 0;
RI = 0;
i %= 4;
if(flag==0)resA.tmp_buf[i] = SBUF;
if(flag==1)resB.tmp_buf[i] = SBUF;
i++;
}
//从机的main函数
#define SLAVE1_ADDRESS 0XC1
#define SLAVE2_ADDRESS 0XC2
typedef union
{
float dat;
unsigned char tmp_buf[4];
}Data;
Data Sen;
float Roll=20.23;//角度
void main()
{
UART_Init(0xFD);
while(1)
{
Sen.dat = Roll;
}
}
void UART_Routine() interrupt 4
{
RI = 0;
if(SBUF == SLAVE1_ADDRESS) //如果收到主机传唤
{
Send_Data(Sen.tmp_buf[0]);
Send_Data(Sen.tmp_buf[1]);
Send_Data(Sen.tmp_buf[2]);
Send_Data(Sen.tmp_buf[3]);
}
}
文末提一句,main中的代码使用了一个union类型的定义,这样就能将32位的float数据转变成4个8位数据通过串口发送了。因为串口一次只能发送八位数据,从别人那里学到的嘿嘿
如果感到对您有帮助请点个赞支持一下吧~
本文介绍了使用STC89C516RD单片机和TTL转485模块进行RS485通信的步骤,包括串口配置、波特率设置和中断函数的应用。通过设置SM2位实现多机通讯协议,允许主机和从机之间的数据交换,且提供了主从机的代码模板。
6438

被折叠的 条评论
为什么被折叠?



