时间路行者之 51单片机(STC89C52RC)第七章 定时器以及按键控制LED流水灯模式&定时器时钟 您所在的位置:网站首页 电脑上的定时器在哪 时间路行者之 51单片机(STC89C52RC)第七章 定时器以及按键控制LED流水灯模式&定时器时钟

时间路行者之 51单片机(STC89C52RC)第七章 定时器以及按键控制LED流水灯模式&定时器时钟

2024-07-09 23:18| 来源: 网络整理| 查看: 265

定时器

定时器介绍:

        51单片机的定时器属于单片机的内部资源,其电路的连接和运转均在单片机内部完成

定时器作用:         (1)用于计时系统,可实现软件计时,或者使程序每隔一固定时间完成一项操作         (2) 替代长时间的Delay,提高CPU的运行效率和处理速度

STC89C52定时器资源

定时器个数:

        3个 (TO、T1、T2),TO和T1与传统的51单片机兼容T2是此型号单片机增加的资源不同的型号注意:

        定时器的资源和单片机的型号是关联在一起的,可能会有不同的定时器个数和操作方式,但一般来说,TO和T1的操作方式是所有51单片机所共有的

定时器框图

        定时器在单片机内部就像一个小闹钟一样,根据时钟的输出信号每隔“一秒”,计数单元的数值就增加一,当计数单元数值增加到“设定的闹钟提醒时间”时,计数单元就会向中断系统发出中断申请产生“响铃提醒”,使程序跳转到中断服务函数中执行

定时器工作模式

        STC89C52的TO和T1均有四种工作模式

        模式0: 13位定时器/计数器         模式1: 16位定时器/计数器 (常用)         模式2: 8位自动重装模式         模式3: 两个8位计数器         工作模式1框图

        SYSclk:系统时钟,即晶振周期,本开发板上的晶振为12MHZ

这是定时器

计数器

 在单片机上看是T0处,用于接外界的计时器

中断系统

        P2口作为地址复用,P3口有第二功能引脚(这里面就有中断的相关引脚)

        IAP15中断源有21个

IAP里面的5个中断源

        分时的操作(我在服务你的同时被中断后可以服务别人,这样就实现了服务对象的多样化);

        实现实时处理(可以在编译的时候,中断的子程序);

        进行故障的处理(例如:看门狗程序,进入死循环后,可以跳出并进行复位)

(下面有可能是考点)         中断系统是为使CPU具有对外界紧急事件的实时处理能力而设置的.

        当中央处理机CPU正在处理某件事的时候外界发生了紧急事件请求,要求CPU暂停当前的工作,转而去处理这个紧急事件,处理完以后,再回到原来被中断的地方,继续原来的工作,这样的过程称为中断。实现这种功能的部件称为中断系统,请示CPU中断的请求源称为中断源。

        微型机的中断系统一般允许多个中断源,当几个中断源同时向CPU请求中断,要求为它服务的时候,这就存在CPU优先响应哪一个中断源请求的问题。通常根据中断源的轻重缓急排队,优先处理最紧急事件的中断请求源,即规定每一个中断源有一个优先级别。CPU总是先响应优先级别最高的中断请求。

        当CPU正在处理一个中断源请求的时候(执行相应的中断服务程序) ,发生了另外一个优先级比它还高的中断源请求。如果CPU能够暂停对原来中断源的服务程序,转而去处理优先级更高的中断请求源,处理完以后,再回到原低级中断服务程序,这样的过程称为中断嵌套。这样的中断系统称为多级中断系统,没有中断嵌套功能的中断系统称为单级中断系统。

中断程序流程

 

 STC89C52中断资源

        中断源个数:8个(外部中断0、定时器0中断、外部中断1、定时器中断源个数:8个1中断、串口中断、外部中断2、外部中断3)

        中断优先级个数:4个         中断号:

        注意:中断的资源和单片机的型号是关联在一起的,不同的型号可能会有不同的中断资源,例如中断源个数不同、中断优先级个数不同等等

 定时器和中断系统

IAP15和89c52的中断系统图相同

