首页 > 系统相关 >【Linux 中断】红外接收器设备驱动

【Linux 中断】红外接收器设备驱动

时间:2023-03-09 09:59:12浏览次数:64  
标签:接收器 return int dev infrared 红外 cdev Linux buf

一、概述

现在很多家电都使用了红外,而智能家居的诞生,连音响都带了红外遥控功能。为了解决家里遥控器比较多的情况,多数手机都支持了红外功能,这和以前的万能遥控器一样。这里主要记录红外接收的驱动,当然明白怎么接收的,对于遥控的发射就比较简单了。

二、红外接收器

  1. 外观

  2. 接收的工作原理
    红外探头应该也是光敏电阻的一种,当接收到波长在750-1150NM的光时,OUT 引脚就会产生一个 38kHz 的 PWM 波。一般在电路中都会给 OUT 引脚进行一个上拉,所以没有检测到红外光时,OUT 引脚是稳定的高电平。通过这个现象我们就可以进行无线通信。
    注意:750-1150NM的光时是肉眼不可见的,不过可以通过手机摄像头进行查看

  3. 通信协议
    了解完原理后,只需要配上相应的通信协议就可以使用红外进行无线通信了。常用的红外线信号传输协议有ITT协议、NEC协议、NokiaNRC协议、Sharp协议、SonySIRC协议、PhilipSRC-5协议、PhilipsRC-6协议,以及PhilipsRECS-80协议等。

    需要了解不同协议区别的可以参考:几种常用的红外线信号传输协议,红外的协议种类比较多,部分公司也会自己指定不同的协议,比如小米公司的遥控器,见小米红外遥控器如何适配到其他应用设备之上

    此笔记主要使用 NEC 协议完成驱动的编写,其他的协议驱动也可以参考完成。

三、 NEC协议

  1. 数据帧格式

    引导码 地址码0 地址码1 命令码 命令反码 引导码(重复)
    LSB-MSB(0-7) LSB-MSB(8-15) LSB-MSB(16-23) LSB-MSB(24-31)

    注意:在标准的NEC协议中,地址码1为地址码0的反码,而在许多遥控器中,地址码0和地址码1共同作为红外遥控器的编码值。

  2. PPM(脉冲位置调制)

  3. 接收波形

    注意:实际波形在低电平期间是一个 38kHz 的 PWM 波。

  4. 数据解析
    在接收数据时需要过滤 38kHz 的波形,如下所示:

    /**
     * @brief 红外中断响应函数
     * 
     * @param irq
     * @param dev_id
     * @return 0,成功;其他负值,失败
    */
    static irqreturn_t infrared_interrupt(int irq, void *dev_id)
    {
    	unsigned previous_offset;		// 上一次的时间
    	unsigned start_offset;			// 波型的起始时间差
    	long long now = ktime_to_us(ktime_get());
        
    	/* 由于红外接收传感器在接收到红外信号时产生一个38KHz的信号,所以接收时需要过滤,使信号变为一个低电平信号 */
    	/*-------------------------------- 滤波 --------------------------------*/
    
    	/* 从当前时刻开始接收一个下降沿开始的方波周期 */
    	if (0 == infrared_pwm.flag )
    	{
    		infrared_pwm.start_time = now;
    		infrared_pwm.flag  = 1;
    	}
    	
    	/* 计算两次下降沿的时差 */
    	previous_offset = now - infrared_pwm.previous;
    	infrared_pwm.previous = now;
    
    	/* 过滤红外接收器自生产生38KHz的信号,周期大约是 26us */
    	if (previous_offset < 60)
    	{
    		return IRQ_HANDLED;
    	}
    
    	/* 下降沿开始的时差,也就是一个周期的时间 */
    	start_offset = now - infrared_pwm.start_time;
    	/* 消除上次持续的信号 */
    	if (start_offset == 0)
    	{
    		return IRQ_HANDLED;
    	}
    
    	/* 完成一个周期的数据采集 */
    	infrared_pwm.flag = 0;
    
    	// infrared_pwm.low_time = start_offset - previous_offset + 52;		// 低电平时间
    	// infrared_pwm.high_time = previous_offset - 52;					// 高电平时间
    	
    	/* NEC 解码 */
    	infrared_nec_decode(start_offset);
    
    	return IRQ_HANDLED;
    }
    
    

