目录
一、基础知识
013------超声波模块驱动开发(基于I.MX6uLL与SR04)-CSDN博客
二、分析为什么打印会影响中断
cpp
asmlinkage __visible int printk(const char *fmt, ...)
{
va_list args;
int r;
// 初始化可变参数列表
va_start(args, fmt);
// 调用 vprintk_func 函数来格式化字符串并输出到内核日志
r = vprintk_func(fmt, args);
// 清理可变参数列表
va_end(args);
// 返回 vprintk_func 的结果
return r;
}
EXPORT_SYMBOL(printk);
我们一层一层往下找找到了这个
这是一个保护中断的自旋锁,这里上锁后其它需要用中断的进程就无法使用了。
所以当超声波模块中断触发后我们没有接收到。这就是为什么打印会影响中断的原因,这里相当于32库函数里面关闭全局中断的那个操作,实现虽然不一样但是作用是一样的。
三、驱动程序
cpp
#include "asm-generic/errno.h"
#include "asm-generic/gpio.h"
#include "asm/delay.h"
#include "linux/jiffies.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 CMD_TRIG 100
struct gpio_desc{
int gpio;
int irq;
char *name;
int key;
struct timer_list key_timer;
} ;
static struct gpio_desc gpios[2] = {
{115, 0, "trig", },
{116, 0, "echo", },
};
/* 主设备号 */
static int major = 0;
static struct class *gpio_class;
/* 环形缓冲区 */
#define BUF_LEN 128
static int g_keys[BUF_LEN];
static int r, w;
struct fasync_struct *button_fasync;
#define NEXT_POS(x) ((x+1) % BUF_LEN)
static int is_key_buf_empty(void)
{
return (r == w);
}
static int is_key_buf_full(void)
{
return (r == NEXT_POS(w));
}
static void put_key(int key)
{
if (!is_key_buf_full())
{
g_keys[w] = key;
w = NEXT_POS(w);
}
}
static int get_key(void)
{
int key = 0;
if (!is_key_buf_empty())
{
key = g_keys[r];
r = NEXT_POS(r);
}
return key;
}
static DECLARE_WAIT_QUEUE_HEAD(gpio_wait);
/* 实现对应的open/read/write等函数,填入file_operations结构体 */
static ssize_t sr04_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
//printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
int err;
int key;
if (is_key_buf_empty() && (file->f_flags & O_NONBLOCK))
return -EAGAIN;
// printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
wait_event_interruptible(gpio_wait, !is_key_buf_empty());
key = get_key();
if (key == -1)
return -ENODATA;
err = copy_to_user(buf, &key, 4);
return 4;
}
static unsigned int sr04_poll(struct file *fp, poll_table * wait)
{
//printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
poll_wait(fp, &gpio_wait, wait);
return is_key_buf_empty() ? 0 : POLLIN | POLLRDNORM;
}
static int sr04_fasync(int fd, struct file *file, int on)
{
if (fasync_helper(fd, file, on, &button_fasync) >= 0)
return 0;
else
return -EIO;
}
// ioctl(fd, CMD, ARG)
static long sr04_ioctl(struct file *filp, unsigned int command, unsigned long arg)
{
// send trig
switch (command)
{
case CMD_TRIG:
{
//printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
gpio_set_value(gpios[0].gpio, 1);
udelay(20);
gpio_set_value(gpios[0].gpio, 0);
// start timer
mod_timer(&gpios[1].key_timer, jiffies + msecs_to_jiffies(50));
}
}
return 0;
}
/* 定义自己的file_operations结构体 */
static struct file_operations sr04_drv = {
.owner = THIS_MODULE,
.read = sr04_read,
.poll = sr04_poll,
.fasync = sr04_fasync,
.unlocked_ioctl = sr04_ioctl,
};
static irqreturn_t sr04_isr(int irq, void *dev_id)
{
struct gpio_desc *gpio_desc = dev_id;
int val;
static u64 rising_time = 0;
u64 time;
val = gpio_get_value(gpio_desc->gpio);
//printk("sr04_isr echo pin %d is %d\n", gpio_desc->gpio, val);
if (val)
{
/* 上升沿记录起始时间 */
rising_time = ktime_get_ns();
}
else
{
if (rising_time == 0)
{
//printk("missing rising interrupt\n");
return IRQ_HANDLED;
}
/* 下降沿记录结束时间, 并计算时间差, 计算距离 */
// stop timer
del_timer(&gpios[1].key_timer);
time = ktime_get_ns() - rising_time;
rising_time = 0;
put_key(time);
wake_up_interruptible(&gpio_wait);
kill_fasync(&button_fasync, SIGIO, POLL_IN);
}
return IRQ_HANDLED;
}
static void sr04_timer_func(unsigned long data)
{
put_key(-1);
wake_up_interruptible(&gpio_wait);
kill_fasync(&button_fasync, SIGIO, POLL_IN);
}
/* 在入口函数 */
static int __init sr04_init(void)
{
int err;
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
// trig pin
err = gpio_request(gpios[0].gpio, gpios[0].name);
gpio_direction_output(gpios[0].gpio, 0);
// echo pin
{
gpios[1].irq = gpio_to_irq(gpios[1].gpio);
err = request_irq(gpios[1].irq, sr04_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, gpios[1].name, &gpios[1]);
setup_timer(&gpios[1].key_timer, sr04_timer_func, (unsigned long)&gpios[1]);
}
/* 注册file_operations */
major = register_chrdev(0, "100ask_sr04", &sr04_drv); /* /dev/gpio_desc */
gpio_class = class_create(THIS_MODULE, "100ask_sr04_class");
if (IS_ERR(gpio_class)) {
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
unregister_chrdev(major, "100ask_sr04");
return PTR_ERR(gpio_class);
}
device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "sr04"); /* /dev/sr04 */
return err;
}
/* 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数
*/
static void __exit sr04_exit(void)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
device_destroy(gpio_class, MKDEV(major, 0));
class_destroy(gpio_class);
unregister_chrdev(major, "100ask_sr04");
// trig pin
gpio_free(gpios[0].gpio);
// echo pin
{
free_irq(gpios[1].irq, &gpios[1]);
del_timer(&gpios[1].key_timer);
}
}
/* 7. 其他完善:提供设备信息,自动创建设备节点 */
module_init(sr04_init);
module_exit(sr04_exit);
MODULE_LICENSE("GPL");
四、应用程序
加入poll机制查询是不是有数据
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");
fds[0].fd = fd;
fds[0].events = POLLIN;
if (1 == poll(fds, 1, 5000))
{
if (read(fd, &val, 4) == 4)
printf("get distance: %d cm\n", val*17/1000000);
else
printf("get distance err\n");
}
else
{
printf("get distance poll timeout/err\n");
}
sleep(1);
}
close(fd);
return 0;
}
五、验证及其它
我们现在用的4.9.88版本用setup_timer
新的5.几的内核使用timer_setup
key_timer_expire函数的参数也不一样,4点几是long,5点几是把结构体传入。
现象和上次一样
013------超声波模块驱动开发(基于I.MX6uLL与SR04)-CSDN博客
这个数据后面需要处理一下不然太不稳定了。