Linux驱动基础(HC-SR04超声波模块)

这篇具有很好参考价值的文章主要介绍了Linux驱动基础(HC-SR04超声波模块)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言

本篇文章将讲解HC-SR04超声波模块的驱动程序编写,有了上篇SR501模块驱动程序编写的基础后这篇文章大家将会学的非常轻松。

一、HC-SR04超声波模块介绍

HC-SR04超声波模块是一种常用于距离测量和障碍物检测的模块。它通过发射超声波信号并接收回波来计算所测量物体与传感器之间的距离。

HC-SR04超声波模块内置有发射器、接收器和控制电路。当模块接收到输入信号后,发射器将发射出一定频率的超声波脉冲信号,该信号在空气中传播并被障碍物反射后,被接收器检测到并转换成电信号返回给模块。模块通过计算从发射到接收所经历时间的差值,即回波延迟时间,乘以声波在空气中的行进速度,得出传感器与障碍物之间的距离。

HC-SR04超声波模块的工作范围一般在2厘米到4米之间,并且可以通过调节工作电压和发送脉冲的频率来改变其工作范围。该模块体积小、功耗低,常用于机器人导航、无人机、汽车避障、智能安防等场景中。

二、超声波时序原理讲解

超声波时序原理的讲解可以看我前面STM32的文章,原理上都是一样的。

地址:超声波模块原理

三、设备树编写

超声波模块需要使用到两个引脚一个是trig触发信号引脚,一个是echo接收信号引脚。trig需要被配置为输出引脚,echo配置为输入引脚,并且配置为中断模式。

这里特别需要注意的是在gpios前面加上trig和echo,这样在驱动里面就可以直接通过名字来找到对应的引脚了。

   sr04 {  /* for imx6ull */
        compatible = "my,sr04";
        trig-gpios = <&gpio4 19 GPIO_ACTIVE_HIGH>;
        echo-gpios = <&gpio4 20 GPIO_ACTIVE_HIGH>;
    };

四、驱动程序编写

1.确定主设备号

主设备设置为0让系统自动帮我们分配主设备号。

static int major=0;/*主设备号*/

2.编写file_operations结构体

我们需要提供这个结构体并且编写其中的open和read函数,供应用程序使用。

static ssize_t sr04_read (struct file *file, char __user *buf, size_t size, loff_t *off)
{
	int err;
	int timeout;
	/*发出至少10us的触发信号*/
	gpiod_set_value(sr04_trig, 1);
	udelay(15);
	gpiod_set_value(sr04_trig, 0);

	/* 等待数据 */
	timeout = wait_event_interruptible_timeout(sr04_wq, sr04_data_ns, HZ);	
	if (timeout)
	{
		err = copy_to_user(buf, &sr04_data_ns, 4);
		sr04_data_ns = 0;
		return 4;
	}
	else
	{
		return -EAGAIN;
	}

	
	return 0;

}

static int sr04_open (struct inode *inode, struct file *file)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}



static struct file_operations sr501_ops={
	.owner		= THIS_MODULE,
	.open		= sr501_open,
	.read		= sr501_read,	
};

3.注册file_operations结构体

在Linux中注册其实就是指在Linux内核中添加我们自己编写的这个file_operations结构体。这个注册的工作在入口函数中完成。

static int __init sr04_init(void)
{
	int err;
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	/*确定主设备号*/
	major=register_chrdev(major, "mysr04", &sr04_ops);
	/*创建类*/
	sr04_class=class_create(THIS_MODULE, "sr04");
	if (IS_ERR(sr04_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "mysr04");
		return PTR_ERR(sr04_class);
	}

	init_waitqueue_head(&sr04_wq);//初始化队列
	
	err=platform_driver_register(&sr04);
	
	return 0;
}

4.出口函数编写

有入口函数就会有出口函数,在入口函数中做的是设备的注册等工作,那么出口函数就是做相反的工作,将设备注销。

static void __exit sr04_exit(void)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	platform_driver_unregister(&sr04);
	class_destroy(sr04_class);
	unregister_chrdev(major, "mysr04");	
}

module_init(sr04_init);
module_exit(sr04_exit);

MODULE_LICENSE("GPL");

5.probe函数和remove函数编写

创建platform_driver结构体和of_device_id结构体,使用of_device_id结构体中的compatible 属性和设备树进行匹配,匹配完成后会调用到probe函数。

static int sr04_probe(struct platform_device *pdev)
{
	int err;

	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	/*1.获取硬件信息*/
	sr04_echo=gpiod_get(&pdev->dev, "echo", GPIOD_IN);
	if (IS_ERR(sr04_echo)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	}
	sr04_trig=gpiod_get(&pdev->dev, "trig", GPIOD_OUT_LOW);
	if (IS_ERR(sr04_trig)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	}

	/*得到irq*/
	irq = gpiod_to_irq(sr04_echo);
	/*申请中断并设置为双边沿触发*/
	err = request_irq(irq, sr04_isr, IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, "sr04", NULL);
	if (err != 0) {
		printk("request_irq is err\n");
	}
	
	/*2.创建设备节点*/	
	device_create(sr04_class, NULL, MKDEV(major, 0), NULL, "sr04");
        
    return 0;	
}