四、linux 中断驱动

在中断驱动中我使用了异步通知的方式,与应用程序进行通信

/**
 * @brief 红外接收器初始化函数
 * 
 * @return 0,成功;其他负值,失败
*/
static int infrared_init(void)
{
	int res;

	/* 申请 GPIO 资源 */
	infrared_dev.gpio = INFRARED_GPIO;
	res = gpio_request(infrared_dev.gpio, "infrared");
	if (res) 
	{
		pr_err("infrared dev: Failed to request gpio\n");
		return res;
	}

	/* 将 GPIO 设置为输入模式 */
	gpio_direction_input(infrared_dev.gpio);

	/* 申请中断 */
	infrared_dev.irq_num = gpio_to_irq(infrared_dev.gpio);
	res = request_irq(infrared_dev.irq_num, infrared_interrupt, IRQF_TRIGGER_FALLING, "infrared", NULL);
	if (res) 
	{
		gpio_free(infrared_dev.gpio);
		return res;
	}

	return 0;
}

/**
 * @brief 打开设备
 * 
 * @param inode 传递给驱动的 inode
 * @param filp 设备文件,file 结构体有个叫做 private_data 的成员变量
 * 一般在 open 的时候将 private_data 指向设备结构体。
 * @return 0 成功;其他 失败
 */
static int infrared_open(struct inode *inode, struct file *filp)
{
	/* 将设备数据设置为私有数据 */
	filp->private_data = &infrared_dev;

	printk(PRINTK_GRADE "infrared_open\n");
    return 0;
}

/**
 * @brief 从设备读取数据
 * 
 * @param filp 要打开的设备文件(文件描述符)
 * @param buf 返回给用户空间的数据缓冲区
 * @param count 要读取的数据长度
 * @param offt 相对于文件首地址的偏移
 * @return 0 成功;其他 失败
 */
static ssize_t infrared_read(struct file *filp, char __user *buf, size_t count, loff_t *offt)
{
	int res = 0;
	// struct infrared_dev_t *infrared_dev = filp->private_data;

	res = copy_to_user(buf, infrared_receive_data, count);
    if(res != 0) {
		printk(PRINTK_GRADE "111111111111111\n");
		return -1;
    }

	// printk(PRINTK_GRADE "infrared_read\n");
    return 0;
}

/**
 * @brief 向设备写数据
 * @param filp 设备文件,表示打开的文件描述符
 * @param buf 要写给设备写入的数据
 * @param count 要写入的数据长度
 * @param offt 相对于文件首地址的偏移
 * @return 写入的字节数,如果为负值,表示写入失败
*/
static ssize_t infrared_write(struct file *filp, const char __user *buf, size_t count, loff_t *offt)
{
    int res = 0;
	// struct infrared_dev_t *infrared_dev = filp->private_data;

	char write_buf[1024] = {"0"};
	res = copy_from_user(write_buf, buf, count);
    if(res != 0) {
        return -1;
    }
	printk("kernel recevdata:%s\r\n", write_buf);
    return 0;
}

static int infrared_fasync(int fd, struct file *filp, int on)
{
	struct infrared_dev_t *infrared_dev = filp->private_data;

	printk(PRINTK_GRADE "infrared_fasync\n");
	/* 异步通知初始化 */
    return fasync_helper(fd, filp, on, &infrared_dev->fasync_queue);
}

/**
 * @brief 关闭/释放设备
 * @param filp 要关闭的设备文件(文件描述符)
 * @return 0 成功;其他 失败
*/
static int infrared_release(struct inode *inode, struct file *filp)
{
	int res = 0;
	printk(PRINTK_GRADE "infrared_release\n");

	/* 删除异步通知 */
	infrared_fasync(-1, filp, 0);
    return res;
}

/* 设备操作函数结构体 */
static struct file_operations infrared_ops = {
    .owner = THIS_MODULE, 
    .open = infrared_open,
    .read = infrared_read,
    .write = infrared_write,
    .release = infrared_release,
	.fasync = infrared_fasync,
};

