前不久接触到红外NEC编码,闲来无事,就想在Android上面实现红外NEC编码的解析(如果不了解NEC编码的同学,可以找度娘,相关资料很多很详细)。由于接收管的原因,收到的红外波形和发射的红外波形是一样的,比如:接收到的引导码是9ms的高电平和4.5ms的低电平,重复码是9ms高电平和2.25ms低电平,0是0.56ms高电平和0.56ms低电平,1是0.56高电平和1.69ms低电平,另外每个编码发送完成后会有一个高电平表示结束(可以用示波器查看)。

具体的思路:

1、数据采集,中断采用边沿触发的方式,两次中断间隔时间相减就是高或者低电平持续时间,然后把记录到的数据放入一个queue里面,queue使用的是kernel自带的kfifo,这样就获得数据,然后唤醒下面2中的kernel thread处理queue里面的数据

2、数据处理,创建一个kernel thread,在线程里面不断的读取queue里面的数据,先通过 process_ir_rx_cmd 判断是引导码还是重复码,如果是引导码接下来使用 process_ir_rx_data 解析处理相关数据,如果是重复码就上报之前的数据即可。

具体的代码如下:

#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/time.h>
#include <linux/kfifo.h>
#include <linux/kthread.h>
#include <linux/wait.h>
#include <linux/input.h>static struct platform_device *ir_rx_dev = NULL;
static int irq_num;
static unsigned int trigger_level;
static struct task_struct *ir_rx_ts;
static struct input_dev *ir_rx_input_dev=NULL;//定义相关命令和数据解析的状态
enum ir_rx_state{IR_RX_OK,IR_RX_CMD_START,IR_RX_CMD_REPEAT,IR_RX_CMD_ERROR,IR_RX_DATA_0,IR_RX_DATA_1,IR_RX_DATA_ERR,
};//用一个二维数组保存接收到的红外命令和底层键值得对应关系
static unsigned int data_key_map[18][2]={{66,KEY_0},{22,KEY_1},{25,KEY_2},{13,KEY_3},{12,KEY_4},{24,KEY_5},{94,KEY_6},{8,KEY_7},{28,KEY_8},{90,KEY_9},{7,KEY_VOLUMEDOWN},{9,KEY_VOLUMEUP},{70,KEY_UP},{21,KEY_DOWN},{68,KEY_LEFT},{67,KEY_RIGHT},{64,KEY_PAUSE},{74,KEY_BACK},
};//用来保存数据 time和高低电平的状态
typedef struct{struct timespec tv;int level;//1 falling 2 rasing
}ir_rx_data;//see kfifo.h 这个是DEFINE_KFIFO展开后的,是kernel3.18里面的
/*ir_rx_fifo type is
struct {union {struct _kfifo kfifo;ir_rx_data *type;const ir_rx_data *const_type;char (*rectype)[0];ir_rx_data *ptr;ir_rx_data const *ptr_const;};ir_rx_data buf[512];
}ir_rx_fifo =
{{{.in  = 0, .out  = 0, .mask = __is_kfifo_ptr(&(fifo)) ?  : ARRAY_SIZE((fifo).buf) - 1,.esize   = sizeof(*(fifo).buf),.data    = __is_kfifo_ptr(&(fifo)) ?NULL :(fifo).buf,}}
}
*///定义kfifo 类型是ir_rx_data大小是512
static DEFINE_KFIFO(ir_rx_fifo,ir_rx_data,512);//queue len =512 data type ir_rx_data
//INIT_KFIFO(ir_rx_fifo);//创建一个wait_queue_head_t
DECLARE_WAIT_QUEUE_HEAD(ir_rx_wait);//中断处理函数
static irqreturn_t ir_rx_handler(int irq,void *data){ir_rx_data cur_ird,store_ird;static ir_rx_data prv_ird={0};int ret = 0;//如果当前是falling触发,设置level是1,否则设置level是2,并记录时间和修改触发方式相反getrawmonotonic(&cur_ird.tv);//time if(trigger_level==IRQF_TRIGGER_FALLING){//falling triggercur_ird.level = 1 ;//level trigger_level = IRQF_TRIGGER_RISING;}else{//rising triggercur_ird.level = 2 ;//leveltrigger_level = IRQF_TRIGGER_FALLING;}irq_set_irq_type(irq_num,trigger_level);//当前的触发时间减去前一次的触发时间就是电平的持续时间,根据前一次level状态设置是高电平还是低电平if(prv_ird.level!=0){if(prv_ird.level==1){store_ird.tv = ns_to_timespec(timespec_to_ns(&cur_ird.tv)-timespec_to_ns(&prv_ird.tv));store_ird.level = 0;//1 mean low level}else{store_ird.tv = ns_to_timespec(timespec_to_ns(&cur_ird.tv)-timespec_to_ns(&prv_ird.tv));store_ird.level = 1;//high level}//把数据放入queue里面ret = kfifo_put(&ir_rx_fifo,store_ird);if(!ret){printk("error the ir_rx_fifo is full\n");}//唤醒kernel thread执行wake_up(&ir_rx_wait);}//保存本次的数据prv_ird = cur_ird;return IRQ_HANDLED;
}//中断相关初始化
static int ir_rx_irq_init(struct platform_device *dev){struct pinctrl *ir_rx_pinxtrl;struct pinctrl_state *ir_rx_pinctrl_state;int ret = 0;ir_rx_pinxtrl = devm_pinctrl_get(&dev->dev);if(!IS_ERR(ir_rx_pinxtrl)){ir_rx_pinctrl_state = pinctrl_lookup_state(ir_rx_pinxtrl,"ir_rx_int");}if((!IS_ERR(ir_rx_pinxtrl))&&(!IS_ERR(ir_rx_pinctrl_state))){pinctrl_select_state(ir_rx_pinxtrl,ir_rx_pinctrl_state);}irq_num = irq_of_parse_and_map(dev->dev.of_node,0);trigger_level = IRQF_TRIGGER_RISING;ret = request_irq(irq_num,ir_rx_handler,trigger_level,"ir-rx-int",NULL);if(!ret){printk("ir_rx_irq_init request_irq ok \n");}return ret;
}//读取queue里面的数据,有数据才读,没有就堵塞在这
static void get_data(ir_rx_data *data){int ret = 0;wait_event(ir_rx_wait, kfifo_len(&ir_rx_fifo));ret = kfifo_get(&ir_rx_fifo,data);if(!ret){printk("get_data empty data\n");}
}//解析是引导码还是重复码
static enum ir_rx_state process_ir_rx_cmd(void){ir_rx_data data;enum ir_rx_state state;s64 time;get_data(&data);if(data.level == 1){time = timespec_to_ns(&data.tv);if(time> 8000000 && time < 11000000){get_data(&data);time = timespec_to_ns(&data.tv);if(data.level==0 && (time>3000000 && time <7000000)){state = IR_RX_CMD_START;}else if(data.level==0 && (time>1500000 && time <3100000)){state = IR_RX_CMD_REPEAT;}else{state = IR_RX_CMD_ERROR;}}else{state = IR_RX_CMD_ERROR;}}else{state = IR_RX_CMD_ERROR;}return state;
}//解析数据 只解析一位的数据,1返回IR_RX_DATA_1,0返回IR_RX_DATA_0,没有解析出来返回IR_RX_DATA_ERRstatic enum ir_rx_state get_bit(void){s64 time;ir_rx_data data;enum ir_rx_state state;get_data(&data);time = timespec_to_ns(&data.tv);if(data.level == 1){if(time> 300000 && time < 900000){get_data(&data);time = timespec_to_ns(&data.tv);if(data.level==0 && (time>1300000 && time <2000000)){state = IR_RX_DATA_1;}else if(data.level==0 && (time>300000 && time <900000)){state = IR_RX_DATA_0;}}else{state = IR_RX_DATA_ERR;}}else{state = IR_RX_DATA_ERR;}return state;
}//解析一个字节的数据,由于低位在前,如果get_bit是1,只需要data |= 1<<i 即可,为0不用管
static enum ir_rx_state get_byte(u8 *byte){int i=0;u8 data = 0;enum ir_rx_state state = IR_RX_OK;for(i=0;i<8;i++){switch (get_bit()){case IR_RX_DATA_0:break;case IR_RX_DATA_1:data |= 1<<i;break;case IR_RX_DATA_ERR:default:state = IR_RX_DATA_ERR;goto out;break;};}*byte = data;
out:return state;
}//处理数据,读取4字节的数据,通过反码判断接收到的数据是否正确,
static enum ir_rx_state process_ir_rx_data(u8 *data){u8 rev_data[4]={0};int i=0;enum ir_rx_state state = IR_RX_OK;for(i=0;i<4;i++){state = get_byte(&rev_data[i]);if(state!=IR_RX_OK)goto out;}if((rev_data[0] == (u8)(~rev_data[1])) && (rev_data[2] == (u8)(~rev_data[3]))){*data = rev_data[2];state = IR_RX_OK;}
out:return state;
}//上报数据
static void report_data(u8 data){int i=0;for(i=0;i<18;i++){if(data == data_key_map[i][0]){input_report_key(ir_rx_input_dev,data_key_map[i][1],1);input_sync(ir_rx_input_dev);input_report_key(ir_rx_input_dev,data_key_map[i][1],0);input_sync(ir_rx_input_dev);}}
}static void process_ir_rx_repeat(u8 data){if(!data){report_data(data);}
}//kernel thread一直循环处理数据
static int ir_rx_handler_thread(void *data){struct sched_param ir_rx_sp = {.sched_priority = 10,};enum ir_rx_state state;u8 code = 0;sched_setscheduler(ir_rx_ts,SCHED_RR,&ir_rx_sp);    do{state = process_ir_rx_cmd();//判断是引导码还是重复码switch(state){case IR_RX_CMD_START:state = process_ir_rx_data(&code);//如果引导码接下来处理数据if(state == IR_RX_OK){report_data(code);}break;case IR_RX_CMD_REPEAT:process_ir_rx_repeat(code);//重复码就重复上报break;case IR_RX_CMD_ERROR:default:break;}}while(!kthread_should_stop());return 0;
}//input相关的初始化
static int ir_rx_input_dev_init(struct device *dev){int i=0,ret;ir_rx_input_dev = devm_input_allocate_device(dev);if(ir_rx_input_dev==NULL){return -EINVAL;}for(i=0;i<10;i++){input_set_capability(ir_rx_input_dev,EV_KEY,i+2);//key_1 - key_9 key_0}input_set_capability(ir_rx_input_dev,EV_KEY,KEY_VOLUMEDOWN);input_set_capability(ir_rx_input_dev,EV_KEY,KEY_VOLUMEUP);input_set_capability(ir_rx_input_dev,EV_KEY,KEY_UP);input_set_capability(ir_rx_input_dev,EV_KEY,KEY_DOWN);input_set_capability(ir_rx_input_dev,EV_KEY,KEY_LEFT);input_set_capability(ir_rx_input_dev,EV_KEY,KEY_RIGHT);input_set_capability(ir_rx_input_dev,EV_KEY,KEY_PAUSE);input_set_capability(ir_rx_input_dev,EV_KEY,KEY_BACK);ret = input_register_device(ir_rx_input_dev);return ret;
}static int ir_rx_probe(struct platform_device *dev){int ret = 0;ir_rx_dev = dev;ret = ir_rx_irq_init(dev);if(ret){goto ir_rx_irq_init_fail;}ret = ir_rx_input_dev_init(&dev->dev);if(ret){goto alloc_input_dev_fail;}ir_rx_ts = kthread_run(ir_rx_handler_thread,NULL,"ir-rx-thread");if(IS_ERR(ir_rx_ts)){goto create_thread_fail;}return 0;
create_thread_fail:
alloc_input_dev_fail:free_irq(irq_num,NULL);
ir_rx_irq_init_fail:return ret;
}static struct of_device_id ir_match_node={.compatible = "test,ir_rx",
};static struct platform_driver ir_rx_drv = {.probe = ir_rx_probe,.driver = {.name = "ir_rx",.of_match_table = &ir_match_node,},
};static int __init ir_rx_init(void){if(platform_driver_register(&ir_rx_drv)){printk("register ir_rx_drv fail\n");}return 0;
}static void __exit ir_rx_exit(void){}module_init(ir_rx_init);
module_exit(ir_rx_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("fangyun");

总结:

实际数据处理中,精准度还行,能实现相关的功能。不足之处,由于系统不是只处理这个红外,中断也很多,很多代码会屏蔽中断,导致有些时候获得的时间误差比较大,尽管放大了电平判断的时间,但是还是存在误差。基本感觉正确率在95%以上,如果对这个判断精度要求高,还是搞个单片机专门来处理会更好。

Android 上面实现红外解析(NEC编码)相关推荐

  1. ESP32 开发笔记(三)源码示例 13_IR_Send_RMT 使用RMT实现红外数据发送(NEC编码)

    开发板购买链接 https://item.taobao.com/item.htm?spm=a2oq0.12575281.0.0.50111deb2Ij1As&ft=t&id=62636 ...

  2. 三菱空调红外码值编码规则解析

    三菱空调红外码值编码规则解析 一.空调红外控制原理 空调遥控器是通过发射断断续续的红外光来传递信息的,我们可以把发射的时候看做是"1",没发射的时候看做是"0" ...

  3. nec c语言笔试题,红外NEC编码发射程序_C语言(国外英文资料).doc

    红外NEC编码发射程序_C语言(国外英文资料) 红外NEC编码发射程序_C语言(国外英文资料) * * * * * * * Hardware platform: homemade infrared d ...

  4. 红外遥控器快速编码解码(NEC)

    红外遥控器快速编码解码 NEC编解码模块 红外遥控简介 NEC编码 红外编解码模块 接线说明 串口查看数据 数据验证 总结 原文链接:https://www.yourcee.com/newsinfo/ ...

  5. FPGA 24 工程模块 红外遥控(NEC协议)解码

    FPGA 24 红外遥控(NEC协议)解码 主要功能 :设计了一个红外 NEC协议的解码模块 实现(设计)流程:通过遥控器发送的红外信号,外围红外信号接收传感器对数据进行接收,得到一个在基频上的高低电 ...

  6. Android中三种常用解析XML的方式(DOM、SAX、PULL)简介及区别

    XML在各种开发中都广泛应用,Android也不例外.作为承载数据的一个重要角色,如何读写XML成为Android开发中一项重要的技能.今天就由我向大家介绍一下在Android平台下几种常见的XML解 ...

  7. Android 插件化原理解析——Activity生命周期管理

    之前的 Android插件化原理解析 系列文章揭开了Hook机制的神秘面纱,现在我们手握倚天屠龙,那么如何通过这种技术完成插件化方案呢?具体来说,插件中的Activity,Service等组件如何在A ...

  8. Android中XML数据解析

    转载请注明出处:http://blog.csdn.net/yegongheng/article/details/38296207 XML初步 今天我们来学习另一种非常重要的数据交换格式-XML.XML ...

  9. Android 音视频采集与软编码总结

    请尊重原创,转载请注明出处:http://blog.csdn.net/mabeijianxi/article/details/75807435(本文已在 "任玉刚" 微信公众号发布 ...

最新文章

  1. 东京奥运会73枚动态图标刷爆朋友圈,中国网友怒赞:不愧是设计大国!
  2. Maven自动FTP远程部署
  3. python3 for mac_PyCharm for Mac-PyCharm Mac版下载 V2018.3.2-PC6苹果网
  4. 来来来!DD带大家一起赢台MacBook Pro回家过年!
  5. Float构建三栏DIV CSS网页布局
  6. BFS最短路打印路径
  7. 分类模型的评估方法-精确率(Precision)
  8. com.alibaba.druid.sql.parser.ParserException: syntax error, QUES %, pos 80 like报错解决
  9. AFei Loves Magic
  10. DIY人脸跟踪电风扇送女朋友(4)
  11. Qt工作笔记-QTreeWidgetItem中type的基本用法
  12. 关于读研的一些感悟1
  13. 网络 一篇博文搞懂五种常见的IO模型
  14. php redirect with post,试图理解Post / Redirect / Get设计模式(用PHP实...
  15. 黑莓:一家把未来押宝无人驾驶的老牌手机厂商
  16. 数组 / 伪数组 判断及方法调用 (权威指南笔记)
  17. java基于springboot+vue的旧物置换网站
  18. 计算机设备和打印机打不开,Windows7设备和打印机窗口打不开如何解决
  19. JavaScript小项目之BMI值计算
  20. 修改WSL的Ubuntu环境下ls显示的文件夹文字颜色和背景色

热门文章

  1. windows下编译Sqlite-3.38.0及使用(存储json)
  2. 网上经常听到的“终端”指的什么?
  3. no vaild maven installation found
  4. TI C66x DSP 系统events及其应用 - 1
  5. 一篇文章带你解决 MongoDB 连接 localhost 和 127.0.0.1 可以连接,但是改成具体的IP地址就无法连接
  6. 【​观察】英特尔傲腾DC P4800X有哪些适用场景?
  7. Teamviewer删除账号
  8. java导出excel文件_POI生成EXCEL文件
  9. 戴尔易安信引领科技创新,以全面的端到端解决方案助力企业“数”造未来
  10. Spatio-Temporal Dynamics and Semantic Attribute Enriched Visual Encoding for Video Captioning