找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 4228|回复: 13
打印 上一主题 下一主题
收起左侧

小白请教大神们几个51单片机中断的问题,请指点,谢谢!

[复制链接]
跳转到指定楼层
楼主
20黑币
小白请教大神们几个51中断的问题,请指点,谢谢!
我想利用89c51测量两个按键按下先后时间间隔功能。先按启动,再按停止然后数码管显示出两者先后间隔时间,如果先按停止,不做处理。必须先按启动,然后再按停止才能显示时间,也就是启动和停止有先后顺序,顺序后了不处理。利用中断1和中断0在下降沿触发方式和定时器0方式2去实现,用prutues仿真,按一下启动,再按一下停止,可以正常显示时间。仿真时发现几个问题。
1.按一下启动,再按一下启动,IE1就会置1,(为什么?)再按一下停止,显示时间,但T0不停止,仍然计时,需再按一下停止,定时器T0停止,显示时间。我想实现有功能是按下启动,定时器启动,再按启动没有反应,按下停止,停止计时,显示时间,再按下停止或没按启动先按停止,没有反应。但是现在如果按两下启动同,必须按两下停止才能停止定时器。
2.按一下启动,再按一下停止,显示时间,但再按一下停止,TE0就会置1,(为什么?)此时按一下启动,显示就会变成00,定时器也不启动,再按下启动,定时器才会启动。
请问大神们,出现这种情况是什么原因,如果不改变中断1和中断0在下降沿触发方式和定时器0方式2,如何解决,源文件附后,请大神们指点。

新手小白一枚,送点黑币意思一下,请大神们不吝赐教,谢谢!用protues仿真,所以按键没有去抖,应该不是没去抖的问题。



  1. #include<reg51.h>
  2. #define uchar unsigned char
  3. #define uint unsigned int

  4. uint Time=0;
  5. uint Time_Count=0;

  6. uchar Duan[] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0xFF,0x7f};//段码0-9+全暗+"."

  7. uchar Wei[4]={0x08,0x04,0x02,0x01};   //位的控制端        (仿真)

  8. uchar Time_Buffer[]={0,0,0,0};                                        //时间缓存

  9. void delayms(uint x)                //延时函数
  10. {
  11.         uint i,j;
  12.         for(i = x; i > 0; i --)
  13.                 for(j = 125; j > 0; j --);               
  14. }

  15. void Show_Time()                                //数码管动态扫描显示
  16. {
  17.                 uchar i;
  18.         /*****************数据转换*****************************/
  19.         
  20.         
  21.                 Time_Buffer[0] = Time / 1000;                                //千位
  22.                 Time_Buffer[1] = Time % 1000 / 100;        //百位
  23.                 Time_Buffer[2] = Time % 100 /10;                //十位
  24.                 Time_Buffer[3] = Time % 10;                                        //个位

  25.                 if(Time_Buffer[0] == 0)        
  26.                         {        
  27.                                 Time_Buffer[0] = 10;
  28.                                 if(Time_Buffer[1] == 0)
  29.                                 {        
  30.                                         Time_Buffer[1] = 10;
  31.                                 }
  32.                         }               
  33.                
  34.                 for(i = 0;i < 4;i ++)
  35.                 {
  36.                         P0 = 0xFF;
  37.                         P1 = Wei[i];
  38.                         P0 = Duan[Time_Buffer[i]];
  39.                         delayms(2);
  40.                 }
  41. }

  42. /*************************************************************************
  43.                                 主函数        
  44. **************************************************************************/
  45. void main()
  46. {
  47.                 EX1=1;
  48.                 EX0=1;
  49.                 IT0=1;//低电平有效 下降沿有效
  50.                 IT1=1;
  51.                 EA=1;                                
  52.                
  53.                 TMOD=0x02;                //设置为自动重载方式
  54.                 TH0=0x9c;                        //设置为100us的计数
  55.                 TL0=0x9c;
  56.                 TR0=0;
  57.                 Time=1898;
  58.                 while(1)
  59.                 {
  60.                         Show_Time();
  61.                 }
  62. }


  63. void Start_ExInt1()interrupt 2
  64. {
  65.                 TR0=1;
  66.                 EX1=0;
  67.                 ET0=1;               
  68.                 EX0=1;
  69. }

  70. void Timer_T0() interrupt 1
  71. {
  72.                 Time_Count++;

  73.                 EX0=1;
  74.                 if ( Time_Count==9999) //计时范围0-9999
  75.                 {
  76.                          Time_Count=0;
  77.                 }
  78. }
  79. void Stop_Int0() interrupt 0
  80. {
  81.                 TR0=0;
  82.                 Time= Time_Count;
  83.                 Time_Count=0;
  84.                
  85.                 EX1=1;
  86.                 EX0=0;
  87.                 ET0=0;
  88. }
