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
上一篇: 多条件筛选数据