51按键外部中断控制流水灯

实验二 外部按键输入

一、实验目的
1、了解单片机检测口方法
2、了解单片机外部中断原理
3、了解按键输入原理
二、实验内容
1、完成按键扫描控制流水灯
2、完成按键外部中断控制流水灯
三、实验原理
在这里插入图片描述

四、实验电路与程序
1、软件实验一:按键扫描控制流水灯
1)实验要求:读取四个按键的输入,检测到按下后控制灯流动一段时间,然后继续读取按键输入。
2)实验目的:1. 掌握按键消抖方法;2. 掌握单片机端口检测方法
3)实验说明:通过本实验,可以了解单片机读取IO口电平的方法,同时也可以了解单片机编程,调试方法。
4)、程序框图
在这里插入图片描述

5)、代码
main.c

  1. #include <reg52.h>

  2. #include <key.h>

  3. void main(void)

  4. {

  5. unsigned char key_val;

  6. P1=0xFF;

  7. while(1)

  8. {

  9. key_val=key_scan();//扫描键盘

  10. light_led(key_val);//选择不同的点灯方式

  11. }

  12. }

    key.h

  13. #ifndef _KEY_H

  14. #define _KEY_H

  15. #include <reg52.h>

  16. void delay(unsigned int z);

  17. unsigned char key_scan(void);

  18. void light_led(unsigned char mode);

  19. void flow_forward(void);

  20. void flow_back(void);

  21. void light_all(void);

  22. void dark_all(void);

  23. #endif

    key.c

  24. #include <key.h>

  25. unsigned char key_scan(void)

  26. {

  27. while(P3==0x0f)//等待按键按下

  28. {

  29. ;

  30. }

  31. delay(10);//软件消抖

  32. switch (P3)

  33. {

  34. case 0xFE: return 0;//判断是哪个按键按下

  35. case 0xFD: return 1;

  36. case 0xFB: return 2;

  37. case 0xF7: return 3;

  38. }

  39. }

  40. void flow_forward(void)

  41. {

  42. int i=0;

  43. P1=0xFE;

  44. for(i=0;i<8;i++)

  45. {

  46. delay(500);

  47. P1=(P1<<1)|(P1>>7);//循环左移1位

  48. }

  49. }

  50. void flow_back(void)

  51. {

  52. int i=0;

  53. P1=0xFE;

  54. for(i=0;i<8;i++)

  55. {

  56. delay(500);

  57. P1=(P1>>1)|(P1<<7);//循环右移1位

  58. }

  59. }

  60. void light_all(void)//点亮所有灯

  61. {

  62. P1=0;

  63. }

  64. void dark_all(void)//熄灭所有灯

  65. {

  66. P1=1;

  67. }

  68. void light_led(unsigned char mode)

  69. {

  70. switch (mode)

  71. {

  72. case 0:dark_all();//灯全灭

  73. break;

  74. case 1:light_all();//灯全亮

  75. break;

  76. case 2:flow_forward();//流水灯正流

  77. break;

  78. case 3:flow_back();//流水灯反流

  79. }

  80. }

2、软件实验二:按键外部中断控制流水灯
1)实验要求:按键1按下时流水灯正流,按键0按下时流水灯反流。
2)实验目的:1. 掌握中断服务子程序的编写方法; 2. 掌握定时器中断的配置方法。
3)实验说明:通过本实验,可以了解单片机掌握中断服务子程序的编写方法和定时器中断的配置方法,同时也可以了解单片机编程,调试方法。
4)、程序框图
在这里插入图片描述

5)、代码
汇编代码

  1. ORG 0000H ;程序执行的起始地址
  2. LJMP Main ;跳转到main函数
  3. ORG 0003H ;外部中断0起始地址
  4. LJMP 0100H ;外部中断0服务子程序地址
  5. ORG 0013H ;外部中断1起始地址
  6. LJMP 0200H ;外部中断1服务子程序地址
  7. Main: ;主函数
  8. MOV IE, #10000101B ;置位EA,EX1,EX0
  9. MOV TCON, #00000000B ;外部中断0和1都是低电平触发
  10. LOOP: JMP LOOP ;while(1)死循环
  11. ORG 0100H ;外部中断0服务子程序
  12. EXT0:
  13. CALL DELAY1 ;短延时,按键消抖
  14. MOV R3, #32 ;灯流4次
  15. MOV A, #0FEH
  16. EXT0L: MOV P1, A ;P1口赋值
  17. RR A ;循环右移一位
  18. CALL DELAY
  19. DJNZ R3, EXT0L
  20. RETI ;中断返回
  21. ORG 0200H ;外部中断1服务子程序
  22. EXT1:
  23. CALL DELAY1 ;短延时,按键消抖
  24. MOV R3, #32 ;灯流4次
  25. MOV A, #0FEH
  26. EXT1L: MOV P1, A ;P1口赋值
  27. RL A ;循环左移一位
  28. CALL DELAY
  29. DJNZ R3, EXT1L
  30. RETI ;中断返回
  31. DELAY: MOV R1, #200 ;执行200*200个跳转
  32. DELAY1: MOV R2, #200
  33. DELAY2: DJNZ R2, DELAY2
  34. DJNZ R1, DELAY1
  35. RET
  36. END

