一、实验目的
1、了解读取和清零定时器标志位的方法。
2、了解定时器中断的方法。
3、了解定时器初始化设置的方法。
二、实验内容
1、完成读取定时器溢出标志位来控制流水灯
2、完成定时器中断服务函数控制流水灯
三、实验原理
只用一个定时器:
四、实验电路与程序
1、软件实验一:读取定时器溢出标志位来控制流水灯。
1)实验要求:读取定时器溢出标志位并计数,每过一秒钟流水灯移位一位。
2)实验目的:1. 掌握定时器初始化设置方法;2. 掌握读取和清零定时器溢出标志位TF的方法。
3)实验说明:通过本实验,可以了解单片机定时器初始化设置方法,掌握读取和清零定时器溢出标志位TF的方法,同时也可以了解单片机编程,调试方法。
4)、程序框图
2.定时时间由T0和T1级联提供
5)、代码
1.定时时间由定时器0或定时器2提供
main.c
- #include <reg52.h>
- #include <key.h>
- #include <interrupt.h>
- int task_flag;
- int timer_flag;
- int timer_flag_2;
- void main()
- {
- Config_EXTI();//初始化外部中断
- Config_Timer();//初始化定时器
- P1=0xFE;//初始化GPIO
- while(1)//任务调度器
- {
- if(timer_flag)//每1000ms执行一次任务
- {
- timer_flag=0;
- switch (task_flag)//判断执行哪个任务
- {
- }
- }
- }
- }
key.h
- #ifndef _KEY_H
- #define _KEY_H
- #include <reg52.h>
- unsigned char key_scan(void);
- void flow_forward(void);
- void flow_back(void);
- #endif
key.c
- #include <key.h>
- void flow_forward(void)
- {
- P1=(P1<<1)|(P1>>7);//循环左移1位
- }
- void flow_back(void)
- {
- P1=(P1>>1)|(P1<<7);//循环右移1位
- }
interrupt.h
- #ifndef _INTERRUPT_H
- #define _INTERRUPT_H
- #include <reg52.h>
- #include <key.h>
- void delay(unsigned int z);
- void Config_EXTI(void);
- void Config_Timer(void);
- extern int task_flag;
- extern int timer_flag;
- extern int timer_flag_2;
- #endif
interrupt.c
- #include <interrupt.h>
- void delay(unsigned int z)//延时ms
- {
- unsigned int x,y;
- for(x = z; x > 0; x–)
- for(y = 114; y > 0 ; y–);
- }
- void Config_Timer(void)
- {
- }
- void Config_EXTI(void)
- {
- EA=1;//开中断总允许
- IT0=1;//下降沿触发外部中断0
- EX0=1;//开外部中断0
- IT1=1;//下降沿触发外部中断1
- EX1=1;//开外部中断1
- }
- void EXT0_Handle() interrupt 0
- {
- delay(10);
- task_flag=1;//开启正向流水灯任务
- }
- void EXT1_Handle() interrupt 2
- {
- delay(10);
- task_flag=2;//开启反向流水灯任务
- }
- void T0_Handle() interrupt 1
- {
- //重装初值
- TL0=(65536-50000)%256;//低位
- if(num<20)//每1000ms把timer_flag置位
- {
- num++;
- }
- else
- {
- num=0;
- timer_flag=~timer_flag;
- }
- }
- void T1_Handle() interrupt 3
- {
- TL1=(65536-50000)%256;//低位
- if(num<20)//每1000ms把timer_flag置位
- {
- num++;
- }
- else
- {
- num=0;
- timer_flag_2=~timer_flag_2;
- }
- }
2.定时时间由T0和T1级联提供
main.c
- #include <reg52.h>
- #include <key.h>
- #include <interrupt.h>
- int task_flag;
- int timer_flag;
- int timer_flag_2;
- void main()
- {
- Config_EXTI();//初始化外部中断
- Config_Timer();//初始化定时器
- P1=0xFE;//初始化GPIO
- while(1)//任务调度器
- {
- if(timer_flag)//每1000ms执行一次任务
- {
- timer_flag=0;
- switch (task_flag)//判断执行哪个任务
- {
- }
- }
- }
- }
interrupt.h
- #ifndef _INTERRUPT_H
- #define _INTERRUPT_H
- #include <reg52.h>
- #include <key.h>
- void delay(unsigned int z);
- void Config_EXTI(void);
- void Config_Timer(void);
- extern int task_flag;
- extern int timer_flag;
- extern int timer_flag_2;
- sbit P2_0=P2^0;
- #endif
interrupt.c
- #include <interrupt.h>
- void delay(unsigned int z)//延时ms
- {
- unsigned int x,y;
- for(x = z; x > 0; x–)
- for(y = 114; y > 0 ; y–);
- }
- void Config_Timer(void)
- {
- TMOD=0x61;//timer0方式1,timer1方式2
- TH1=256-10;//time1装初值20
- TH1=256-10;
- IP=0x05;
- EA=1;
- ET0=1;
- ET1=1;
- TR0=1;
- TR1=1;
- }
- void Config_EXTI(void)
- {
- EA=1;//开中断总允许
- IT0=1;//下降沿触发外部中断0
- EX0=1;//开外部中断0
- IT1=1;//下降沿触发外部中断1
- EX1=1;//开外部中断1
- }
- void EXT0_Handle() interrupt 0
- {
- delay(10);
- task_flag=1;//开启正向流水灯任务
- }
- void EXT1_Handle() interrupt 2
- {
- delay(10);
- task_flag=2;//开启反向流水灯任务
- }
- void T0_Handle() interrupt 1
- {
- //重装初值
- TL0=(65536-50000)%256;//低位
- P2_0=!P2_0;//P2_0连T1输入
- T1=P2_0;//如果P2_0直接连T1,这句可以注释掉
- }
- void T1_Handle() interrupt 3
- {
- timer_flag=~timer_flag;
- }
key.h
- #ifndef _KEY_H
- #define _KEY_H
- #include <reg52.h>
- unsigned char key_scan(void);
- void flow_forward(void);
- void flow_back(void);
- #endif
key.c
- #include <key.h>
- void flow_forward(void)
- {
- P1=(P1<<1)|(P1>>7);//循环左移1位
- }
- void flow_back(void)
- {
- P1=(P1>>1)|(P1<<7);//循环右移1位
- }
汇编程序: - ORG 0000H ;程序执行的起始地址
- LJMP Main ;跳转到main函数
- ORG 000BH ;定时器中断0起始地址
- LJMP 0100H ;定时器中断0服务子程序地址
- ORG 001BH ;定时器中断1起始地址
- LJMP 0200H ;定时器中断1服务子程序地址
- Main: ;主函数
- MOV P1, #0FEH ;P1口初始化
- MOV P2, #001H ;P2口初始化
- SETB EA ;开定时器中断
- SETB ET0
- SETB ET1
- SETB TR0
- SETB TR1
- MOV TMOD, #061H ;tim0方式1,tim1方式2
- MOV TH0, #03CH ;(65536-45872)/256
- MOV TL0, #0B0H ;12M晶振定时50ms
- MOV TH1, #0F6H ;
- MOV TL1, #0F6H ;自动加载值,每次计10次溢出
- LOOP: JMP LOOP ;while(1)死循环
- ORG 0100H ;定时器中断0服务子程序
- TIM0:
- MOV A, P2
- XRL A, #01H
- MOV P2, A ;通过P2^0给T1中断信号
- MOV TH0, #03CH ;(65536-45872)/256
- MOV TL0, #0B0H ;12M晶振定时50ms
- RETI ;中断返回
- ORG 0200H ;定时器中断1服务子程序
- TIM1:
- MOV A, P1
- RL A
- MOV P1, A ;P1移位一位,流水灯
- RETI
- END
五、实验总结
- 在键盘扫描程序中,卡的比较久时间的是点灯。后面发现不同的开发板,灯的位置不一样。第二个卡的比较久的地方是判断P3口时,没有考虑到高四位的情况,考虑进去之后,switch p3就正常了。
2.设计键盘外部中断的时候,考虑到如果把流水灯放在中断回调函数里面进行的话,会导致执行回调函数的时候,别的中断来了会很麻烦。于是改为在回调函数里面设置标志位,while(1)里面根据标志位来选择执行哪个点灯代码。但是流水灯需要延时,如果用跑空循环来作延时的话,有点像是阻塞型任务,别的中断来的时候,中断套中断,就很麻烦。于是就开了一个定时器,定时器中断里面设置一个flag,每1秒钟flag置位一下,然后去看看要不要切换任务,以及执行哪个任务。这样子就给单片机节省出大量的资源来了。 - 汇编代码比较遗憾的地方是没有加进定时器中断,delay用跑空循环实现。
4.keil在option里面的target应该设置时钟的频率,proteus也应该设置晶振频率,否则在proteus里面仿真的时候,定时器的实际定时时间可能会有出入。
5.proteus如果接的上拉电阻名字叫“pullup”,则与之相连的单片机io口会一直是高电平,导致流水灯流不起来(因为FF移位之后还是FF),因此要使用普通电阻。
6.keil汇编时,十六进制数如果是以字母开头的,要加0,例如0FEH,不然会报错。
来源:waiting不是违停
声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!