IAP15的内部中断接口

 \overline{INT0}外部中断0(连P3.2):IT0触发方式(外部请求)

                                                        IT0=0 电平触发                 低电平触发

                                                        IT0=1 边沿触发分为         上升沿(低电平转高电平)

                                                                                               下降沿(高电平转低电平)                                                                                                                        

                                               IE0为中断标志位:是用来存储前面的触发源是否有效

                                                                               (如果触发成功IE0=1,否则的等于0)

                                               EX0 中断允许:外部中断0的中断允许

                                               EA为总的中断:集体关闭中断的允许,更加方便

                                               PX0优先级:当PX0=1的时候为高优先级

                                                                (其中里面还有自然优先级的概念)

TCON控制中断请求的控制寄存器

IE,IP也都同理

 T0是靠内部的定时器进行溢出来触发请求                                            

                                                

TF0是T0的中断标志位

TR0是控制定时器启动的

相关程序书写:

1、将定时器0工作在模式1中:

        想要让定时器0在模式1下工作,那么就需要将M0打开,所以M0的值为1,其余的都为0,所以得出二进制的数为0000 0001,

        GATE:为门控端,表示控制启动方、方式

GATE=0(为软启动)GATE=1(为硬启动)

        C/T:为0时,为定时器模式;为1的时候,为计数器模式

        M0和M1:用于设置定时器的工作方式

                00 01都是16位的计数器,方式00具有重复计数的功能,而00只能自己再写初值(写到中断函数中)

                10 为一个8位的计数器(也具有自动重复计数功能)最多计数256个数

                11 只有T0才有方式3,TL1和TH1就各自干各自的事情(这时候T1:可以继续计数,不能发送中断请求(被TH1借走了)了,这时候就辅助串口工作(给串口产生计时脉冲(波特率(是应该脉冲信号))))

        定时器结束后,就会向单片机发送中断请求(就会让TF0=1或TF1=1)

        中断撤销:是自动撤销(TF0=0或TF1=0)

不可位寻址:表示不能控制位

可位寻址:可以控制每一位

  TMOD=0;//选择定时器   TF=0;//选0是因为防止中断,(TF0为中断艺术标志位)   TR0=1;//TR0是判断定时器是否开启,如果想要让电机开始工作,就赋值为1。

计数器是8个为1位的,8个只能存256个

//控制中断器的开关 ET0=1;//打开中断的第一个开关 EA=1;//打开第二个开关 PT0=0;//打开第三个开关

中断函数

//闹钟响了以后要调到这个指函数中,起到中断的作用 //初始化后大概一ms后会发生中断 //后面跟的小尾巴就是中断子程序 void Timer0_Routine() interrupt 1 { }

这个中断系统可以用STC-ISP来进行配置(需要注意配置的时候选的频率等因素)

复制过来的代码中会少一个中断定时器(中断是是需要自己开的),开头的那个12T因为是新的版本,89C52的旧版是用不到的所以可以直接删掉

(太重要了,我听了3遍才知道的技巧)

中断系统的(完整代码)

中断子程序是不能够被调用的,

中断子程序有固定的地址入口

#include /******************************* 初始化 *******************************/ void Timer0_Init(){ // TMOD=0;//选择定时器//0000 0001 //(利用“与或”的方法) //利用下面这两句代码来配置上面的那一句代码,可以保证在配置第四位的时候不影响高四位 TMOD&=0xF0;//把TMOD低四位清零,高四位保持不变(“与”清零) TMOD|=0x01;//把TMOD低位置1,高四位保持不变(“或”置1)(任何数或零都为任何数) TF0=0;//选0是因为防止中断,(TF0为中断艺术标志位) TR0=1;//TR0是判断定时器是否开启,如果想要让定时器开始工作,就赋值为1。 TH0=64535/256;//表示高位 TL0=64535%256;//表示存下了低位(高位和地位,相当于用两个盒子把数装进去) /******上面可以用stc-isp生成,下面的中断还是需要自己打开(其中如果单片机不是12T的可以将生成的第一条删除)*********/ //控制中断器的开关 ET0=1;//打开中断的第一个开关 EA=1;//打开第二个开关 PT0=0;//打开第三个开关 } /******************************* 中断函数 *******************************/ //闹钟响了以后要调到这个指函数中,起到中断的作用 //初始化后大概一ms后会发生中断 //后面跟的小尾巴就是中断子程序 unsigned int T0Count;//定义了一个新的函数,用来尝试计时一分钟 void Timer0_Routine() interrupt 1 { //计数器是同步计数,起始值是64536计时才是1ms。 TH0=64535/256; TL0=64535%256;//为了防止溢出过后,初始值会发生变化,所以在这里需要重置一下 T0Count++;//来计中断的次数 if(T0Count>=1000){ T0Count=0;//将T0Count恢复原值 P2_0=~P2_0;//表示在执行Timer0_Init()的时候,主函数中虽然没有写Timer0_Routine()但还是会点亮灯,这就说明中断起到了作用 } } void main(){ Timer0_Init(); while(1){ } } static unsigned int T0Count;//定义静态变量,这样定义的话就不会丢失数字

