http://blog.csdn.net/dragon12345666/article/details/22315025 对应程序例程文件地址
1、AT89S52的6个中断源
2个外部中断:INT0 (对应引脚 P3^2) , INT1 (对应引脚 P3^3)
3个片内定时器/计数器中断:T0 (P3^4) , T1(P3^5) [TF0,TF1,(TF2)]
1个串行口中断:TI/RI
2、AT89S52外部中断:INT0 , INT1
配置1:定时器/中断控制寄存器 TCON (88H)
TCON :
TF1 | TR1 | TF0 | TR0 | IE1 | IT1 | IE0 | IT0 |
TF1 、TF0 : 定时器0、1溢出中断申请标志位
= 0 没溢出 , = 1 溢出-->申请中断-->进入中断后标志位自动清零
IR1 、 IR0 : 定时器运行的启动/停止控制位
= 0 停止运行 , = 1 启动定时器
---------------------------------------------------------------------------------------------------------
IE0 、IE1 : 外部中断的中断请求标志位
= 0 无外部中断申请 , =1有外部中断INT0 或 INT1中断申请
IT0 , IT1 : 外部中断请求的触发方式的设置位
= 0 低电平触发 , = 1 负跳变触发(下降沿触发)
---------------------------------------------------------------------------------------------------------
配置2:中断允许寄存器 IE (0A8H)
IE :
EA | — | ET2 | ES | ET1 | EX1 | ET0 | EX0 |
= 1 时,允许有中断产生 , = 0 时,禁止全部中断
各具体中断开关 :ET2 ES ET1 EX1 ET0 EX0
= 1 , 开对应中断 , =0时,禁止对应中断
配置3:中断优先级控制寄存器 IP (0B8H)
IP :
— | — | PT2 | PS | PT1 | PX1 | PT0 | PX0 |
= 0 设置为低优先级
不设置:默认为低优先级
外部中断INT0 、INT1初始化函数如下:
/******************************************************************************
* 名称:INT_0_1_Init()
* 功能:初始化外部中断 INT0 和 INT1
* 参数:无
* 返回:无
******************************************************************************/
void INT_0_1_Init(void)
{
/*------------------初始化中断-------------------*/
//1、配置定时器中断控制寄存器 TCON :
// TCON: TF1, TR1, TF0, TR0,| IE1, IT1, IE0, IT0
IT0 = 0 ; //低电平触发
IT1 = 0 ;
//2、配置中断允许寄存器 IE :
// IE: EA, - , ET2, ES, ET1, EX1, ET0, EX0
EA = 1 ;
EX0 = 1 ;
EX1 = 1 ;
//3、配置优先级控制寄存器 IP :(可不配置,默认按低优先级列表顺序排优先级)
// IP: - , - , PT2, PS, PT1, PX1, PT0, PX0
IP = 0x00 ;
}
中断服务程序如下:
/******************************************************************************
* 名称: void SMG_NUM_int1(void)
* 功能: 中断服务程序,实现数码管上的数值增加
* 修饰符 1:interrupt 2 :将该函数转化成中断INT1的中断服务程序
* 修饰符 2:using 1 :本函数内使用工作寄存器组1 ,最好省略不写,系统自动分配
******************************************************************************/
void SMG_NUM_int1(void) interrupt 2
{
//1、判断是否中断源有按键按下,对按键进行软件消抖 (单片机INT1引脚接了独立按键)
if(INT1_KEY == 0)
{
delay1ms(15) ;
if(INT1_KEY == 0)
{
//2、等待按键释放
while(!INT1_KEY)
{ ; }
//3、按键释放后,key_num 减 1
if(key_num > 0)
{
key_num -- ;
}
else
{
key_num = 0 ;
}
display_num(key_num);
}
}
}
完整外部中断程序:
参照 http://blog.csdn.net/dragon12345666/article/details/22315025 中的程序示例 1、2
3、AT89S52的定时器和计数器
注意:1、16位计数器,计数的最大范围:0 ----> 65536 ( 2 的 16 次方 )
2、定时器是通过对机器周期脉冲的计数来实现定时的。
机器频率 = 晶振频率 f晶振/12 ; (12分频)
机器周期 = (1 / f晶振)* 12 ;
如:12M的晶振,一个机器周期就是1us 。
4、AT89S52的3个定时器 T0 T1 T2 ,2中模式:查询模式、中断模式 , 4种工作方式。
定时器方式寄存器 TMOD (89H)
TMOD : (T1----T0)
GATE | C/T~ | M1 | M0 | GATE | C/T~ | M1 | M0 |
M1 ,M0
= 0 0 工作方式0:13位定时器(主要和老单片机8048、8748等兼容);
= 0 1 工作方式1:16位定时器;
= 1 0 工作方式2: 可自动重装的8位定时器;
= 1 1 工作方式3: 将T0分为2个8位的定时器,但此时T1不能工作。
------------------------------------------------------------------------------------------------------
C/T~ : 定时器/计数器 选择位 (参照下图分析)
= 1 : 外部事件计数器 (对T0/T1对应管脚的负脉冲进行计数)
= 0 : 片时钟定时器 (对机器周期脉冲计数来实现定时)
------------------------------------------------------------------------------------------------------
GATE : 门控置位 (参照下图分析)
= 0 : Timer的启动/停止由软件对 TR0/TR1 写 1 或 0 来控制
= 1 : Timer的启动/停止由外部中断INTx 和 软件对TRx写 1 或 0 共同控制
(INTx 和 TRx 同时为1时,Tx运行,否则不运行)
------------------------------------------------------------------------------------------------------
定时器T0 -- 查询模式 -- 工作方式1 初始化程序段如下:
/******************************************************************************
* 名称:ScanMode_T0_Init()
* 功能:定时器T0 查询模式 工作方式1 , 定时50ms
* 参数:无
* 返回:无
******************************************************************************/
void ScanMode_T0_Init(void)
{
/*------------------初始化定时器T0 查询模式 工作方式1-------------------*/
//1、配置定时器方式寄存器 TMOD :
// TMOD: GATE, C/T~, M1, M0,| GATE, C/T~, M1, M0 (T1 | T0)
TMOD = 0x01 ; //0000 0010 :T0 定时器 , 工作方式1 16位定时器
//2、给定时器赋初值 :
TH0 = (65536 - 50000) / 256 ; //定时时长50,000us
TL0 = (65536 - 50000) % 256 ; //定时器初值为 65536 - 50000
//3、配置定时器中断控制寄存器 TCON :
// TCON: TF1, TR1, TF0, TR0,| IE1, IT1, IE0, IT0
TF0 = 0 ; //清零定时器T0溢出标志位
TR0 = 1 ; //运行定时器T0
}
定时器T0 -- 中断模式 -- 工作方式1 初始化程序段如下:
/******************************************************************************
* 名称:InterruptMode_T0_Init()
* 功能:定时器T0 中断模式 工作方式1 , 定时50ms
* 参数:无
* 返回:无
******************************************************************************/
void InterruptMode_T0_Init(void)
{
/*------------------初始化定时器T0 中断模式 工作方式1-------------------*/
//1、配置中断允许寄存器 IE :
// IE: EA, - , ET2, ES, ET1, EX1, ET0, EX0
EA = 1 ; //开总中断
ET0 = 1 ; //允许定时器ET0产生中断
//2、配置定时器方式寄存器 TMOD :
// TMOD: GATE, C/T~, M1, M0,| GATE, C/T~, M1, M0 (T1 | T0)
TMOD = 0x01 ; //0000 0010 :T0 定时器 , 工作方式1 16位定时器
//2、给定时器赋初值 :
TH0 = (65536 - 50000) / 256 ; //定时时长50,000us
TL0 = (65536 - 50000) % 256 ; //定时器初值为 65536 - 50000
//3、配置定时器(中断)控制寄存器 TCON :
// TCON: TF1, TR1, TF0, TR0,| IE1, IT1, IE0, IT0
TR0 = 1 ; //运行定时器T0
}
写定时器 T0 的中断服务程序
/******************************************************************************
* 名称: void LED_InterruptMode_T0(void)
* 功能: 定时器T0中断服务程序,实现P0口的8位LED按2Hz频率闪烁
* 修饰符 1:interrupt 1 :将该函数转化成定时器中断T0的中断服务程序
* 修饰符 2:using 1 :本函数内使用工作寄存器组 1 , 可省略不写,系统自动分配
******************************************************************************/
void LED_InterruptMode_T0(void) interrupt 1
{
int i ;
i++ ;
//定时器工作在中断方式,溢出申请中断,进入中断后溢出位自动清零(硬件实现)
TH0 = (65536 - 50000) / 256 ;
TL0 = (65536 - 50000) % 256 ;
if(i % 5 == 0)
{
P0 = ~P0 ;
i = 0 ;
}
}
定时器T0 --> 工作方式2 --> 查询模式 8位自动重装定时器 初始化函数
/******************************************************************************
* 名称:ScanMode_T0_Init()
* 功能:定时器T0 查询模式 工作方式1 , 定时50ms
* 参数:无
* 返回:无
******************************************************************************/
void ScanMode_T0_Init(void)
{
/*------------------初始化定时器T0 查询模式 工作方式1-------------------*/
//1、配置定时器方式寄存器 TMOD :
// TMOD: GATE, C/T~, M1, M0,| GATE, C/T~, M1, M0 (T1 | T0)
TMOD = 0x02 ; //0000 0010 :T0 定时器 , 工作方式2 8位自动重装定时器
//2、给定时器赋初值 :
TL0 = 256 - 200 ; //定时时长200us
TH0 = TL0 ;
//3、配置定时器中断控制寄存器 TCON :
// TCON: TF1, TR1, TF0, TR0,| IE1, IT1, IE0, IT0
TF0 = 0 ; //清零定时器T0溢出标志位
TR0 = 1 ; //运行定时器T0
}
定时器T0 --> 工作方式2 --> 中断模式 8位自动重装定时器 初始化函数
/******************************************************************************
* 名称:ScanMode_T0_Init()
* 功能:定时器T0 中断模式 工作方式2 , 定时0.5ms
* 参数:无
* 返回:无
******************************************************************************/
void InterruptMode_T0_Init(void)
{
/*------------------初始化定时器T0 中断模式 工作方式1-------------------*/
//1、配置中断允许寄存器 IE :
// IE: EA, - , ET2, ES, ET1, EX1, ET0, EX0
EA = 1 ; //开总中断
ET0 = 1 ; //允许定时器ET0产生中断
//2、配置定时器方式寄存器 TMOD :
// TMOD: GATE, C/T~, M1, M0,| GATE, C/T~, M1, M0 (T1 | T0)
TMOD = 0x02 ; //0000 0010 :T0 定时器 , 工作方式2 8位自动重装定时器
//2、给定时器赋初值 :
TL0 = 256 - 200 ; //定时时长200us
TH0 = TL0 ;
//3、配置定时器(中断)控制寄存器 TCON :
// TCON: TF1, TR1, TF0, TR0,| IE1, IT1, IE0, IT0
TR0 = 1 ; //运行定时器T0
}
5、while(1) { ... } 和 while(变量 != 某值) { ... } 的区别。(昨晚犯了一个很二的错误)
单片机、嵌入式的程序必须都是嵌套在一个死循环里执行,因为不可能让芯片没执行一次程序都再给芯片重烧一次程序吧。
while(1) { ... } 是一个死循环;但是 while(变量 != 某值) { ... } 却不一定是一个死循环,因为当“变量 == 某值 ”的时候,while的判断条件将不再满足,跳出循环。
所以在基于芯片写程序时,如果while的判断条件是一个表达式,则最好不要用其作为程序执行的死循环的外壳,解决办法:在其外侧再加一个while(1) { ... } 这样的死循环。
比如我想让AT89S52单片机的P0端口的8个LED灯按照1Hz的频率闪烁,下面一段程序却无法实现:
/******************************************************************************
* 说明:AT89S52芯片,12M晶振
* 初始化定时器T0 --> 查询模式 --> 工作方式1
* 给定时器赋初值前,必须清零溢出标志位 ,
* 通过定义变量 和 定时器 配合增加延时时长
* 本程序:通过定时器工作在查询模式,实现P0口LED灯的状态每隔0.5s翻转一次
******************************************************************************/
#include<reg52.h>
/******************************************************************************
* 名称:ScanMode_T0_Init()
* 功能:定时器T0 查询模式 工作方式1 , 定时50ms
* 参数:无
* 返回:无
******************************************************************************/
void ScanMode_T0_Init(void)
{
/*------------------初始化定时器T0 查询模式 工作方式1-------------------*/
//1、配置定时器方式寄存器 TMOD :
// TMOD: GATE, C/T~, M1, M0,| GATE, C/T~, M1, M0 (T1 | T0)
TMOD = 0x01 ; //0000 0010 :T0 定时器 , 工作方式1 16位定时器
//2、给定时器赋初值 :
TH0 = (65536 - 50000) / 256 ; //定时时长50,000us
TL0 = (65536 - 50000) % 256 ; //定时器初值为 65536 - 50000
//3、配置定时器中断控制寄存器 TCON :
// TCON: TF1, TR1, TF0, TR0,| IE1, IT1, IE0, IT0
TF0 = 0 ; //清零定时器T0溢出标志位
TR0 = 1 ; //运行定时器T0
}
void main(void)
{
int i = 0 ;
ScanMode_T0_Init() ; //初始化定时器T0 , 查询模式,工作方式1,16位定时器
P0 = 0x00 ; //点亮P0口的LED灯
while(TF0 == 1)
{
TF0 = 0 ; //定时器,查询模式,定时器赋初值前必须清零溢出标志位
TH0 = (65536 - 50000) / 256 ;
TL0 = (65536 - 50000) % 256 ;
TR0 = 1; //赋初值后,运行定时器
i++ ;
if(i%10 == 0) //每个10*50,000us = 0.5s , P0口状态翻转一次
{
P0 = ~P0 ;
}
}
}
原因:只有当定时器T0溢出时,while(TF0 == 1)的判断条件才满足,执行循环内程序,否则main()函数内部的程序顺序执行一遍,整个工程的程序执行结束,不再循环!
void main(void)
{
int i = 0 ;
ScanMode_T0_Init() ; //初始化定时器T0 , 查询模式,工作方式1,16位定时器
P0 = 0x00 ; //点亮P0口的LED灯
while(1)
{
while(TF0 == 1)
{
TF0 = 0 ; //定时器,查询模式,定时器赋初值前必须清零溢出标志位
TH0 = (65536 - 50000) / 256 ;
TL0 = (65536 - 50000) % 256 ;
TR0 = 1; //赋初值后,运行定时器
i++ ;
if(i%10 == 0) //每个10*50,000us = 0.5s , P0口状态翻转一次
{
P0 = ~P0 ;
}
}
}
}
定时器T0 --> 工作方式2 --> 中断模式 8位自动重装定时器 初始化函数
6、有关中断优先级的一个问题:
//5、配置优先级控制寄存器 IP :(可不配置,默认按低优先级列表顺序排优先级)
// IP: - , - , PT2, PS, PT1, PX1, PT0, PX0
IP = 0x04 ;
完整程序参照:
http://blog.csdn.net/dragon12345666/article/details/22315025#t7