复制代码


KEY1-2.rar (62.48 KB, 下载次数: 4)


未按键时.png (50.34 KB, 下载次数: 131)

未按键时.png
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:383215 发表于 2020-6-7 23:51 | 只看该作者
你的程序根本不可能实现你想要的功能,主要原因是按键去抖动你没有解决,你只有稳定得到键值才有可能测量出两个按键的时间,两个按键的时间大于0.5秒以上,不需要中断,一个定时器足够用了,初学单片机编程,不能滥用中断,中断用多的情况下问题更多,要想得到更高的精度,除非用RS触发器搞个硬件去抖动,你现在的程序也许可以用,你这个程序,看似简单,编好了还是不容易的,我不需要黑币,你自己留着吧,这个论坛我需要的资料不太多。
回复

使用道具 举报

板凳
ID:123289 发表于 2020-6-8 11:09 | 只看该作者
按键必须去抖动,方式很多,各有其特点。
你的命题对按键的先后次序有要求,所以要仔细规划一下,去抖动的方式。
原则:按键按下时是有弹动的,即按一下,不只一下,短时内是多下。
你只认第一下,而短时内的其它下不认可就行了。
你是用中断识别按键的,就在中断里识别;
你是扫键识别的,就在扫键时识别。办法自己想。
祝你成功。
回复

使用道具 举报

地板
ID:104797 发表于 2020-6-8 22:31 | 只看该作者
谢谢两位大神的指点。
我只是想要地protuse中仿真,不需要在硬件上实现(只是为了学习单片机,想实现这个功能)请问也需要去抖动吗?
void Start_ExInt1()interrupt 2
{
                TR0=1;
                EX1=0;
                ET0=1;               
                EX0=1;
}
这段中断代码中,关断了INT1,为什么再按一下IE1为什么会置1?如果不想让它置位应该怎么做?用这种方法关关闭和打开中断可以不?还有中断和扫描哪种方法更可靠的采集到启动和停止按键的信号?刚入手,所以很多细节问题没弄明白。谢谢各位大神们。
回复

使用道具 举报

5#
ID:383215 发表于 2020-6-8 23:57 | 只看该作者
IE还受IT影响,这个不是关键,关键之关键就是你必须得到稳定键值,我从来没有搞过protuse仿真,我估计protuse仿真不太可能把按键之类的元器件真实仿真出来,不管什么单片机,效率最高的编程方式是实验板+仿真器+示波器,没有这些东西,编程效率太低太低了。
回复

使用道具 举报

6#
ID:104797 发表于 2020-6-9 06:06 来自手机 | 只看该作者
想明白了IE置1的问题,但是没想出怎么解决。
回复

使用道具 举报

7#
ID:213173 发表于 2020-6-9 10:22 | 只看该作者
lhpm641 发表于 2020-6-9 06:06
想明白了IE置1的问题,但是没想出怎么解决。

