51单片机串口多机通信的原理与编程实现

  • 51单片机串口多机通信
    • 需要用的的寄存器 (了解的可直接跳到下一节)
      • TMOD 定时器/计数器模式控制寄存器
      • TCON 定时器控制寄存器
      • SCON 串口控制寄存器
      • PCON 电源控制位寄存器
      • IE 中断中断使能寄存器
    • 补充说明,TH1 预置值计算,中断源
      • 波特率计算公式
      • 中断源
    • 主从机工作模式和串口初始化代码
      • (Master host)主机串口初始化,串口发送,中断接收代码
      • (Slave host )从机串口初始化,串口发送,中断接收代码
    • 主从机的地址标识
    • 主从机的数据帧约束
    • 接线图和注意事项
    • 后续可以进行的功能扩展(设想)
    • 完整代码链接

51单片机串口多机通信

实验所用硬件: STC89C52、11.0592MHz 晶振,9600bps

需要用的的寄存器 (了解的可直接跳到下一节)

  • TMOD 定时器/计数器模式控制寄存
  • TCON 定时器控制寄存器
  • SCON 串口控制寄存器
  • PCON 电源控制位寄存器
  • IE(interrupt enable) 中断中断使能寄存器

TMOD 定时器/计数器模式控制寄存器

定时器/计数器模式控制寄存器TMOD是一个逐位定义的8位寄存器,但只能使用字节寻址,其字节地址为89H。

D7 D6 D5 D4 D3 D2 D1 D0
GATE C/T M1 M0 GATE C/T M1 M0
  • D0~D3 :为T0定时/计数器的设置,D4~D7为T1定时/计数器的设置 。
  • GATE :为门控位,GATE=0时,只要在编写程序时,使TCON中的TRO或TR1为1,就可以启动定时器/计数器工作。
    • GATE=1时,不仅要在编写程序时,使TCON中的TR0或TR1为1,且需要外部引脚也为高电平,才能工作。
  • C/T :定时/计数模式切换,C/T=0时为定时模式,C/T=1时为计数模式。
  • M1,M0 :用来选择定时计/计数器的工作方式,一般使用都是采用16位的计时计数器
