前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >RK 平台 IR 红外遥控

RK 平台 IR 红外遥控

作者头像
Jasonangel
发布2024-04-15 12:44:39
3960
发布2024-04-15 12:44:39
举报
文章被收录于专栏:嵌入式Linux系统开发

1、红外简介

红外遥控是我们常见的一种无线收发设备,具有抗干扰能力强,功耗低,成本低,易实现等优点。被很多电子设备采用,如电视遥控、空调遥控等。

红外遥控的发射电路是采用红外发光二极管来发出经过调制的红外光波;红外接收的电路由红外接收二极管、三极管或者硅光电池组成,把发出来的红外光经过转换变成相对应的电信号,再发送到后置放大器中。

目前为止,红外遥控协议已多达十种,如:RC5、SIRCS、Sy、RECS80、Denon、NEC、Motorola、Japanese、SAMSWNG 和 Daewoo 等。目前 RK 平台只支持 NEC 编码的红外协议。

2、红外协议:NEC

红外遥控的编码使用的是 NEC Protocol 的 PWM 机制,PWM 有三种工作模式:reference mode, one-shot mode 和 continuous mode. 红外遥控器就采用 reference mode,这种模式下 PWM 可以捕获输入高低电平的宽度,并产生中断,CPU 接收到中断后去相应的寄存器读取。NEC 协议的特征如下:

  1. 8 位地址和 8 位命令长度
  2. 地址和命令传输两次,以提高可靠性
  3. PWM 脉冲位置调制,以发射红外载波的占空比表“0”和“1“
  4. 载波频率为 38KHz
  5. 位时间 1.125ms 或 2.25ms

NEC 协议使用比特的脉冲距离编码,每个脉冲是一个 560us 的连续载波,一个逻辑“1”传输时间为 2.25ms(560us 脉冲+1680us 低电平),一个逻辑“0”的传输时间为 1.125ms(560us 脉冲+560us 低电平)。

遥控接收头在收到脉冲的时候为低电平,在没有脉冲的时候为高电平,这样,我们接收头端收到的信号为:逻辑“1”是 560us 低加 1680us 高,而逻辑“0”是 560us 低 +560us 高。

上图首先发送 9ms 的 AGC 的高脉冲信号,接着发送 4.5ms 的起始低电平,然后是地址和命令,地址和命令发送两次,第二次是第一次的反码。因此地址和命令加起来是四个字节,分别是地址码、地址反码、命令码、命令反码。按照低位在前,高位在后的顺序发送。采用反码是为了增加传输的可靠性(可用于校验)。

上图显示,一条命令只会发送一次的,即使遥控器上的按键一直按下,每次 110ms 就会发送一个重复码。重复码由 9ms 的 AGC 高脉冲和 2.25 的低电平及 560us 的高电平组成。

逻辑“1”是由 560us 的高电平加 1.69ms 的低电平脉冲组成;逻辑“0”则是由 560us 高电平加 560us 的低电平脉冲组成。

3、RK3399 实例分析

RK 平台 pwm driver 目录如下:

代码语言:javascript
复制
/kernel/drivers/input/remotectl/rockchip_pwm_remotectl.c
/kernel/drivers/pwm/pwm-rockchip.c

红外接收器有很多,如 LF0038GKLL-1、HS0038B、VS1838B 等,博主手里 RK3399 带的是 HS0038B,硬件原理图如下:

红外遥控的驱动使用的是 PWM 接口,所以驱动基本不用写,都是内核里现成的,使用 PWM 的脉冲去测量高低电平的长度,从而计算出相应的红外值,并注册一个 input 子系统,我们的工作只需要在设备树提供对应的设备节点和配置红外值即可。

dts 配置如下,挂在 pwm3 下:

代码语言:javascript
复制
&pwm3 {
        status = "okay";
        interrupts = <GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH 0>;
        compatible = "rockchip,remotectl-pwm";
        remote_pwm_id = <3>;
        handle_cpu_id = <0>;

        ir_key0 {
                rockchip,usercode = <0xff40>;
                rockchip,key_table =
                        <0xb2   KEY_POWER>,
                        <0xe5   KEY_HOME>,
                        <0xbd   KEY_BACK>,
                        <0xba   KEY_MENU>,
                        <0xf4   KEY_UP>,
                        <0xf1   KEY_DOWN>,
                        <0xef   KEY_LEFT>,
                        <0xee   KEY_RIGHT>,
                        <0xf2   KEY_ENTER>,
                        <0xf0   KEY_REPLY>,
                        <0xea   KEY_VOLUMEUP>,
                        <0xe3   KEY_VOLUMEDOWN>,
                        <0xbc   KEY_MUTE>,
                        <0xfe   KEY_1>,
                        <0xfd   KEY_2>,
                        <0xfc   KEY_3>,
                        <0xfb   KEY_4>,
                        <0xfa   KEY_5>,
                        <0xf9   KEY_6>,
                        <0xf8   KEY_7>,
                        <0xf7   KEY_8>,
                        <0xb6   KEY_9>,
                        <0xff   KEY_0>,
                        <0xed   KEY_BACKSPACE>,
   <0xf3 115>,
   <0xe7 114>,
                        <0xaf   KEY_POWER>,
                        <0x8b   KEY_VOLUMEUP>,
                        <0xb9   KEY_VOLUMEDOWN>;
        };
};

