实验二 外部按键输入
一、实验目的
1、了解单片机检测口方法
2、了解单片机外部中断原理
3、了解按键输入原理
二、实验内容
1、完成按键扫描控制流水灯
2、完成按键外部中断控制流水灯
三、实验原理
5)、代码
main.c
-
#include <reg52.h>
-
#include <key.h>
-
void main(void)
-
{
-
unsigned char key_val;
-
P1=0xFF;
-
while(1)
-
{
-
key_val=key_scan();//扫描键盘
-
light_led(key_val);//选择不同的点灯方式
-
}
-
}
key.h
-
#ifndef _KEY_H
-
#define _KEY_H
-
#include <reg52.h>
-
void delay(unsigned int z);
-
unsigned char key_scan(void);
-
void light_led(unsigned char mode);
-
void flow_forward(void);
-
void flow_back(void);
-
void light_all(void);
-
void dark_all(void);
-
#endif
key.c
-
#include <key.h>
-
unsigned char key_scan(void)
-
{
-
while(P3==0x0f)//等待按键按下
-
{
-
;
-
}
-
delay(10);//软件消抖
-
switch (P3)
-
{
-
case 0xFE: return 0;//判断是哪个按键按下
-
case 0xFD: return 1;
-
case 0xFB: return 2;
-
case 0xF7: return 3;
-
}
-
}
-
void flow_forward(void)
-
{
-
int i=0;
-
P1=0xFE;
-
for(i=0;i<8;i++)
-
{
-
delay(500);
-
P1=(P1<<1)|(P1>>7);//循环左移1位
-
}
-
}
-
void flow_back(void)
-
{
-
int i=0;
-
P1=0xFE;
-
for(i=0;i<8;i++)
-
{
-
delay(500);
-
P1=(P1>>1)|(P1<<7);//循环右移1位
-
}
-
}
-
void light_all(void)//点亮所有灯
-
{
-
P1=0;
-
}
-
void dark_all(void)//熄灭所有灯
-
{
-
P1=1;
-
}
-
void light_led(unsigned char mode)
-
{
-
switch (mode)
-
{
-
case 0:dark_all();//灯全灭
-
break;
-
case 1:light_all();//灯全亮
-
break;
-
case 2:flow_forward();//流水灯正流
-
break;
-
case 3:flow_back();//流水灯反流
-
}
-
}
2、软件实验二:按键外部中断控制流水灯
1)实验要求:按键1按下时流水灯正流,按键0按下时流水灯反流。
2)实验目的:1. 掌握中断服务子程序的编写方法; 2. 掌握定时器中断的配置方法。
3)实验说明:通过本实验,可以了解单片机掌握中断服务子程序的编写方法和定时器中断的配置方法,同时也可以了解单片机编程,调试方法。
4)、程序框图

5)、代码
汇编代码
- ORG 0000H ;程序执行的起始地址
- LJMP Main ;跳转到main函数
- ORG 0003H ;外部中断0起始地址
- LJMP 0100H ;外部中断0服务子程序地址
- ORG 0013H ;外部中断1起始地址
- LJMP 0200H ;外部中断1服务子程序地址
- Main: ;主函数
- MOV IE, #10000101B ;置位EA,EX1,EX0
- MOV TCON, #00000000B ;外部中断0和1都是低电平触发
- LOOP: JMP LOOP ;while(1)死循环
- ORG 0100H ;外部中断0服务子程序
- EXT0:
- CALL DELAY1 ;短延时,按键消抖
- MOV R3, #32 ;灯流4次
- MOV A, #0FEH
- EXT0L: MOV P1, A ;P1口赋值
- RR A ;循环右移一位
- CALL DELAY
- DJNZ R3, EXT0L
- RETI ;中断返回
- ORG 0200H ;外部中断1服务子程序
- EXT1:
- CALL DELAY1 ;短延时,按键消抖
- MOV R3, #32 ;灯流4次
- MOV A, #0FEH
- EXT1L: MOV P1, A ;P1口赋值
- RL A ;循环左移一位
- CALL DELAY
- DJNZ R3, EXT1L
- RETI ;中断返回
- DELAY: MOV R1, #200 ;执行200*200个跳转
- DELAY1: MOV R2, #200
- DELAY2: DJNZ R2, DELAY2
- DJNZ R1, DELAY1
- RET
- END
C程序代码
main.c
- #include <reg52.h>
- #include <key.h>
- #include <interrupt.h>
- int task_flag;
- int timer_flag;
- 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;
- #endif
interrupt.c
- #include <interrupt.h>
- void delay(unsigned int z)//延时ms
- {
- }
- 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
- {
- }
- void EXT1_Handle() interrupt 2
- {
- }
- void T0_Handle() interrupt 1
- {
- }
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位
- }
五、实验总结 - 在键盘扫描程序中,卡的比较久时间的是点灯。后面发现不同的开发板,灯的位置不一样。第二个卡的比较久的地方是判断P3口时,没有考虑到高四位的情况,考虑进去之后,switch p3就正常了。
2.设计键盘外部中断的时候,考虑到如果把流水灯放在中断回调函数里面进行的话,会导致执行回调函数的时候,别的中断来了会很麻烦。于是改为在回调函数里面设置标志位,while(1)里面根据标志位来选择执行哪个点灯代码。但是流水灯需要延时,如果用跑空循环来作延时的话,有点像是阻塞型任务,别的中断来的时候,中断套中断,就很麻烦。于是就开了一个定时器,定时器中断里面设置一个flag,每1秒钟flag置位一下,然后去看看要不要切换任务,以及执行哪个任务。这样子就给单片机节省出大量的资源来了。 - 汇编代码比较遗憾的地方是没有加进定时器中断,delay用跑空循环实现。
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!