M1 M0 工作模式 说明
0 0 0 13位计时计数器(8192)
0 1 1 16位计时计数器(65535)
1 0 2 8位计时计数器,可自动重新载入计数值(256)
1 1 3 当成两组独立的8位计时器 (256,T0 和T1 不能同时使用

TCON 定时器控制寄存器

定时器控制寄存器,作用是控制定时器的启、停,标志定时器溢出和中断情况。

D7 D6 D5 D4 D3 D2 D1 D0
TF1 TR1 TF0 TR0 IE1 IT1 IE0 IT0
  • TF1 :TF1=1表示T1有中断产生。(Timer Flag,定时器标志位)
  • TR1 :TR1=1表示T1开始运行。(单片机中T0引脚,需要高低电平的驱动)
  • TF0 :TF0=1表示T0有中断产生。
  • TR0 :TR0=1表示T0开始运行。(单片机中T1引脚,需要高低电平的驱动)
  • IE1 :IE1=1表示INT1有中断产生。
  • IT1 :IT1=1表示INT1为下降沿触发,IT1=0表示INT1为低电平触发。
  • IE0 :IE0=1表示INT0有中断产生。
  • IT0 :IT0=1表示INT0为下降沿(负跳变)触发,IT0=0表示INT0为低电平触发。
  1. 外部中断:

    • IE0/IE1:外部中断请求标志位 当INT0(INT1)引脚出现有效的请求信号,此位由单片机自动置1,CPU开始响应,处理中断,而当入中断程序后由单片机自动置0.

    • IT0/IT1:外部中断触发方式控制位 //选择有效信号 IT0/IT1=1:脉冲触发方式,下降沿有效。IT0/IT1=0:电平触发方式,低电平有效。

SCON 串口控制寄存器

D7 D6 D5 D4 D3 D2 D1 D0
SM0 SM1 SM2 REN TB8 RB8 T1 RI
  • SM0、SM1工作方式控制位
  • SM2:多机通信控制位,1-允许、0-不允许
  • REN:串行接收允许位。1-允许、0-不允许
  • TB8:发送数据第九位
  • RB8:接收数据第九位
  • TI:发送中断标志位
  • RI:接收中断标志位
  1. SM0和SM1

    串行口工作方式选择位 ,两个选择位对应四种通信方式,如下表,其中fosc是振荡频率

    SM0 SM1 工作方式 功能 波特率
    0 0 方式0 8位同步移位寄存器 fosc/12
    0 1 方式1 10位UART 可变
    1 0 方式2 11位UART fosc/32 或 fosc/64
    1 1 方式3 11位UART 可变
    1. 工作方式2

      (SM0 SM1 :1 0):串行口为11位异步通信接口。

      • 发送或接收一帧信息包括1位起始位“0”、8位数据位、1位可编程位、1位停止位“1”。
      • 发送数据:
        • 发送前,先根据通信协议由软件设置TB8为“奇偶校验位”或“数据标识位”,然后将要发送的数据写入SBUF,即能启动发送器。
        • 发送过程是由执行任何一条以SBUF为目的寄存器的指令而启动的,把8位数据装入SBUF,同时还把TB8装到发送移位寄存器的第9位上,然后从TXD(P3.1)端口输出一帧数据。
      • 接收数据:
        • 先置REN=1,使串行口为允许接收状态,同时还要将RI清“0”。
        • 然后再根据SM2的状态和所接收到的RB8的状态决定此串行口在信息到来后是否置R1=1,并申请中断,通知CPU接收数据。
    2. 工作方式3

      (SM0 SM1 :1 1):为波特率可变的11位异步通信方式,除了波特率有所区别之外,其余方式都与方式2相同。

  2. SM2

    多机通信控制位,主要用于方式2和方式3。

    • 若SM2 = 1,允许多机通信。

      • 多机通信协议规定,第9位数据(D8)为1,说明本帧数据为地址帧;
      • 若第9位数据为0,则本帧数据为数据帧。
    • 若SM2 = 0, 即不属于多机通信情况,则接收完一帧数据后,不管第9位数据是0还是1,都置RI = 1,接收到的数据装入SBUF中。

      • 在方式0时SM2必须置0。在方式1时,若SM2 = 1,则只有接收到有效停止位时,RI才置1,以便接收下一帧数据。
    1. 多机通信时

      • 当一个89c51(主机)与多个89c51(从机)通信时,所有从机的SM2位都置1,主机首先发送的一帧数据为地址,即某从机号,其中第9位为1。
      • 所有的从机接收数据后,将其中第9位数据装入RB8中。各个从机根据接收到的第9位数据(RB8中)的值来决定从机是否再接收主机的信息。
        • 若(RB8)= 0,说明是数据帧,则使接收中断标志位RI = 0,信息丢失,
        • 若RB8 = 1,说明是地址帧,数据装入SBUF并置RI = 1,中断所有从机,被寻址的目标从机清除SM2,以接收主机发来的一帧数据,其它从机仍然保持SM2 = 1。
  3. REN

    允许接收控制位,由软件置1或清0

    • REN = 1时,允许接收,相当于串行接收的开关
    • REN = 0时,禁止接收

    在串行通信接收控制过程中,如果满足RI = 0和REN = 1的条件,就允许接收。

  4. TB8, RB8

    • TB8

    发送数据的第9位(D8)装入TB8中。在方式2或方式3中,根据发送数据的需求由软件置位或复位。在许多通信协议中可用作奇偶校验位,也可以在多机通信中作为发送地址帧或者数据帧的标志位。

    • RB8

    接收数据的第9位,原理同TB8

  5. TI, RI

    • TI

    发送中断标志位,在一帧数据发送完时被置位。在串行发送到停止位的开始时由硬件置位,可用软件查询。它同时也申请中断。TI置位意味着向CPU提供“发送缓冲器SBUF已空”的信息,CPU可以准备发送下一帧数据。串行口发送中断被响应后,TI不会自动清0,必须软件清0.

    • RI

    接收中断标志,在接收到一帧数据后由硬件置位。当RI = 1时,申请中断,表示一帧数据接收结束,并已装入接收SBUF中,要求CPU取走数据,CPU响应中断,取走数据。RI位也必须由软件来清0,。

    • 注意

    串行发送中断标志TI和接收中断标志RI是同一个中断源,CPU事先不知道是发送中断TI还是接收中断RI产生的中断请求,所以,在全双工通信时,必须由软件来判别。复位时SCON所有位都清0.

PCON 电源控制位寄存器

电源控制位寄存器PCON中只有SMOD位与串口工作有关,如下图所示

D7 D6 D5 D4 D3 D2 D1 D0
SMOD X X X GF1 GF0 PD IDL

SMOD:波特率倍增位。在方式1、2、3中,当SMOD = 1时,波特率提高一倍。

IE 中断中断使能寄存器

D7 D6 D5 D4 D3 D2 D1 D0
符号 EA ET2 ES ET1 EX1 ET0 EX0
复位值 0 0 0 0 0 0 0

想要中断发生时,CPU能处理中断函数,就必须使能相应的中断,通过IE配置即可。为1时使能,为0不使能

  • EA (enable ALL ):中断使能总开关位。1开启,0关闭。若EA不开启,即便5个中断都开启,CPU也不会处理中断。
  • EX0:外部中断INT0使能
  • EX1:外部中断INT1使能
  • ET0:定时器0中断使能
  • ET1:定时器1中断使能
  • ET2:定时器2中断使能
  • ES: 串口中断使能

补充说明,TH1 预置值计算,中断源

TH1 预置值计算公式

注意 SMOD=0时,K=1;SMOD=1时,K=2;

T H 1 = 256 − K ∗ F o s c 384 ∗ B a u d R a t e TH1 = 256 - \frac{K*F_osc}{384*BaudRate} TH1=256384BaudRateKFosc

举例,波特率为9600 bps,所用晶振为 11.0592 MHz,SMOD = 1,TH1 预置值应为 0xFA.
T H 1 = 256 − 2 ∗ 11059200 384 ∗ 9600 = 250 ( 0 x F A ) TH1 = 256 - \frac{2*11059200}{384*9600} = 250 (0xFA) TH1=2563849600211059200=250(0xFA)

中断源

中断源 引发原因 默认优先级 中断序号(C语言) 入口地址(汇编用)中断标号*8 +3得到
INT0 引脚输入低电平或者下降沿引发。此时标志位IE0=1 最高 0 0003
T0 T0对应的TF0溢出时发生。此时标志位TF0 = 1 1 000B
INT1 引脚输入低电平或者下降沿引发。此时标志位IE1=1 2 0013
T1 T1对应的TF1溢出时发生。此时标志位TF1 = 1 3 001B
串口中断 串口完成一帧字符的接受/发送引发。 4 0023
T2(如果有的话) T2对应的TF2溢出时发生。标志位TF2 = 1 最低 5 002B

若配置好了相应的中断,当中断发生时,单片机就会自动去调用中断函数,来处理中断。

在执行中断函数前,除了串行口中断的标志位需要用代码指令软件归零外,其他的中断标志位都是硬件自动归零。

  • 注意

中断函数可以写在分文件里,在.c源文件中的写法。

void functionNmae() interrupt 中断序号 {

}

在头文件中的写法

void functionName(); //直接省略中断序号即可

主从机工作模式和串口初始化代码

51单片机的主从模式,首先要设定工作方式3:(主从模式+波特率可变)。SCON串口功能寄存器:SM0=1;SM1=1(工作方式3)

(Master host)主机串口初始化,串口发送,中断接收代码

  • 串口发送

主机的配置发送“地址”时,把TB8设定为1,发送数据时TB8设定为0,(类似于:主机 TB8=1发送的是地址,TB8=0发送的是数据)

void TXdata(uchar addr,uchar *str){
  TB8 = 1; //发送地址
  SBUF = addr; //把地址发送出去
  while(!TI); //判断是否发送成功(发送成功后TI会置1,需手动清0)
  TI = 0;
  TB8 = 0; //发送数据
  while(*str != '\0') //发送数组
    {
      SBUF = (*str);
      while(!TI);
      TI = 0;
      str++;
    }
}

假设主机将发送“1234”给地址为1的从机:调用函数:TXdata(1,“1234$”);

  • 主机接收
void chuan() interrupt 4 //串口中断服务函数
{
  ES = 0; //关闭串口中断
  if(RI) //再次判断,是否接收到数据(接收到数据后,RI会置1,需手动清0)
    {
      RXData = SBUF;
      if(RXstart) //判断是否接收到过本地址
	{
	  if(RXData != '$') //判断是否接收到 数据结束 标志 $
	    {
	      temp[j] = RXData; //没有接收到结束标志,正常保存数据至数组
	      j++;
	    }
	  else //接收到 结束标志 $
	    {
	      RXstart= 0; //本次接收结束
	      j = 0;
	    }
	}
      if(RXData == 1) //判断是否呼叫本机,地址范围:000 – 254(00 - FE)
	{
	  RXstart = '0'; //开始接收数据
	}
    }
  RI = 0; //清除接收标志位
  ES = 1; //重新开启串口中断
}
  • 主机串口初始化
void UART_init()
{
  TMOD = 0x20; //定时器1,工作方式2:8位、自动重装
  TH1 = 0xfd; //fd: 9600bps @ 11.0592M
  TL1 = 0xfd; //e8: 1200bps @ 11.0592M
  //f4: 2400bps @ 11.0592M
  REN = 1; //允许串口接收
  SM0 = 1;
  SM1 = 1; //SM0和SM1:串口工作模式3,主从模式 + 波特率可变
  //SM2 = 1; //只接收地址(从机如此配置,主机不需要)
  ES = 1; //开串口中断
  TR1 = 1; //启动定时器1
  EA = 1; //中断 总开关
}

(Slave host )从机串口初始化,串口发送,中断接收代码

  • 注意

从机发送给主机的数据帧要以字符 ‘0’ 开头,标识这个要主机接收的。

  • 串口发送
void TXdata(uchar *str){
  while(*str != '\0') //发送数组
    {
      SBUF = (*str);
      while(!TI);
      TI = 0;
      str++;
    }
}
  • 从机接收
    • 从机接收时,首先串口初始化时,使SM2=1(接收地址模式,即只能接收到TB8=1的数据,才触发中断),主机发送TB=0的数据,被认为是总线上的主机发送给别机的通信数据,本机丢弃,不产生中断。
    • 接收的地址与本机地址相符后,使SM2=0(接收数据模式,接收数据正常触发中断)。(类似于:从机 SM2=1只接收地址,SM2=0只接收数据)
void chuan() interrupt 4 //串口中断服务函数
{
  ES = 0; //关闭串口中断
  if(RI) //再次判断,是否接收到数据(接收到数据后,RI会置1,需手动清0)
    {
      RXData = SBUF;
      if(RXstart) //判断是否接收到过本地址
	{
	  if(RXData != '$') //判断是否接收到 数据结束 标志 $
	    {
	      temp[j] = RXData; //没有接收到结束标志,正常保存数据至数组
	      j++;
	    }
	  else //接收到 结束标志 $
	    {
	      RXstart= 0; //本次接收结束
	      SM2 = 1; //重新 配置为:只接收地址 模式,下次发送TB8=1才中断
	      j = 0;
	    }
	}
      if(RXData == 1) //判断是否呼叫本机,地址范围:000 – 254(00 - FE)
	{
	  RXstart = 1; //开始接收数据
	  SM2 = 0; //配置为:接收数据 模式
	}
    }
  RI = 0; //清除接收标志位
  ES = 1; //重新开启串口中断
}
  • 从机串口初始化
void UART_init()
{
  TMOD = 0x20; //定时器1,工作方式2:8位、自动重装
  TH1 = 0xfd; //fd: 9600bps @ 11.0592M
  TL1 = 0xfd; //e8: 1200bps @ 11.0592M
  //f4: 2400bps @ 11.0592M
  REN = 1; //允许串口接收
  SM0 = 1;
  SM1 = 1; //SM0和SM1:串口工作模式3,主从模式 + 波特率可变
  SM2 = 1; //只接收地址(从机如此配置,主机不需要)
  ES = 1; //开串口中断
  TR1 = 1; //启动定时器1
  EA = 1; //中断 总开关
}

主从机的地址标识

地址用一个8位字符表示可以,大小可以设置从 000-255。主机默认是0。

主从机的数据帧约束

无论是主机还是从机发送的数据帧都要以 ‘address’ 开头, ‘$’ 结尾。

  • { ‘address’, ‘…’, ‘$’ }

接线图和注意事项

51单片机串口多机通信的原理与编程实现_第1张图片
主机的RX 与所有从机的TX 连接。主机的TX 与所有从机的RX 连接。

  1. 从机和从机之间通信,只能通过主机中转。
  2. 各从机的TXD输出不能设置为推挽输出,要设置为开漏输出。
  3. 通信总线不能过长,最好不超过2米。

后续可以进行的功能扩展(设想)

  • 添加控制指令

由 主机 -> 从机. 可以设置数据帧 {‘slave 1 addresss’, ‘GET’, ‘$’}. 表示主机想要获取从机从传感器中获得的数据。

  • 主机中转传输

如设置一个指令字符帧开头 {‘master address’, ‘TRANSFER’, ‘slave 2 address’, “Data”, ‘$’}. 表示数据由主机代为转发到从机 “slave 2 address”.

完整代码链接

  • 主机

serialmaster.h
serialmaster.c

  • 从机

serial_slave.h
serial_slave.c

你可能感兴趣的:(嵌入式,嵌入式,C51,单片机,C,串口)