IR遥控分析资料:
1、红外解码器对红外遥控输入信号进行解码。支持两种操作模式:
[1]、硬件解码IR传输协议兼容帧解码器模式(NEC 三菱 Thomson 东芝 Sony SIRC RC5 RC6 RCMM Duokan Comcast Sanyo模式)
[2]、通用可编程时间测量帧解码器模式(通用模式)。
2、红外解码说明
2.1在硬件解码模式下,解码器采用信号模式搜索机制对数据帧进行解码。它可以检测逻辑0、1、00、01、10和11,以及数据帧的开始和结束。每当解码器检测和解码数据帧时,数据都保存在数据寄存器中
2.2在一般模式下,解码器采用边缘检测机制对数据帧进行解码。它可以检测每个输入信号的边缘,并记录两个边缘之间的时间。时间测量结果保存在控制寄存器中
2.3用户应根据遥控器的选择,设置适当的操作模式【重点】。
2.4在信号输入和解码器之间有一个简单的基于时间的信号滤波器。该滤波器可编程,有助于提高信号的完整性
3、红外译码器功能块中的寄存器分为3组
3.1控制和时钟寄存器,用于配置红外解码器和获取解码器状态。它也用于配置时钟检测速率。
3.2调谐寄存器用于调整硬件解码器模式的检测参数。
3.3数据寄存器,用于保存硬件解码器模式下的解码数据。
红外解码器有13种工作模式:12种硬件解码模式和一般模式。
在硬件解码模式下,数据帧基于硬件解码红外传输协议。
在一般模式下,可以对软件进行编程以适应其他传输协议,这些协议应该是基于脉冲距离编码的;
嵌入式处理器与红外译码器之间的通信是基于中断的。启用IR解码器并检测到任何有效的远程控制器信号输入后,将产生一个中断。处理器通过读取IR译码寄存器来处理中断并获取详细信息;
红外解码器的信号检测和解码是基于时间的。在所有的检测和计算处理中,将元素时间间隔称为T率,作为基本的检测窗口时间单元。软件可以通过设置适当的值到寄存器 AO_MF_IR_DEC_REG0 来配置速率;
滤波器可用于过滤输入信号在任何一种工作模式。滤波器的工作逻辑是:在信号从低到高的上升沿后,信号保持在高的时间最少的滤波器T滤波器前被接受为有效的高信号。在信号从高到低的下降沿之后,
信号至少要保持在低的T滤波器上,才能被接受为有效的低信号。
4、驱动代码流程浅析:
4.1dts部分,文件:common/arch/arm64/boot/dts/amlogic/mesontxlx.dtsi
306 remote_pins:remote_pin{
307 amlogic,setmask = <AO 0x1>;
308 amlogic,clrmask = <AO 0x200000>;
309 amlogic,pins = "GPIOAO_6";
310 };
759 remote:rc@0xff808040 {
760 compatible = "amlogic, aml_remote";
761 dev_name = "meson-remote";
762 reg = <0x0 0xff808040 0x00 0x44>,
763 <0x0 0xff808000 0x00 0x20>;
764 status = "okay";
765 protocol = <REMOTE_TYPE_NEC>; //配置IR 控制器的初始模式->支持的协议,这里默认配置NEC
766 interrupts = <0 196 1>;
767 pinctrl-names = "default";
768 pinctrl-0 = <&remote_pins>;
769 map = <&custom_maps>;
770 max_frame_time = <200>; /*set software decoder max frame time*/
771 };
773 custom_maps:custom_maps {
774 mapnum = <3>;
775 map0 = <&map_0>;
776 map1 = <&map_1>;
777 map2 = <&map_2>;
778 map_0: map_0{
779 mapname = "amlogic-remote-1";
780 customcode = <0xfb04>;
781 release_delay = <80>;
782 size = <44>; //keymap size
783 keymap = <REMOTE_KEY(0x01, KEY_1)
784 REMOTE_KEY(0x02, KEY_2)
785 REMOTE_KEY(0x03, KEY_3)
786 REMOTE_KEY(0x04, KEY_4)
787 REMOTE_KEY(0x05, KEY_5)
788 REMOTE_KEY(0x06, KEY_6)
789 REMOTE_KEY(0x07, KEY_7)
790 REMOTE_KEY(0x08, KEY_8)
791 REMOTE_KEY(0x09, KEY_9)
792 REMOTE_KEY(0x0a, KEY_0)
793 REMOTE_KEY(0x1F, KEY_FN_F1)
794 REMOTE_KEY(0x15, KEY_MENU)
795 REMOTE_KEY(0x16, KEY_TAB)
796 REMOTE_KEY(0x0c, KEY_CHANNELUP)
797 REMOTE_KEY(0x0d, KEY_CHANNELDOWN)
798 REMOTE_KEY(0x0e, KEY_VOLUMEUP)
799 REMOTE_KEY(0x0f, KEY_VOLUMEDOWN)
800 REMOTE_KEY(0x11, KEY_HOME)
801 REMOTE_KEY(0x1c, KEY_RIGHT)
802 REMOTE_KEY(0x1b, KEY_LEFT)
803 REMOTE_KEY(0x19, KEY_UP)
804 REMOTE_KEY(0x1a, KEY_DOWN)
805 REMOTE_KEY(0x1d, KEY_ENTER)
806 REMOTE_KEY(0x17, KEY_MUTE)
807 REMOTE_KEY(0x49, KEY_FINANCE)
};
};
4.2部分代码构成,其他可到该目录查看:
drivers/amlogic/input/remote/remote_meson.c //中心代码
drivers/amlogic/input/remote/remote_cdev.c //字符设备相关代码
drivers/amlogic/input/remote/remote_regmap.c //寄存器操作相关代码
drivers/amlogic/input/remote/remote_core.c //框架相关代码
drivers/amlogic/input/remote/sysfs.c //文件系统相关代码
drivers/amlogic/input/remote/remote_raw.c //原始码值的接收和处理
我这里从remote_probe函数开始跟:
我这里从remote_probe函数开始跟:
->remote_allocate_device;//
->dev->input_device = input_allocate_device(); //分配并注册一个输入设备
->ir_input_device_init(dev->input_device, &pdev->dev, "aml_keypad"); //初始化输入子设备
->ir_hardware_init(struct platform_device *pdev) //初始化IR设备
---------------------------------------------------------------
->ret = ir_get_devtree_pdata(pdev); //这里去获取dts的配置参数
->ret = of_property_read_u32(pdev->dev.of_node,"protocol", &chip->protocol); //这里解析dts配置的protocol参数,设置ir的协议支持,
//如chip->protocol = 1(NEC协议),protocol的宏定义在./include/dt-bindings/input/meson_rc.h中
//如果没有设置,则默认是支持进入NEC协议(chip->protocol = 1)模式
->ret = get_custom_tables(pdev->dev.of_node, chip); //获取在dts的客制映射信息,红外码值和linux code映射以及其他参数
->chip->set_register_config(chip, chip->protocol); //设置IR寄存器进入对应chip->protocol配置的协议模式
->chip->set_register_config = ir_register_default_config; //配置IR寄存器,函数原型在remote_regmap.c中
->ir_contr_init(struct remote_chip *chip, int type, unsigned char id); //配置ir寄存器初始化
->remote_reg_write(chip, LEGACY_IR_ID, REG_REG1, 0x0); //禁用旧的红外控制器
->ir_contr_init(chip, MULTI_IR_TYPE_MASK(type), MULTI_IR_ID); //初始化多格式红外控制器的寄存器,设置IR控制器进入对应的IR协议模式
//MULTI_IR_ID = 0,在./drivers/amlogic/input/remote/remote_meson.h中定义
->remote_reg_write(chip, id, REG_REG1, 0x01); //重设ir解码器并禁用ir解码器的状态机
->for ( ; size > 0; ) {
remote_reg_write(chip, id, reg_map->reg, reg_map->val); //根据reg_map,依次设置当前IR控制器寄存器值
}
->ret = request_irq(chip->irqno, ir_interrupt, IRQF_SHARED, "keypad", (void *)chip); //这里申请中断,注册中断执行函数: ir_interrupt
/* 有遥控按键的时候会触发中断,调用中断子程序 ir_interrupt */
->在 ir_interrupt 中利用多格式红外控制器的时间测量实现多协议的软件解码(可到该函数去看看)
->if (MULTI_IR_SOFTWARE_DECODE(rc->protocol)) { //识别软件是否解码
-----------------ir_interrupt-----------------
rc->ir_work = MULTI_IR_ID;
duration = val*10*1000;
type = RAW_PULSE;
sprintf(buf, "------\n");
debug_log_printk(rc->r_dev, buf);
->remote_raw_event_store_edge(rc->r_dev, type, duration); //存储原始码值
->rc = remote_raw_event_store(dev, &ev);
->kfifo_in(&dev->raw->kfifo, ev, sizeof(*ev)) != sizeof(*ev) //Kfifo是内核里面的一个first in first out数据结构,
//它采用环形循环队列(ringbuffer)的数据结构来实现;它提供一个无边界的字节流服务
->remote_raw_event_handle(rc->r_dev);
->wake_up_process(dev->raw->thread);
-----------------ir_interrupt----------------
->else
---------------------------------------------
for (cnt = 0; cnt < (ENABLE_LEGACY_IR(rc->protocol)
? 2:1); cnt++) {
remote_reg_read(rc, cnt, REG_STATUS, &contr_status);
if (IR_DATA_IS_VALID(contr_status)) {
rc->ir_work = cnt;
break;
}
}
----------------------------------------------
->tasklet_schedule(&tasklet); //在适当的时候调度tasklet任务机制
->amlremote_tasklet //在tasklet中需要先获取扫描码,然后获取解码状态,状态可能是从获取扫描码功能设置的标志
->scancode = chip->ir_contr[chip->ir_work].get_scancode(chip); //获取扫描码,即原始码值
->.get_scancode = ir_nec_get_scancode,
->ir_nec_get_scancode(struct remote_chip *chip) //NEC获取扫描码的接口函数
----------------- 读取解码器状态值和数据帧寄存器值 ------------------ |---------------------------------|
->remote_reg_read(chip, MULTI_IR_ID, REG_STATUS, &decode_status); // |###### 读取解码器的状态值 ###### |
->chip->decode_status = status; /*set decode status*/ //设置解码器状态|---------------------------------|
|--------------------------|
->remote_reg_read(chip, MULTI_IR_ID, REG_FRAME, &code); // |###读取数据帧寄存器数据###|寄存器定义在./drivers/amlogic/input/remote/remote_meson.h
->chip->r_dev->cur_hardcode = code; |--------------------------|
->code = (code >> 16) & 0xff;
->return code; //经过一定处理[把数据帧寄存器数据左移16bit然后和0xff与运算后取值]后返回得到遥控码值code;
//假如读到数据帧寄存器值为framecode=0x6699bd02的遥控码值是0x99
----------------- 读取解码器状态值和数据帧寄存器值 ------------------
->status = chip->ir_contr[chip->ir_work].get_decode_status(chip); //获取解码状态值
->.get_decode_status = ir_nec_get_decode_status, //NEC获取解码器状态值
->status = chip->decode_status; //解码器状态值为decode_status
->if (status == REMOTE_NORMAL) { //REMOTE_NORMAL == 0,码器状态值为0,说明按键码是新按下的码值
//
remote_dbg(chip->dev, "receive scancode=0x%x\n", scancode);
remote_keydown(chip->r_dev, scancode, status); //上报键值和状态
} else if (status & REMOTE_REPEAT) { //REMOTE_REPEAT == 1, 解码器状态值为1,说明按键码重复之前的码值
//status解码器状态值在./drivers/amlogic/input/remote/remote_core.h宏定义。
remote_dbg(chip->dev, "receive repeat\n");
->remote_keydown(chip->r_dev, scancode, status); //按键重复,上报键值和状态
} else{
dev_err(chip->dev, "receive error %d\n", status);
}
\-----------------------------------------------------------------/
->remote_keydown(chip->r_dev, scancode, status); /*上报键值和状态 */|
| ->ir_report_rel(dev, scancode, status)) { /*报告相对轴事件*/ |
| ->keycode = dev->getkeycode(dev, scancode); |
| ir_do_keydown(dev, scancode, keycode); /*上报按键事件 */ |
| ->if (KEY_RESERVED != keycode) { |
| dev->keypressed = true; |
| dev->last_scancode = scancode; /*红外遥控码值*/ |
| dev->last_keycode = keycode; /*linux code值*/ |
| input_report_key(dev->input_device, keycode, 1); //input子系统上报linux键值码,keycode在include/dt-bindings/input/input.h宏定义,
//这里在adb执行getevent,然后操作遥控可以get到上报的linux code值【16进制数据】
| input_sync(dev->input_device); //这里转换成16进制report
/-----------------------------------------------------------------\
->.get_custom_code = ir_nec_get_custom_code //NEC获取客户码
->custom_code = chip->r_dev->cur_hardcode & 0xffff; //这里是取数据帧寄存器数据的低16位为custom_code
//假如读到数据帧寄存器值为framecode=0x6699bd02, 则客户码是0xbd02
->tasklet_enable(&tasklet); //使能tasklet任务机制
---------------------------------------------------------
->ret = ir_cdev_init(chip); //初始化字符设备[]
->ret = alloc_chrdev_region(&chip->chr_devno, 0, 1, AML_REMOTE_NAME); //动态分配设备号
->ret = ir_sys_device_attribute_init(chip); //初始化sys文件系统
->class_register(&remote_class); //注册class
->dev = device_create(&remote_class, NULL, chip->chr_devno, chip, chip->dev_name); //基于设备类创建设备节点
-------------------------
->static ssize_t protocol_show; //cat节点protocol查看当前的IR协议模式
->static ssize_t protocol_store; //echo > 节点protocol设置选择进入的IR协议模式
->ret = kstrtoint(buf, 0, &protocol); //设置进入对应的红外协议模式
->chip->protocol = protocol;
->chip->set_register_config(chip, chip->protocol);
-------------------------
休眠:
.suspend = remote_suspend,
->remote_suspend(struct platform_device *pdev, pm_message_t state); //休眠调用
->disable_irq(chip->irqno); //关闭中断
唤醒:
.resume = remote_resume,
->remote_resume(struct platform_device *pdev); //唤醒调用
->chip->set_register_config(chip, chip->protocol); //重新设置IR寄存器进入对应chip->protocol配置的协议模式
5、remote相关节点
gtt92x:/ # ls -l sys/devices/virtual/remote/amremote/
total 0
-rw-r--r-- 1 root root 4096 1970-02-24 15:27 debug_enable
-rw-r--r-- 1 root root 4096 1970-02-24 15:27 debug_log
-r--r--r-- 1 root root 4096 1970-02-24 15:27 dev
-rw-r--r-- 1 root root 4096 1970-02-24 15:27 keymap
-r--r--r-- 1 root root 4096 1970-02-24 15:27 map_tables
drwxr-xr-x 2 root root 0 1970-02-24 15:26 power
-rw-r--r-- 1 root root 4096 1970-02-24 15:29 protocol
-rw-r--r-- 1 root root 4096 1970-02-24 15:27 repeat_enable
lrwxrwxrwx 1 root root 0 1970-02-24 15:27 subsystem -> ../../../../class/remote
-rw-r--r-- 1 root root 4096 1970-02-24 15:26 uevent
gtt92x:/ #
节点sys/devices/virtual/remote/amremote/protocol可以设置当前的红外协议模式,当前配置为NEC模式,在dts配置protocol = <REMOTE_TYPE_NEC>;
echo 1 > sys/devices/virtual/remote/amremote/protocol //设置为NEC协议REMOTE_TYPE_NEC模式
echo 131 > sys/devices/virtual/remote/amremote/protocol //设置为RAW协议REMOTE_TYPE_RAW_XMP_1模式
相关的红外协议模式在./include/dt-bindings/input/meson_rc.h中有宏定义,可以根据meson_rc.h往节点protocol写入对应数值设置当前支持的红外协议模式。
17 /*hardware decode one protocol by using multi-format IR controller*/
18 #define REMOTE_TYPE_UNKNOW 0x00
19 #define REMOTE_TYPE_NEC 0x01
20 #define REMOTE_TYPE_DUOKAN 0x02
21 #define REMOTE_TYPE_XMP_1 0x03
22 #define REMOTE_TYPE_RC5 0x04
23 #define REMOTE_TYPE_RC6 0x05
24 #define REMOTE_TYPE_TOSHIBA 0x06
25
26 /*hardware decode one protocol by using legacy IR controller*/
27 #define REMOTE_TYPE_LEGACY_NEC 0xff
28
29 /**
30 *software decode multiple protocols by using Time measurement
31 *of multi-format IR controller
32 *using bit[7] to identify the software decode
33 */
34 #define REMOTE_TYPE_RAW_NEC 0x81
35 #define REMOTE_TYPE_RAW_XMP_1 0x83