实验所用硬件: STC89C52、11.0592MHz 晶振,9600bps
定时器/计数器模式控制寄存器TMOD是一个逐位定义的8位寄存器,但只能使用字节寻址,其字节地址为89H。
D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
---|---|---|---|---|---|---|---|
GATE | C/T | M1 | M0 | GATE | C/T | M1 | M0 |
M1 | M0 | 工作模式 | 说明 |
---|---|---|---|
0 | 0 | 0 | 13位计时计数器(8192) |
0 | 1 | 1 | 16位计时计数器(65535) |
1 | 0 | 2 | 8位计时计数器,可自动重新载入计数值(256) |
1 | 1 | 3 | 当成两组独立的8位计时器 (256,T0 和T1 不能同时使用 |
定时器控制寄存器,作用是控制定时器的启、停,标志定时器溢出和中断情况。
D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
---|---|---|---|---|---|---|---|
TF1 | TR1 | TF0 | TR0 | IE1 | IT1 | IE0 | IT0 |
外部中断:
IE0/IE1:外部中断请求标志位 当INT0(INT1)引脚出现有效的请求信号,此位由单片机自动置1,CPU开始响应,处理中断,而当入中断程序后由单片机自动置0.
IT0/IT1:外部中断触发方式控制位 //选择有效信号 IT0/IT1=1:脉冲触发方式,下降沿有效。IT0/IT1=0:电平触发方式,低电平有效。
D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
---|---|---|---|---|---|---|---|
SM0 | SM1 | SM2 | REN | TB8 | RB8 | T1 | 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位异步通信接口。
工作方式3
(SM0 SM1 :1 1):为波特率可变的11位异步通信方式,除了波特率有所区别之外,其余方式都与方式2相同。
SM2
多机通信控制位,主要用于方式2和方式3。
若SM2 = 1,允许多机通信。
若SM2 = 0, 即不属于多机通信情况,则接收完一帧数据后,不管第9位数据是0还是1,都置RI = 1,接收到的数据装入SBUF中。
多机通信时
REN
允许接收控制位,由软件置1或清0
在串行通信接收控制过程中,如果满足RI = 0和REN = 1的条件,就允许接收。
TB8, RB8
发送数据的第9位(D8)装入TB8中。在方式2或方式3中,根据发送数据的需求由软件置位或复位。在许多通信协议中可用作奇偶校验位,也可以在多机通信中作为发送地址帧或者数据帧的标志位。
接收数据的第9位,原理同TB8
TI, RI
发送中断标志位,在一帧数据发送完时被置位。在串行发送到停止位的开始时由硬件置位,可用软件查询。它同时也申请中断。TI置位意味着向CPU提供“发送缓冲器SBUF已空”的信息,CPU可以准备发送下一帧数据。串行口发送中断被响应后,TI不会自动清0,必须软件清0.
接收中断标志,在接收到一帧数据后由硬件置位。当RI = 1时,申请中断,表示一帧数据接收结束,并已装入接收SBUF中,要求CPU取走数据,CPU响应中断,取走数据。RI位也必须由软件来清0,。
串行发送中断标志TI和接收中断标志RI是同一个中断源,CPU事先不知道是发送中断TI还是接收中断RI产生的中断请求,所以,在全双工通信时,必须由软件来判别。复位时SCON所有位都清0.
电源控制位寄存器PCON中只有SMOD位与串口工作有关,如下图所示
D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
---|---|---|---|---|---|---|---|
SMOD | X | X | X | GF1 | GF0 | PD | IDL |
SMOD:波特率倍增位。在方式1、2、3中,当SMOD = 1时,波特率提高一倍。
位 | 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不使能
注意 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=256−384∗BaudRateK∗Fosc
举例,波特率为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=256−384∗96002∗11059200=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)
主机的配置发送“地址”时,把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; //中断 总开关
}
从机发送给主机的数据帧要以字符 ‘0’ 开头,标识这个要主机接收的。
void TXdata(uchar *str){
while(*str != '\0') //发送数组
{
SBUF = (*str);
while(!TI);
TI = 0;
str++;
}
}
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’ 开头, ‘$’ 结尾。
主机的RX 与所有从机的TX 连接。主机的TX 与所有从机的RX 连接。
由 主机 -> 从机. 可以设置数据帧 {‘slave 1 addresss’, ‘GET’, ‘$’}. 表示主机想要获取从机从传感器中获得的数据。
如设置一个指令字符帧开头 {‘master address’, ‘TRANSFER’, ‘slave 2 address’, “Data”, ‘$’}. 表示数据由主机代为转发到从机 “slave 2 address”.
serialmaster.h
serialmaster.c
serial_slave.h
serial_slave.c