51单片机温习002
http://blog.****.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.****.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.****.net/dragon12345666/article/details/22315025#t7