/**
 * @brief 注册字符设备驱动
 * 
 * @return 0,成功;其他负值,失败
*/
static int infrared_register(void)
{
    int ret = -1; 						// 保存错误状态码

	/* GPIO 中断初始化 */
	ret = infrared_init();

	/* 1、创建设备号 */
	/* 采用动态分配的方式,获取设备编号,次设备号为0 */
	/* 设备名称为 infrared_NAME,可通过命令 cat /proc/devices 查看 */
	/* INFRARED_CNT 为1,只申请一个设备编号 */
	ret = alloc_chrdev_region(&infrared_dev.devid, 0, INFRARED_CNT, INFRARED_NAME);
	if (ret < 0)
	{
		pr_err("%s Couldn't alloc_chrdev_region, ret = %d \r\n", INFRARED_NAME, ret);
		goto fail_region;
	}

	/* 2、初始化 cdev */
	/* 关联字符设备结构体 cdev 与文件操作结构体 file_operations */
	infrared_dev.cdev.owner = THIS_MODULE;
	cdev_init(&infrared_dev.cdev, &infrared_ops);

	/* 3、添加一个 cdev */
	/* 添加设备至cdev_map散列表中 */
	ret = cdev_add(&infrared_dev.cdev, infrared_dev.devid, INFRARED_CNT);
	if (ret < 0)
	{
		pr_err("fail to add cdev \r\n");
		goto del_unregister;
	}

	/* 4、创建类 */
	infrared_dev.class = class_create(THIS_MODULE, INFRARED_NAME);
	if (IS_ERR(infrared_dev.class)) 
	{
		pr_err("Failed to create device class \r\n");
		goto del_cdev;
	}

	/* 5、创建设备,设备名是 INFRARED_NAME */
	/*创建设备 INFRARED_NAME 指定设备名,*/
	infrared_dev.device = device_create(infrared_dev.class, NULL, infrared_dev.devid, NULL, INFRARED_NAME);
	if (IS_ERR(infrared_dev.device)) {
		goto destroy_class;
	}

	return 0;

destroy_class:
	device_destroy(infrared_dev.class, infrared_dev.devid);
del_cdev:
	cdev_del(&infrared_dev.cdev);
del_unregister:
	unregister_chrdev_region(infrared_dev.devid, INFRARED_CNT);
fail_region:
	/* 释放个人初始化申请的资源,如del_init(); */
	free_irq(infrared_dev.irq_num, NULL);
	gpio_free(infrared_dev.gpio);
	return -EIO;
}

/**
 * @brief 注销字符设备驱动
 * 
 * @return 0,成功;其他负值,失败
*/
static void infrared_unregister(void)
{
	/* 1、删除 cdev */
	cdev_del(&infrared_dev.cdev);
	/* 2、注销设备号 */
	unregister_chrdev_region(infrared_dev.devid, INFRARED_CNT);
	/* 3、注销设备 */
	device_destroy(infrared_dev.class, infrared_dev.devid);
	/* 4、注销类 */
	class_destroy(infrared_dev.class);

	/* 释放中断 */
	free_irq(infrared_dev.irq_num, NULL);
	/* 释放 IO */
	gpio_free(infrared_dev.gpio);
}

/**
 * @brief 驱动入口函数
 * 
 * @return 0,成功;其他负值,失败
*/
static int __init infrared_driver_init(void)
{
	pr_info("infrared_driver_init\n");
	return infrared_register();
}

/**
 * @brief 驱动出口函数
 * 
 * @return 0,成功;其他负值,失败
*/
static void __exit infrared_driver_exit(void)
{
	pr_info("infrared_driver_exit\n");
	infrared_unregister();
}

/* 将上面两个函数指定为驱动的入口和出口函数 */
module_init(infrared_driver_init);
module_exit(infrared_driver_exit);

/* LICENSE 和作者信息 */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("JIAOZHU");
MODULE_INFO(intree, "Y");

五、完整的驱动程序

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/io.h>
#include <linux/device.h>
#include <linux/semaphore.h>
#include <linux/of_irq.h>
#include <linux/irq.h>


/***************************************************************
文件名 : infrared.c
作者 : jiaozhu
版本 : V1.0
描述 : 红外接收器驱动
其他 : 无
日志 : 初版 V1.0 2023/3/3
***************************************************************/

/* 红外接收器的数据引脚 59 */
#define INFRARED_GPIO 59

#define PRINTK_GRADE KERN_INFO

/*------------------ 字符设备内容 ----------------------*/
#define INFRARED_NAME 	"infrared"
#define INFRARED_CNT	(1)

static unsigned char infrared_receive_data[4];

