用家里旧的红外遥控器控制树莓派小车

栏目: 软件资讯 · 发布时间: 5年前

内容简介:红外遥控是一种无线、非接触控制技术,在工业控制、航空航天、家电等领域都得到了广泛应用。大部分的电视的遥控器,空调遥控器就都是红外遥控。在baidu上检索了树莓派小车的各种控制方案,没有找到红外遥控的控制方案。所以本文尝试使用红外控制方案对树莓派小车进行控制。1. 树莓派小车。(树莓派小车的安装不是本文重点,如果读者不熟悉小车的安装,请自行搜索。)2. 红外接收头

0×01 前言

红外遥控是一种无线、非接触控制技术,在工业控制、航空航天、家电等领域都得到了广泛应用。大部分的电视的遥控器,空调遥控器就都是红外遥控。在baidu上检索了树莓派小车的各种控制方案,没有找到红外遥控的控制方案。所以本文尝试使用红外控制方案对树莓派小车进行控制。

0×02 所需材料

1. 树莓派小车。(树莓派小车的安装不是本文重点,如果读者不熟悉小车的安装,请自行搜索。)

2. 红外接收头

型号:VS1838B(价格很便宜,几毛钱一个。)

用家里旧的红外遥控器控制树莓派小车

3. 家里废旧的红外遥控器

用家里旧的红外遥控器控制树莓派小车

0×03 红外通讯协议学习

遥控器的基带通信协议很多,大概有几十种,用的最多的就是 NEC 协议了,下面开始了解下NEC协议。

用家里旧的红外遥控器控制树莓派小车

图 1 NEC 协议数据格式

NEC 协议的数据格式包括了引导码、用户码、用户码(或者用户码反码)、按键键码和键码反码,最后一个停止位。停止位主要起隔离作用,一般不进行判断,编程时我们也不予理会。其中数据编码总共是 4 个字节 32 位。第一个字节是用户码,第二个字节可能也是用户码,或者是用户码的反码,具体由生产商决定,第三个字节就是当前按键的键数据码,而第四个字节是键数据码的反码,可用于对数据的纠错。

注意: NEC 协议中的每一位数据本身也需要进行编码,编码后再进行载波调制。

引导码:9ms 的载波+4.5ms 的空闲。

比特值“0”:560us 的载波+560us 的空闲。

比特值“1”:560us 的载波+1.68ms 的空闲。

红外接收头,当收到有载波的信号的时候,会输出一个低电平,空闲的时候会输出高电平,我们用逻辑分析仪抓出来一个红外按键通过解码后的图形来了解一下,如图 2 所示。

用家里旧的红外遥控器控制树莓派小车

图 2  红外遥控器按键编码

从图上可以看出,先是 9ms 载波加 4.5ms 空闲的起始码,数据码是低位在前,高位在后,数据码第一个字节是 8 组 560us 的载波加 560us 的空闲,也就是 0×00,第二个字节是 8 组 560us的载波加 1.68ms 的空闲,可以看出来是 0xFF,这两个字节就是用户码和用户码的反码。按键的键码二进制是 0x0C,反码就是 0xF3,最后跟了一个 560us 载波停止位。对于我们的遥控器来说,不同的按键,就是键码和键码反码的区分,用户码是一样的。这样我们就可以通过单片机的程序,把当前的按键的键码给解析出来。

额外提一句:空调的遥控器比较特殊,用户码和键盘均存储数据。

0×04 监听红外遥控

VS1838B的3个管脚分别为OUT、GND、VCC。当收到有载波的信号的时候,OUT管脚会输出一个低电平,空闲的时候会输出高电平。

本次将树莓派的18引脚作为红外接收引脚。

监听红外的机能代码如下:

#define IR_INPUT_PIN  (18)

#define ERROR  (0xFE)

// 请事先破获遥控器的各个键的按键码,然后与小车的前、后、左、右、停的行为相对应。
// 以下5个值需要与遥控器的按键码匹配。
#define IR_CONTROL_KEY_UP       (0x01)
#define IR_CONTROL_KEY_DOWN     (0x02)
#define IR_CONTROL_KEY_LEFT     (0x03)
#define IR_CONTROL_KEY_RIGHT    (0x04)
#define IR_CONTROL_KEY_STOP     (0x05)

// 信号量
sem_t g_edge_falling, g_edge_rising;

void setup();
void edgeFalling();
void edgeRising();
void GPIO_wait_for_edge(int event);

bool IRStart();
unsigned char getByte();
unsigned char getKey();