流水灯

        用_crol_和_cror_来实现循环左移和循环右移

完整代码如下

//main.c #include #include "Timer0.h" #include "Key.h" #include unsigned char KeyNum,LEDMode;//LEDMode为流水灯模式 void main(){ P2=0xFE;//点亮最低位LED Timer0Init(); while(1){ KeyNum=Key();//给 KeyNumber赋值 if(KeyNum) { if(KeyNum==1) { LEDMode++; if(LEDMode>=2)LEDMode=0; } } } } ///******************************* //初始化 //*******************************/ //void Timer0_Init(){ TMOD=0;//选择定时器//0000 0001 // //(利用“与或”的方法) // //利用下面这两句代码来配置上面的那一句代码,可以保证在配置第四位的时候不影响高四位 // TMOD&=0xF0;//把TMOD低四位清零,高四位保持不变(“与”清零) // TMOD|=0x01;//把TMOD低位置1,高四位保持不变(“或”置1)(任何数或零都为任何数) // // TF0=0;//选0是因为防止中断,(TF0为中断艺术标志位) // TR0=1;//TR0是判断定时器是否开启,如果想要让定时器开始工作,就赋值为1。 // TH0=64535/256;//表示高位 // TL0=64535%256;//表示存下了低位(高位和地位,相当于用两个盒子把数装进去) // // /******上面可以用stc-isp生成,下面的中断还是需要自己打开(其中如果单片机不是12T的可以将生成的第一条删除)*********/ // 控制中断器的开关 // ET0=1;//打开中断的第一个开关 // EA=1;//打开第二个开关 // PT0=0;//打开第三个开关 // //} /******************************* 中断函数//一般都放在主函数中 *******************************/ //闹钟响了以后要调到这个指函数中,起到中断的作用 //初始化后大概一ms后会发生中断 //后面跟的小尾巴就是中断子程序 void Timer0_Routine() interrupt 1 { static unsigned int T0Count;//定义静态变量//定义了一个新的函数,用来尝试计时一分钟 //计数器是同步计数,起始值是64536计时才是1ms。 TH0=0x18; TL0=0xfc;//为了防止溢出过后,初始值会发生变化,所以在这里需要重置一下 T0Count++;//来计中断的次数 if(T0Count>=500){//每个500毫秒进来一次 T0Count=0;//将T0Count恢复原值 if(LEDMode==0){ P2=_crol_(P2,1); } if(LEDMode==1){ P2=_cror_(P2,1); } // P2_0=~P2_0;//表示在执行Timer0_Init()的时候,主函数中虽然没有写Timer0_Routine()但还是会点亮灯,这就说明中断起到了作用 } } //Timer0.c #include /** * @brief 定时器初始化,1毫秒@12.000MHz * @param 无 * @retval 无 */ //Timer0.c void Timer0Init(void) //1毫秒@12.000MHz { // AUXR &= 0x7F; //定时器时钟12T模式//这句话在旧版的89C52中是可以删去的 TMOD &= 0xF0; //设置定时器模式 TMOD |= 0x01; //设置定时器模式 TL0 = 0x18; //设置定时初值 TH0 = 0xFC; //设置定时初值 TF0 = 0; //清除TF0标志 TR0 = 1; //定时器0开始计时 ET0=1;//打开中断的第一个开关 EA=1;//打开第二个开关 PT0=0;//打开第三个开关 } /*定时器中断函数模板 void Timer0_Routine() interrupt 1 { static unsigned int T0Count;//定义静态变量//定义了一个新的函数,用来尝试计时一分钟 //计数器是同步计数,起始值是64536计时才是1ms。 TH0=64535/256; TL0=64535%256;//为了防止溢出过后,初始值会发生变化,所以在这里需要重置一下 T0Count++;//来计中断的次数 if(T0Count>=1000){ T0Count=0;//将T0Count恢复原值 P2_0=~P2_0;//表示在执行Timer0_Init()的时候,主函数中虽然没有写Timer0_Routine()但还是会点亮灯,这就说明中断起到了作用 } } */ //Timer0.h #ifndef __TIMER0_H__ #define __TIMER0_H__ void Timer0Init(void); //1毫秒@12.000MHz #endif //Key.c #include #include "Delay.h" /** * @brief 获取独立按键键码 * @param 无 * @retval 按下独立按键的键码,范围:0~4,无按键按下时值为0 */ unsigned char KeyNumber=0;//char的初值是不确定的 unsigned char Key(){ if(P3_1==0){Delay(20);while(P3_1);Delay(20);KeyNumber=1;} if(P3_0==0){Delay(20);while(P3_0);Delay(20);KeyNumber=2;} if(P3_2==0){Delay(20);while(P3_2);Delay(20);KeyNumber=3;} if(P3_3==0){Delay(20);while(P3_3);Delay(20);KeyNumber=4;} return KeyNumber; } //Key.h #ifndef __KEY_H__ #define __KEY_H__ unsigned char Key(); #endif