/*------------------ 设备数据结构体 ----------------------*/
struct infrared_dev_t 
{
	dev_t devid; 						// 设备号
	struct cdev cdev; 					// cdev
	struct class *class; 				// 类
	struct device *device; 				// 设备
	struct device_node *nd;				// 设备节点
	int irq_num; 						// 中断号
	int gpio;							// 数据接收引脚
	struct fasync_struct *fasync_queue; // 异步相关结构体
};

struct infrared_dev_t infrared_dev;		// 设备数据结构体 

/*------------------ 红外波形过滤结构体 ----------------------*/
struct infrared_pwm_t
{
	long long previous;					// 记录上一次的时间,64bit
	int flag;               			// 表示每个方波周期的开始
	long long start_time;				// 周期的起始时间
	int low_time;						// 低电平时间
	int high_time;						// 高电平时间
};

struct infrared_pwm_t infrared_pwm =		// 红外波形采集
{
	.flag = 0,
	.previous = 0,
	.start_time = 0,
	.low_time = 0,
	.high_time = 0,
};

/*------------------ 红外 NEC 数据解析结构体 ------------------*/
struct nec_decode_buf_t
{
	int flag;               			// 表示 NEC 数据开始
	unsigned  times[128];				// 记录每帧的时间
	int num;							// 表示第几帧
};

struct nec_decode_buf_t nec_buf =
{
	.flag = 0,
	.num = 0,
};


/**
 * @brief 红外 NEC 数据解析
 * 
 * @param period 一个方波周期
*/
static void infrared_nec_decode(int period)
{
	int i, j;
	unsigned char temp;

	if ((period > 13000) && (period < 14000))
	{
		nec_buf.flag = 1;
		nec_buf.num = 0;
		return;
	}

	if (nec_buf.num < 32)
	{
		nec_buf.times[nec_buf.num ++] = period;
	}

	if ((period > 10500) && (period < 13500))
	{
		if (nec_buf.flag)
		{
			for(i = 0; i < 4; i++)		// 一共4个字节
			{
            	temp = 0;
 
				for(j = 0; j < 8; j++)
				{
					if ((nec_buf.times[i * 8 + j] > 2100) && (nec_buf.times[i * 8 + j] < 2400) )
					{
						temp |= 1 << j;
					}
				}
				// printk("%02x	", temp);
				infrared_receive_data[i] = temp;
			}
			// printk("\n");
			nec_buf.flag  = 0;
			
		}
		else
		{
			// printk(PRINTK_GRADE "Repetitive signal\n");
			memset(infrared_receive_data, 0xFF, sizeof(infrared_receive_data));
		}
		
		/* 发送异步通知 */
		kill_fasync(&infrared_dev.fasync_queue, SIGIO, POLL_IN);
	}

}

/**
 * @brief 红外中断响应函数
 * 
 * @param irq
 * @param dev_id
 * @return 0,成功;其他负值,失败
*/
static irqreturn_t infrared_interrupt(int irq, void *dev_id)
{
	unsigned previous_offset;		// 上一次的时间
	unsigned start_offset;			// 波型的起始时间差
	long long now = ktime_to_us(ktime_get());
    
	/* 由于红外接收传感器在接收到红外信号时产生一个38KHz的信号,所以接收时需要过滤,使信号变为一个低电平信号 */
	/*-------------------------------- 滤波 --------------------------------*/

	/* 从当前时刻开始接收一个下降沿开始的方波周期 */
	if (0 == infrared_pwm.flag )
	{
		infrared_pwm.start_time = now;
		infrared_pwm.flag  = 1;
	}
	
	/* 计算两次下降沿的时差 */
	previous_offset = now - infrared_pwm.previous;
	infrared_pwm.previous = now;

	/* 过滤红外接收器自生产生38KHz的信号,周期大约是 26us */
	if (previous_offset < 60)
	{
		return IRQ_HANDLED;
	}

	/* 下降沿开始的时差,也就是一个周期的时间 */
	start_offset = now - infrared_pwm.start_time;
	/* 消除上次持续的信号 */
	if (start_offset == 0)
	{
		return IRQ_HANDLED;
	}

	/* 完成一个周期的数据采集 */
	infrared_pwm.flag = 0;

	// infrared_pwm.low_time = start_offset - previous_offset + 52;		// 低电平时间
	// infrared_pwm.high_time = previous_offset - 52;					// 高电平时间
	
	/* NEC 解码 */
	infrared_nec_decode(start_offset);

	return IRQ_HANDLED;
}