static int sr04_remove(struct platform_device *pdev)
{		
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	device_destroy(sr04_class, MKDEV(major, 0));
	free_irq(irq, NULL);
	gpiod_put(sr04_trig);
	gpiod_put(sr04_echo);
	return 0;
}

static const struct of_device_id my_sr04[] = {
    { .compatible = "my,sr04" },
    { },
};


static struct platform_driver sr04={
	.driver = {
		.name = "sr04",
		.of_match_table = my_sr04,	
	},
	.probe = sr04_probe,
	.remove	= sr04_remove,	
};

7.测试程序编写


#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 <unistd.h>


/*
 * ./sr04_test /dev/sr04
 *
 */
int main(int argc, char **argv)
{
	int fd;
	int ns;

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


	/* 2. 打开文件 */
//	fd = open(argv[1], O_RDWR | O_NONBLOCK);
	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, &ns, 4) == 4)
		{
			printf("get distance: %d ns\n", ns);
			printf("get distance: %d mm\n", ns*340/2/1000000);  /* mm */
		}
		else
			printf("get distance: -1\n");
		sleep(1);
	}
	
	close(fd);
	
	return 0;
}




8.全部驱动程序

#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>
#include <linux/workqueue.h>
#include <asm/current.h>
#include <linux/delay.h>
#include <linux/timex.h>



static int major=0;
static struct class *sr04_class;
static struct gpio_desc *sr04_echo;
static struct gpio_desc *sr04_trig;
static int irq;
static u64 sr04_data_ns = 0;
static wait_queue_head_t sr04_wq;



static ssize_t sr04_read (struct file *file, char __user *buf, size_t size, loff_t *off)
{
	int err;
	int timeout;
	/*发出至少10us的触发信号*/
	gpiod_set_value(sr04_trig, 1);
	udelay(15);
	gpiod_set_value(sr04_trig, 0);

	/* 等待数据 */
	timeout = wait_event_interruptible_timeout(sr04_wq, sr04_data_ns, HZ);	
	if (timeout)
	{
		err = copy_to_user(buf, &sr04_data_ns, 4);
		sr04_data_ns = 0;
		return 4;
	}
	else
	{
		return -EAGAIN;
	}

	
	return 0;

}

static int sr04_open (struct inode *inode, struct file *file)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}



static struct file_operations sr04_ops={
	.owner		= THIS_MODULE,
	.open		= sr04_open,
	.read		= sr04_read,	
};

static irqreturn_t sr04_isr(int irq, void *dev_id)
{
	int val = gpiod_get_value(sr04_echo);

	if(val)
	{
		/*上升沿*/
		
sr04_data_ns = ktime_get_ns();
	}
	else
	{
		/*下降沿*/
		sr04_data_ns = ktime_get_ns() - sr04_data_ns;
		/* 2. 唤醒APP:去同一个链表把APP唤醒 */
		wake_up(&sr04_wq);
	}

	return IRQ_HANDLED; // IRQ_WAKE_THREAD;
}



static int sr04_probe(struct platform_device *pdev)
{
	int err;

	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	/*1.获取硬件信息*/
	sr04_echo=gpiod_get(&pdev->dev, "echo", GPIOD_IN);
	if (IS_ERR(sr04_echo)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	}
	sr04_trig=gpiod_get(&pdev->dev, "trig", GPIOD_OUT_LOW);
	if (IS_ERR(sr04_trig)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	}

	/*得到irq*/
	irq = gpiod_to_irq(sr04_echo);
	/*申请中断并设置为双边沿触发*/
	err = request_irq(irq, sr04_isr, IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, "sr04", NULL);
	if (err != 0) {
		printk("request_irq is err\n");
	}
	
	/*2.创建设备节点*/	
	device_create(sr04_class, NULL, MKDEV(major, 0), NULL, "sr04");
        
    return 0;	
}

static int sr04_remove(struct platform_device *pdev)
{		
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	device_destroy(sr04_class, MKDEV(major, 0));
	free_irq(irq, NULL);
	gpiod_put(sr04_trig);
	gpiod_put(sr04_echo);
	return 0;
}

static const struct of_device_id my_sr04[] = {
    { .compatible = "my,sr04" },
    { },
};


static struct platform_driver sr04={
	.driver = {
		.name = "sr04",
		.of_match_table = my_sr04,	
	},
	.probe = sr04_probe,
	.remove	= sr04_remove,	
};


static int __init sr04_init(void)
{
	int err;
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	/*确定主设备号*/
	major=register_chrdev(major, "mysr04", &sr04_ops);
	/*创建类*/
	sr04_class=class_create(THIS_MODULE, "sr04");
	if (IS_ERR(sr04_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "mysr04");
		return PTR_ERR(sr04_class);
	}

	init_waitqueue_head(&sr04_wq);//初始化队列
	
	err=platform_driver_register(&sr04);
	
	return 0;
}

static void __exit sr04_exit(void)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	platform_driver_unregister(&sr04);
	class_destroy(sr04_class);
	unregister_chrdev(major, "mysr04");	
}