C程序代码
main.c

  1. #include <reg52.h>
  2. #include <key.h>
  3. #include <interrupt.h>
  4. int task_flag;
  5. int timer_flag;
  6. void main()
  7. {
  8. Config_EXTI();//初始化外部中断
  9. Config_Timer();//初始化定时器
  10. P1=0xFE;//初始化GPIO
  11. while(1)//任务调度器
  12. {
  13. if(timer_flag)//每1000ms执行一次任务
  14. {
  15. timer_flag=0;
  16. switch (task_flag)//判断执行哪个任务
  17. {
  18. case 1 :flow_forward();//流水灯正流
    
  19. break;
    
  20. case 2 :flow_back();//流水灯反流
    
  21. break;
    
  22. }
  23. }
  24. }
  25. }

interrupt.h

  1. #ifndef _INTERRUPT_H
  2. #define _INTERRUPT_H
  3. #include <reg52.h>
  4. #include <key.h>
  5. void delay(unsigned int z);
  6. void Config_EXTI(void);
  7. void Config_Timer(void);
  8. extern int task_flag;
  9. extern int timer_flag;
  10. #endif

interrupt.c

  1. #include <interrupt.h>
  2. void delay(unsigned int z)//延时ms
  3. {
  4.   unsigned int x,y;
    
  5.   for(x = z; x > 0; x--)
    
  6.   for(y = 114; y > 0 ; y--);
    
  7. }
  8. void Config_Timer(void)
  9. {
  10. TMOD=0x01;//设置定时器0为工作方式1(M1 M0为01),是向上计数
    
  11. TH0=(65536-45872)/256;//装初值11.0582晶振定时50ms数为45872,高位
    
  12. TL0=(65536-45872)%256;//低位
    
  13. EA=1;//开总中断
    
  14. ET0=1;//开定时器0中断
    
  15. TR0=1;//启动定时器0
    
  16. }
  17. void Config_EXTI(void)
  18. {
  19. EA=1;//开中断总允许
  20. IT0=1;//下降沿触发外部中断0
  21. EX0=1;//开外部中断0
  22. IT1=1;//下降沿触发外部中断1
  23. EX1=1;//开外部中断1
  24. }
  25. void EXT0_Handle() interrupt 0
  26. {
  27.  delay(10);
    
  28.  task_flag=1;//开启正向流水灯任务
    
  29. }
  30. void EXT1_Handle() interrupt 2
  31. {
  32.  delay(10);
    
  33.  task_flag=2;//开启反向流水灯任务
    
  34. }
  35. void T0_Handle() interrupt 1
  36. {
  37. static int num=0;
    
  38.    TMOD=0x01;//重装初值
    
  39. TH0=(65536-45872)/256;//高位
    
  40. TL0=(65536-45872)%256;//低位
    
  41. if(num<20)//每1000ms把timer_flag置位
    
  42. {
    
  43.    num++;
    
  44. }
    
  45. else
    
  46. {
    
  47.   num=0;
    
  48.   timer_flag=~timer_flag;
    
  49. }
    
  50. }

key.h

  1. #ifndef _KEY_H
  2. #define _KEY_H
  3. #include <reg52.h>
  4. unsigned char key_scan(void);
  5. void flow_forward(void);
  6. void flow_back(void);
  7. #endif

key.c

  1. #include <key.h>
  2. void flow_forward(void)
  3. {
  4. P1=(P1<<1)|(P1>>7);//循环左移1位
  5. }
  6. void flow_back(void)
  7. {
  8. P1=(P1>>1)|(P1<<7);//循环右移1位
  9. }
    五、实验总结
  10. 在键盘扫描程序中,卡的比较久时间的是点灯。后面发现不同的开发板,灯的位置不一样。第二个卡的比较久的地方是判断P3口时,没有考虑到高四位的情况,考虑进去之后,switch p3就正常了。
    2.设计键盘外部中断的时候,考虑到如果把流水灯放在中断回调函数里面进行的话,会导致执行回调函数的时候,别的中断来了会很麻烦。于是改为在回调函数里面设置标志位,while(1)里面根据标志位来选择执行哪个点灯代码。但是流水灯需要延时,如果用跑空循环来作延时的话,有点像是阻塞型任务,别的中断来的时候,中断套中断,就很麻烦。于是就开了一个定时器,定时器中断里面设置一个flag,每1秒钟flag置位一下,然后去看看要不要切换任务,以及执行哪个任务。这样子就给单片机节省出大量的资源来了。
  11. 汇编代码比较遗憾的地方是没有加进定时器中断,delay用跑空循环实现。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值