楼主想要完成上述预想的功能没必要写这么复杂,只要把逻辑关系捋清,很容易实现。不论仿真或实物也完全不需要按键消抖。给你改好了,你试试。

  1. #include<reg51.h>
  2. #define uchar unsigned char
  3. #define uint unsigned int
  4. uchar Duan[] = {//共阳数码管段码"0~f-. "
  5. 0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,
  6. 0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,0xbf,0x7f,0xFF};
  7. uchar Wei[4]={0x01,0x02,0x04,0x08};   //位的控制端        (仿真)
  8. uchar Time_Buffer[4];  //时间缓存
  9. uint  Time=0;
  10. uint  Time_Count=0;
  11. uchar Cnt100us=0;
  12. bit   flag=0;
  13. /*
  14. void delayms(uint x)                //延时函数
  15. {
  16.         uint i,j;
  17.         for(i = x; i > 0; i --)
  18.                 for(j = 125; j > 0; j --);
  19. }*/

  20. void Show_Time() //数码管动态扫描显示
  21. {
  22.         uchar i;
  23.         uint  j;
  24.         static uchar k=0;

  25.         /*****************数据分解*****************************/
  26.         if(flag==0)//判断启动标志复位 处理数据
  27.         {
  28.                 j=Time;       
  29.                 for(i=4;i>0;i--)
  30.                 {
  31.                         Time_Buffer[i-1]=Duan[j%10];//由低位到高位保存
  32.                         j/=10;
  33.                 }
  34.         }
  35.         else       //等待计时结束
  36.         {
  37.                 for(i=4;i>0;i--)
  38.                 {
  39.                         Time_Buffer[i-1]=0xbf;//全部'-'
  40.                 }
  41.         }
  42.         P0 = 0xFF;
  43.         P1 = Wei[k];
  44.         if(k==0)P0=Time_Buffer[k]&0x7f;//加小数点
  45.         else P0=Time_Buffer[k];
  46.         k++;k%=4;
  47. }

  48. /******************
  49.        主函数        
  50. ******************/
  51. void main()
  52. {
  53.         IT0 = 1;//下降沿触发
  54.         IT1 = 1;
  55.         EX0 = 1;
  56.         EX1 = 1;
  57.         EA  = 1;  
  58.        
  59.         TMOD=0x02; //设置为自动重载方式
  60.         TH0=0x9c;  //设置为100us的计数
  61.         TL0=0x9c;
  62.         ET0=1;
  63.         TR0=0;
  64.         while(1)
  65.         {
  66.                 Show_Time();
  67.         }
  68. }

  69. void Timer_T0() interrupt 1
  70. {
  71.         if(++Cnt100us>=10)       //1ms
  72.         {
  73.                 Cnt100us=0;
  74.                 if(++Time_Count>9999) //计时范围0-9999
  75.                         Time_Count=0;      //10s
  76.         }
  77. }
  78. void Stop_Int0() interrupt 0
  79. {
  80.         if(TR0==0)       //与INT1互锁
  81.         {
  82.                 flag=1;                  //启动标志
  83.                 Cnt100us=0;   //初始值清0
  84.                 Time_Count=0; //初始值清0
  85.                 TR0=1;                  //开始计时
  86.         }
  87. }

  88. void Start_ExInt1()interrupt 2
  89. {
  90.         if(flag)         //判断启动状态 与INT0互锁
  91.         {
  92.                 TR0=0;        //停止计时
  93.                 flag=0;             //启动标志复位
  94.                 Time=Time_Count;//读取计数值
  95.         }
  96. }
复制代码



回复

使用道具 举报

8#
ID:383215 发表于 2020-6-9 17:31 | 只看该作者