/**
 * @brief 红外接收器初始化函数
 * 
 * @return 0,成功;其他负值,失败
*/
static int infrared_init(void)
{
	int res;

	/* 申请 GPIO 资源 */
	infrared_dev.gpio = INFRARED_GPIO;
	res = gpio_request(infrared_dev.gpio, "infrared");
	if (res) 
	{
		pr_err("infrared dev: Failed to request gpio\n");
		return res;
	}

	/* 将 GPIO 设置为输入模式 */
	gpio_direction_input(infrared_dev.gpio);

	/* 申请中断 */
	infrared_dev.irq_num = gpio_to_irq(infrared_dev.gpio);
	res = request_irq(infrared_dev.irq_num, infrared_interrupt, IRQF_TRIGGER_FALLING, "infrared", NULL);
	if (res) 
	{
		gpio_free(infrared_dev.gpio);
		return res;
	}

	return 0;
}

/**
 * @brief 打开设备
 * 
 * @param inode 传递给驱动的 inode
 * @param filp 设备文件,file 结构体有个叫做 private_data 的成员变量
 * 一般在 open 的时候将 private_data 指向设备结构体。
 * @return 0 成功;其他 失败
 */
static int infrared_open(struct inode *inode, struct file *filp)
{
	/* 将设备数据设置为私有数据 */
	filp->private_data = &infrared_dev;

	printk(PRINTK_GRADE "infrared_open\n");
    return 0;
}

/**
 * @brief 从设备读取数据
 * 
 * @param filp 要打开的设备文件(文件描述符)
 * @param buf 返回给用户空间的数据缓冲区
 * @param count 要读取的数据长度
 * @param offt 相对于文件首地址的偏移
 * @return 0 成功;其他 失败
 */
static ssize_t infrared_read(struct file *filp, char __user *buf, size_t count, loff_t *offt)
{
	int res = 0;
	// struct infrared_dev_t *infrared_dev = filp->private_data;

	res = copy_to_user(buf, infrared_receive_data, count);
    if(res != 0) {
		printk(PRINTK_GRADE "111111111111111\n");
		return -1;
    }

	// printk(PRINTK_GRADE "infrared_read\n");
    return 0;
}

/**
 * @brief 向设备写数据
 * @param filp 设备文件,表示打开的文件描述符
 * @param buf 要写给设备写入的数据
 * @param count 要写入的数据长度
 * @param offt 相对于文件首地址的偏移
 * @return 写入的字节数,如果为负值,表示写入失败
*/
static ssize_t infrared_write(struct file *filp, const char __user *buf, size_t count, loff_t *offt)
{
    int res = 0;
	// struct infrared_dev_t *infrared_dev = filp->private_data;

	char write_buf[1024] = {"0"};
	res = copy_from_user(write_buf, buf, count);
    if(res != 0) {
        return -1;
    }
	printk("kernel recevdata:%s\r\n", write_buf);
    return 0;
}

static int infrared_fasync(int fd, struct file *filp, int on)
{
	struct infrared_dev_t *infrared_dev = filp->private_data;

	printk(PRINTK_GRADE "infrared_fasync\n");
	/* 异步通知初始化 */
    return fasync_helper(fd, filp, on, &infrared_dev->fasync_queue);
}

/**
 * @brief 关闭/释放设备
 * @param filp 要关闭的设备文件(文件描述符)
 * @return 0 成功;其他 失败
*/
static int infrared_release(struct inode *inode, struct file *filp)
{
	int res = 0;
	printk(PRINTK_GRADE "infrared_release\n");

	/* 删除异步通知 */
	infrared_fasync(-1, filp, 0);
    return res;
}

/* 设备操作函数结构体 */
static struct file_operations infrared_ops = {
    .owner = THIS_MODULE, 
    .open = infrared_open,
    .read = infrared_read,
    .write = infrared_write,
    .release = infrared_release,
	.fasync = infrared_fasync,
};

