AT89C51一共有5个中断源
(1):外部中断0,外部中断请求信号由引脚输入,低电平或下降沿有效,中断请求标志位IE0。
(2):外部中断1,外部中断请求信号由引脚输入,低电平或下降沿有效,中断请求标志位IE1。
(3)T0:定时/计数溢出时发出中断请求信号,中断请求标志位TF0(也就是之前说的溢出标志位)。
(4)T1:定时/计数溢出时发出中断请求信号,中断请求标志位TF1(也就是之前说的溢出标志位)。
(5)串行口中断:发出或接收1字节数据以后发出中断请求信号,中断请求标志位发送时为TI,接收时为RI。
控制中断的特殊功能寄存器包括:
(1)定时/计数控制寄存器TCON,
中断函数的格式:函数返回值类型 函数名(形式参数表) interrupt n using m
编写AT89C51单片机中断程序时,应遵循以下规则:
(1)中断函数没有返回值,因此将中断函数类型定义为void类型。
(2)中断函数不包含任何形式参数,形式参数表为空。
(3)在任何情况下都不能直接调用中断函数,否则会产生编译错误。
(4)如果在中断函数中再调用其他函数,则被调用的函数所使用的寄存器区必须与中断函数使用的寄存器区不同。
n是中断号,对应51单片机从外部中断0到串行口的5个中断向量,n的取值为0~4,编译器从8 × n + 3地址处产生中断向量
中断源对应的中断号,中断向量,和中中断优先级默认顺序如下
AT89C51单片机在内部RAM中可以使用4个工作寄存器区,每个工作区包括8个工作寄存器(R0~R7)
C51拓展了一个关键字using,using后面的m用来选择AT89C51的4个不同的工作寄存器区。
using是一个选项,如果不选用该选项,中断函数中所用工作寄存器的内容将会保持到堆栈中。
关键字using对函数目标代码的影响如下:在中断函数的入口处将当前工作寄存器区的内容保护到堆栈中,函数返回之前将被保护的寄存器区的内容从堆栈中恢复。使用关键字using在函数中确定一个工作寄存器区时必须十分小心,要保证任何工作寄存器区的切换都只在指定的控制区域中发生,否则将产生不正确的函数结果;
不正确的结果举例:如果一个低优先级中断正在被执行,这个中断使用的工作寄存器区是0,另外一个高优先级中断产生并且工作寄存区也0,这个时候就会产生数据紊乱。
低优先级中断可以被高优先级中断打断:一个低优先级中断正在执行,此时一个高优先级中断产生,那么会先执行高优先级中断,执行完高优先级中断再执行低优先级中断。
同级中断不能相互打断
低优先级中断不能打断高优先级中断
#include
sbit P1_0 = P1^0;
int main()
{
IT1=1;//设置中断触发方式,下降沿触发
EX1=1; //开启开外部中断1
EA=1;//开启总中断
while(1);
}
void int_1() interrupt 2 //外部中断函数1,中断号为2,使用工作寄存器区为0
{
P1_0=!P1_0;
}
定时器/计数器 | 位数 | 高8位 | 低8位 | 工作模式 | 定时模式的计数方式 | 计数模式的计数方式 |
---|---|---|---|---|---|---|
T0 | 16位 | TH0 | TL0 | 定时和计数 | 对片内机器周期进行计数 | 对片外加在T0(P3.4)引脚上的外部脉冲计数 |
T1 | 16位 | TH1 | TL1 | 定时和计数 | 对片内机器周期进行计数 | 对片外加在T1(P3.5)引脚上的外部脉冲计数 |
注意:T0 和 T1 都属于加 1 计数,每计一个脉冲,计数器加 1。由于单片机时钟频率固定,因此可由计数值得到精确的时间。 |
---|
定时/计数方式寄存器TMOD,字节地址89H,不可位寻址(不能单独一位赋值)。详细操作见下表
定时/计数控制寄存器 TCON,字节地址 88H,可位寻址(可以单独一位赋值)。见下表
**定时计数控制寄存器**
定时器和计数器实际都是通过计数器来计数,定时器是对周期不变的脉冲计数(一般来自于系统时钟),由计数的个数和脉冲的周期可计算出时间,同时,通过一个给定的预期值(即比较值,对应预期的计数值,也就是预期时间),当计数值达到预期值时产生中断,这样就实现了定时,应用程序通过设置不同的预期值实现不同时长的定时。
计数器是对某一事件进行计数,这个事件每发生一次,计数值加/减1,而这个事件的产.生可能是没有规律的。也就是计数器的用途是对事件的发生次数进行计数,由计数值来反映事件产生的次数。
目的:使用定时器中断功能控制LED亮灭
提示:
12MHz 时钟频率下,256us 以下的定时,多采用方式 2;
256us~65536us 及以上的定时,多采用方式 1。
目的:产生周期为200us,占空比1:2的方波实现脉宽调制PWM
(1)设置TMOD寄存器规定工作模式:方式2,T0
(2)计算定时器的初值:
方式 2 下初值的计算公式为:
初值 X = 256 - 定时时长/机器周期。
因为51单片机每12个时钟周期为一个机器周期
机器周期的计算公式:机器周期=12*(1/时钟频率)
12MHz 时钟频率下,机器周期为 1us。
初值 X = 256-100/1=156。也可以转换成 16 进制,此时初值 X = 0x9C。
给 T0 赋初值,TH0=156,TL0=156。或者 TH0=0x9C,TL0=0x9C。计数由 TL0 完成,
TL0 计数过程中,TH0 保持 156 不变。当 TL0 从 156 计数到 256 时达到最大值溢出,此时TH0 将初值 156 重新赋给 TL0 开始下轮计数
(3)设置 IE 寄存器允许中断
(4)设置 TCON 寄存器启动或停止定时器 T0
#include
sbit P1_0 = P1^0;
int main()
{
TMOD=0x02; //定时器 T0 方式 2
TH0=156; //12MHz 时钟频率下,定时器初值
TL0=156;
EA=1; //允许总中断
ET0=1; //允许 T0 中断
TR0=1; //启动定时器 T0
while(1); //无限循环,等待定时溢出中断信号到来后跳转到中断函数执行
}
void T0_INT() interrupt 1
{
P1_0=!P1_0;//P1.0 取反,然后返回 while(1)等待下一轮定时溢出中断信号
}
只有方式2是自动装载初始值
方式1初始值的计算:初始值=65536-定时时长/机器周期
51单片机每12个时钟表示一个机器周期
#include
sbit P1_0 = P1^0;
int main()
{
TMOD=0x01; //定时器 T0 方式 1
TH0=0xFF; //填充12MHz 时钟频率下,定时器初值
TL0=0X9C;
EA=1; //允许总中断
ET0=1; //允许 T0 中断
TR0=1; //启动定时器 T0
while(1)
{
if(TF0==1)// 判断溢出标志位
{
TF0 = 0;//清除溢出标志位,实际上在中断响应后,溢出标志位会自动清0
TH0=0XFF; //再次填充12MHz 时钟频率下,定时器初值
TL0=0X9C;
}
}; //无限循环,等待定时溢出中断信号到来后跳转到中断函数执行
}
void T0_INT() interrupt 1
{
P1_0=!P1_0;//P1.0 取反,然后返回 while(1)等待下一轮定时溢出中断信号
}
占空比:就是输出的PWM中,高电平保持的时间与该PWM的时钟周期的时间之比,如一个PWM的频率是1000Hz,那么它的时钟周期就是1ms,就是1000us,如果高电平出现的时间是200us,那么低电平的时间肯定是800us,那么占空比就是200:1000,也就是说PWM的占空比就是1:5。
分辨率:也就是占空比最小能达到多少,如8位的PWM,理论的分辨率就是1:255(单斜率),16位的的PWM理论就是1:65535(单斜率)。
频率就是这样的:如16位的PWM,它的分辨率达到了1:65535,要达到这个分辨率,T/C就必须从0计数到65535才能达到,如果计数从0计到80之后又从0开始计到80…,那么它的分辨率最小就是1:80了,但是,它也快了,也就是说PWM的输出频率高了。
双斜率 / 单斜率:
假设一个PWM从0计数到80,之后又从0计数到80…这个就是单斜率
假设一个PWM从0计数到80,之后是从80计数到0…这个就是双斜率
可见,双斜率的计数时间多了一倍,所以输出的PWM频率就慢了一半,但是分辨率却是1:(80+80) =1:160,就是提高了一倍。
pwm 也就是模拟方式稳定输出,通过调节pwm的占空比(分辨率),和周期调节电压电流大小。
全双工异步通信通信数据线。
全双工:就是两个单片机之间串行数据可以同时双向传输
异步通信:就是收发双方使用各自的时钟控制数据收发过程,不需要同步时钟信号
串行口通信:将一个字节的8位数据低位在前,高位在后一位一位地串行接收或发送
RXD(P3.0):数据接收引脚
TXD(P3.1):数据发送引脚来实现
SCON:串行口控制寄存器,字节地址(98H),可位寻址
PCON:电源控制寄存器(87H),不可位寻址
SUBF:串行发送和接送共用的数据缓冲寄存器,字节地址为(99H),物理上独立,收发使用不同的读写指令来区分
波特率:传输速率
串行口可以产生接收和发送中断
串行口具有不同的工作方式
掉电保持模式(休眠模式):当单片机进入掉电模式时,外部晶振,CPU,定时器,串口全部停止工作,使单片机进入休眠的指令将会称为休眠前单片机执行的最后一条指令,进入休眠后,芯片中程序还位涉及到的数据存储及和特殊功能寄存器中的数据都会保持原值。直到外部中断或者硬件复位模式唤醒单片机。使用中断唤醒,程序会从来原来停止处继续往下执行,使用复位唤醒单片机程序将从头开始执行。
空闲模式:单片机进入空闲模式时,除CPU处于休眠状态外,其余硬件全部处于活动状态,芯片中程序未涉及到的数据存储器和特殊功能寄存器中的数据在空闲模式期间都将报纸原值。但假若定时器正在运行,那么计数器寄存器中的值还会增加,单片机在空闲模式下可由任一个中断或硬件复位唤醒,需要注意的是,使中断唤醒单片机时,程序从原来停止处继续向下执行,使用复位唤醒单片机程序将从头开始执行。进入空闲模式是为了降低功耗。
简单来说:
空闲模式,功耗降低一大半,掉电功耗几乎为0,都可通过中断唤醒。
空闲模式,代码不再执行,只有定时器工作,能通过定时器中断唤醒。
掉电模式,只能通过外部中断唤醒。
波特率:串行口每秒发送或接收数据的位数,收发双方必须保持一致否者会产生错误
工作方式由SCON(串口控制寄存器)的SM0和SM1定义;其中工作方式0和工作方式2,波特率是固定的,方式1和方式3,波特率是可变得,由定时器T1得溢出率,即T1每秒溢出的次数来确定。
串行口的工作方式0位同步移位寄存器输入输入方式。此方式并不用于串行数据通信,而是用于外界移位寄存器,用来拓展并行I/O口
方式0以8位数据为1帧,没有起始位和停止位,先接收或发送最低为,其帧格式如下
方式1以0为数据为1帧,包含一个起始位(0),8个数据位,1个停止位,先接收或发送最低位,方式1的帧格式如下图
方式1的波特率可变,一般由定时器T1方式2(8位自动重装初始值)来确定,波特率公式为:
波特率=2SMODX(T1的溢出率)/32
设定时器T1方式2的初始值为X,则有
T1的溢出率=时钟频率/(12X(256-X))
所以初始值=T1 的初值 X = 256-fosc×2SMOD / (12×波特率×32)
fosc:芯片时钟频率
如果时钟频率 fosc=12MHz,T1 的初值计算出来不会是整数,将导致波
特率有一定误差,要得到精确的波特率,时钟频率 fosc 一般选择 11.0592MHz
串口通信方式2为9位串行通信方式,可用于双机通信也可用于多机通信。
方式2以11位数据为1帧,包含1个起始位(0),8个数据位,1个奇偶校验位(1/0),1个停止位(1),先接收或发送最低位。方式2的帧格式如下图
方式2的波特率公式为:波特率=2SMODX芯片时钟频率/64
所以工作方式2的波特率只有2种,fosc/32或focs/64
串口的工作方式3为9位串行通信方式,可用于双机通信也可用于多机通信。
方式3的帧结构与方式2相同。
方式3的波特率可变,设置方法和方式1相同
(1)设置 SCON 寄存器规定工作方式
(2)设置 PCON 寄存器波特率倍增位
(3)设置 TMOD 寄存器规定工作方式
(4)计算定时器 T1 方式 2 下的初值。
(5)设置 TCON 寄存器启动或停止定时器 T1
代码U1_1 接收端
#include
void main(void)
{
SCON=0x50;//串行口通信方式 1,接收允许
PCON=0x00;//SMOD=0
TMOD=0x20;//定时器 T1 方式 2
TH1=0xF4; //波特率为 2400bit/s,时钟频率 11.0592MHz 的初值
TL1=0xF4;
TR1=1; //启动 T1 定时
EA=1; //允许总中断
ES=1; //允许串行口中断
while(1); //接收中 RI 为 0,直到接收完毕 RI 硬件置 1
}
void UART_INT() interrupt 4 using 1
{
RI=0; //软件清 0,开始下一轮接收
P0=SBUF; //接收到的数据由缓冲器送给 P0
}
U1发送端
#include
sbit P3_3=P3^3;
void INIT_UART(void) //串口初始化函数
{
SCON=0X50;//串口通信方式1,接收允许
PCON=0X00;//波特率增倍系数SMOD=0,
TMOD=0x20;//定时器 T1 方式 2
TH1=0XF4;//波特率=2400bit/s,初值=256-(时钟频率/(波特率/1*32)/12)=11.0592*1 000 000 /2400/32/12
TL1=0xF4;
TR1=1; //启动 T1 定时
}
int OFF_ON=0XFF;
int main()
{
INIT_UART();//串口初始化
EX1=1; //允许串行口中断
IT1=1; //下降沿触发
EA=1; //允许总中断
while(1);
}
void INT_2() interrupt 2 using 0
{
OFF_ON=~OFF_ON;
SBUF=OFF_ON; //将 0x55 送入缓冲器,低位到高位开始发送
while(TI==0);//发送中 TI 为 0,直到发送完毕 TI 硬件置 1
TI=0; ///软件清 0,开始下一轮发送
}
实验现象: 按一下亮,再按一下小灯关