前几天看到论坛里的旧帖《自己动手制作 “POV LED” 旋转LED显示屏》,文中楼主提到......外部中断函数产生了两次以上......
估计就是霍尔传感器接到外部中断IO上。实际磁钢每次经过霍尔元件时,单片机触发了两次或多次外部中断。这使我想起前一阵子的自制绕线机的事情。
前一阵子,有个电路上需要压电陶瓷片,得用自制磁芯变压器。初级10绕制00圈,次级是30和60圈。
希望绕线时能控制铜线位置,这样初级每层铜线比较整齐,所以要求绕线速度必须要低。后来发现用两节七号镍氢电池给TT直流减速电机供电,转速差不多一秒一圈。速度低,比较合适,我可以拉着铜线,适时调整绕线铜线位置。
减速电机轴一端套上码盘,另一端套上变压器骨架的。当时,先用的红外对射模块(其他名称有测速传感器模块 计数器模块 电机测试模块 槽型光耦模块等等),码盘上有20个孔,用黑胶带封了19个,只留下一个孔,这样码盘转动一圈,红外模块只会输出一次下降沿。
单片机程序下载后,运行时发现,实际数码管上的计数数值不是按一递增的,大多数时候是按二递增的,偶尔会按一递增或按三、四递增的。
以为是红外模块有故障,换了一个,现象依旧。换传感器,在码盘边缘粘上一个小磁钢,换用霍尔传感器模块,单片机运行时也是同样的现象。
奇怪了,当时因为着急用变压器,没再分析故障了,在代码里面计数数值除以二,结果再显示在数码管上。按此方法绕制了磁芯变压器,装上电路可正常工作的。后来就没有再查找这个计数故障了。
直到看到那个旧帖,才想起来,也有人遇到同样的问题了(楼主当时还是个初中生,真厉害!!)。
但对楼主分析的原因不敢苟同,于是翻了翻单片机教程和几本书,有了自己的想法。
以下是个人看法,业余水平,欢迎各位看官拍砖。
无论是红外对射模块,还是霍尔传感器,其实电路上就是运放比较器,比较器是能稳定可靠工作的,应该不是故障源头。问题应该集中在红外发射接收对管和霍尔元件装置上。
红外发射管发射的红外线光束是散射的。不像激光那样笔直和纤细。减速轴上安装的码盘也没有和红外对射模块严格定位,做到码盘平面与对射光轴严格垂直。由于码盘还在运动,难免有抖动的。那么光电接收管收到的红外光很可能既有发射管直射过来的,也包括从码盘小孔侧壁上反射过来的,甚至还可能还包括衍射过来的红外光。
主要因为机械抖动或振动,红外线的传播有了多个路径,多个路径传播的红外线中就有那么几个光线强度足以致模块输出了低电平信号,也就有了下降沿。霍尔元件和磁钢也是类似的。若是有示波器或逻辑分析仪,抓取一段,就能看到这个直观现象的。
还记得机械按键嘛,使用时必须“消抖”,否则就可能出现多次中断的情况。既然如此,那就“消抖”是了。
人手按按键,前后几十毫秒的时间,和机械按键不一样的是,这里机械抖动或振动很可能不到一毫秒,需要实测才知道的。我没有那些测试设备,那么就用笨办法,一个一个地试验。哪位有条件的,抽空抓取一段看看。所以,消抖的方法不能像机械按键那么简单的。
我想到的办法是单稳态电路。
收到外部触发信号后,电路被触发,进入单稳态;
单稳态时间要稍长,这样就无视后来的几个触发信号了;
单稳态时间也不能太长,以免影响下一次正常的触发信号。
我用的是555时基集成电路,多试了几个电容和电阻,调整了RC参数,计数就正确了。最后用的是0.1微法的电解电容和150欧姆的电阻,计算时间常数是1.1*0.1*150=16.5微秒。想通了,就简单了!
后来,换用码盘,没有遮孔的,计数也正常了。减速电机换用4节镍氢电池供电,提高转速(大概2、3圈每秒),计数也是正常的。
准备绕线了。
视频太大附件也传不了的。
绕完了。
当然了,若是用在其他更高速的地方,估计还得再调整RC参数的。
单片机源程序如下:
- #include<reg52.h>
- sbit flashLed=P1^1; //闪烁灯
- unsigned char T0RH = 0; //T0重载值的高字节
- unsigned char T0RL = 0; //T0重载值的低字节
- unsigned int uiCount=0;
- bit flag200ms=0;
- unsigned char code Tab[18]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E,0x7F,0xFF};
- unsigned char ledChar[8]={0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
- /* 配置并启动T0,ms-T0定时时间 */
- void ConfigTimer0(unsigned int ms)
- {
- unsigned long tmp;
- tmp = 11059200 / 12;
- tmp = (tmp * ms) / 1000;
- tmp = 65536 - tmp;
- tmp = tmp + 18;
- T0RH = (unsigned char)(tmp>>8);
- T0RL = (unsigned char)tmp;
- TMOD &= 0xF0;
- TMOD |= 0x01;
- TH0 = T0RH;
- TL0 = T0RL;
- ET0 = 1;
- TR0 = 1;
- }
- void LedScan()
- {
- static unsigned char i = 0; //动态扫描索引
- unsigned char location=0x01;
- P0=0xFF; //关闭所有段选位,显示消隐
- P2=0xff;
- location=location<<i;
- location=~location;
- P0=ledChar[i];
- P2=location;
- i++;
- i=i%8;
- }
- /* T0中断服务函数,执行数码管扫描显示 */
- void InterruptTimer0() interrupt 1
- {
- static unsigned char tc=0;
- tc++;
- TH0 = T0RH; //重新加载重载值
- TL0 = T0RL;
- if(tc==200)
- {
- tc=0;
- flag200ms=1;
- flashLed=~flashLed;
- }
- LedScan(); //数码管扫描显示
- }
- //外部中断函数
- void INT0_Count() interrupt 0
- {
- uiCount++;
- }
- void main(void)
- {
- unsigned char k=0;
- unsigned int uiTmp=0;
- EX0=1; //允许外部中断INT0中断
- IT0=1; //外部中断INT0采用脉冲负跳变触发方式
- EA = 1;
- uiCount=0;
- ConfigTimer0(1); //配置T0定时1ms
- while (1)
- {
- if(flag200ms)
- {
- flag200ms=0;
- uiTmp=uiCount;
- for(k=0;k<8;k++)
- ledChar[k]=0xff;
- ledChar[7]=Tab[uiTmp%10];
- if(uiTmp>=10)
- ……………………
- …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码
所有资料51hei提供下载:
绕线计数器a51程序.zip
(24.56 KB, 下载次数: 29)
|