/**
 * @brief 注册字符设备驱动
 * 
 * @return 0,成功;其他负值,失败
*/
static int infrared_register(void)
{
    int ret = -1; 						// 保存错误状态码

	/* GPIO 中断初始化 */
	ret = infrared_init();

	/* 1、创建设备号 */
	/* 采用动态分配的方式,获取设备编号,次设备号为0 */
	/* 设备名称为 infrared_NAME,可通过命令 cat /proc/devices 查看 */
	/* INFRARED_CNT 为1,只申请一个设备编号 */
	ret = alloc_chrdev_region(&infrared_dev.devid, 0, INFRARED_CNT, INFRARED_NAME);
	if (ret < 0)
	{
		pr_err("%s Couldn't alloc_chrdev_region, ret = %d \r\n", INFRARED_NAME, ret);
		goto fail_region;
	}

	/* 2、初始化 cdev */
	/* 关联字符设备结构体 cdev 与文件操作结构体 file_operations */
	infrared_dev.cdev.owner = THIS_MODULE;
	cdev_init(&infrared_dev.cdev, &infrared_ops);

	/* 3、添加一个 cdev */
	/* 添加设备至cdev_map散列表中 */
	ret = cdev_add(&infrared_dev.cdev, infrared_dev.devid, INFRARED_CNT);
	if (ret < 0)
	{
		pr_err("fail to add cdev \r\n");
		goto del_unregister;
	}

	/* 4、创建类 */
	infrared_dev.class = class_create(THIS_MODULE, INFRARED_NAME);
	if (IS_ERR(infrared_dev.class)) 
	{
		pr_err("Failed to create device class \r\n");
		goto del_cdev;
	}

	/* 5、创建设备,设备名是 INFRARED_NAME */
	/*创建设备 INFRARED_NAME 指定设备名,*/
	infrared_dev.device = device_create(infrared_dev.class, NULL, infrared_dev.devid, NULL, INFRARED_NAME);
	if (IS_ERR(infrared_dev.device)) {
		goto destroy_class;
	}

	return 0;

destroy_class:
	device_destroy(infrared_dev.class, infrared_dev.devid);
del_cdev:
	cdev_del(&infrared_dev.cdev);
del_unregister:
	unregister_chrdev_region(infrared_dev.devid, INFRARED_CNT);
fail_region:
	/* 释放个人初始化申请的资源,如del_init(); */
	free_irq(infrared_dev.irq_num, NULL);
	gpio_free(infrared_dev.gpio);
	return -EIO;
}

/**
 * @brief 注销字符设备驱动
 * 
 * @return 0,成功;其他负值,失败
*/
static void infrared_unregister(void)
{
	/* 1、删除 cdev */
	cdev_del(&infrared_dev.cdev);
	/* 2、注销设备号 */
	unregister_chrdev_region(infrared_dev.devid, INFRARED_CNT);
	/* 3、注销设备 */
	device_destroy(infrared_dev.class, infrared_dev.devid);
	/* 4、注销类 */
	class_destroy(infrared_dev.class);

	/* 释放中断 */
	free_irq(infrared_dev.irq_num, NULL);
	/* 释放 IO */
	gpio_free(infrared_dev.gpio);
}

/**
 * @brief 驱动入口函数
 * 
 * @return 0,成功;其他负值,失败
*/
static int __init infrared_driver_init(void)
{
	pr_info("infrared_driver_init\n");
	return infrared_register();
}

/**
 * @brief 驱动出口函数
 * 
 * @return 0,成功;其他负值,失败
*/
static void __exit infrared_driver_exit(void)
{
	pr_info("infrared_driver_exit\n");
	infrared_unregister();
}

/* 将上面两个函数指定为驱动的入口和出口函数 */
module_init(infrared_driver_init);
module_exit(infrared_driver_exit);

/* LICENSE 和作者信息 */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("JIAOZHU");
MODULE_INFO(intree, "Y");


六、测试程序

#include <fcntl.h>
#include <stdio.h>
#include <unistd.h> 
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <signal.h>


/***************************************************************
文件名 : drive_read_app.c
作者 : jiaozhu
版本 : V1.0
描述 : 驱动读取测试
其他 : 使用方法:./drive_read_app [/dev/xxx]
argv[1] 需要读取的驱动
日志 : 初版 V1.0 2023/3/3
***************************************************************/


int fd;
char *filename;

