首页 > 系统相关 >Linux驱动入门实验班——SR04超声波模块驱动(附百问网视频链接)

Linux驱动入门实验班——SR04超声波模块驱动(附百问网视频链接)

时间:2024-08-15 20:26:09浏览次数:11  
标签:SR04 int 实验班 drv sr04 static gpio 驱动 include

目录

一、工作原理

1.触发信号

2.回响信号

二、接口图

​三、编写思路

1.构造file_operations结构体

2.实现函数,填充结构体

3.编写入口函数

4.编写中断处理函数

5.编写定时器超时函数

6.编写出口函数

7.声明入口、出口函数以及协议 

四、应用程序

五、注意事项:

六、源码

驱动

应用

课程链接


一、工作原理

1.触发信号

HC-SR04超声波测距模块的触发信号可以通过给Trig引脚输入一个至少10微秒的高电平脉冲来实现。触发信号的步骤如下:

  1. 将Trig引脚设置为输出模式。

  2. 将Trig引脚输出高电平。

  3. 延时至少10微秒。

  4. 将Trig引脚输出低电平。

两次触发信号之间的间隔不能小于50ns

2.回响信号

        回响信号的脉冲信号与测量距离成正比。根据信号的发出和返回的间隔时间可以算出距离。

        公式:distance = time * 17 / 1000000 (cm)

二、接口图

根据接口图,可以计算出trig和echo的引脚编号

三、编写思路

1.构造file_operations结构体

/* 定义自己的file_operations结构体                                              */
static struct file_operations sr04_drv = {
	.owner	 = THIS_MODULE,
	.read    = sr04_read,
	.poll    = sr04_poll,
	.fasync  = sr04_fasync,
	.unlocked_ioctl = sr04_ioctl,
};

2.实现函数,填充结构体

2.1实现sr04_read()

从环形缓冲区中获取数据,将获取到的数据传给应用层。如果应用层传入非阻塞标识符且环形缓冲区中没有数据就直接返回;若是阻塞且没数据的话就等待数据。

2.2实现sr04_ioctl()

发送一个维持20微妙的高电平后拉低

3.编写入口函数

①调用下列函数,进行注册:

  • register_chrdev()
  • class_create()
  • device_create()

②gpio_request 为trig引脚请求GPIO,然后让它一开始保持低电平输出。

③为echo引脚注册中断,并且注册一个定时器

4.编写中断处理函数

①当中断发生时,读取引脚电平

②若为高电平,记录时间ktime_get_ns()

③若为低电平,则记录与上一个高电平的间隔时间,删除定时器,将间隔时间放入环形缓冲区,唤醒等待的read()函数。

5.编写定时器超时函数

给环形缓冲区传入一个-1,且唤醒等待中的read()函数

6.编写出口函数

 释放掉入口函数中注册的资源。

使用到的函数:

  • device_destroy()
  • class_destroy()
  • unregister_chrdev()
  • free_irq()
  • del_timer()

7.声明入口、出口函数以及协议 

  • module_init()
  • module_exit()
  • MODULE_LICENSE("GPL")

四、应用程序

1.打开文件

2.调用ioctl

3.使用read函数,读取间隔时间

4.将读取到的数据转换成距离

5.休眠一秒,防止trig信号间隔时间太短

五、注意事项:

1.不要在中断处理函数里执行printk。

2.不在ioctl发出trig信号后不要printk、在sr04_read里也不要printk。

3.APP不要频繁地调用ioctl发出trig信号。

4.不要在驱动程序中使用除法。

六、源码

驱动

#include "asm-generic/gpio.h"
#include "asm/delay.h"
#include <linux/module.h>
#include <linux/poll.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
#define CMD_TRIG 100

static int major;
static struct class *sr04_class;
struct fasync_struct *sr04_fasync;

struct gpio_desc{
	int gpio;
	int irq;
	char *name;
	int key;
	struct timer_list key_time;
};

static struct gpio_desc gpios[2] = {
	{115, 0, "trig",},
	{116, 0, "echo",},
};

static int r, w;
static int g_buf[BUF_LEN];

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

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

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