void setup()
{
    if(-1==wiringPiSetup())
    {
        std::cerr<<"wiringPi setup error"<<std::endl;
        exit(-1);
    }
    
    pinMode(IR_INPUT_PIN,INPUT);            //配置引脚为输入
    pullUpDnControl(IR_INPUT_PIN,PUD_UP);   //引脚上拉到3.3v

    //注册中断处理函数
    if(0>wiringPiISR(IR_INPUT_PIN,INT_EDGE_FALLING,edgeFalling))
    {
        std::cerr<<"interrupt function[INT_EDGE_FALLING] register failure"<<std::endl;
        exit(-1);
    }

    if(0>wiringPiISR(IR_INPUT_PIN,INT_EDGE_RISING,edgeRising))
    {
        std::cerr<<"interrupt function[INT_EDGE_RISING] register failure"<<std::endl;
        exit(-1);
    }
}

void edgeFalling() {
    sem_post(&g_edge_falling);
}

void edgeRising() {
    sem_post(&g_edge_rising);
}

void GPIO_wait_for_edge(int event){

    if(INT_EDGE_FALLING==event){
        sem_init(&g_edge_falling, 0, 0);
        sem_wait(&g_edge_falling);
    }
    else if(INT_EDGE_RISING==event) {
        sem_init(&g_edge_rising, 0, 0);
        sem_wait(&g_edge_rising);
    }
}

bool IRStart() {
    time_t timeFallingEdge[] = {0,0};
    time_t timeRisingEdge = 0;
    time_t timeSpan[] = {0, 0};

    //GPIO.wait_for_edge(PIN, GPIO.FALLING);
    GPIO_wait_for_edge(INT_EDGE_FALLING);
    timeFallingEdge[0] = time(NULL);

    //GPIO.wait_for_edge(PIN, GPIO.RISING);
    GPIO_wait_for_edge(INT_EDGE_RISING);
    timeRisingEdge = time(NULL);

    //GPIO.wait_for_edge(PIN, GPIO.FALLING);
    GPIO_wait_for_edge(INT_EDGE_FALLING);

    timeFallingEdge[1] = time(NULL);
    timeSpan[0] = timeRisingEdge - timeFallingEdge[0];
    timeSpan[1] = timeFallingEdge[1] - timeRisingEdge;
    if (timeSpan[0] > 0.0085 && \
       timeSpan[0] < 0.0095 && \
       timeSpan[1] > 0.004 && \
       timeSpan[1] < 0.005){
        return true;
    }
    else {
        return false;
    }
}

unsigned char getByte() {
    unsigned char byte = 0;
    time_t timeRisingEdge = 0;
    time_t timeFallingEdge = 0;
    time_t timeSpan = 0;
    for(int i =0; i<8; ++i) {
        //GPIO.wait_for_edge(PIN, GPIO.RISING);
        GPIO_wait_for_edge(INT_EDGE_RISING);
        timeRisingEdge = time(NULL);
        
        //GPIO.wait_for_edge(PIN, GPIO.FALLING);
        GPIO_wait_for_edge(INT_EDGE_FALLING);
        timeFallingEdge = time(NULL);
        timeSpan = timeFallingEdge - timeRisingEdge;
        if(timeSpan > 0.0016 && timeSpan < 0.0018) {
            byte |= 1 << i;
        }
    }

    return byte;
}

unsigned char getKey() {
    unsigned char byte[] = {0x00,0x00,0x00,0x00};

    if(false==IRStart()) {
        sleep(0.11);   // One message frame lasts 108 ms.
        return ERROR;
    }
    else {
        for( int i=0;i<4;++i) {
            byte[i] = getByte();
        }

        if((byte[0] + byte[1] == 0xff)&&(byte[2] + byte[3] == 0xff)) {
            return byte[2];
        }
        else {
            return ERROR;
        }
    }
}