handle-cpu-id 代表 ir 中断在第 0 个 CPU 上处理,RK3399 是四核。

rockchip,usercode 表示 NEC 里面的地址。

这里将红外编码值映射成 Linux 下标准键。

系统启动时 rockchip_pwm_remotectl.c 根据 dts 的配置会注册如下设备:

查看中断节点,红外的中断如下,博主用手机开关几次空调,发现开发板的红外中断也有触发:

执行下面的指令开启 kernel 红外信息打印。如下图所示:

代码语言:javascript
复制
echo 1 > sys/module/rockchip_pwm_remotectl/parameters/code_print

4、驱动源码分析

rockchip_pwm_remotectl.c

代码语言:javascript
复制
static const struct of_device_id rk_pwm_of_match[] = {
 { .compatible =  "rockchip,remotectl-pwm"},
 { }
};

MODULE_DEVICE_TABLE(of, rk_pwm_of_match);

static struct platform_driver rk_pwm_driver = {
 .driver = {
  .name = "remotectl-pwm",
  .of_match_table = rk_pwm_of_match,
#ifdef CONFIG_PM
  .pm = &remotectl_pm_ops,
#endif
 },
 .remove = rk_pwm_remove,
};

module_platform_driver_probe(rk_pwm_driver, rk_pwm_probe);
代码语言:javascript
复制
static int rk_pwm_probe(struct platform_device *pdev)
{
 struct rkxx_remotectl_drvdata *ddata;
 struct device_node *np = pdev->dev.of_node;

 ddata = devm_kzalloc(&pdev->dev, sizeof(struct rkxx_remotectl_drvdata),GFP_KERNEL);
 ddata->state = RMC_PRELOAD;
 ddata->temp_period = 0;
 ddata->base = devm_ioremap_resource(&pdev->dev, r);

 platform_set_drvdata(pdev, ddata);
 num = rk_remotectl_get_irkeybd_count(pdev);
 if (num == 0) {
  pr_err("remotectl: no ir keyboard add in dts!!\n");
  ......
 }
 ddata->maxkeybdnum = num;
 remotectl_button = devm_kzalloc(&pdev->dev,num*sizeof(struct rkxx_remotectl_button),GFP_KERNEL);
 if (!remotectl_button) {
  pr_err("failed to malloc remote button memory\n");
  ......
 }
 input = devm_input_allocate_device(&pdev->dev);
 if (!input) {
  pr_err("failed to allocate input device\n");
  ......
 }
 input->name = pdev->name;
 input->phys = "gpio-keys/remotectl";
 input->dev.parent = &pdev->dev;
 input->id.bustype = BUS_HOST;
 input->id.vendor = 0x0001;
 input->id.product = 0x0001;
 input->id.version = 0x0100;
 ddata->input = input;
 ddata->input = input;
 irq = platform_get_irq(pdev, 0);
 if (ret < 0) {
  dev_err(&pdev->dev, "cannot find IRQ\n");
  goto error_pclk;
 }
 ddata->irq = irq;
 ddata->wakeup = 1;
 of_property_read_u32(np, "remote_pwm_id", &pwm_id);
 ddata->remote_pwm_id = pwm_id;
 DBG("remotectl: remote pwm id=0x%x\n", pwm_id);
 of_property_read_u32(np, "handle_cpu_id", &cpu_id);
 ddata->handle_cpu_id = cpu_id;
 DBG("remotectl: handle cpu id=0x%x\n", cpu_id);
 rk_remotectl_parse_ir_keys(pdev);
 tasklet_init(&ddata->remote_tasklet, rk_pwm_remotectl_do_something,(unsigned long)ddata);

 ret = input_register_device(input);
 input_set_capability(input, EV_KEY, KEY_WAKEUP);
 device_init_wakeup(&pdev->dev, 1);
 enable_irq_wake(irq);
 setup_timer(&ddata->timer, rk_pwm_remotectl_timer,(unsigned long)ddata);
 wake_lock_init(&ddata->remotectl_wake_lock,WAKE_LOCK_SUSPEND, "rockchip_pwm_remote");
 cpumask_clear(&cpumask);
 cpumask_set_cpu(cpu_id, &cpumask);
 irq_set_affinity(irq, &cpumask);
 ret = devm_request_irq(&pdev->dev, irq, rockchip_pwm_irq,IRQF_NO_SUSPEND, "rk_pwm_irq", ddata);

 return ret;
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-04-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 嵌入式Linux系统开发 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、红外简介
  • 2、红外协议:NEC
  • 3、RK3399 实例分析
  • 4、驱动源码分析
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档