Linux driver HC_SR04 distance sensor usage reference and simple notes

Foreword

HC_SR04 This distance sensor interface is relatively simple. Simply implement a driver as a learning example.

Driver file

#include <linux/init.h>
#include <linux/module.h>
#include <linux/unistd.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/gpio.h>
#include <mach/platform.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/sched.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/wait.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/time.h>

#define HCSR_MAGIC 'h'
#define MEASURE _IO(HCSR_MAGIC, 0)

#define GPIOB_BASE ((void *)0xC001B000)
#define GPIOC_BASE ((void *)0xC001C000)

// pin resource definition
struct pin_resource {<!-- -->
    const char *name;
    int gpio;
};

// private data
struct hcsr_dev
{<!-- -->
    struct pin_resource trig;
    struct pin_resource echo;
    int btn_cnt;
    //Define character device variable description
    struct cdev hcsr_cdev;
    //Define device class pointer
    struct class *cls;
    //Device number information
    dev_t dev;
    atomic_t available; //Append
    struct device *device; //Register device
    wait_queue_head_t rwqh;
    volatile int measure_state;
    int distance;
    struct timespec timestart;
    struct timespec timestop;
};

struct hcsr_dev hcdev;

//measurement function
long measure()
{<!-- -->
    long ret = -1;
    //Signal 1
    gpio_set_value(hcdev.trig.gpio, 1);
    //Delay
    udelay(12);
    //printk("on %d\\
",gpio_get_value(hcdev.trig.gpio));
    //Signal 0
    gpio_set_value(hcdev.trig.gpio, 0);
    //Go to sleep
    hcdev.measure_state = 0;
    //wait for interrupt
    if (wait_event_interruptible(hcdev.rwqh, hcdev.measure_state)) {<!-- -->
        printk("Distance measurement terminated\\
");
        return -1;
    }
    //Calculate half the time
    long difftime = (1000000000 * (hcdev.timestop.tv_sec-hcdev.timestart.tv_sec) + (hcdev.timestop.tv_nsec-hcdev.timestart.tv_nsec));
    ret = (difftime / 2);

return ret;
}

//Control function
static long hcsr_ioctl(struct file *file, unsigned int cmd, unsigned long buf) {<!-- -->
    // examine
if (_IOC_TYPE(cmd) != HCSR_MAGIC)
return -ENOTTY;
    
    //Parse control commands
    switch(cmd) {<!-- -->
        case MEASURE:
        {<!-- -->
            long tmp = measure();
            copy_to_user((void *)buf, (void *) & amp;tmp, sizeof(tmp));
        }
        break;
    }
    return 0;
}

//Close release
static int hcsr_release(struct inode *inode, struct file *filp)
{<!-- -->
    atomic_inc( & amp;hcdev.available);
return 0;
}

//Open the device
static int hcsr_open(struct inode *inode, struct file *filp) {<!-- -->
    if (atomic_dec_and_test( & amp;hcdev.available) >= 0)
        return 0;
    else {<!-- -->
        atomic_inc( & amp;hcdev.available);
        return -EBUSY;
    }
}

//Define operation interface
static struct file_operations hcsr_fops = {<!-- -->
    .release = hcsr_release,
    .open = hcsr_open,
    .unlocked_ioctl = hcsr_ioctl,
};

//Interrupt handling
static irqreturn_t echo_isr(int irq, void *info) {<!-- -->
    if (gpio_get_value(hcdev.echo.gpio) == 1) {<!-- -->
        getnstimeofday( & amp;hcdev.timestart);
    }
    else {<!-- -->
        getnstimeofday( & amp;hcdev.timestop);

        //Setup completed
        hcdev.measure_state = 1;
        //Measurement completed
        wake_up_interruptible( & amp;hcdev.rwqh);
    }
    
    return IRQ_HANDLED;
}

//Match successful kernel call
static int hcsr_probe(struct platform_device *pdev) {<!-- -->
    int ret;
    int i;

    for(i =0; i < 2; i + + ) {<!-- -->
        struct resource *btn_tree;
        btn_tree = platform_get_resource(pdev, IORESOURCE_IO, i);
        if (btn_tree != NULL) {<!-- -->
            if (strcmp(btn_tree->name, "Trig") == 0) {<!-- -->
                hcdev.trig.gpio = btn_tree->start;
                hcdev.trig.name = btn_tree->name;
                printk("%s = %d\\
", hcdev.trig.name, hcdev.trig.gpio);
            }
            else if (strcmp(btn_tree->name, "Echo") == 0) {<!-- -->
                hcdev.echo.gpio = btn_tree->start;
                hcdev.echo.name = btn_tree->name;
                printk("%s = %d\\
", hcdev.echo.name, hcdev.echo.gpio);
            }
        }
    }
    if (hcdev.trig.gpio == 0 || hcdev.echo.gpio == 0) {<!-- -->
        ret = -EFAULT;
        goto error;
    }

    //Apply for device number
    alloc_chrdev_region( & amp;hcdev.dev, 0, 1, "hc_sr04");
    //Initialize character device variables to their associated hardware operation interfaces
    cdev_init( & amp;hcdev.hcsr_cdev, & amp;hcsr_fops);
    //Register the character device driver to the kernel. At this time, the kernel has a real character device driver.
    ret = cdev_add( & amp;hcdev.hcsr_cdev, hcdev.dev, 1);
if (ret)
goto add_err;

    //Create device file
    hcdev.device = device_create(hcdev.cls, NULL, hcdev.dev, NULL, "myhcsr%d", pdev->id); //can format
if (IS_ERR(hcdev.device)) {<!-- -->
ret = PTR_ERR(hcdev.device);
goto dev_err;
}

    //triger output configuration, default 0
    ret = gpio_request(hcdev.trig.gpio, hcdev.trig.name);
    if (ret)
        goto trig_req_err;
    gpio_direction_output(hcdev.trig.gpio, 0);

    //echo interrupt configuration
    int irq = gpio_to_irq(hcdev.echo.gpio);
    ret = gpio_request(hcdev.echo.gpio, hcdev.echo.name);
    if (ret)
        goto echo_req_err;
    gpio_direction_output(hcdev.echo.gpio, 0);

    ret = request_irq(irq, echo_isr, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, // rising and falling
                hcdev.echo.name, //interrupt name
                 & amp;hcdev.echo); //Interrupt data
    if (ret)
goto echo_irq_err;

    init_waitqueue_head( & amp;hcdev.rwqh);

    atomic_set( & amp;hcdev.available, 1);
    return 0; //Return 0 on success, negative value on failure

    free_irq(irq, & amp;hcdev.echo);
echo_irq_err:
    //Clear echo
    gpio_free(hcdev.echo.gpio);
echo_req_err:
    //Clear trig
    gpio_free(hcdev.trig.gpio);
trig_req_err:
    //Delete device files
    device_destroy(hcdev.cls, hcdev.dev);
dev_err:
    //Uninstall character device driver
    cdev_del( & amp;hcdev.hcsr_cdev);
add_err:
    //Release device number
    unregister_chrdev_region(hcdev.dev, 1);
error:
    return ret;
}

//Uninstall hardware node or software node kernel call
static int hcsr_remove(struct platform_device *pdev) {<!-- -->
    //Clear pin
    int irq = gpio_to_irq(hcdev.echo.gpio);
    free_irq(irq, & amp;hcdev.echo);
    gpio_free(hcdev.echo.gpio);
    gpio_set_value(hcdev.trig.gpio, 0);
    gpio_free(hcdev.trig.gpio);
    //Delete device files and pick apples
    device_destroy(hcdev.cls, hcdev.dev);
    //Uninstall character device driver
    cdev_del( & amp;hcdev.hcsr_cdev);
    //Release device number
    unregister_chrdev_region(hcdev.dev, 1);

    return 0; //Return 0 successfully
}

//Define initialization software node object
static struct platform_driver hcsr_drv = {<!-- -->
    .driver = {<!-- -->
        .name = "hc_sr04", //name matching
    },
    .probe = hcsr_probe, //Hardware and driver match successfully, kernel call
    .remove = hcsr_remove
};
//Driver entry
static int hcsr_drv_init(void) {<!-- -->
    int ret;
    //Create device class object
    hcdev.cls = class_create(THIS_MODULE, "hc_sr04");
    if (IS_ERR(hcdev.cls))
        return PTR_ERR(hcdev.cls);

    //Register the node to the kernel drv linked list
    ret = platform_driver_register( & amp;hcsr_drv);
if (ret)
class_destroy(hcdev.cls);

    return 0;
}
//Uninstall the driver
static void hcsr_drv_exit(void) {<!-- -->
    //Uninstall the node from the kernel drv list
    platform_driver_unregister( & amp;hcsr_drv);
    //Delete device class object
    class_destroy(hcdev.cls);
}
module_init(hcsr_drv_init);
module_exit(hcsr_drv_exit);
MODULE_LICENSE("GPL");

The device file needs to be configured according to the actual hardware. There are 4 pins in total, 2 for power and 2 for signal

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio.h> //gpio library
#include <mach/platform.h> //gpio number

static struct resource hc_sr_res[] = {<!-- -->
    //Describe GPIO number information
    [0] = {<!-- -->
        .start = PAD_GPIO_C + 24, //Trig->RX receive
        .end = PAD_GPIO_C + 24,
        .name = "Trig",
        .flags = IORESOURCE_IO //GPIO number type information
    },
    [1] = {<!-- -->
        .start = PAD_GPIO_C + 25, //TX->Echo send
        .end = PAD_GPIO_C + 25,
        .name = "Echo",
        .flags = IORESOURCE_IO //GPIO number type information
    },
};

static void hc_sr_release(struct device *dev)
{<!-- -->
    // remove warning
}

//Define initialization hardware node object (truck)
static struct platform_device hc_sr_dev = {<!-- -->
    .name = "hc_sr04", //Hardware node name, used for matching
    .id = 0, //The number of the hardware node
    .resource = hc_sr_res, //Load hardware resource information
    .num_resources = ARRAY_SIZE(hc_sr_res), //Number of hardware information described by resource
    .dev = {<!-- -->
        .release = hc_sr_release // There will be a warning if not defined, not used here.
    }};

static int led_dev_init(void)
{<!-- -->
    //Register the hardware node object to the kernel dev list
    platform_device_register( & amp;hc_sr_dev);
    return 0;
}

static void led_dev_exit(void)
{<!-- -->
    // Unload the hardware node object from the kernel dev list
    platform_device_unregister( & amp;hc_sr_dev);
}

module_init(led_dev_init);
module_exit(led_dev_exit);
MODULE_LICENSE("GPL");

Simple test

#include <sys/stat.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <poll.h>
#include <signal.h>
#include <pthread.h>
#include "hc_sr04.h"

int main(int argc, char *argv[]) {<!-- -->

    int fd;
    int ret;
    long time;
    if (argc < 1) {<!-- -->
        printf("sample ./hcsr_test\\
");
        return -1;
    }

    fd = open("/dev/myhcsr0", O_RDONLY);
    if(fd == -1) {<!-- -->
        return -1;
    }

    ret = ioctl(fd, MEASURE, & amp;time);
    
    printf("Distance: %.2f meters\\
", time / 1000000000.0 * 340); // seconds * sound wave speed

    return 0;
}

Measurement effect