void *listenIRThread(void *arg) {

    printf("IRM Test Start ...\n");

    setup();

    while(true) {
        unsigned char key = getKey();
        if(key != ERROR) {
            printf("Get the key: 0x%02x\n",key);

            switch(key)
            {
                case IR_CONTROL_KEY_UP:
                {
                    // 前进
                    std::cout << "command: CARRUN FORWARD"<< std::endl;
        
                    DirectionReq *req = new DirectionReq();
                    req->setValue(DIRECTION_FORWARD);
                    ControlManager::instance()->postActionReq(req);

                    break;
                }
        
                case IR_CONTROL_KEY_DOWN:
                {
                    // 后退
                    std::cout << "command: CARRUN BACK"<< std::endl;
        
                    DirectionReq *req = new DirectionReq();
                    req->setValue(DIRECTION_BACK);
                    ControlManager::instance()->postActionReq(req);\

                    break;
                }

                case IR_CONTROL_KEY_LEFT:
                {
                    // 左转
                    std::cout << "command: CARRUN LEFT"<< std::endl;
        

                    DirectionReq *req = new DirectionReq();
                    req->setValue(DIRECTION_LEFT);
                    ControlManager::instance()->postActionReq(req);   

                    break;
                }

                case IR_CONTROL_KEY_RIGHT:
                {
                    // 右转
                    std::cout << "command: CARRUN RIGHT"<< std::endl;
        
                    DirectionReq *req = new DirectionReq();
                    req->setValue(DIRECTION_RIGHT);
                    ControlManager::instance()->postActionReq(req);    

                    break;
                }
                
                case IR_CONTROL_KEY_STOP:
                {
                    // 停车
                    std::cout << "command: CARRUN STOP"<< std::endl;
        
                    StatusReq *req = new StatusReq();
                    ControlManager::instance()->postStatusReq(req);

                    break;
                }

                default:
                    break;
            }
        }

    }
}

0×05 控制小车动作

本次使用 python 语言提供的接口来控制小车的动作:前进、后退、左转、右转、停止。

用python控制小车动作的代码如下:

 #!/usr/bin/Python
# -*- coding: UTF-8 -*-

#引入gpio的模块
import RPi.GPIO as GPIO
import time


#设置in1到in4接口
IN1 = 12
IN2 = 16
IN3 = 18
IN4 = 22

#初始化接口
def car_init():
    #设置GPIO模式
    GPIO.setmode(GPIO.BOARD)

    GPIO.setup(IN1,GPIO.OUT)
    GPIO.setup(IN2,GPIO.OUT)
    GPIO.setup(IN3,GPIO.OUT)
    GPIO.setup(IN4,GPIO.OUT)

#前进的代码
def car_forward():
    GPIO.output(IN1,GPIO.HIGH)
    GPIO.output(IN2,GPIO.LOW)
    GPIO.output(IN3,GPIO.HIGH)
    GPIO.output(IN4,GPIO.LOW)
    time.sleep(0.15)
    GPIO.cleanup()

#后退
def car_back():
    GPIO.output(IN1,GPIO.LOW)
    GPIO.output(IN2,GPIO.HIGH)
    GPIO.output(IN3,GPIO.LOW)
    GPIO.output(IN4,GPIO.HIGH)
    time.sleep(0.15)
    GPIO.cleanup()

#左转
def car_left():
    GPIO.output(IN1,False)
    GPIO.output(IN2,False)
    GPIO.output(IN3,GPIO.HIGH)
    GPIO.output(IN4,GPIO.LOW)
    time.sleep(0.15)
    GPIO.cleanup()

#右转
def car_right():
    GPIO.output(IN1,GPIO.HIGH)
    GPIO.output(IN2,GPIO.LOW)
    GPIO.output(IN3,False)
    GPIO.output(IN4,False)
    time.sleep(0.15)
    GPIO.cleanup()

#停止
def car_stop():
    GPIO.output(IN1,GPIO.LOW)
    GPIO.output(IN2,GPIO.LOW)
    GPIO.output(IN3,GPIO.LOW)
    GPIO.output(IN4,GPIO.LOW)
    GPIO.cleanup()

0×06 结束

到此红外遥控器控制树莓派小车小车的控制系统就介绍完了。

本文重点讲解的是红外协议的解析部分,而小车的控制策略部分没有详细讲解,读者可以参考我之前写的几篇小车相关的文章,进行了解。

最后,整套代码已经发到了百度网盘上。

链接: https://pan.baidu.com/s/1vHUFd8Uui17EIlkehNClrw 提取码: q49

*本文作者:xutiejun,转载请注明来自FreeBuf.COM


以上所述就是小编给大家介绍的《用家里旧的红外遥控器控制树莓派小车》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

白帽子讲浏览器安全

白帽子讲浏览器安全

钱文祥 / 电子工业出版社 / 2016-3 / 79.00元

浏览器是重要的互联网入口,一旦受到漏洞攻击,将直接影响到用户的信息安全。作为攻击者有哪些攻击思路,作为用户有哪些应对手段?在《白帽子讲浏览器安全》中我们将给出解答,带你了解浏览器安全的方方面面。《白帽子讲浏览器安全》兼顾攻击者、研究者和使用者三个场景,对大部分攻击都提供了分析思路和防御方案。《白帽子讲浏览器安全》从攻击者常用技巧的“表象”深入介绍浏览器的具体实现方式,让你在知其然的情况下也知其所以......一起来看看 《白帽子讲浏览器安全》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具