基于RK3399Pro的红外(NEC格式)接收-不精准

目录

原理图

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格式的遥控器的编码

猜你喜欢

转载自blog.csdn.net/ZOROE123/article/details/121269399