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; }