android 红外遥控器实现原理

一、红外遥控器是什么鬼

现有的红外遥控器有两种:一种是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红外信号形式如下图:

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

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

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

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

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

NEC协议数据格式如下图:

说明:一个红外码的周期是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

 

猜你喜欢

转载自blog.csdn.net/zhanxingshan007/article/details/81096124