红外遥控发射器(补)

先上张电路图。pcspkr就是PC机上小喇叭的线,当设定为某个频率时,会发出该频率的[0,+5v]方波信号。当在相应寄存器中写入0时,该引脚无信号。这样只要控制该寄存器值就能控制红外遥控信号的发出了。红外遥控器有一个基准频率,一般为36KHz或者38KHz,视具体的电视机而定,当频率偏离该值时,遥控距离迅速减小(猜测是因为无法达到足够的共振频率)。

image

上次软件篇中提到不能驱动pcspkr发出大于32khz的信号,事实证明这是pcspkr kernel module的一个限制。

http://lxr.free-electrons.com/source/drivers/input/misc/pcspkr.c

 50         if (value > 20 && value < 32767)
 51                 count = PIT_TICK_RATE / value;

这样pcspkr不能发出低于20HZ或者超过32767HZ的声音。严格的说这不算是一个bug,因为这已经属于人耳无法听到的范畴了,但是对想要发出高频红外信号的我来说,却是不小的困惑。

继续研究后,发现Linux下可以通过直接对端口读写来绕过这个限制。这样只需要在应用程序中使用pcspkr.c驱动中的这一小段code就行了。大概曾经在DOS下写过pc小喇叭程序的朋友应该都很熟悉这段code.

 55         if (count) {
 56                 /* set command for counter 2, 2 byte write */
 57                 outb_p(0xB6, 0x43);
 58                 /* select desired HZ */
 59                 outb_p(count & 0xff, 0x42);
 60                 outb((count >> 8) & 0xff, 0x42);
 61                 /* enable counter 2 */
 62                 outb_p(inb_p(0x61) | 3, 0x61);
 63         } else {
 64                 /* disable counter 2 */
 65                 outb(inb_p(0x61) & 0xFC, 0x61);
 66         }

在调试中发现红外发射管的发射强度比较弱(用摄像头和电视遥控器比较结果),于是把电阻换成47欧姆的了!这样最大电流是80毫安,但是因为方波信号的duty cycle是1/2,所以平均电流是40毫安,小于手册的最大持续电流,应该是安全的。PCSPKR只能发出方波信号,这种情况下反而成为一种优点了。

关于遥控器的代码,可以到lirc的网站上去下一个各种remote的网站的大集http://www.lirc.org/remotes.tar.bz2,只要你的遥控器不是太冷门,一般总能找到遥控器的代码。关于遥控器的编码格式可以从这篇博客入门http://jimlu.spaces.live.com/blog/cns!9B1C2AEA8D078F9A!609.entry?sa=211980024,lirc的文档中也能找到不少资料。

参考了下lirc的网站,各种home brew的红外接收发射方案都有,有用串口或者并口的,比较夸张的是通过声卡的line in接口做红外接收的信号分析的。不过貌似用pcspkr的没有,算是pc控制红外发射的一种新方法,呵呵。

最后放上程序,虽然不是通用的,不过稍加修改应该可以适用各种情况。

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/io.h>
#include <sys/ioctl.h>
#include <linux/kd.h>
#include “cycle.h”
#define PIT_TICK_RATE 1193182ul
#define PULSE_FREQ 38000

int console_fd;
struct remote_space_enc {
    int header[2];
    int one[2];
    int zero[2];
    int predata;
    int predata_bits;
    int trail[2];
    int toggle_bit_mask;
};

struct remote_space_enc panasonic_tv =
    {
        {3430, 1700},
        {440, 1320},
        {440, 440},
        0x400401,
        24,
        {400,74158},
        0x0,
    };

inline void vsleep (usec)
{
    ticks tick1 = getticks(), tick2;
    while(1) {
        tick2 = getticks();
        if (elapsed(tick2, tick1) > usec*1600) {
            break;
        }
    }
}

inline void _0 ()
{
    /* disable counter 2 */
        outb(inb(0x61) & 0xFC, 0x61);

}

inline void _1 ()
{
    int count = PIT_TICK_RATE/PULSE_FREQ;
    /* set command for counter 2, 2 byte write */
    outb(0xB6, 0x43);
    /* select desired HZ */
    outb(count & 0xff, 0x42);
    outb((count >> 8) & 0xff, 0x42);
    /* enable counter 2 */
    outb(inb(0x61) | 3, 0x61);
}

inline void pulse(int usec)
{
    _1();
    vsleep (usec);
}

inline void space(int usec)
{
    _0();
    vsleep (usec);
}

inline void send_bits (int one[2], int zero[2], int code, int width)
{
    int i;
    int mask;

    mask = 1<<(width-1);

    for (i=0;i<width;i++) {
        if (code & mask) {
            pulse (one[0]);
            space (one[1]);
        } else {
            pulse (zero[0]);
            space (zero[1]);
        }
        mask = mask >> 1;
    }
}

inline void send_code (struct remote_space_enc * remote, int code, int width)
{
    send_bits(remote->header, remote->zero, 1, 1);
    send_bits(remote->one, remote->zero, remote->predata, remote->predata_bits);
    send_bits(remote->one, remote->zero, code, width);
    send_bits(remote->trail, remote->zero, 1, 1);
}

int main(int argc, char** argv)
{
    int j;
    int len;
    int code;
    if (argc<=1) {
        printf (“Must supply the signal you want to send\n”);
        exit (1);
    }
    console_fd = open (“/dev/console”, O_WRONLY);
    if (console_fd == -1) {
        printf (“open console file handle failed\n”);
        exit (1);
    }
    ioperm (0x42, 2, 1);
    ioperm (0x61, 2, 1);
     len = strlen(argv[1]);
    code = 0;
    for (j=0;j<len;j++) {
        if (argv[1][j] >= ‘0’ && argv[1][j] <= ‘9’) {
            code = (code << 4) + argv[1][j] – ‘0’;
        } else if (argv[1][j] >= ‘a’ && argv[1][j] <= ‘f’) {
            code = (code << 4) + argv[1][j] – ‘a’ + 10;
        } else if (argv[1][j] >= ‘A’ && argv[1][j] <= ‘F’) {
            code = (code << 4) + argv[1][j] – ‘A’ + 10;
        } else {
            break;
        }
    }
    send_code (&panasonic_tv, code, j*4);
    _0();
    close (console_fd);
    printf (“\n”);
    return 0;
}

“红外遥控发射器(补)”的一个回复

发表评论

邮箱地址不会被公开。 必填项已用*标注