欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

android 红外遥控器实现原理

程序员文章站 2022-07-04 12:27:13
...

一、红外遥控器是什么鬼

现有的红外遥控器有两种:一种是PWM(脉冲宽度调制),另外一种是PPM(脉冲位置调制);

这两种调制方式对应两种编码形式NEC(PWM对应的编码形式)和philips的RC-5,RC-6,RC-7;

说明:Linux内核中,红外驱动仅支持NEC编码格式,所以本文只讨论NEC编码格式的红外遥控器;

 

1)、PWM(脉冲宽度调制)原理

说是原理,其实说白一点,就是0和1要怎么表示

二进制0表示方法:以脉宽为0.565ms、间隔0.565ms、周期为1.125ms的组合表示二进制0

二进制1表示方法:以脉宽为0.565ms、间隔1.685ms、周期为2.25ms的组合表示二进制1

2)、红外遥控器产生红外信号原理

红外遥控器产生的0和1红外信号形式如下图:

android 红外遥控器实现原理

红外遥控器二进制0调制原理如下图:

android 红外遥控器实现原理

说明:38K载波可提高信号抗干扰和传输距离

红外遥控器发送和接收动态原理图如下:

android 红外遥控器实现原理

3)、红外遥控器NEC编码原理

NEC编码,说白了,就是规范一窜01数据表示的意义,其实上面提到的0和1的表示也是属于NEC协议规范的

NEC协议数据格式如下图:

android 红外遥控器实现原理

android 红外遥控器实现原理

说明:一个红外码的周期是108ms,如果长按红外按键不放,会以108ms为周期重复发Repaet(重复码)

(引导码+0.56ms低电平2.25ms高电平组合)

二、Linux内核红外遥控器驱动原理

驱动要干的事情主要是:NEC解码、键码映射、按键上报

 

1)、NEC解码

NEC解码是通过中断方式处理的,以RTD1296为例分析:

RTD1296红外驱动位于:drivers/soc/realtek/rtd129x/irda/venus_ir.c

驱动代码中irq注册:request_irq(ir_dev->ir_irq, rtk_ir_isr, IRQF_SHARED, "Venus_IR", rtk_ir_isr)

接下来看 rtk_ir_isr 这个中断处理函数

这个函数中并没有NEC软件解码部分,而是直接从寄存器读取红外码(NEC解码是硬件完成的)

我们在来看下RK3288的:

RK3288红外驱动位于:drivers/input/remotectl/rockchip_pwm_remotectl.c

驱动代码中irq注册:devm_request_irq(&pdev->dev, irq, rockchip_pwm_irq,IRQF_NO_SUSPEND, "rk_pwm_irq", ddata);

NEC软解码任务:tasklet_init(&ddata->remote_tasklet, rk_pwm_remotectl_do_something,(unsigned long)ddata);

2)、键码映射

以RTD1296为例:

RTD1296红外驱动位于:drivers/soc/realtek/rtd129x/irda/venus_ir.c

驱动代码中映射函数:venus_ir_IrMessage2Keycode(unsigned int message, unsigned int *p_keycode)

映射表在dts:arch/arm64/boot/dts/realtek/rtd-1295-irT377.dtsi 中指明

3)、按键上报

按键上报调用Linux 内核标准API:input_report_key(struct input_dev *dev, unsigned int code, int value)  

// value 1表示按键按下 0表示按键弹起

三、Android层红外键码处理原理

android framework成会去获取键码

代码位于:frameworks/native/services/inputflinger/EventHub.cpp  的 getEvents

input_event& iev = readBuffer[i]  获取Linux 内核上报的键码

scanDevicesLocked()  主要是扫描/dev/input下输入设备,并加载键码映射表

loadKeyMapLocked(device)  加载键码映射表

说明:键码映射表KeyLayout File 文件格式 Vendor_vendor_Product_product.kl 或 name.kl 

文件放置于固件out/.../system/usr/keylayout/ 下

再上去最后会到 frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

函数interceptKeyBeforeQueueing()做具体的业务处理

至此,android红外遥控器实现原理结束。

四、Android层红外码获取详解

1)、先以getevent 命令说起:

代码位于:android/system/core/toolbox/getevent.c

功能:不断读取/dev/input/* 下设备事件

2)、再来看看,android EventHub是怎么获取/dev/input/* 下设备事件

代码位于:android/frameworks/native/services/inputflinger/EventHub.cpp

EventHub::getevents分析:

int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);

epoll_wait利用linux INotify+Epoll原理监听 /dev/input/*下目录变化

if (eventItem.events & EPOLLIN) {
                int32_t readSize = read(device->fd, readBuffer,
                        sizeof(struct input_event) * capacity);

}

read 读取 /dev/input/inputx 内容

3)、在EventHub 之上的是 InputReader

再查找EventHub被谁调用: find ./framework -name "*.cpp" |grep -v EventHub |xargs grep EventHub

./native/services/inputflinger/InputReader.cpp:InputReader::InputReader(const sp<EventHubInterface>& eventHub,
./native/services/inputflinger/InputReader.cpp:        mContext(this), mEventHub(eventHub), mPolicy(policy),
./native/services/inputflinger/InputReader.cpp:    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
.
.
.
./native/services/inputflinger/InputManager.cpp:        const sp<EventHubInterface>& eventHub,
./base/services/core/jni/com_android_server_input_InputManagerService.cpp:    sp<EventHub> eventHub = new EventHub();



1、发现 InputReader.cpp 调用了EventHub getEvents
2、com_android_server_input_InputManagerService.cpp 实例化 EventHub

InputReader::loopOnce()   调用 getEvents

bool InputReaderThread::threadLoop() {

     mReader->loopOnce();

     return true;

}

再查找InputReader又是被谁调用的: find ./ -name "*.cpp" |grep -v InputReader |xargs grep InputReader

./native/services/inputflinger/InputManager.cpp:        const sp<InputReaderPolicyInterface>& readerPolicy,
./native/services/inputflinger/InputManager.cpp:    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
./native/services/inputflinger/InputManager.cpp:        const sp<InputReaderInterface>& reader,
./native/services/inputflinger/InputManager.cpp:    mReaderThread = new InputReaderThread(mReader);
./native/services/inputflinger/InputManager.cpp:    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
./native/services/inputflinger/InputManager.cpp:        ALOGE("Could not start InputReader thread due to error %d.", result);
./native/services/inputflinger/InputManager.cpp:        ALOGW("Could not stop InputReader thread due to error %d.", result);
./native/services/inputflinger/InputManager.cpp:sp<InputReaderInterface> InputManager::getReader() {

InputReader 和 InputReaderThread 都在 InputManager 中被实例化

再InputManager 中

void InputManager::initialize() {

    mReaderThread = new InputReaderThread(mReader);

    mDispatcherThread = new InputDispatcherThread(mDispatcher);

}

找到了InputDispatcherThread, InputReaderThread获取到的数据会交给InputDispatcherThread(InputDispatcher)处理

4)、从InputReader到InputDispatcher

代码位于: ./android/framework/native/services/inputflinger/InputDispatcher.cpp

待续...
   
   

                 
                 
 

 

adb调试相关命令:

cat /proc/bus/input/devices  查看input设备

dumpsys input 查看按键设备对应的键值映射表

getevent -l  查看输入event事件

 

参考链接:

加载按键映射表: https://www.cnblogs.com/TaigaCon/p/4763035.html

按键处理流程:http://www.bubuko.com/infodetail-2288719.html

android层input流程:https://blog.csdn.net/u010122827/article/details/49738811