static int get_val(void)
{
	int val = 0;
	if (!is_empty())
	{
		val = g_buf[r];
		r = (r + 1) % BUF_LEN;
	}
	return val;
}

static DECLARE_WAIT_QUEUE_HEAD(gpio_wait);

static ssize_t sr04_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	int val;
	int ret;
	if (is_empty() && (file->f_flags & O_NONBLOCK))
	{
		return -EINVAL;
	}

	wait_event_interruptible(gpio_wait, !is_empty());

	val = get_val();
	ret = copy_to_user(buf, &val, 4);

	return 4;
}

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

static unsigned int sr04_drv_poll (struct file *filp, struct poll_table_struct *wait)
{
	poll_wait(filp, &gpio_wait, wait);
	return is_empty() ? 0 : POLLIN | POLLRDNORM;
}

static long sr04_drv_ioctl (struct file *file, unsigned int command, unsigned long arg)
{
	switch (command)
	{
		case CMD_TRIG:
		{
			gpio_set_value(gpios[0].gpio, 1);
			udelay(20);
			gpio_set_value(gpios[0].gpio, 0);
		}
	}
	return 0;
}

static struct file_operations sr04_drv = {
	.owner = THIS_MODULE,
	.read = sr04_drv_read,
	.poll    = sr04_drv_poll,
	.fasync  = sr04_drv_fasync,
	.unlocked_ioctl = sr04_drv_ioctl,
};

static irqreturn_t sr04_isr(int irq, void *dev_id)
{
	struct gpio_desc *gpio_desc = dev_id;
	int val;
	static u64 rising_time;
	u64 time;

	val = gpio_get_value(gpio_desc->gpio);

	if (val)
	{
		rising_time = ktime_get_ns();
	}
	else 
	{
		if (rising_time == 0)
		{
			return IRQ_HANDLED;
		}
		time = ktime_get_ns() - rising_time;
		rising_time = 0;

		put_val(time);

		wake_up_interruptible(&gpio_wait);
		kill_fasync(&sr04_fasync, SIGIO, POLL_IN);
	}

	return IRQ_HANDLED;
}

static int __init sr04_drv_init(void)
{
	int ret;
	
	ret = gpio_request(gpios[0].gpio, gpios[0].name);
	gpio_direction_output(gpios[0].gpio, 0);

	gpios[1].irq = gpio_to_irq(gpios[1].gpio);

	ret = request_irq(gpios[1].irq, sr04_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, gpios[1].name, &gpios[1]);
	
	major = register_chrdev(major, "sr04_drv", &sr04_drv);
	sr04_class = class_create(THIS_MODULE, "sr04_class");
	device_create(sr04_class, NULL, MKDEV(major, 0), NULL, "sr04_drv");

	return ret;
}

static void __exit sr04_drv_exit(void)
{
	device_destroy(sr04_class, MKDEV(major, 0));
	class_destroy(sr04_class);
	unregister_chrdev(major, "sr04_drv");

	gpio_free(gpios[0].gpio);
	free_irq(gpios[1].irq, &gpios[1]);
}

module_init(sr04_drv_init);
module_exit(sr04_drv_exit);

MODULE_LICENSE("GPL");

应用


#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>
#include <sys/ioctl.h>

#define CMD_TRIG  100

static int fd;

/*
 * ./button_test /dev/sr04
 *
 */
