Linux驱动入门实验班——IRDA红外遥控模块驱动(附百问网视频链接)

目录

前言

一、红外遥控协议

二、数据位

三、编程思路

1.中断处理函数

2.数据解析函数

3.定时器超时函数

4.read函数

四、源码

驱动

应用

课程链接


前言

在这里主要记录学习韦东山老师Linux驱动人入门实验班的笔记,韦东山老师的驱动课程讲的非常好,想要学习驱动的小伙伴可以去b站学习他的课程。

一、红外遥控协议

我们按下遥控器按键的时候,遥控器自动发送某个红外信号,接收头接收到 红外信号,然后把红外信号转换成电平信号,通过IRD这根线,传给SOC。整个 传输,只涉及单向传输,由HS0038向主芯片传送。 因此,我们只需要编写程序,从IRD上获取数据即可,在这之前,我们需要 先了解下数据是怎么表示的,也就是传输的红外数据的格式。

红外协议有:NEC、SONY、RC5、RC6等,常用的就是NEC格式,因此我们 主要对NEC进行讲解。

NEC 协议的开始是一段引导码,这个引导码由一个9ms的低脉冲加上一个4.5ms的高脉冲组成,它用来通 知接收方我要开始传输数据了。

然后接着的是数据,数据由4字节组成:地址、地址(取反)、数据、数据(取 反),取反是用来校验用的。

二、数据位

从前面的图可以知道,NEC每次要发32位(地址、地址取反、数据、数据 取反,每个8位)的数据。数据的1和0,开始都是0.56ms的低脉冲,对于数 据1,后面的高脉冲比较长,对于数据0,后面的高脉冲比较短。

三、编程思路

1.中断处理函数

  • 记录中断发生时刻
  • 累计中断次数
  • 次数达标后,删除定时器,解析数据,放入环形缓冲区,唤醒APP
  • 启动定时器

2.数据解析函数

  • 平时GPIO为高;
  • 发现GPIO为低时,判断它有9ms的低电平:,对于引导码,或连发码,它们都有9ms的低电平。
  • 分辨是引导码,还是连发码,在 9ms 的低电平之后,判断高电平持续时间,引导码的高电平维持时间是 4.5ms,连发码的高电平维持时间是2.25ms。 发现是连发码时,直接结束译码。 发现是引导码时,还得继续接收32位数据。
  • 接收数据,对于数据0和1都有0.56ms的低电平,需要判断的是低电平过后高电平维持的时间。
  • 校验数据

3.定时器超时函数

设置定时器的作用是为了多一层保险,如果数据接收超时或者丢失能够及时发现。

4.read函数

如果数据不为-1,就将数据拷贝回应用程序。

四、源码

驱动

cpp 复制代码
#include "asm/gpio.h"
#include "asm/uaccess.h"
#include <linux/module.h>
#include <linux/poll.h>

#include "asm-generic/gpio.h"
#include "asm/gpio.h"
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>

#define BUF_LEN 128

struct gpio_desc{
	int gpio;
	int irq;
	char *name;
	int irda;
	struct timer_list irda_timer;
};

static struct gpio_desc gpios[] = {
	{115, 0, "irda", },
};

static int major;
static struct class *irda_class;
static int r, w;
static unsigned char g_buf[BUF_LEN];
static DECLARE_WAIT_QUEUE_HEAD(irda_wait);
struct fasync_struct *irda_fasync;

static u64 g_irda_irq_times[68];
static int g_irda_irq_cnt = 0;

static int is_buf_empty(void)
{
	return (r == w);
}

static int is_buf_full(void)
{
	return (r == (w + 1) % BUF_LEN);
}

static void put_val(int value)
{
	if (!is_buf_full())
	{
		g_buf[w] = value;
		w = (w + 1) % BUF_LEN;
	}
}

static unsigned char get_val(void)
{
	unsigned char value = 0;
	if (!is_buf_empty())
	{
		value = g_buf[r];
		r = (r + 1) % BUF_LEN;
	}

	return value;
}

static void irda_timer_expire(unsigned long data)
{
	g_irda_irq_cnt = 0;
	put_val(-1);
	put_val(-1);
	wake_up_interruptible(&irda_wait);
	kill_fasync(&irda_fasync, SIGIO, POLL_IN);
}

static int irda_drv_fasync (int fd, struct file *filp, int on)
{
	if (fasync_helper(fd, filp, on, &irda_fasync) >= 0)
	{
		return 0;
	}
	else
	{
		return -EIO;
	}
}

static void parse_irda_datas(void)
{
	u64 time;
	int i;
	int m, n;
	unsigned char datas[4];
	unsigned char data = 0;
	int bits = 0;
	int byte = 0;
	time = g_irda_irq_times[1] - g_irda_irq_times[0];
	if (time < 8000000 || time > 10000000)
	{
		goto err;
	}

	time = g_irda_irq_times[2] - g_irda_irq_times[1];
	if (time < 3500000 || time > 55000000)
	{
		goto err;
	}

	for (i = 0; i < 32; i++)
	{
		m = 3 + 2 * i;
		n = m + 1;
		time = g_irda_irq_times[n] - g_irda_irq_times[m];
		data <<= 1;
		bits++;
		if (time > 1000000)
		{
			data |= 1;
		}

		if (bits == 8)
		{
			datas[byte] = data;
			byte++;
			data = 0;
			bits = 0;
		}
	}
	
		datas[1] = ~datas[1];
		datas[3] = ~datas[3];

		if ((datas[0] != datas[1]) || (datas[2] != datas[3]))
		{
			goto err;
		}

		put_val(datas[0]);
		put_val(datas[2]);
		wake_up_interruptible(&irda_wait);
		kill_fasync(&irda_fasync, SIGIO, POLL_IN);
		return;
err:
	g_irda_irq_cnt = 0;
	put_val(-1);
	put_val(-1);
	wake_up_interruptible(&irda_wait);
	kill_fasync(&irda_fasync, SIGIO, POLL_IN);
}

