Linux驱动开发-编写超声波测距模块的驱动

举报
DS小龙哥 发表于 2021/12/31 00:21:41 2021/12/31
【摘要】 当前采用的这种超声波测距模块在各大高校实验室、毕设、课设里用的很多,原理很简单,通过声波测距,发出的声音碰到障碍物会反弹,声音在空气里传播的速度是已知的,根据时间就能计算出测量的距离。这款超声波模块内部自带了时间计算电路,型号是HC-SR04 ,它可提供 2cm-400cm 的非接触式距离感测功能,距精度可达高到 3mm; 整个模块包括了超声波发射器、 接收器与控制电路。

1. 介绍

当前采用的这种超声波测距模块在各大高校实验室、毕设、课设里用的很多,原理很简单,通过声波测距,发出的声音碰到障碍物会反弹,声音在空气里传播的速度是已知的,根据时间就能计算出测量的距离。这款超声波模块内部自带了时间计算电路,型号是HC-SR04 ,它可提供 2cm-400cm 的非接触式距离感测功能,距精度可达高到 3mm; 整个模块包括了超声波发射器、 接收器与控制电路。

基本工作原理:
(1) 采用 IO 口 TRIG 触发测距, 给至少 10us 的高电平信号;
(2) 模块自动发送 8 个 40khz 的方波, 自动检测是否有信号返回;
(3) 有信号返回, 通过 IO 口 ECHO 输出一个高电平, 高电平持续的时间就是超声波从发射到返回的时间。

测量距离的公式:

uS/58=厘米或者 uS/148=英寸; 
或是: 距离=高电平时间*声速(340M/S)/2;
建议测量周期为 60ms 以上, 以防止发射信号对回响信号的影响。

下面是超声波模块与开发板的连线方式:

image-20211229094758902

2. 示例代码

下面是超声波测距模块的驱动代码,将超声波模块的输出脚接在开发板支持中断的IO口上,配置为上升沿触发,当超声波输出脚检测到高电平就进去中断服务函数,在中断服务函数里调度工作队列,最终在工作函数里完成高电平的时间长度获取,计算测量的距离,直接在驱动代码里打印出来。

下面是测量的结果:

image-20211229094915304

2.1 驱动代码

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/gpio.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/interrupt.h>

static struct timer_list timer;
static struct work_struct work;
static int irq;
#define TRIG EXYNOS4_GPB(7)      //输出触发信号   第8个IO口
#define ECHO EXYNOS4_GPX1(0)    //ECHO 回响信号输出--中断   第9个IO口


/*获取高电平持续时间--us单位*/
static u32 GetTimeH(void)
{
	ktime_t my_time,my_time2;
	unsigned int i,j;
	my_time=ktime_get(); //获取当前时间
	i=ktime_to_us(my_time); //转 us
	while(gpio_get_value(ECHO)){}
	my_time2=ktime_get(); //获取当前时间
	j=ktime_to_us(my_time2); //转 us
	return j-i;
}

/*
工作函数
*/
static void csb_work_func(struct work_struct *work)
{
	u32 time=GetTimeH();
	printk("厘米:%d cm\n",time/58);
}

/*外部中断服务函数*/
irqreturn_t csb_irq_handler_func(int irq, void *dev)
{
	/*添加工作到工作队列*/
	schedule_work(&work);
	return IRQ_HANDLED;
}

/*内核定时器中断服务函数*/
static void timer_function(unsigned long data)
{
	static u8 i=0;
	mod_timer(&timer,msecs_to_jiffies(1000)+jiffies);
	i=!i;
	if(i)
	{
		gpio_set_value(TRIG,1);
	}
	else
	{
		gpio_set_value(TRIG,0);
	}
}

static int __init tiny4412_linux_csb_init(void)
{
	/*请求GPIO口使用权*/
	gpio_request(TRIG,"CSB");
    /*配置GPIO引脚*/
	s3c_gpio_cfgpin(TRIG,S3C_GPIO_OUTPUT);
	/*GPIO默认输出值*/
	gpio_set_value(TRIG,0);

	/*初始化工作函数*/
	INIT_WORK(&work,csb_work_func);
	/*1. 获取中断号*/
	irq=gpio_to_irq(ECHO);
	/*2. 注册中断*/
	request_irq(irq,csb_irq_handler_func,IRQF_TRIGGER_RISING,"tiny4412_csb",NULL);
	
	timer.expires=HZ+jiffies; /*单位是节拍*/
	timer.function=timer_function;
	timer.data=666;
	/*1. 初始化定时器*/
	init_timer(&timer);
	/*2. 添加定时器到内核*/
	add_timer(&timer);
    printk("驱动测试: 驱动安装成功\n");
    return 0;
}

static void __exit tiny4412_linux_csb_cleanup(void)
{
	/*3. 删除定时器*/
	del_timer_sync(&timer);
	free_irq(irq,NULL);
    printk("驱动测试: 驱动卸载成功\n");
}

module_init(tiny4412_linux_csb_init);    /*驱动入口--安装驱动的时候执行*/
module_exit(tiny4412_linux_csb_cleanup); /*驱动出口--卸载驱动的时候执行*/

MODULE_LICENSE("GPL");  /*设置模块的许可证--GPL*/

2.2 Makefile

KER_DRI=/home/wbyq/work/linux-3.5/linux-3.5
all:
	make -C $(KER_DRI) M=`pwd` modules
	cp *.ko /home/wbyq/work/rootfs/code -f
	make -C $(KER_DRI) M=`pwd` modules clean
obj-m += linux_csb.o
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。