int main(int argc, char **argv)
{
	int val;
	struct pollfd fds[1];
	int timeout_ms = 5000;
	int ret;
	int	flags;

	int i;
	
	/* 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)
	{
		ioctl(fd, CMD_TRIG);
		printf("I am goning to read distance: \n");
		if (read(fd, &val, 4) == 4)
			printf("get distance: %d cm\n", val*17/1000000);
		else
			printf("get distance err\n");

		sleep(1);
	}

	close(fd);
	
	return 0;
}

课程链接

42_模板1实战_SR04超声波测距模块驱动编程 (100ask.net)icon-default.png?t=N7T8https://video.100ask.net/p/t_pc/course_pc_detail/video/v_636c7629e4b01126eaa32250?product_id=p_634cbce4e4b00a4f37500252&content_app_id=&type=6

标签:SR04,int,实验班,drv,sr04,static,gpio,驱动,include
From: https://blog.csdn.net/m0_75183905/article/details/141215819

相关文章

  • Spring Boot集成Apache Kafka实现消息驱动
    SpringBoot集成ApacheKafka实现消息驱动大家好,我是微赚淘客返利系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!ApacheKafka是一个分布式流处理平台,广泛用于构建实时数据管道和流处理应用程序。SpringBoot提供了对ApacheKafka的集成支持,使得在SpringBoot应用中实现消......
  • 微孔雾化片驱动(电子烟/雾化器/补水仪/喷雾器)—和迅康科技
    HK9001系列是微孔雾化片驱动专用集成电路,自适应频率范围为100KHz~120KHz(中心频率108KHz),自适应频率范围为140KHz~160KHz(中心频率150KHz)的微孔雾化片,正常工作电压4.5-5.0V,其内部集成微孔雾化片频率适应电路,可以检测微孔雾化片需要频率进行驱动,自适应频率快。集成无水电流......
  • 多变的车灯,绕不开的LED驱动芯片
    近年来随着汽车的不断普及,车灯方面也在不断发展,由最开始的卤素灯发展为氙气大灯,再到现在的LED大灯和矩阵式LED大灯。在今年的北京车展中,其中不乏令人眼前一亮的方案。比如大众途观Lpro搭载MicroLED投影大灯,意味着该方案首次已经下沉至30万以内的市场价格区间。还有极氪MI......
  • Linux驱动入门实验班——SR501红外模块驱动(附百问网视频链接)
    目录 一、工作方式二、接口图三、编写思路1.构造file_operations结构体2.实现read函数3.编写入口函数4.编写中断处理函数5.编写出口函数6.声明出入口函数以及协议四、源码五、课程链接一、工作方式SR501人体红外感应模块有两种工作模式:        通过......
  • linux内核模块 字符设备驱动模板
    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档文章目录前言一、linux内核模块是什么?二、代码示例总结前言提示:这里可以添加本文要记录的大概内容:内核版本5.10.92linux内核模块字符设备驱动模板cdev注册字符设备,创建一个/dev/下设备节点和/sy......
  • Linux驱动开发基础(LED驱动)
    所学来自百问网目录1.LED原理2.普适的GPIO引脚操作方法2.1GPIO模块的一般结构2.2GPIO框图2.3寄存器的操作2.3.1一般的操作方式2.3.2高效的操作方式3.基于IMX6UL_6ULL的GPIO操作方法3.1GPIO框图3.2CCM3.3IOMUXC3.4GPIO模块内部3.5读写GPIO4.LED驱......
  • 事件驱动系统设计之将事件检索与事件处理解耦
    0前言part1讨论了集成过程中遇到的挑战以及幂等事件处理的作用。解决集成问题之后,我们需要反思事件检索的问题。我们的经验教训表明,将事件检索与事件处理解耦至关重要。1事件处理与请求/响应API紧耦合part1讨论了将请求/响应API集成到事件驱动微服务中时,由于基于请求/响......
  • 驱动开发环境搭建
    1.安装VS2019首先,我们需要安装VS2019,资源链接如下:VS2019+WDK:https://pan.baidu.com/s/1LYIn1MXLjY_zgEgLr8SgYA?pwd=xyji在安装的时候我们需要注意务必要注意,图上已勾选的选项必须要全部勾选,漏勾可能会导致各种奇怪的问题!可选部分我们需要勾选的安装项如下:需要注意的......
  • JDBC加载MySQL驱动【底层实现】
    JDBC4.0如何加载引入依赖<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.32</version></dependency>上代码importjava.sql.Connection;impor......
  • ubuntu网卡驱动修复
    问题引起更换显卡驱动时频繁重启,突然发现右上角没有网络图标了。尝试法1网络上查到的大多与NetworkManager相关。sudoserviceNetworkManagerstopsudorm/var/lib/NetworkManager/NetworkManager.statesudogedit/etc/NetworkManager/NetworkManager.conf这一步将打......