module_init(sr04_init);
module_exit(sr04_exit);

MODULE_LICENSE("GPL");

总结

本篇文章讲解HC-SR04超声波驱动程序的编写,大家只要掌握好了驱动的基本框架,那么剩下的工作和单片机中的就是一样了。文章来源地址https://www.toymoban.com/news/detail-453807.html

到了这里,关于Linux驱动基础(HC-SR04超声波模块)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • HC-SR04超声波测距模块介绍

    超声波简介       超声波是由机械振动产生的, 可在不同介质中以不同的速度传播, 具有定向性好、能量集中、传输过程中衰减较小、反射能力较强等优点。超声波传感器可广泛应用于非接触式检测方法,它不受光线、被测物颜色等影响, 对恶劣的工作环境具有一定的适应能力

    2023年04月19日
    浏览(25)
  • 【mcuclub】超声波测距模块HC-SR04

      编号 名称 功能 1 VCC 电源正 2 TRIG 触发控制信号输入 3 ECHO 回响信号输出 4 GND 电源地 由于超声波指向性强,能量消耗缓慢,在介质中传播的距离较远,因而超声波经常用于距离的测量,如测距仪和物体测量仪等都可以通过超声波来实现。利用超声波检测往往比较迅速、方便

    2023年04月08日
    浏览(20)
  • 树莓派使用HC-SR04超声波测距

      超声波测距原理很简单:   1、通过记录发送超声波的时间、记录超声波返回的时间,返回时间与发送时间相减得到超声波的持续时间。   2、通过公式:( 超声波持续时间 * 声波速度 ) / 2 就可以得出距离;   HC-SR04参数:   工作电压: 5V   工作电流:15mA   

    2024年02月06日
    浏览(22)
  • STM32外设系列—HC-SR04(超声波)

    🎀 文章作者:二土电子 🌸 关注文末公众号获取其他资料和工程文件! 🐸 期待大家一起学习交流! 超声波测距的原理非常简单,超声波发生器在某一时刻发出一个超声波信号,当这个超声波信号遇到被测物体后会反射回来,被超声波接收器接收到。这样只要计算出从发出

    2024年02月09日
    浏览(19)
  • 硬件速攻-HC-SR04超声波传感器

    ▬▬▬▬▬▶测试模块实验◀▬▬▬▬▬ HC-SR04超声波测距传感器 HC-SR04超声波模块是一种常用的测距传感器,可以通过发射超声波脉冲并接收其回波来测量物体与模块之间的距离。它广泛应用于许多领域,如机器人、无人机、智能车辆等。 XCOM串口实时打印距离 VCC 5V GND GND

    2024年02月12日
    浏览(15)
  • 【STM32】超声波传感器HC-SR04知识

            超声波传感器模块上面通常有两个超声波元器件,一个用于发射,一个用于接收。         电路板上有四个引脚:VCC、GND、Trig(触发)、Echo(回应)。         工作电压与电流:5V,15mA         感应距离:2~400cm         感测角度:不小于15度   

    2024年02月02日
    浏览(20)
  • 超声波测距模块HC-SR04详解(基于51单片机)

    本篇文章是个人整理的包含超声波测距模块HC-SR04的基本介绍与基本工作原理以及分别通过LCD1602、数码管和串口显示距离的实例讲解与代码的笔记,部分内容来自《HC-SR04超声波测距模块说明书》,代码使用模块化编辑,部分模块来自江科大自化协的51单片机教学视频。 希望大

    2023年04月16日
    浏览(17)
  • 【嵌入式系统应用开发】FPGA——基于HC-SR04超声波测距

    硬件 DE2-115 HC-SR04超声波传感器 软件 Quartus 18.1 使用DE2-115开发板驱动HC-SR04模块,并将所测得数据显示到开发板上的数码管。 HC-SR04 超声波测距模块可提供 2cm-400cm的非接触式距离感测功能,测距精度可达高到 3mm;模块包括超声波发射器、接收器与控制电路。图1为 HC-SR04 外观,

    2024年02月08日
    浏览(26)
  • HC-SR04超声波测距模块使用方法和例程(STM32)

    基于STM32和HC-SR04模块实现超声波测距功能 最近在学STM32做个简单的应用实践一下,顺便水一篇文章。 本文用的单片机是STM32F103C8T6,超声波测距模块是HC-SR04,显示测距结果用的是0.96寸OLED屏模块。 下图中小于10cm时的显示结果有点问题,代码已修复并更新 修复后的结果: 视频

    2024年02月03日
    浏览(22)
  • 基于STM32F103C8T6(HAL库)的HC-SR501红外人体传感及HC-SR04超声波测距

    一、基于STM32F103C8T6最小系统板和STM32CubeMX实现LED灯循环闪烁 二、基于STM32F103C8T6和STM32CubeMX实现UART串口通信数据收发 三、实战小例程 基于STM32F103C8T6最小系统板和STM32CubeMX驱动WS2812B光立方 四、基于STM32F103C8T6最小系统板HAL库CubeMX驱动HC-SR501红外人体传感模块 五、基于STM32F103C8

    2023年04月17日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包