笑话啊!好好看看吧,这是我用示波器捕捉的一个按键开关由高电平按键之后变为低电平的波形,这是实实际际测量出来的波形,我的示波器档次太低,我又用了一个质量很好的按钮开关,看起来抖动期才有300多微秒,只有几次抖动,实际上不止这些,质量差的按键开关抖动期至少几毫秒,抖动次数几十次,我编下来的程序,几十毫秒消除抖动的程序最好用,也最可靠,楼主看懂这个波形就知道为什么程序不可能编成功了,楼主只有踏踏实实的用硬件仿真,可以测试出不同按键的抖动期和抖动次数,并且找到去除抖动的编程方法,得到稳定的键值,接下来的编程就是小菜一碟了,我看了这个论坛的一些程序,能得到稳定的键值的程序并不多,不管是一个按钮还是多个按钮,编程方法都是一模一样的,无非就是读键值、去抖动、然后得到稳定键值,我说的这些才是学习单片机编程的捷径,如果楼主抱着protuse仿真不放,再听信一些人说的连去抖动都不用就能编出这个程序,那就是自己哄哄自己玩玩而已了,protuse不可能模拟出实际的按键开关抖动,protuse不可能把楼主的程序编好,就算可以编出来,也是毫无意义,还是自己哄自己。
回复

使用道具 举报

9#
ID:213173 发表于 2020-6-10 07:45 | 只看该作者
任何机械开关切换时都会发生抖动,这是客观存在的事实。按键没有任何操作的情况下绝对不会发生抖动,只要发生抖动必然是有操作。如果认为在任何条件下必须对按键抖动做处理,其认识和应对方法还局限在教科书的层面。实践是检验真理的唯一标准。
回复

使用道具 举报

10#
ID:383215 发表于 2020-6-10 14:47 | 只看该作者
wulin 发表于 2020-6-10 07:45
任何机械开关切换时都会发生抖动,这是客观存在的事实。按键没有任何操作的情况下绝对不会发生抖动,只要发 ...

你说的非常正确,我的单片机程序,只要有按键,就是“在任何条件下必须对按键抖动做处理”,很早以前我就把按键去抖动程序编的非常稳定了,包括单个、多个、交叉矩阵、串行的按键去抖动。像楼主的程序,按键去抖动不需要任何延时程序,也不需要定时中断,就用查询的方法去抖动,我已经说的很清楚了,任何按键去抖动就是读键值、去抖动、得到稳定键值,再说明白点,就是连续读键值达到某个次数都是离键值,只要一次不为离键都不算离键,连续读键值达到某个次数都是按键值,只要一次不为按键都不算按键,用这个方法,就能得到稳定键值,包括按下动作、弹开动作、单击动作、双击动作、三击动作、以及各种组合动作,都很容易编出来,至于两个按键的时间,用一个定时器中断就能判断出来。需要更高精度,可以用RS触发器做成硬件按键去抖动,单片机程序就不用编键盘去抖动的程序了,就能做到最高精度。
我编的单片机程序,不在乎有多复杂,只在乎占用CPU时间,占用CPU时间越短的程序,编程质量越高,我编的键盘去抖动程序很简单,就算“在任何条件下必须对按键抖动做处理”,其实占用CPU时间很短。“在任何条件下必须对按键抖动做处理”,是按键动作程序最基本的要求,与任何一本教科书没有关系。你说的实践是检验真理的唯一标准,绝对正确,用protuse软件仿真,不是实践,仅仅是理论上的一个软件模拟而已,楼主的这个程序,用我说的查询去抖动的方法编程,protuse软件估计可以模拟出来,但是,与实际的按键抖动肯定有差异,你们应该用硬件检验成功的程序,再用protuse软件模拟仿真试试,就知道protuse软件模拟仿真到底有多少价值了。楼主的这个程序,用硬件测试,花不了几个钱,一个51单片机最小系统板,数码管用1602液晶代替,再接两个按键开关就够了。我的51、AVR、STM32实验板都是自制,基本够我用了,仿真器和示波器是必须的,没有这两样东西,我宁愿不编程序,因为编程效率实在太低了。
回复

使用道具 举报

11#
ID:104797 发表于 2020-6-10 21:01 | 只看该作者
感谢大神们的指点,经验宝贵,多谢指教。刚入手,看来我要多和实际联系起来,还有很长的路要走。刚入手了一块试验板,以后请大神们多传授点经验。
回复

使用道具 举报

