51 SCM Serial Multi-computer Communication
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为低电平触发。
- 外部中断:
-
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:接收中断标志位
- 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 | 可变 |
- 工作方式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接收数据。
- 工作方式3
(SM0 SM1 :1 1):为波特率可变的11位异步通信方式,除了波特率有所区别之外,其余方式都与方式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,以便接收下一帧数据。
- 多机通信时
- 当一个89c51(主机)与多个89c51(从机)通信时,所有从机的SM2位都置1,主机首先发送的一帧数据为地址,即某从机号,其中第9位为1。
- 所有的从机接收数据后,将其中第9位数据装入RB8中。各个从机根据接收到的第9位数据(RB8中)的值来决定从机是否再接收主机的信息。
- 若(RB8)= 0,说明是数据帧,则使接收中断标志位RI = 0,信息丢失,
- 若RB8 = 1,说明是地址帧,数据装入SBUF并置RI = 1,中断所有从机,被寻址的目标从机清除SM2,以接收主机发来的一帧数据,其它从机仍然保持SM2 = 1。
- REN
允许接收控制位,由软件置1或清0
- REN = 1时,允许接收,相当于串行接收的开关
- REN = 0时,禁止接收
在串行通信接收控制过程中,如果满足RI = 0和REN = 1的条件,就允许接收。
- TB8, RB8
- TB8
发送数据的第9位(D8)装入TB8中。在方式2或方式3中,根据发送数据的需求由软件置位或复位。在许多通信协议中可用作奇偶校验位,也可以在多机通信中作为发送地址帧或者数据帧的标志位。
- RB8
接收数据的第9位,原理同TB8
- 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;
\[TH1 = 256 - \frac{K*F_osc}{384*BaudRate}\]举例,波特率为9600 bps,所用晶振为 11.0592 MHz,SMOD = 1,TH1 预置值应为 0xFA. \(TH1 = 256 - \frac{2*11059200}{384*9600} = 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’, ‘…’, ‘$’ }
接线图和注意事项
主机的RX 与所有从机的TX 连接。主机的TX 与所有从机的RX 连接。
- 从机和从机之间通信,只能通过主机中转。
- 各从机的TXD输出不能设置为推挽输出,要设置为开漏输出。
- 通信总线不能过长,最好不超过2米。
后续可以进行的功能扩展(设想)
- 添加控制指令
由 主机 -> 从机. 可以设置数据帧 {‘slave 1 addresss’, ‘GET’, ‘$’}. 表示主机想要获取从机从传感器中获得的数据。
- 主机中转传输
如设置一个指令字符帧开头 {‘master address’, ‘TRANSFER’, ‘slave 2 address’, “Data”, ‘$’}. 表示数据由主机代为转发到从机 “slave 2 address”.
完整代码链接
- 主机
- 从机