static irqreturn_t irda_irq_handler(int irq, void *dev_id)
{
	struct gpio_desc *gpio_desc = dev_id;
	u64 time;
	
	time = ktime_get_ns();
	g_irda_irq_times[g_irda_irq_cnt] = time;
	g_irda_irq_cnt++;

	if (g_irda_irq_cnt == 68)
	{
		parse_irda_datas();
		del_timer(&gpio_desc->irda_timer);
		g_irda_irq_cnt = 0;
	}

	mod_timer(&gpio_desc->irda_timer, jiffies + msecs_to_jiffies(100));
	return IRQ_HANDLED;
}

static ssize_t irda_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	int ret;
	unsigned char kernel_buf[2];

	if (is_buf_empty() && (file->f_flags & O_NONBLOCK))
	{
		return -EINVAL;
	}

	wait_event_interruptible(irda_wait, !is_buf_empty());
	kernel_buf[0] = get_val();
	kernel_buf[1] = get_val();
	if (kernel_buf[0] == (unsigned char)-1 && kernel_buf[1] == (unsigned char)-1)
	{
		return -EIO;
	}

	ret = copy_to_user(buf, kernel_buf, 2);
	return 2;
}

static struct file_operations irda_drv = {
	.owner = THIS_MODULE,
	.read = irda_drv_read,
	.fasync = irda_drv_fasync,
};

static int __init irda_init(void)
{
	int i;
	int err;
	int count = sizeof(gpios) / sizeof(gpios[0]);

	for (i = 0; i < count; i++)
	{
		gpios[i].irq = gpio_to_irq(gpios[i].gpio);
		setup_timer(&gpios[i].irda_timer, irda_timer_expire, (unsigned long)&gpios[i]);
		err = request_irq(gpios[i].irq, irda_irq_handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, gpios[i].name, &gpios[i]);
	}

	major = register_chrdev(0, "irda_drv", &irda_drv);
	irda_class = class_create(THIS_MODULE, "irda_class");
	device_create(irda_class, NULL, MKDEV(major, 0), NULL, "irda_drv");

	return err;
}

static void __exit irda_exit(void)
{
	device_destroy(irda_class, MKDEV(major, 0));
	class_destroy(irda_class);
	unregister_chrdev(major, "irda_drv");
}

module_init(irda_init);
module_exit(irda_exit);

MODULE_LICENSE("GPL");

应用

cpp 复制代码
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>
#include <signal.h>

static int fd;

/*
 * ./button_test /dev/irda
 *
 */
int main(int argc, char **argv)
{
	unsigned char buf[2];
	
	/* 1. 判断参数 */
	if (argc != 2) 
	{
		printf("Usage: %s <dev>\n", argv[0]);
		return -1;
	}


	/* 2. 打开文件 */
	fd = open(argv[1], O_RDWR);
	if (fd == -1)
	{
		printf("can not open file %s\n", argv[1]);
		return -1;
	}

	while (1)
	{
		if (read(fd, buf, 2) == 2)
			printf("get irda: deivce 0x%02x, data 0x%02x\n", buf[0], buf[1]);
		else
			printf("get irda: -1\n");
	}

	close(fd);
	
	return 0;
}

课程链接

百问网韦老师的驱动入门实验班https://video.100ask.net/p/t_pc/course_pc_detail/video/v_637cc4a4e4b068e9c3dc6631?product_id=p_634cbce4e4b00a4f37500252&content_app_id=&type=6

相关推荐
waving-black15 分钟前
利用frp和腾讯云服务器将内网暴露至外网(内网穿透)
linux·服务器·腾讯云·frp·内网穿透
章鱼哥嵌入式开发22 分钟前
# STM32F103 SD卡读写程序
stm32·单片机
stormsha27 分钟前
Linux中su与sudo命令的区别:权限管理的关键差异解析
linux·运维·服务器·鸿蒙系统·ux·batch命令
筏.k1 小时前
grep、wc 与管道符快速上手指南
linux
Johny_Zhao1 小时前
华为MAAS、阿里云PAI、亚马逊AWS SageMaker、微软Azure ML各大模型深度分析对比
linux·人工智能·ai·信息安全·云计算·系统运维
CodeOfCC2 小时前
c语言 封装跨平台线程头文件
linux·c语言·windows
广药门徒2 小时前
定时器时钟来源可以从输入捕获引脚输入
单片机·嵌入式硬件
科文小白狼2 小时前
Linux下VSCode开发环境配置(LSP)
linux·vscode·里氏替换原则·lsp
jugt3 小时前
CentOS 7.9安装Nginx1.24.0时报 checking for LuaJIT 2.x ... not found
linux·运维·centos
多多*4 小时前
LUA+Reids实现库存秒杀预扣减 记录流水 以及自己的思考
linux·开发语言·redis·python·bootstrap·lua