中断思路

1、触发中断:选择触发方式

                开总中断

                开对应的中断

                选择设置自然优先级、

2、定义中断子程序函数

                (中断关键词 interrupt 0~4)

3、主函数

        while(1);(之后等着子程序被触发)

换灯的亮的案例

时钟的代码 //main.c #include #include "Delay.h" #include "LCD1602.h" #include "Timer0.h" unsigned char Sec=55,Min=59,Hour=23;//定义一个变量秒 void main(){ LCD_Init();//LCD1602初始化 Timer0Init();//对时钟进行初始化 LCD_ShowString(1,1,"Clock:");//初始化显示 LCD_ShowString(2,1," : :"); while(1){ LCD_ShowNum(2,1,Hour,2); LCD_ShowNum(2,4,Min,2); LCD_ShowNum(2,7,Sec,2); } } void Timer0_Routine() interrupt 1 //在中断函数中不要执行过长的任务 { static unsigned int T0Count;//定义静态变量//定义了一个新的函数,用来尝试计时一分钟 //计数器是同步计数,起始值是64536计时才是1ms。 TL0 = 0x18; //设置定时初值 TH0 = 0xFC; // TH0=64535/256; // TL0=64535%256;//为了防止溢出过后,初始值会发生变化,所以在这里需要重置一下 T0Count++;//来计中断的次数 if(T0Count>=1000){ //每隔1s T0Count=0; Sec++; if(Sec>=60){ Sec=0; Min++; if(Min>=60){ Min=0; Hour++; if(Hour>=24){ Hour=0; } } } } } //LCE1602.h #ifndef __LCD1602_H__ #define __LCD1602_H__ //用户调用函数: void LCD_Init(); void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char); void LCD_ShowString(unsigned char Line,unsigned char Column,char *String); void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length); void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length); void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length); void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length); #endif //LCE1602.c #include //引脚配置: sbit LCD_RS=P2^6; sbit LCD_RW=P2^5; sbit LCD_EN=P2^7; #define LCD_DataPort P0 //函数定义: /** * @brief LCD1602延时函数,12MHz调用可延时1ms * @param 无 * @retval 无 */ void LCD_Delay() { unsigned char i, j; i = 2; j = 239; do { while (--j); } while (--i); } /** * @brief LCD1602写命令 * @param Command 要写入的命令 * @retval 无 */ void LCD_WriteCommand(unsigned char Command) { LCD_RS=0; LCD_RW=0; LCD_DataPort=Command; LCD_EN=1; LCD_Delay(); LCD_EN=0; LCD_Delay(); } /** * @brief LCD1602写数据 * @param Data 要写入的数据 * @retval 无 */ void LCD_WriteData(unsigned char Data) { LCD_RS=1; LCD_RW=0; LCD_DataPort=Data; LCD_EN=1; LCD_Delay(); LCD_EN=0; LCD_Delay(); } /** * @brief LCD1602设置光标位置 * @param Line 行位置,范围:1~2 * @param Column 列位置,范围:1~16 * @retval 无 */ void LCD_SetCursor(unsigned char Line,unsigned char Column) { if(Line==1) { LCD_WriteCommand(0x80|(Column-1)); } else if(Line==2) { LCD_WriteCommand(0x80|(Column-1+0x40)); } } /** * @brief LCD1602初始化函数 * @param 无 * @retval 无 */ void LCD_Init() { LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵 LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关 LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动 LCD_WriteCommand(0x01);//光标复位,清屏 } /** * @brief 在LCD1602指定位置上显示一个字符 * @param Line 行位置,范围:1~2 * @param Column 列位置,范围:1~16 * @param Char 要显示的字符 * @retval 无 */ void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char) { LCD_SetCursor(Line,Column); LCD_WriteData(Char); } /** * @brief 在LCD1602指定位置开始显示所给字符串 * @param Line 起始行位置,范围:1~2 * @param Column 起始列位置,范围:1~16 * @param String 要显示的字符串 * @retval 无 */ void LCD_ShowString(unsigned char Line,unsigned char Column,char *String) { unsigned char i; LCD_SetCursor(Line,Column); for(i=0;String[i]!='\0';i++) { LCD_WriteData(String[i]); } } /** * @brief 返回值=X的Y次方 */ int LCD_Pow(int X,int Y) { unsigned char i; int Result=1; for(i=0;i0;i--) { LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0'); } } /** * @brief 在LCD1602指定位置开始以有符号十进制显示所给数字 * @param Line 起始行位置,范围:1~2 * @param Column 起始列位置,范围:1~16 * @param Number 要显示的数字,范围:-32768~32767 * @param Length 要显示数字的长度,范围:1~5 * @retval 无 */ void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length) { unsigned char i; unsigned int Number1; LCD_SetCursor(Line,Column); if(Number>=0) { LCD_WriteData('+'); Number1=Number; } else { LCD_WriteData('-'); Number1=-Number; } for(i=Length;i>0;i--) { LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0'); } } /** * @brief 在LCD1602指定位置开始以十六进制显示所给数字 * @param Line 起始行位置,范围:1~2 * @param Column 起始列位置,范围:1~16 * @param Number 要显示的数字,范围:0~0xFFFF * @param Length 要显示数字的长度,范围:1~4 * @retval 无 */ void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length) { unsigned char i,SingleNumber; LCD_SetCursor(Line,Column); for(i=Length;i>0;i--) { SingleNumber=Number/LCD_Pow(16,i-1)%16; if(SingleNumber0;i--) { LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0'); } } //Timer0.c #include /** * @brief 定时器初始化,1毫秒@12.000MHz * @param 无 * @retval 无 */ void Timer0Init(void) //1毫秒@12.000MHz { // AUXR &= 0x7F; //定时器时钟12T模式//这句话在旧版的89C52中是可以删去的 TMOD &= 0xF0; //设置定时器模式 TMOD |= 0x01; //设置定时器模式 TL0 = 0x18; //设置定时初值 TH0 = 0xFC; //设置定时初值 TF0 = 0; //清除TF0标志 TR0 = 1; //定时器0开始计时 //上面的可以用ISP进行生成 //但下面的需要自己书写 ET0=1;//打开中断的第一个开关 EA=1;//打开第二个开关 PT0=0;//打开第三个开关 } /*定时器中断函数模板 void Timer0_Routine() interrupt 1 //在中断函数中不要执行过长的任务 { static unsigned int T0Count;//定义静态变量//定义了一个新的函数,用来尝试计时一分钟 //计数器是同步计数,起始值是64536计时才是1ms。 TL0 = 0x18; //设置定时初值 TH0 = 0xFC; // TH0=64535/256; // TL0=64535%256;//为了防止溢出过后,初始值会发生变化,所以在这里需要重置一下 T0Count++;//来计中断的次数 if(T0Count>=1000){ //每隔1s T0Count=0;//将T0Count恢复原值 P2_0=~P2_0;//表示在执行Timer0_Init()的时候,主函数中虽然没有写Timer0_Routine()但还是会点亮灯,这就说明中断起到了作用 } } */ //Timer0.h #ifndef __TIMER0_H__ #define __TIMER0_H__ void Timer0Init(void); //1毫秒@12.000MHz #endif


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有