基于RK3399Pro的红外(NEC格式)接收-不精准 收藏
分类专栏: RK3399Pro 模块学习 研发管理 文章标签: c语言 开发语言 后端
版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:基于RK3399Pro的红外(NEC格式)接收-不精准_ZOROE123的博客-CSDN博客
目录
原理图
IR红外编程原理
IR NEC 协议
协议特征
调制
协议格式
数据协议
编写驱动程序
入口函数
杂项设备
文件操作集
读取函数
POLL机制
中断处理函数
完整驱动代码
测试代码
Makefile文件
测试步骤
编译源码
加载驱动
执行测试程序
实验现象
原理图
IR红外编程原理
最强的红外协议参考链接
IR NEC 协议
NEC协议参考链接
参考博客
协议特征
-
使用双向编码(又称曼彻斯特编码);
-
使用38K载波对编码后的波形进行调制;
-
位时间 1.12ms 或 2.25ms
调制
我们定义脉冲560µS为脉冲基本宽度T;根据脉冲时间长短来解码。推荐载波占空比为1/3至1/4:(1) Logic “1” 位宽为2.25ms,脉冲时间560us(T + 3T);
(2) Logic “0” 位宽为1.12ms,脉冲时间560us(T + T)。
(3 ) 重复码: 位宽为11.25ms,脉冲时间9ms(16T + 4T)。
协议格式
1 | 8 | 8 | 8 | 8 |
---|---|---|---|---|
起始位 | Address | Address(反码) | Command | Command(反码) |
16T+8T | b0...b7 | b0...b7 | b0...b7 | b0...b7 |
-
起始位(Start Bit): 16T + 8T。
-
地址位(Address): 8bit,最低有效位(LSB)先发送。
-
反相地址位(!Address): 8bit,最低有效位(LSB)先发送。其值与地址位(Address)相反,用于验证接收的信息的准确性。
-
命令位(Command): 8bit,最低有效位(LSB)先发送。
-
反相命令位(!Command): 8bit,最低有效位(LSB)先发送。其值与命令位(Command)相反,用于验证接收的信息的准确性。
数据协议
NEC协议格式如下图所示:
以上是一个正常的序列,也可能存在一种情况:一直按着1个键,此时发送的是以110ms为周期的重复码,即发送一次命令码之后,不会再次发送命令码,而是每隔110ms时间,发送一段重复码。如下图:在这里插入图片描述需要注意的是:红外一体接收头为了提高接受灵敏度,输入高电平,其输出的是相反的低电平。
编写驱动程序
红外驱动采用字符设备驱动模型,通过杂项设备注册。
入口函数
static int __init test_init(void)
{
int ret;
ret = misc_register(&gec3399_irda_misc); //注册字符设备
if(ret < 0)
{
printk("misc register error\n");
goto err0;
}
init_waitqueue_head(&irda_wait); //等待队列初始化
//中断申请,下降沿有效,中断处理函数irq_func。
ret = request_irq(gpio_to_irq(IR_IO), irq_func, IRQF_TRIGGER_FALLING, "myir", NULL);
if (ret < 0)
goto err1;
printk("IR driver is OK \r\n");
return 0;
err1:
misc_deregister(&gec3399_irda_misc);
err0:
return ret;
}
杂项设备
static struct miscdevice gec3399_irda_misc = {
.minor = MISC_DYNAMIC_MINOR,
.fops = &gec3399_irda_fops,
.name = "irda_drv",
}; //混杂设备结构体定义和初始化
文件操作集
static const struct file_operations gec3399_irda_fops = {
.owner = THIS_MODULE,
.read =gec3399_irda_read,
.poll = gec3399_irda_poll,
}; //文件操作集合
读取函数
通过这个read接口函数把读取到的数据上报给用户层
ssize_t gec3399_irda_read(struct file *filp, char __user *buf, size_t len, loff_t *off)
{
int ret;
wait_event_interruptible(irda_wait, irda_pressed); //等待按键按下
ret = copy_to_user(buf,irda_data,4); //拷贝到用户空间
if(ret != 0)
{
printk("No infrared is received\n");
len = -EFAULT;
}
irda_pressed = 0;
memset(irda_data,0,4);
return len;
}
POLL机制
static unsigned int gec3399_irda_poll( struct file *file,struct poll_table_struct *wait)
{
unsigned int mask = 0;
poll_wait(file, &irda_wait, wait);
if (irda_pressed){
mask |= POLLIN | POLLRDNORM;
return mask;
}
return 0;
}
中断处理函数
irqreturn_t irq_func(int irqno, void *arg)
{
long long now = ktime_to_us(ktime_get());
unsigned int offset;
int i, j, tmp;
if (!flag) //数据开始
{
flag = 1;
prev = now;
return IRQ_HANDLED;
}
offset = now - prev;
prev = now;
//第一步:判断引导码
if ((offset > 13000) && (offset < 14000)) //判断是否收到引导码,引导码13.5ms
{
num = 0;
return IRQ_HANDLED;
}
//不是引导码时间,数据位时间
if (num < 32)
times[num++] = offset;
if (num >= 32)
{
for (i = 0; i < 4; i++) //共4个字节
{
tmp = 0;
for (j = 0; j < 8; j++) //每字节8位
{
if (times[i*8+j] > 1800) //如果数据位的信号周期大于1.8ms, 则是二进制数据1
tmp |= 1<<j;
}
irda_data[i] = tmp;
//printk("%02x ", tmp);
}
//printk("%x\n",*(int*)irda_data);
wake_up_interruptible(&irda_wait); //唤醒等待队列
irda_pressed = 1;
flag = 0; //重新开始帧
}
return IRQ_HANDLED;
}
完整驱动代码
//头文件来源于linux内核源码
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/miscdevice.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/ioport.h>
#include <linux/ioctl.h>
#include <linux/interrupt.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/miscdevice.h>
#include <linux/poll.h>
//红外接收头,三个引脚
// 三个引脚向下, 突出的半圆面向自己,从左往右引脚分别是: 数据脚(接IO口), 地线(gnd), 电源线(vcc, 3.3v)
// 红外接收头的数据脚接的是PL11
#define IR_IO (32*0 + 8*0 + 6)
int flag = 0; //表示数据帧的开始
int num = 0; //表示数据帧里的第几位数据
static long long prev = 0; //记录上次的时间
unsigned int times[40]; //记录每位数据的时间
static wait_queue_head_t irda_wait;
static int irda_pressed = 0; //按键响应,初始为不响应
char irda_data[4] = {0};
ssize_t gec3399_irda_read(struct file *filp, char __user *buf, size_t len, loff_t *off)
{
int ret;
wait_event_interruptible(irda_wait, irda_pressed); //等待按键按下
ret = copy_to_user(buf,irda_data,4); //拷贝到用户空间
if(ret != 0)
{
printk("No infrared is received\n");
len = -EFAULT;
}
irda_pressed = 0;
memset(irda_data,0,4);
return len;
}
irqreturn_t irq_func(int irqno, void *arg)
{
long long now = ktime_to_us(ktime_get());
unsigned int offset;
int i, j, tmp;
if (!flag) //数据开始
{
flag = 1;
prev = now;
return IRQ_HANDLED;
}
offset = now - prev;
prev = now;
if ((offset > 13000) && (offset < 14000)) //判断是否收到引导码,引导码13.5ms
{
num = 0;
return IRQ_HANDLED;
}
//不是引导码时间,数据位时间
if (num < 32)
times[num++] = offset;
if (num >= 32)
{
for (i = 0; i < 4; i++) //共4个字节
{
tmp = 0;
for (j = 0; j < 8; j++) //每字节8位
{
if (times[i*8+j] > 1800) //如果数据位的信号周期大于2ms, 则是二进制数据1
tmp |= 1<<j;
}
irda_data[i] = tmp;
//printk("%02x ", tmp);
}
//printk("%x\n",*(int*)irda_data);
wake_up_interruptible(&irda_wait); //唤醒等待队列
irda_pressed = 1;
flag = 0; //重新开始帧
}
return IRQ_HANDLED;
}
static unsigned int gec3399_irda_poll( struct file *file,struct poll_table_struct *wait)
{
unsigned int mask = 0;
poll_wait(file, &irda_wait, wait);
if (irda_pressed){
mask |= POLLIN | POLLRDNORM;
return mask;
}
return 0;
}
static const struct file_operations gec3399_irda_fops = {
.owner = THIS_MODULE,
.read =gec3399_irda_read,
.poll = gec3399_irda_poll,
}; //文件操作集合
static struct miscdevice gec3399_irda_misc = {
.minor = MISC_DYNAMIC_MINOR,
.fops = &gec3399_irda_fops,
.name = "irda_drv",
}; //混杂设备结构体定义和初始化
static int __init test_init(void)
{
int ret;
ret = misc_register(&gec3399_irda_misc); //注册字符设备
if(ret < 0){
printk("misc register error\n");
goto err0;
}
init_waitqueue_head(&irda_wait); //等待队列初始化
ret = request_irq(gpio_to_irq(IR_IO), irq_func, IRQF_TRIGGER_FALLING, "myir", NULL);
if (ret < 0)
goto err1;
return 0;
err1:
misc_deregister(&gec3399_irda_misc);
err0:
return ret;
}
static void __exit test_exit(void)
{
misc_deregister(&gec3399_irda_misc);
free_irq(gpio_to_irq(IR_IO), NULL);
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");
测试代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <poll.h>
int fd_irda;
char irda_data[4] = {0};
int main(void)
{
int ret;
fd_irda = open("/dev/irda_drv", O_RDWR);
if(fd_irda < 0)
{
perror("open irda driver");
return -1;
}
struct pollfd pollfd_irda = {
.fd = fd_irda,
.events = POLLIN|POLLRDNORM,
};
while(1)
{
ret = poll(&pollfd_irda,1,0);
if(ret < 0)
{
perror("poll key driver\n");
}
else if(ret > 0)
{
ret = read(fd_irda,irda_data,4);
if(ret<0)
{
perror("read error\n");
return -1;
}
printf("%x\n",*(int*)irda_data);
}
else{
printf("poll wait time out \n");
}
}
close(fd_irda);
return 0;
}
Makefile文件
obj-m += irda_drv.o
KERNELDIR:=/file/RK3399Pro/rk3399pro_git_repo/kernel
PWD:=$(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
test:
aarch64-linux-gnu-gcc irda_test.c -o irda_test
clean:
rm -rf *.o *.order .*.cmd *.ko *.mod.c *.symvers *.tmp_versions
测试步骤
编译源码
在ubuntu中输入:
make
得到驱动目标文件irda_drv.ko
输入:
make test
得到测试目标文件:irda_test
加载驱动
在开发板命令终端输入:
insmod irda_drv.ko
执行测试程序
在开发板命令终端输入:
chmod 777 irda_test
./irda_test
实验现象
读取到NEC格式的遥控器的编码