内容
外部中断-使用独立按键K3控制LED亮灭;
定时器(中断)-通过定时器0中断控制D1指示灯间隔1秒闪烁;
串口通信(中断)-通过串口(UART)实现与PC机对话,51单片机的串口收到PC机发来的数据后原封不动的返回给PC机显示;
中断系统介绍
中断简介
中断是为使单片机具有对外部或内部随机发生的事件实时处理而设置的,中断功能的存在,很大程度上提高了单片机处理外部或内部事件的能力;它也是单片机最重要的功能之一,是我们学习单片机必须要掌握的;
举一个生活事例:你打开火,烧上一壶水,然后去洗衣服;在洗衣服的过程中,突然听到水壶发出水开的报警声,这时,你停止洗衣服动作,立即去关掉火,然后将开水灌入暖水瓶中;灌完开水后,你又回去继续洗衣服;这个过程中实际上就发生了一次中断;
对于单片机来讲,中断是指CPU在处理某一事件A时,发生了另一事件B,请求CPU迅速去处理(中断发生);CPU暂时停止当前的工作(中断响应),转去处理事件B(中断服务);待CPU将事件B处理完毕后,再回到原来事件A被中断的地方继续处理事件A(中断返回),这一过程称为中断;
引起CPU中断的根源称为中断源;中断源向CPU提出中断请求,CPU暂时中断原来的事务A,转去处理事件B,对事件B处理完毕后,再回到原来被中断的地方(即断点),称为中断返回;实现上述中断功能的部件称为中断系统(中断机构);
中断的开启与关闭、设置启用哪一个中断等都是由单片机内部的一些特殊功能寄存器来决定的,操作单片机的IO口即操作IO口寄存器,只不过编译器已经帮我们把IO口寄存器封装好,直接操作IO即可;
中断技术不仅可以解决快速主机与慢速I/O设备的数据传送问题,而且还具有如下优点:
- 分时操作,CPU可以分时为多个I/O设备服务,提高了计算机的利用率;
- 实时响应,CPU能够及时处理应用系统的随机事件,系统的实时性大大增强;
- 可靠性高,CPU具有处理设备故障及掉电等突发性事件能力,从而使系统可靠性提高;
中断结构
STC89C5X系列单片机提供了8个中断请求源,它们分别是:外部中断O(INTO)、外部中断1(INT1)、外部中断2(INT2)、外部中断3(INT3)、定时器0中断、定时器1中断、定时器2中断、串口(UART)中断;
(注意:51系列单片机一定有基本的5个中断,但不全有8个中断,需要查看芯片手册,通常我们使用的都是基本的5个中断:INT0、INT1、定时器0/1,串口中断)
以51单片机均有的5个中断来介绍,其内部结构框图如下所示:
外部中断0/1对应p32/p33口,定时中断0/1对应p34/p35口,串行中断接收/发送即RX/TX对应p30/p31口
- INT0对应的是P3.2口的附加功能,可由IT0(TCON.0)选择其为低电平有效还是下降沿有效;当CPU检测到P3.2引脚上出现有效的中断信号时,中断标志IE0(TCON.1)置 1,向CPU申请中断;
- INT1对应的是P3.3口的附加功能,可由IT1(TCON.2)选择其为低电平有效还是下降沿有效;当CPU检测到P3.3引脚上出现有效的中断信号时,中断标志IE1(TCON.3)置 1,向CPU申请中断;
- T0对应的是P3.4口的附加功能,TF0(TCON.5),片内定时/计数器T0溢出中断请求标志;当定时/计数器T0发生溢出时,置位TF0,并向CPU申请中断;
- T1对应的是P3.5口的附加功能,TF1(TCON.7),片内定时/计数器T1溢出中断请求标志;当定时/计数器T1发生溢出时,置位TF1,并向CPU申请中断;
- RX和TX对应的是P3.0和P3.1口的附加功能,RI(SCON.0)或TI(SCON.1),串行口中断请求标志;当串行口接收完一帧串行数据时置位RI或当串行口发送完一帧串行数据时置位TI,向CPU申请中断;
所有的中断都具有四个中断优先级(基本型只有两个)
用户可以用关总中断允许位(EA/IE.7)或相应中断的允许位来屏蔽所有的中断请求,也可以用打开相应的中断允许位来使CPU响应相应的中断申请;
其中有些中断源可以用软件独立地控制为开中断或关中断状态;
每一个中断的优先级别均可用软件设置,高优先级的中断请求可以打断低优先级的中断,反之,低优先级的中断请求不可以打断高优先级及同优先级的中断;当两个相同优先级的中断同时产生时,将由查询次序来决定系统先响应哪个中断;
STC89C5X系列单片机的各个中断查询次序表如下图所示:
通过设置新增加的特殊功能寄存器IPH中的相应位,可将中断优先级设为四级,如果只设置IP或XICON,那么中断优先级就只有两级,与传统8051单片机两级中断优先级完全兼容;
上图中的中断查询次序即为中断号,这个中断号在编程时非常重要,当中断来临时,只有中断号正确才能进入中断;
中断寄存器
中断允许控制位IE
CPU对中断系统所有中断以及某个中断源的开放和屏蔽是由中断允许寄存器IE控制的;
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
字节地址:A8H | EA | ES | ET1 | EX1 | ET0 | EX0 | IE |
EX0(IE.0),外部中断0允许位;
ET0(IE.1),定时/计数器T0中断允许位;
EX1(IE.2),外部中断0允许位;
ET1(IE.3),定时/计数器T1中断允许位;
ES(IE.4),串行口中断允许位;
EA(IE.7),CPU中断允许(总允许)位;
中断请求标志TCON
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
字节地址:88H | TF1 | TR1 | TF0 | TR0 | IE1 | IT1 | IE0 | IT0 | TCON |
IT0(TCON.0),外部中断0触发方式控制位;当IT0=0时,为电平触发方式;当IT0=1时,为边沿触发方式(下降沿有效);
IE0(TCON.1),外部中断0中断请求标志位;
IT1(TCON.2),外部中断1触发方式控制位;
IE1(TCON.3),外部中断1中断请求标志位;
TF0(TCON.5),定时/计数器T0溢出中断请求标志位;
TF1(TCON.7),定时/计数器T1溢出中断请求标志位;
中断优先级
同一优先级中的中断申请不止一个时,则有中断优先权排队问题;
同一优先级的中断优先权排队,由中断系统硬件确定的自然优先级形成,其排列如所示:
中断源 | 中断标志 | 中断服务程序入口 | 优先级顺序 |
---|---|---|---|
外部中断0(INTO) | IE0 | 0003H | 1 |
定时/计数器0(TO) | TF0 | 000BH | 2 |
外部中断1(INTI) | IE1 | 0013H | 3 |
定时/计数器1(T1) | TF1 | 001BH | 4 |
串行口 | RI或TI | 0023H | 5 |
中断号
中断响应条件
以下三条同时满足时,CPU 才有可能响应中断:
- 中断源有中断请求;
- 此中断源的中断允许位为1;
- CPU开中断(即EA=1);
中断的使用
1 确定好要使用的中断类型,选择相应的中断号;
2 选择好要触发的类型;
3 写好中断后要做的操作;
以外部中断0为例
首先要有中断配置程序:(1、2)
EA = 1; // 打开总中断开关
EX0 = 1; // 开外部中断0
IT0 = 0 / 1; // 设置外部中断的触发方式
中断服务函数:(3)
void int0() interrupt 0 using 1 // using 1可忽略
{
// 编写用户所需的功能代码
}
外部中断
在上面的中断系统中已经将外部中断描述的比较清楚,这里不再重复描述;
内容
使用独立按键K3控制LED亮灭
原理图
k3按键连接p32口,而led1连接p20口
思路
当k3按钮按下时,对应的p32口电位由高变低,会产生下降沿,因此我们可以选择,使用外部中断0,选择下降沿触发方式;
在中断函数里消抖,即确保检测k3按键是否按下(为低电位),然后变化led1的电位;
(注意:外部中断0/1对应p32/p33口,定时中断0/1对应p34/p35口,串行中断接收/发送即RI/TI-对应p30/p31口)
编码
main.c
/*
* @Description: 按下K3键控制D1指示灯亮灭
*/
#include "reg52.h"
typedef unsigned int u16; // 对系统默认数据类型进行重定义
typedef unsigned char u8;
// 定义LED1管脚
sbit LED1 = P2 ^ 0;
// 定义独立按键K3控制脚
sbit KEY3 = P3 ^ 2;
/**
* @description: 延时函数(循环一次大约10us)
* @param {u16} ten_us
* @return {*}
*/
void delay_10us(u16 ten_us)
{
while (ten_us--)
;
}
/**
* @description: 延时函数(循环一次大约1ms)
* @param {u16} ms
* @return {*}
*/
void delay_ms(u16 ms)
{
u16 i, j;
for (i = ms; i > 0; i--)
for (j = 110; j > 0; j--)
;
}
/**
* @description: 外部中断0配置函数
* @return {*}
*/
void exti0_init(void)
{
IT0 = 1; // 跳变沿触发方式(下降沿)
EX0 = 1; // 打开INT0的中断允许
EA = 1; // 打开总中断
}
/**
* @description: 外部中断0中断函数
* @return {*}
*/
void exti0() interrupt 0 // 中断号必须对应上
{
delay_10us(1000); // 消抖
if (KEY3 == 0) // 再次判断K3键是否按下
LED1 = !LED1; // LED1状态翻转
}
void main()
{
exti0_init(); // 外部中断0配置
while (1)
{
}
}
编译和结果
按F7编译,无错误,生成.hex文件,使用pz-isp将hex文件下载到单片机
结果:按下k3键后能控制指示灯d1亮灭
定时器(中断)
内容
通过定时器0中断控制D1指示灯间隔1秒闪烁
定时器介绍
CPU时序的有关知识
- 振荡周期:为单片机提供定时信号的振荡源的周期(晶振周期或外加振荡周期);
- 状态周期:2个振荡周期为1个状态周期,用S表示,振荡周期又称S周期或时钟周期;
- 机器周期:1个机器周期含6个状态周期,12个振荡周期;
- 指令周期:完成1条指令所占用的全部时间,它以机器周期为单位;
例如:外接晶振为 12MHz 时,51 单片机相关周期的具体值为:
振荡周期=1/12us;
状态周期=1/6us;
机器周期=1us;
指令周期=1~4us;
51单片机的定时器
- 51单片机有两组定时器/计数器,因为既可以定时,又可以计数,故称之为定时器/计数器;
- 定时器/计数器和单片机的CPU是相互独立的,定时器/计数器工作的过程是自动完成的,不需要CPU的参与;
- 51单片机中的定时器/计数器是根据机器内部的时钟或者是外部的脉冲信号对寄存器中的数据加1;
- 有了定时器/计数器之后,可以增加单片机的效率,一些简单的重复加1的工作可以交给定时器/计数器处理,CPU转而处理一些复杂的事情,同时可以实现精确定时作用;
定时器原理
STC89C5X单片机内有两个可编程的定时/计数器T0、T1和一个特殊功能定时器T2;
定时/计数器的实质是加1计数器(16位),由高8位和低8位两个寄存器THx和TLx组成;
它随着计数器的输入脉冲进行自加1,也就是每来一个脉冲,计数器就自动加1,当加到计数器为全1时,再输入一个脉冲就使计数器回零,且计数器的溢出使相应的中断标志位置1,向CPU 发出中断请求(定时/计数器中断允许时);如果定时/计数器工作于定时模式,则表示定时时间已到;如果工作于计数模式,则表示计数值已满;
溢出时计数器的值减去计数初值就是加1计数器的计数值;
定时器结构
定时器0/1(T0、T1)对应的是单片机P3.4和P3.5管脚;
51单片机定时/计数器的工作由两个特殊功能寄存器控制(工作方式寄存器、控制寄存器);
TMOD是定时/计数器的工作方式寄存器,确定工作方式和功能;TCON是控制寄存器,控制定时器T0、T1的启动和停止及设置溢出标志;
工作方式寄存器TMOD
工作方式寄存器TMOD用于设置定时/计数器的工作方式,低四位用于T0,高四位用于T1
其格式如下:
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
字节地址:89H | GATE | C/T | M1 | M0 | GATE | C/T | M1 | M0 | TMOD |
- GATE,门控位,用于控制定时器的启动是否受外部中断源信号的影响;GATE=0时,只要用软件使TCON中的TR0或TR1为1,就可以启动定时/计数器工作;GATA=1时,要用软件使TR0或TR1为1,同时外部中断引脚INT0/1也为高电平时,才能启动定时/计数器工作;即此时定时器的启动条件,加上了INT0/1引脚为高电平这一条件;
- C/T,定时/计数模式选择位;C/T=0为定时模式;C/T=1为计数模式;
- M1、M0,工作方式设置位;定时/计数器有四种工作方式;
M1M0 | 工作方式 | 说明 |
---|---|---|
00 | 方式0 | 13位定时/计数器 |
01 | 方式1 | 16位定时/计数器 |
10 | 方式2 | 8位自动重装定时/计数器 |
11 | 方式3 | T0分成两个独立的8位定时/计数器;T1此方式下停止计数; |
控制寄存器TCON
TCON的低4位用于控制外部中断,TCON的高4位用于控制定时/计数器的启动和中断申请;
其格式如下:
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
字节地址:88H | TF1 | TR1 | TF0 | TR0 | TCON |
- TF1(TCON.7),T1溢出中断请求标志位;T1计数溢出时由硬件自动置TF1为1,CPU响应中断后TF1由硬件自动清0,T1工作时,CPU可随时查询TF1的状态;所以,TF1可用作查询测试的标志;TF1也可以用软件置1或清0,同硬件置1或清0的效果一样;
- TR1(TCON.6),T1运行控制位;TR1置1时,T1开始工作;TR1置0时,T1停止工作;TR1由软件置1或清0,所以,用软件可控制定时/计数器的启动与停止;
- TF0(TCON.5),T0溢出中断请求标志位,其功能与TF1类同;
- TR0(TCON.4),T0运行控制位,其功能与TR1类同;
定时器的工作方式
方式0
方式0为13位计数,由TL0的低5位(高3位未用)和TH0的8位组成;
TL0的低5位溢出时向TH0进位,TH0溢出时,置位TCON中的TF0标志,向CPU发出中断请求;
其结构图如下所示:
门控位GATE具有特殊的作用;当GATE=0时,经反相后使或门输出为1,此时仅由TR0控制与门的开启;当GATE=1时,由外中断引脚信号控制或门的输出,此时控制与门的开启由外中断引脚信号和TR0共同控制;
计数模式时,计数脉冲是T0引脚上的外部脉冲;
计数初值与计数个数的关系为:X=2^13-N
方式1
方式1的计数位数是16位,由TL0作为低8位,TH0作为高8位,组成了16位加1计数器;
其结构图如下所示:
计数初值与计数个数的关系为:X=2^16-N
方式2
方式2为自动重装初值的8位计数方式;
工作方式2特别适合于用作比较精确的脉冲信号发生器;
其结构图如下所示:
计数初值与计数个数的关系为:X=2^8-N
方式3
方式3只适用于定时/计数器T0,定时器T1处于方式3时相当于TR1=0,停止计数;
工作方式3将T0分成为两个独立的8位计数器TL0和TH0;
其结构如下所示:
这几种工作方式中应用较多的是方式1和方式2;
定时器中通常使用定时器方式1,串口通信中通常使用方式2;
计算定时器初值
首先我们知道机器周期的概念,它是CPU完成一个基本操作所需要的时间,计算公式是:机器周期=1/单片机的时钟频率;
51单片机内部时钟频率是外部时钟的12分频,也就是说当外部晶振的频率输入到单片机里面的时候要进行12分频;比如说你用的是12MHZ晶振,那么单片机内部的时钟频率就是12/12MHZ,当你使用12MHZ的外部晶振的时候,机器周期=1/1M=1us;
比如我们想定时1ms的初值,1ms/1us=1000,也就是要计数1000个,初值=65535-1000+1=64536=FC18H(因为实际上计数器计数到65536=2 * 16才溢出,所以后面要加1),所以初值即为 THx=0XFC,TLx=0X18;
由于定时计数器位数有限,我们不能直接通过初值定时很长时间,所以如果要实现很长时间的定时,比如定时1秒钟,可以通过初值设置定时1ms,每当定时1ms结束后又重新赋初值,并且设定一个全局变量累计定时1ms的次数,当累计到1000次,就表示已经定时1秒了;
定时器的配置
- 1 对TMOD赋值,以确定T0和T1的工作方式,如果使用定时器0即对T0配置,如果使用定时器1即对T1配置;
- 2 根据所要定时的时间计算初值,并将其写入TH0、TL0或TH1、TL1;
- 3 如果使用中断,则对EA赋值,开放定时器中断;
- 4 使TR0或TR1置位,启动定时/计数器定时或计数;
例如使用定时器0,选择定时器工作方式1、设定1ms初值,开启定时器计数功能以及总中断:
void time0_init(void)
{
TMOD |= 0X01; // 选择为定时器0模式,工作方式1
TH0 = 0XFC; // 给定时器赋初值,定时1ms
TL0 = 0X18;
ET0 = 1; // 打开定时器0中断允许
EA = 1; // 打开总中断
TR0 = 1; // 打开定时器
}
(TMOD高四位用于T0,低四位用于T1)
思路
选择定时器0,配置为1ms,循环1000次(通过变量累计值达到1000)来达到计时1s的效果;
变量累计值达到1000时改变led1电位,同时变量清0,重新开始计时;
编码
main.c
/*
* @Description: 通过定时器0中断控制D1指示灯间隔1秒闪烁
*/
#include "reg52.h"
typedef unsigned int u16; // 对系统默认数据类型进行重定义
typedef unsigned char u8;
// 定义LED1管脚
sbit LED1 = P2 ^ 0;
/**
* @description: 延时函数(循环一次大约10us)
* @param {u16} ten_us
* @return {*}
*/
void delay_10us(u16 ten_us)
{
while (ten_us--)
;
}
/**
* @description: 延时函数(循环一次大约1ms)
* @param {u16} ms
* @return {*}
*/
void delay_ms(u16 ms)
{
u16 i, j;
for (i = ms; i > 0; i--)
for (j = 110; j > 0; j--)
;
}
/**
* @description: 定时器0中断配置函数,通过设置TH和TL即可确定定时时间
* @return {*}
*/
void time0_init(void)
{
TMOD |= 0X01; // 选择为定时器0模式,工作方式1
TH0 = 0XFC; // 给定时器赋初值,定时1ms
TL0 = 0X18;
ET0 = 1; // 打开定时器0中断允许
EA = 1; // 打开总中断
TR0 = 1; // 打开定时器
}
/**
* @description: 定时器0中断函数
* @return {*}
*/
void time0() interrupt 1 // 中断号必须对应上
{
static u16 i; // 定义静态变量i
TH0 = 0XFC; // 给定时器赋初值,定时1ms
TL0 = 0X18;
i++;
if (i == 1000)
{
i = 0;
LED1 = !LED1;
}
}
void main()
{
time0_init(); // 定时器0中断配置
while (1)
{
}
}
编译和结果
按F7编译,无错误,生成.hex文件,使用pz-isp将hex文件下载到单片机
结果:指示灯D1间隔1秒闪烁
串口通信(中断)
内容
通过串口(UART)实现与PC机对话,51单片机的串口收到PC机发来的数据后原封不动的返回给PC机显示;
串口通信介绍
通信的基本概念
通信的方式可以分为多种,按照数据传送方式可分为串行通信和并行通信;
按照通信的数据同步方式,可分为异同通信和同步通信;
按照数据的传输方向又可分为单工、半双工和全双工通信;
串行通信与并行通信
1 串行通信
串行通信是指使用一条数据线,将数据一位一位地依次传输,每一位数据占据一个固定的时间长度;
其只需要少数几条线就可以在系统间交换信息,特别适用于计算机与计算机、计算机与外设之间的远距离通信;
串行通信的特点:传输线少,长距离传送时成本低,且可以利用电话网等现成的设备,但数据的传送控制比并行通信复杂;
2 并行通信
并行通信通常是将数据字节的各位用多条数据线同时进行传送,通常是8位、16位、32位等数据一起传输;
并行通信的特点:控制简单、传输速度快;由于传输线较多,长距离传送时成本高,且接收方的各位同时接收存在困难,抗干扰能力差;
异步通信与同步通信
1 异步通信
异步通信是指通信的发送与接收设备使用各自的时钟控制数据的发送和接收过程,为使双方的收发协调,要求发送和接收设备的时钟尽可能一致;
异步通信是以字符(构成的帧)为单位进行传输,字符与字符之间的间隙(时间间隔)是任意的,但每个字符中的各位是以固定的时间传送的,即字符之间不一定有“位间隔”的整数倍的关系,但同一字符内的各位之间的距离均为“ 位间隔”的整数倍;
异步通信的特点:不要求收发双方时钟的严格一致,实现容易,设备开销较小,但每个字符要附加2~3位用于起止位,各帧之间还有间隔,因此传输效率不高;
2 同步通信
同步通信时要建立发送方时钟对接收方时钟的直接控制,使双方达到完全同步;
同步通信时,传输数据的位之间的距离均为“位间隔”的整数倍,同时传送的字符间不留间隙,即保持位同步关系,也保持字符同步关系;
单工、半双工与全双工通信
1 单工通信
单工是指数据传输仅能沿一个方向,不能实现反向传输;
2 半双工通信
半双工是指数据传输可以沿两个方向,但需要分时进行;
3 全双工通信
全双工是指数据可以同时进行双向传输;
通信速率
衡量通信性能的一个非常重要的参数就是通信速率,通常以比特率(Bitrate)来表示;
比特率是每秒钟传输二进制代码的位数,单位是:位/秒(bps);
如每秒钟传送240个字符,而每个字符格式包含10位(1个起始位、1个停止位、8个数据位),这时的比特率为:10位×240 个/秒 = 2400bps
还有一个“波特率”的概念,它表示每秒钟传输了多少个码元;
码元是通信信号调制的概念,通信中常用时间间隔相同的符号来表示一个二进制数字,这样的信号称为码元;
如常见的通信传输中,用0V表示数字0,5V表示数字1,那么一个码元可以表示两种状态0和1,所以一个码元等于一个二进制比特位,此时波特率的大小与比特率一致;
如果在通信传输中,有0V、2V、4V以及6V分别表示二进制数00、01、10、11,那么每个码元可以表示四种状态,即两个二进制比特位,所以码元数是二进制比特位数的一半,这个时候的波特率为比特率的一半;
由于很多常见的通信中一个码元都是表示两种状态,所以我们常常直接以波特率来表示比特率;
串口简介
串口通信(Serial Communication),是指外设和计算机间通过数据信号线、地线等按位进行传输数据的一种通信方式,属于串行通信方式;
串口是一种接口标准,它规定了接口的电气标准,没有规定接口插件电缆以及使用的协议;
在串口通信中,通常我们只使用2、3、5三个管脚,即TXD、RXD、SGND,其他管脚功能暂时看不明白也没关系;
串口的输入输出对应单片机IO口,TXD(输出)对应的是P3.1管脚,RXD(输入)对应的是P3.0管脚;
串口相关寄存器
串口控制寄存器SCON
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
字节地址:98H | SM0 | SM1 | SM2 | REN | TB8 | RB8 | TI | RI | SCON |
- SM0和SM1为工作方式选择位
SM1SM0 | 工作方式 | 说明 | 波特率 |
---|---|---|---|
00 | 方式0 | 移位寄存器 | fsoc/12 |
01 | 方式1 | 10步异位收发器(8位数据) | 可变 |
10 | 方式2 | 11步异位收发器(8位数据) | fsoc/64或fsoc/32 |
11 | 方式3 | 11步异位收发器(8位数据) | 可变 |
- SM2:多机通信控制位,主要用于方式2和方式3;当SM2=1时可以利用收到的RB8来控制是否激活RI(RB8=0时不激活RI,收到的信息丢弃;RB8=1时收到的数据进入SBUF,并激活RI,进而在中断服务中将数据从SBUF读走);当SM2=0时,不论收到的RB8为0和1,均可以使收到的数据进入SBUF,并激活RI(即此时RB8不具有控制RI激活的功能);通过控制SM2,可以实现多机通信;
- REN:允许串行接收位;由软件置REN=1,则启动串行口接收数据;若软件置REN=0,则禁止接收;
- TB8:在方式2或方式3中,是发送数据的第9位,可以用软件规定其作用;可以用作数据的奇偶校验位,或在多机通信中,作为地址帧/数据帧的标志位;在方式0和方式1中,该位未用到;
- RB8:在方式2或方式3中,是接收到数据的第9位,作为奇偶校验位或地址帧/数据帧的标志位;在方式1时,若SM2=0,则RB8是接收到的停止位;
- TI:发送中断标志位;在方式0时,当串行发送第8位数据结束时,或在其它方式,串行发送停止位的开始时,由内部硬件使TI置1,向CPU发中断申请;在中断服务程序中,必须用软件将其清0,取消此中断申请;
- RI:接收中断标志位;在方式0时,当串行接收第8位数据结束时,或在其它方式,串行接收停止位的中间时,由内部硬件使RI置1,向CPU发中断申请;也必须在中断服务程序中,用软件将其清0,取消此中断申请;
电源控制寄存器PCON
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
字节地址:97H | SMOD | PCON |
SMOD:波特率倍增位;在串口方式1、方式2、方式3时,波特率与SMOD有关,当SMOD=1时,波特率提高一倍,复位时,SMOD=0;
串口工作方式
方式0
方式0时,串行口为同步移位寄存器的输入输出方式,主要用于扩展并行输入或输出口;
数据由RXD(P3.0)引脚输入或输出,同步移位脉冲由TXD(P3.1)引脚输出;
发送和接收均为8位数据,低位在先,高位在后;
波特率固定为fosc/12;
对应的输入输出时序图如下所示:
方式0输入
方式0输出
方式1
方式1是10位数据的异步通信口;
TXD为数据发送引脚,RXD为数据接收引脚,传送一帧数据的格式如下所示:(其中1位起始位,8位数据位,1位停止位)
对应的输入输出时序图如下所示:
方式1输入
方式1输出
置REN为1时,接收器以所选择波特率的16倍速率采样RXD引脚电平,检测到RXD引脚输入电平发生负跳变时,则说明起始位有效,将其移入输入移位寄存器,并开始接收这一帧信息的其余位;
接收过程中,数据从输入移位寄存器右边移入,起始位移至输入移位寄存器最左边时,控制电路进行最后一次移位;
当RI=0,且SM2=0(或接收到的停止位为1)时,将接收到的9位数据的前8位数据装入接收SBUF,第9位(停止位)进入RB8,并置RI=1,向CPU请求中断;
方式2和方式3
方式2或方式3时为11位数据的异步通信口;
TXD为数据发送引脚,RXD为数据接收引脚;
其数据格式如下所示:
对应的输入输出时序图如下所示:
方式2、方式3输入
接收时,数据从右边移入输入移位寄存器,在起始位0移到最左边时,控制电路进行最后一次移位;
当RI=0,且SM2=0(或接收到的第9位数据为1)时,接收到的数据装入接收缓冲器SBUF和RB8(接收数据的第9位),置RI=1,向CPU请求中断;如果条件不满足,则数据丢失,且不置位RI,继续搜索RXD引脚的负跳变;
方式2、方式3输出
发送开始时,先把起始位0输出到TXD引脚,然后发送移位寄存器的输出位(D0)到TXD引脚;每一个移位脉冲都使输出移位寄存器的各位右移一位,并由TXD引脚输出;第一次移位时,停止位“1”移入输出移位寄存器的第9位上,以后每次移位,左边都移入0;当停止位移至输出位时,左边其余位全为0,检测电路检测到这一条件时,使控制电路进行最后一次移位,并置TI=1,向CPU请求中断;
计算波特率
不同方式下波特率的计算公式:
方式0的波特率 = fosc/12
方式1的波特率 =(2SMOD/32)·(T1溢出率)
方式2的波特率 =(2SMOD/64)·fosc
方式3的波特率 =(2SMOD/32)·(T1溢出率)
其中,T1溢出率 = fosc/{12*[256 -(TH1)]}
(fosc:时钟频率)
建议使用波特率计算软件计算
串口配置
- 确定T1的工作方式(TMOD寄存器);
- 确定串口工作方式(SCON寄存器);
- 计算T1的初值(设定波特率),装载TH1、TL1;
- 启动T1(TCON中的TR1位);
- 如果使用中断,需开启串口中断控制位(IE寄存器)。
例如,设置串口为工作方式1、波特率为9600、波特率加倍、使用中断:
void uart_init(u8 baud)
{
TMOD |= 0X20; // 设置计数器工作方式2
SCON = 0X50; // 设置为工作方式1
PCON = 0X80; // 波特率加倍
TH1 = baud; // 计数器初始值设置
TL1 = baud;
ES = 1; // 打开接收中断
EA = 1; // 打开总中断
TR1 = 1; // 打开计数器
}
在主函数中调用该函数并传入OXFA值即可,如下:
uart_init(0XFA); // 波特率为 9600
原理图
结构图
线路图
由结构图可知,串口通过RXD发送数据,数据进入SBUF,然后由TXD接收SBUF的数据;
由线路图可知,开发板通过CH340G芯片把51单片机的串口(RTX-U、TDX-U)与PC机的USB口(D+、D-)进行连接;
同时CH340G芯片并不直接连接单片机的串口,而是通过连接在p5端子上,这样方便通过后续使用串口或CH340G芯片做其他实验;
思路
配置串口中断,根据对应方式的数据格式和时序编写中断函数,要求能够存储(可以使用变量保存)接收到的数据(来自pc机),并发送数据;
编码
main.c
/*
* @Description: 通过串口助手发送数据给单片机,单片机原封不动转发给串口助手显示
*/
#include "reg52.h"
typedef unsigned int u16; // 对系统默认数据类型进行重定义
typedef unsigned char u8;
/**
* @description: 延时函数(循环一次大约10us)
* @param {u16} ten_us
* @return {*}
*/
void delay_10us(u16 ten_us)
{
while (ten_us--)
;
}
/**
* @description: 延时函数(循环一次大约1ms)
* @param {u16} ms
* @return {*}
*/
void delay_ms(u16 ms)
{
u16 i, j;
for (i = ms; i > 0; i--)
for (j = 110; j > 0; j--)
;
}
/**
* @description: 串口通信中断配置函数,通过设置TH和TL即可确定定时时间
* @param {u8} baud // 波特率对应的TH、TL值
* @return {*}
*/
void uart_init(u8 baud)
{
TMOD |= 0X20; // 设置计数器工作方式2
SCON = 0X50; // 设置为工作方式1
PCON = 0X80; // 波特率加倍
TH1 = baud; // 计数器初始值设置
TL1 = baud;
ES = 1; // 打开接收中断
EA = 1; // 打开总中断
TR1 = 1; // 打开计数器
}
/**
* @description: 串口通信中断函数
* @return {*}
*/
void uart() interrupt 4 // 中断号必须对应上
{
u8 rec_data; // 定义变量存储接收到的数据
RI = 0; // 清除接收中断标志位
rec_data = SBUF;
SBUF = rec_data; // 将接收到的数据放入到发送寄存器
while (!TI)
; // 等待发送数据完成
TI = 0; // 清除发送完成标志位
}
void main()
{
uart_init(0XFA); // 波特率为9600
while (1)
{
}
}
编译和结果
按F7编译,无错误,生成.hex文件,使用pz-isp将hex文件下载到单片机
(注意要使用黄色跳线帽将P5端子的1、2短接,3、4短接)
结果:使用串口调试助手,发送数据给单片机,单片机能把数据发送回来;