/**
 * @brief 信号响应函数,用于读取红外接收的数据
 *
 * @param num 信号量
*/
void infrared_handler(int num)
{
    int res;
    unsigned char data_buf[4];
    /* 从驱动文件读取数据 */
    res = read(fd, data_buf, sizeof(data_buf));
    if (res == 0)       
    {
        printf("infrared data: %02x %02x %02x %02x\n", data_buf[0], data_buf[1], data_buf[2], data_buf[3]);
    }
    else
    {
        printf("read file %s failed!\r\n", filename);
    }
}

/**
 * @brief main 主程序
 * @param argc argv 数组元素个数
 * @param argv 具体参数
 * @return 0 成功;其他 失败
*/
int main(int argc, char *argv[])
{
    int flags = 0;

    if(argc != 2){
        printf("Error Usage!\r\n");
        return -1;
    }

    filename = argv[1];

    /* 打开驱动文件 */
    fd = open(filename, O_RDWR);
    if(!fd){
        printf("Can't open file %s\r\n", filename);
        return -1;
    }

    signal(SIGIO, infrared_handler);
    /* 设置当前进程接收信号 */
    fcntl(fd, F_SETOWN, getpid());
    flags =fcntl(fd, F_GETFL);
    /* 开启异步通知 */
    fcntl(fd, F_SETFL, flags | FASYNC);

    while (1);

    close(fd);

    return 0;
}


参考链接

几种常用的红外线信号传输协议:https://tech.hqew.com/news_1050217
小米红外遥控器如何适配到其他应用设备之上:<https://blog.csdn.net/qq_40001346/article/details/108639243

标签:接收器,return,int,dev,infrared,红外,cdev,Linux,buf
From: https://www.cnblogs.com/jzcn/p/17197007.html

相关文章

  • 手把手带你玩转Linux
    今天这篇文章带你走进Linux世界的同时,带你手把手玩转Linux,加深对Linux系统的认识。 一、搞好Linux工作必须得不断折腾,说白了,只是动手力量必须强。我在初学Linux的......
  • Linux 中如何开启端口
    有时我们可能需要在Linux服务器中打开端口或在Linux服务器的防火墙中启用端口来运行特定的应用程序。在本文中,小编将带大家分析一下如何在linux服务器中打开端口命令?......
  • linux基础(1)--运行级别及root命令找回
    1系统运行级别0:关机1:单用户【找回丢失密码】2:多用户状态没有网络服务3:多用户状态有网络服务4:系统未使用保留给用户5:图形界面6:系统重启其中,最常用的为3和5。 有关......
  • U盘安装Arch Linux
    此文大多数来自这篇文章,但是修改了里面一些错误的地方和优化了一些内容,以及增加了桌面安装部分和其他一些常用软件安装(wayland下的输入法/firefox等)https://zhuanlan.zhih......
  • Linux运维DAY08
    上周内容: 1.VmwareWorkStition(快照、克隆、网络) 2.Linux目录结构 (每个目录存放是什么类型的文件) 3.Linux路径定位/etc/hostname路径就是对文件定位的一种方......
  • 如何在 Linux 终端中有效地搜索文件【2】- 高级篇
    在本文中,让我们探索find命令的高级使用技巧。如果您是第一次学习 ​​find​​ 命令,我在之前的教程[1]中介绍了文件搜索的基础知识,并探讨了一些使用Linux快速搜索文件......
  • Rocky Linux 9 安装 Node.js
    一、概要1.环境(1)RockyLinux9.1(2)Node.js16.0二、安装1.准备(1)更新仓库sudodnfupdate-y(2)安装NPM依赖的构建工具sudoyumgroupinstall'Deve......
  • Linux安装Docker
    Linux安装Docker1.CentOS安装DockerDocker分为CE和EE两大版本。CE即社区版(免费,支持周期7个月)EE即企业版,强调安全,付费使用,支持周期24个月。DockerCE分为s......
  • 如何在 Linux 中启用 Shell 脚本的调试模式
    脚本是存储在一个文件的一系列命令。在终端上输入一个个命令,按顺序执行的方法太弱了,使用脚本,系统中的用户可以在一个文件中存储所有命令,反复调用该文件多次重新执行命令。......
  • 常见 Linux 提权
    CommonLinuxPrivesc记录常见Linux提权的提权方式:水平提权,垂直提权。水平提权:这是您通过接管与您处于相同权限级别的不同用户来扩大您对受感染系统的影响的地方。......