12#
ID:213173 发表于 2020-6-11 07:55 | 只看该作者
kmsj 发表于 2020-6-10 14:47
你说的非常正确,我的单片机程序,只要有按键,就是“在任何条件下必须对按键抖动做处理”,很早以前我就 ...

与您这样有思想,有经验,非人云亦云的坛友交流非常有益。在7楼回复中用protuse软件仿真,当然不能算是实践。这只不过是按楼主意图用楼主容易看明白方式理解程序而已。但为楼主提供的修改过的程序绝对经得住实物验证。事实也是经实物验证过是稳定可靠的。一般在实际应用中开关信号都是在主循环中查询处理,滞后几十毫秒响应无所谓。而必须用外部中断处理的事件则要求及时响应(滥用外部中断除外),滞后几十毫秒响应就显得很漫长。其实从来没有用protuse软件设计过产品,只是玩玩而已。实际开发产品都是用实验板前期实验,无误后少量定制PCB打样机。经48小时拷机,EMC、高/低温及振动试验。最后(往往不会一次通过)才能投入批量生产。若发生大批量召回事件是要让老板破产的。
回复

使用道具 举报

13#
ID:383215 发表于 2020-6-11 15:53 | 只看该作者
wulin 发表于 2020-6-11 07:55
与您这样有思想,有经验,非人云亦云的坛友交流非常有益。在7楼回复中用protuse软件仿真,当然不能算是实 ...

那就奇怪了,本论坛用protuse软件仿真的人,为什么那么多的问题解决不了?到处都有把程序大段大段的发上来让大家帮着解决的人,我认为这是浪费自己的时间、浪费别人的时间、浪费论坛资源,我看这些程序,只要有硬件仿真器,只要可以设置断点,只要可以单步执行,观察变量,绝大多数的程序有问题自己都可以搞定,学习单片机编程,必须培养自己解决问题的能力,那接到项目搞不定也是发上论坛,没有人帮解决难道要去喝西北风?protuse软件怎么不能像硬件仿真器那样可以设置断点、单步执行、观察变量?如果能为什么那么多问题解决不了?我的硬件仿真已经做的比较完善,不想研究protuse软件了。本论坛用硬件仿真遇到问题发帖的人几乎不见,别人不好说,我自己就是用硬件仿真,绝大多数的问题我自己都能解决,靠的就是仿真器、示波器,以及自制的开发板。我看过一些单片机教程,编个按钮开关都要用掉外中断、定时器,明显就是滥用中断、滥用资源,我编的按键开关程序,连延时都不需要,照样编的很稳定,有一种单片机编程叫做CPU应用率,其实是单片机编程的最基本要求,可以把单片机资源运用的非常合理,编的程序既简单又可靠,达到这个地步的人估计统统闭嘴了。
回复

使用道具 举报

14#
ID:383215 发表于 2020-6-11 15:59 | 只看该作者
lhpm641 发表于 2020-6-10 21:01
感谢大神们的指点,经验宝贵,多谢指教。刚入手,看来我要多和实际联系起来,还有很长的路要走。刚入手了一 ...

学习单片机解决问题最快的方法就是用仿真器设置断点、单步执行、观察变量,一定要培养自己独立解决问题的能力,51仿真器最好要买一、两百元以上可以仿真所有IO口和所有资源的仿真器,可以在keil下硬件仿真,你学习51单片机的速度至少可以提高好几倍,舍不得在开发工具上花钱,只会拖慢自己的学习速度和开发速度,51单片机只有学习价值,没有运用价值了,要快速把51单片机弄懂,然后可以学一下AVR单片机,用洞洞板都可以自制一个JTAG仿真器和下载器,不是特别好用,对付一般学习开发基本够用,最后还要学习STM32,STM32的jlink仿真器相当好用,价格也不贵。51单片机学会之后不能丢,我现在很多东西就是先用51单片机快速编出来,然后移植到AVR或者STM32,我觉得效率很高,你照我的话去做很快就能成为大神,我不是大神,这些东西只是我的业余爱好而已。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表