搜索
bottom↓
回复: 47

有个想法:用普通红外线遥控器来做单片机系统的键盘

[复制链接]

出0入0汤圆

发表于 2004-11-25 15:30:45 | 显示全部楼层 |阅读模式
在做单片机系统时,如果用到10个以上按键的时候,一般会接成矩阵式键盘(或使用键盘芯片),这样做会占用单片机较多的IO口,还需单片机分神来扫描它,显得有点麻烦。

现在我想利用电视机遥控器来代替矩阵式键盘,这样做只需一个红外线接收头,占用单片机一个外部中断,就可以实现32个按键了,而且无需扫描。我买的长虹电视遥控器4块钱一个(便宜吧)。不知道这个想法有没有实用性,在实际使用时会遇到一些什么问题,大家讨论一下吧。

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

发表于 2004-11-25 15:45:07 | 显示全部楼层
这样做没问题,但是至少会占用一个外部中断和一个定时器,如果资源紧张的系统就没有意义了,而对于简单的系统是完全可行的,使用RC5编码可以最大支持64个地址,每个地址64个指令共计4096个按键,PC键盘改装一下就可以用来无线操纵设备了。

出0入0汤圆

发表于 2004-11-25 15:51:31 | 显示全部楼层
这个想法我早就有了,一直没做成。用普通遥控器,先得找出每个键对应的波形才行。

