目录
一、工作原理
1.触发信号
HC-SR04超声波测距模块的触发信号可以通过给Trig引脚输入一个至少10微秒的高电平脉冲来实现。触发信号的步骤如下:
-
将Trig引脚设置为输出模式。
-
将Trig引脚输出高电平。
-
延时至少10微秒。
-
将Trig引脚输出低电平。
两次触发信号之间的间隔不能小于50ns
2.回响信号
回响信号的脉冲信号与测量距离成正比。根据信号的发出和返回的间隔时间可以算出距离。
公式:distance = time * 17 / 1000000 (cm)
二、接口图
根据接口图,可以计算出trig和echo的引脚编号
三、编写思路
1.构造file_operations结构体
cpp
/* 定义自己的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.不要在驱动程序中使用除法。
六、源码
驱动
cpp
#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");
应用
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>
#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;
}