没示波器,干不了:-(



我好想示波器!!!!

出0入0汤圆

发表于 2004-11-25 16:05:37 | 显示全部楼层
早就有人这样做了,现在都有卖的,在2002年电子制作合定本里有详细的资料

出0入0汤圆

 楼主| 发表于 2004-11-25 16:09:20 | 显示全部楼层
我现在用的是长虹 K6C 电视遥控器,市场上很容易买到,这个遥控器使用LC7461编码芯片(还有很多遥控器也是使用这个芯片的),在网上可以找到他的DATASHEET,不用示波器就可以分析波形了。

我现在的程序只使用了一个INT0中断就可以稳定的读出按键值了,相对矩阵式键盘节约了很多资源呢。

出0入0汤圆

发表于 2004-11-25 16:09:51 | 显示全部楼层
用定时器来测试脉宽,判断0/1. 用外中断检测按键. 遥控器采用三菱的m50462

出0入0汤圆

 楼主| 发表于 2004-11-25 16:15:53 | 显示全部楼层
其实测脉宽不用定时器,用中断来捕获脉冲的到来,进入中断之后把中断关掉,用延时来判断0和1。

我觉得这样做最大的好处就是只使用一个中断就可以实现32键盘。不足的是LC7461发送一组数据需要108mS,对与有些系统可能太长了。
-----此内容被Jacky于2004-11-25,16:30:59编辑过

出0入0汤圆

发表于 2004-11-25 16:38:38 | 显示全部楼层
如果用了键盘,那速度本来就不可能要求很高了,人按个键,程序还得有至少10ms的毛刺判断呢。红外应该可以满足的。

出0入0汤圆

发表于 2004-11-25 19:04:22 | 显示全部楼层
用AVR做MP3播放器的就用到遥控器了。

用定时器捕捉或外部中断来做都行,不过都要用上定时器的计时功能。

出0入0汤圆

 楼主| 发表于 2004-11-25 19:23:59 | 显示全部楼层
可以不用定时器

出0入0汤圆

发表于 2004-11-25 19:30:26 | 显示全部楼层
也可以,但只能用于非实时系统。

用查询会独占100mS的CPU时间,很多时候不合适!

难道MP3的播放会被遥控器发出的音量调节指令停止100mS,导致声音的断续吗?

出0入0汤圆

发表于 2005-2-21 09:02:03 | 显示全部楼层
我觉得可以实现的,因为我刚刚做完的红外遥控的,用的是普通的I/O口和定时器实现的,而且,无论0,1的波形的高电平和低电平都可以完全解码,从而更精确,大家常见的延时解码存在问题的,

出0入0汤圆

发表于 2005-2-21 09:14:01 | 显示全部楼层
http://www.zmmcu.com/

这里有公开的全部资料啊.

出0入4汤圆

发表于 2005-2-21 09:14:23 | 显示全部楼层
好想法

出0入0汤圆

 楼主| 发表于 2005-2-21 10:03:50 | 显示全部楼层
我只用了一个INT1,用延时来判断0、1,

也就是说只用一个中断(IO)就实现了32个按键

出0入0汤圆

发表于 2005-2-21 10:15:10 | 显示全部楼层
想法不错,用遥控器太爽了!

出0入0汤圆

 楼主| 发表于 2005-2-21 10:47:07 | 显示全部楼层
这是我的源程序

/*****************************************

红外线遥控器解码程序

输入数据:无

输出:

作者:蒋剑东 广西南宁

CPU: M16L  晶振:片外8M

修改时间:2004-11-22

*****************************************/



#include "iom16.h"

#include "stdio.h"

#include "macros.h"                                 //        常用的宏定义

#define uchar unsigned char

#define uint unsigned int



#define        led_on        PORTB |=  1<<3;

#define        led_off        PORTB &= ~(1<<3);



#define    buz_off   PORTD |=  (1<<7);

#define    buz_on    PORTD &= ~(1<<7);



#define    led1_on   PORTA &= ~(1<<5);

#define    led1_off  PORTA |=  (1<<5);

#define    led2_on   PORTA &= ~(1<<6);

#define    led2_off  PORTA |=  (1<<6);

#define    led3_on   PORTA &= ~(1<<7);

#define    led3_off  PORTA |=  (1<<7);

   



//-----------------                延时程序START                ---------------------------

void delay_nms(uint count)

{

        uint i,j;

        for( j=0;j<count;j++ )

        {

                for( i=0;i<999;i++ )

                {

                        asm("wdr");

                }

        }

}       



void delay_nus(uint count)

{

        uint i;

        for( i=0;i<count;i++ )

        {

                asm("wdr");

        }

}



//-----------------                延时程序END        ---------------------------





void cpu_init(void)

{

        DDRA = DDRB = DDRC = DDRD = 0XFF;                //        不用的I/O口全部配置为输出       



        DDRD  &= ~(1<<2);                //        PD2为INT0,配置为输入

        PORTD &= ~(1<<2);                //        PD2上拉电阻禁止



        UBRR  = 0x33;                        //        波特率9600

        UCR = 0x18;



        MCUCR |=  (1<<1);

        MCUCR &= ~(1<<0);                //        等效于MCUCR = 0x02;  设定INT0为下降沿触发中断

        GICR  |= (1<<6);                //        开INT0中断

        TIMSK = 0x00;                        //        timer interrupt sources



        WDTCR = 0B00001001;                //        使能看门狗,溢出周期32.5ms



        led1_off led2_off led3_off buz_off

       

        SEI();                                        //        打开全局中断

}



void main(void)

{

        cpu_init();

        puts("hellow\r");

        while(1)

    {

                asm("wdr");

    }

}







//-----------------                INT0中断服务程序 START                ---------------------------

#pragma interrupt_handler int0_isr:2

void int0_isr(void)

{

        uchar bit=0;

        uchar i,cycle,continue_count;

        uint data,temp;

        uint user_code,user_code_not;

        CLI();                                        //        关闭全局中断

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

        {

                delay_nms(1);

                if( (PIND&0B00000100)==0B00000100 )               

                {

                        goto ret;        //目的是检测在8毫秒内如果出现高电平就退出解码程序

                }

        }



        //while( (PIND&0B00000100)==0B00000000 );               

        temp = 8000;

        while( ((PIND&0B00000100)==0B00000000) && (temp!=0) ){temp--;}        //        等待4.5ms高电平的到来,避开9毫秒低电平引导脉冲

                                                        //        temp--需0.25us, temp=8000可实现2ms延时,防止干扰脉冲造成的死机。

        delay_nus(5412);                //        延时4.74毫秒避开4.5毫秒的结果码

        for( i=0;i<13;i++ )                //        接收用户码,存入user_code中

        {

                //while( (PIND&0B00000100)==0B00000000 );                //        等待地址码第一位的高电平信号

                temp = 4000;

                while( ((PIND&0B00000100)==0B00000000) && (temp!=0) ){temp--;}        //        等待地址码第一位的高电平信号

                                                                //        temp--需0.25us, temp=4000可实现1ms延时,防止干扰脉冲造成的死等待。

                delay_nus(1002);                //        高电平开始后用882微秒的时间尺去判断信号此时的高低电平状态

                if( (PIND&0B00000100)==0B00000100 )                //        检测到高电平1的话说明该位为1,延时1毫秒等待脉冲高电平结束

                {

                        user_code |= (1<<i);                       

                        delay_nus(1137);

                }

                else                                                                        //        检测到低电平0的话,说明该位为0,继续检测下一位

                {

                        user_code &= ~(1<<i);

                }                                                                                //        检测到低电平0的话,说明该位为0,继续检测下一位

        }

        for( i=0;i<13;i++ )                //        接收用户码取反,存入user_code_not中

        {

                temp = 4000;

                while( ((PIND&0B00000100)==0B00000000) && (temp!=0) ){temp--;}        //        等待地址码第一位的高电平信号

                                                                //        temp--需0.25us, temp=4000可实现1ms延时,防止干扰脉冲造成的死等待。

                delay_nus(1002);                //        高电平开始后用882微秒的时间尺去判断信号此时的高低电平状态

                if( (PIND&0B00000100)==0B00000100 )                //        检测到高电平1的话说明该位为1,延时1毫秒等待脉冲高电平结束

                {

                        user_code_not |= (1<<i);                       

                        delay_nus(1137);

                }

                else                                                                        //        检测到低电平0的话,说明该位为0,继续检测下一位

                {

                        user_code_not &= ~(1<<i);

                }                                                                                //        检测到低电平0的话,说明该位为0,继续检测下一位

        }

       

        for( i=0;i<16;i++ )                //        开始接收16位数据,存入字变量data中,dataH= ~data    dataL= data

        {

                temp = 4000;

                while( ((PIND&0B00000100)==0B00000000) && (temp!=0) ){temp--;}        //        等待数据码第一位的高电平信号

                                                                //        temp--需0.25us, temp=4000可实现1ms延时,防止干扰脉冲造成的死等待。

                delay_nus(1002);                //        高电平开始后用882微秒的时间尺去判断信号此时的高低电平状态

                if( (PIND&0B00000100)==0B00000100 )                //        检测到高电平1的话说明该位为1,延时1毫秒等待脉冲高电平结束

                {

                        data |= (1<<i);                        //        dataH= ~data    dataL= data

                        delay_nus(1137);

                }

                else                                                                        //        检测到低电平0的话,说明该位为0,继续检测下一位

                {

                        data &= ~(1<<i);

                }

        }



        user_code_not ^=0xffff;                //        取反

        user_code_not &=0x1fff;

        user_code     &=0x1fff;



        temp = (data>>8);

        temp ^=0xffff;                                //        取反

        temp &=0x00ff;

        data &=0x00ff;

        if( (temp==data)&&(user_code_not==user_code) )                //        校验

        {

                if      ( data==0x0001)                PORTA ^= (1<<5);

                else if ( data==0x0002)                PORTA ^= (1<<6);

                else if ( data==0x0003)                PORTA ^= (1<<7);



                UDR = data;                                        //        data

                while(!(USR&0X40));

                USR|=0x40;

       

                bit = 1;

                //buz_on                                放在这里不好控制蜂鸣器响的长度,因为若改变delay_nus(500);中的参数会导致下面“连续按键检测”

                //delay_nus(500);                工作不正常,所以将这一段移至“连续按键检测”之后。

                //buz_off

        }



        //        检测是否有连续按键         START

        cycle = 7;                                //        总共延时7毫秒

        continue_count = 0;

        delay_nms(20);                        //        延时20毫秒,然后等待连续按键产生的9毫秒开始信号

        while( cycle!=0 )                //        如果7毫秒之内没有检测到低电平则说明没有连续按键,退出

        {

                if( (PIND&0B00000100)==0B00000000 )       

                {

                        continue_count++;

                        if( continue_count>8 )                //        连续检测到8次以上才发送数据,防止短按时发送多次数据

                        {

                                UDR = data;                                //        data

                                while(!(USR&0X40));

                                USR|=0x40;

                                PORTD ^= (1<<7);

                        }

                        cycle = 7;                                //        有连续按键,重置延时周期数

                        delay_nms(104);                        //        延时104毫秒,然后等待下一个9毫秒开始信号

                        buz_off

                }

                delay_nms(1);                //        每隔一毫秒检测一次

                cycle--;               

        }

        //        检测是否有连续按键         END



        if( bit==1 )

        {

                buz_on                               

                delay_nus(1000);       

                buz_off

        }

ret:

        SEI();                                        //        打开全局中断

}



//-----------------                INT0中断服务程序 END                ---------------------------






-----此内容被Jacky于2005-02-21,10:48:18编辑过

出0入4汤圆

发表于 2005-2-21 11:02:19 | 显示全部楼层
能提供一下原理图吗

出0入0汤圆

 楼主| 发表于 2005-2-21 11:12:51 | 显示全部楼层
原理图我没画,但是很简单

最小系统+万能接收头+串口。就行了

出0入0汤圆

发表于 2005-2-21 11:38:05 | 显示全部楼层
问一下:Jacky 边城浪子



用延时来判断,是不是很精确啊?

另外,你的1,0是根据当时的电平信号判断的么???

你的程序我没有仔细看,如果却是只是根据那个时刻的电平就判断可能不理想啊

出0入0汤圆

发表于 2005-2-21 11:42:20 | 显示全部楼层
我做过红外解码的,可以做到红外信号的每个代码(1或0)的每段电平的长短,即无论编码是怎么样的一种形式,只要单片机的晶振满足,就可以完全解码,而不是只通过读当时的电平来判断的,欢迎交流啊

出0入0汤圆

发表于 2005-2-21 11:44:23 | 显示全部楼层
我只用了一个普通的I/O口,和一个定时器就可以解码,当然要加其他的扩展那就另外加了

出0入0汤圆

发表于 2005-2-21 11:51:18 | 显示全部楼层
这种方法是用来做学习型智能遥控器。

对于实际应用,只需要知道所用红外发射/接收芯片的编码格式就行了,数据手册写得好明白。

自己用MCU做整套协议更好。

用定时器/外部中断会令程序所消耗的时间短多了,而且当中断处理做得好时,解码过程对其他程序的执行影响很小。

出0入0汤圆

 楼主| 发表于 2005-2-21 11:51:31 | 显示全部楼层
回 hello_lzh:



1、保证精确。

前提是你必须精确的知道延时程序的精确运行时间(可以用AVRSTDUIO),每一个脉冲都必须严格把关(可能程序有些繁琐,但做完后占用资源很少)。另外还必须精确的知道红外编码的脉冲长度(可以查数据手册),我用的是7461编码芯片,脉冲比较宽,用延时来卡它已经足够了



2、是根据接收头的电平来判断

3、你可以仔细看一下程序,就可以知道该程序是否可靠了。该程序还有个缺点:解码时必须关闭其他中断

出0入0汤圆

发表于 2005-2-21 13:17:04 | 显示全部楼层
这种解码方法应该是流传比较广泛的中断+读取每个脉冲信号中间点的电平的方法吧

出0入0汤圆

发表于 2005-2-21 13:25:07 | 显示全部楼层
7461编码芯片我没用过,我比较常用的就是9ms+4.5ms+32位数据的编码方式如:6121之类的

还有就是比较简单的编码如:6132,5104啊之类的,无论哪种编码格式,红外信号的高低电平的每一段的时间都可以判断的比较准确,而不是紧紧读取特定时刻的电平,如果从高可靠性来说,我觉得,还是完全解码比较好一点,读取电平的方法在要求不是很高的时刻可以使用的,

咱们接着讨论,我也是接触红外不是很久,咱们相互学习啊

出0入0汤圆

 楼主| 发表于 2005-2-21 13:25:21 | 显示全部楼层
"脉冲信号中间点"

对,就是程序比较难调试

出0入0汤圆

 楼主| 发表于 2005-2-21 13:29:04 | 显示全部楼层
“hello_lzh ”指的“完全解码”能说具体一点吗?

出0入0汤圆

发表于 2005-2-21 13:33:53 | 显示全部楼层
你在线啊,呵呵,好的

出0入0汤圆

发表于 2005-2-21 13:41:40 | 显示全部楼层
我是这样做的,就是只要出现接收头所对应引脚的电平变化,即无论是1->0还是0-->1都作为一段信号的起始点,并用定时器的定时中断计数,下一次的变化发生时判断计数次数,依次类推,常见的就是0.56低电平+0.56高电平(0),0.56低电平+1.69高电平(1),无论0.56,还是其他的时间值,都可以通过计数次数判断出来,而且可以自己设定容差范围的,

出0入0汤圆

发表于 2005-2-21 13:48:41 | 显示全部楼层
我的定时中断是100us,即0.1ms,0.56毫秒的话计数值是5左右,但是判断时应该加上容差范围例如可以选择2-8,或者1-9,只要计数值出现在这个范围之内就作为0.56ms,如果精度高可以选用4-7这个范围,或者更精确5-7,

出0入0汤圆

 楼主| 发表于 2005-2-21 13:52:39 | 显示全部楼层
100us是不是太大了,

“0.56毫秒的话计数值是5左右”我觉得计数值在10以上可能会可靠一些

出0入0汤圆

 楼主| 发表于 2005-2-21 13:54:49 | 显示全部楼层
hello_lzh ,发个程序上来看看吧

出0入0汤圆

发表于 2005-2-21 14:03:11 | 显示全部楼层
如果纯粹是为了解码,那样的可以设置定时时间长度的,可以在允许的范围之内,

但是如果程序中还要实现其他的一些功能,那就不要太短,否则就会出错的,

另外,像其他的一些简单的,比较便宜的单片机,可能代码执行速度受限制的,所以,

这是比较通用的啊,

当然了,如果设置成50us,考虑一下两次中断之间才能做多少工作啊,呵呵

出0入0汤圆

发表于 2005-2-21 14:07:36 | 显示全部楼层
好吧,不过得等一等啊,程序还得再改一改,放心啊,比较快的,

出0入0汤圆

 楼主| 发表于 2005-2-21 14:12:28 | 显示全部楼层
等待ing

好像红外线解码的方法都是用类似的原理的。

这里有一个万能红外线解码器  http://www.mcusky.com/  不知是怎么实现的,程序应该比较复杂,因为要存储几种编码芯片的编码格式

出0入0汤圆

发表于 2005-2-21 14:22:03 | 显示全部楼层
To:Jacky 边城浪子

现在手头上有点事就是给人做的控制器啊,用的是holtek的芯片(比较便宜),但是都一样因为我只用了普通的I/O口和一个定时器,所以,估计这两天会先发一个程序流图上来,完整的程序等我做完手头上的项目就尽快搞定,如果不在线可以发邮件给我的,咱们多多交流啊

出0入27汤圆

发表于 2005-3-10 13:59:26 | 显示全部楼层
这里有一个现成的

http://7i8i.com/cppp/2.htm

出0入0汤圆

发表于 2005-4-13 21:06:04 | 显示全部楼层
我用89C51做过红外线遥控单片机系统的键盘实验,十几个按键,效果还可以。

出0入0汤圆

发表于 2005-4-14 00:10:00 | 显示全部楼层
看看有什么成果.

出0入0汤圆

发表于 2005-4-14 01:28:26 | 显示全部楼层
可以采用AVR芯片的Input Cature Unit来抓取跳变,无须时刻查询io口变化,也无须时刻比较计时器计数值,Input Cature Unit会自动把发生跳变时的定时器计数值保存起来,你只要判断一下该值满足一个脉冲范围即可。

出0入0汤圆

发表于 2007-6-6 23:16:31 | 显示全部楼层
hello_lzh:你好!你的程序能不能贴出给大家作个参考啊

出0入0汤圆

发表于 2007-6-6 23:59:09 | 显示全部楼层
这个东西我很早以前就做成功了,并且同时支持两种遥控器(格式不同),另外当时为了得到遥控编码,本来是想用示波器,可惜记录功能不象想象的那么好用,无法录取一整个波型,最后自己做了个接口电路,接到计算机的声卡上了,效果非常好,用录音软件录音就行了,大家可以自己试一下。小心点就行。

出0入0汤圆

发表于 2007-6-7 02:57:37 | 显示全部楼层
我好想示波器!!!!

现在买了吧。。哈。。。

出0入0汤圆

发表于 2007-6-7 08:14:32 | 显示全部楼层
MCU的IO口直接进行红外解码、无线解码,我都做过,考虑到抗干扰的实际效果,最好采用定时器中断每隔一定时间扫描电平的方式,特别是无线解码,干扰太多了,如果用开外部中断,MCU基本上没法干其他事情了。

出0入0汤圆

发表于 2007-6-7 08:23:55 | 显示全部楼层
用红外解码基本不用考虑干扰,用无线的我也做过,就是2262的软解码,实际运行过程中在没有信号的时候,噪声非常严重。其实红外的编码没有几种的,大约就是三种类型的(我实际见过两种,另外一种是从别人的资料里面看到的),出现这么多的型号的遥控器,其实就是厂家码不同而己。如果是用AVR单片机解码更加方便了,因为可以用捕捉方式,对了,空调的遥控器与此方式不同,我还没实际用过。不过现在遥控器(红外)我有办法取得编码了,呵呵。

出0入0汤圆

发表于 2007-6-7 15:10:29 | 显示全部楼层
各位大虾,无线电怎么发码、解码啊,是不是和红外一样啊?谢谢

我想做个无线电的发射和接收,距离100M到200M,用单片机,有没有兄弟可以提供电路图参考啊,不胜感激!!

出0入0汤圆

发表于 2007-6-8 08:48:44 | 显示全部楼层
关注中
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-12 22:04

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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