linux/drivers/iio/proximity/srf04.c
<<
>>
Prefs
   1/*
   2 * SRF04: ultrasonic sensor for distance measuring by using GPIOs
   3 *
   4 * Copyright (c) 2017 Andreas Klinger <ak@it-klinger.de>
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License, or
   9 * (at your option) any later version.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14 * GNU General Public License for more details.
  15 *
  16 * For details about the device see:
  17 * http://www.robot-electronics.co.uk/htm/srf04tech.htm
  18 *
  19 * the measurement cycle as timing diagram looks like:
  20 *
  21 *          +---+
  22 * GPIO     |   |
  23 * trig:  --+   +------------------------------------------------------
  24 *          ^   ^
  25 *          |<->|
  26 *         udelay(10)
  27 *
  28 * ultra           +-+ +-+ +-+
  29 * sonic           | | | | | |
  30 * burst: ---------+ +-+ +-+ +-----------------------------------------
  31 *                           .
  32 * ultra                     .              +-+ +-+ +-+
  33 * sonic                     .              | | | | | |
  34 * echo:  ----------------------------------+ +-+ +-+ +----------------
  35 *                           .                        .
  36 *                           +------------------------+
  37 * GPIO                      |                        |
  38 * echo:  -------------------+                        +---------------
  39 *                           ^                        ^
  40 *                           interrupt                interrupt
  41 *                           (ts_rising)              (ts_falling)
  42 *                           |<---------------------->|
  43 *                              pulse time measured
  44 *                              --> one round trip of ultra sonic waves
  45 */
  46#include <linux/err.h>
  47#include <linux/gpio/consumer.h>
  48#include <linux/kernel.h>
  49#include <linux/module.h>
  50#include <linux/of.h>
  51#include <linux/platform_device.h>
  52#include <linux/property.h>
  53#include <linux/sched.h>
  54#include <linux/interrupt.h>
  55#include <linux/delay.h>
  56#include <linux/iio/iio.h>
  57#include <linux/iio/sysfs.h>
  58
  59struct srf04_data {
  60        struct device           *dev;
  61        struct gpio_desc        *gpiod_trig;
  62        struct gpio_desc        *gpiod_echo;
  63        struct mutex            lock;
  64        int                     irqnr;
  65        ktime_t                 ts_rising;
  66        ktime_t                 ts_falling;
  67        struct completion       rising;
  68        struct completion       falling;
  69};
  70
  71static irqreturn_t srf04_handle_irq(int irq, void *dev_id)
  72{
  73        struct iio_dev *indio_dev = dev_id;
  74        struct srf04_data *data = iio_priv(indio_dev);
  75        ktime_t now = ktime_get();
  76
  77        if (gpiod_get_value(data->gpiod_echo)) {
  78                data->ts_rising = now;
  79                complete(&data->rising);
  80        } else {
  81                data->ts_falling = now;
  82                complete(&data->falling);
  83        }
  84
  85        return IRQ_HANDLED;
  86}
  87
  88static int srf04_read(struct srf04_data *data)
  89{
  90        int ret;
  91        ktime_t ktime_dt;
  92        u64 dt_ns;
  93        u32 time_ns, distance_mm;
  94
  95        /*
  96         * just one read-echo-cycle can take place at a time
  97         * ==> lock against concurrent reading calls
  98         */
  99        mutex_lock(&data->lock);
 100
 101        reinit_completion(&data->rising);
 102        reinit_completion(&data->falling);
 103
 104        gpiod_set_value(data->gpiod_trig, 1);
 105        udelay(10);
 106        gpiod_set_value(data->gpiod_trig, 0);
 107
 108        /* it cannot take more than 20 ms */
 109        ret = wait_for_completion_killable_timeout(&data->rising, HZ/50);
 110        if (ret < 0) {
 111                mutex_unlock(&data->lock);
 112                return ret;
 113        } else if (ret == 0) {
 114                mutex_unlock(&data->lock);
 115                return -ETIMEDOUT;
 116        }
 117
 118        ret = wait_for_completion_killable_timeout(&data->falling, HZ/50);
 119        if (ret < 0) {
 120                mutex_unlock(&data->lock);
 121                return ret;
 122        } else if (ret == 0) {
 123                mutex_unlock(&data->lock);
 124                return -ETIMEDOUT;
 125        }
 126
 127        ktime_dt = ktime_sub(data->ts_falling, data->ts_rising);
 128
 129        mutex_unlock(&data->lock);
 130
 131        dt_ns = ktime_to_ns(ktime_dt);
 132        /*
 133         * measuring more than 3 meters is beyond the capabilities of
 134         * the sensor
 135         * ==> filter out invalid results for not measuring echos of
 136         *     another us sensor
 137         *
 138         * formula:
 139         *         distance       3 m
 140         * time = ---------- = --------- = 9404389 ns
 141         *          speed       319 m/s
 142         *
 143         * using a minimum speed at -20 °C of 319 m/s
 144         */
 145        if (dt_ns > 9404389)
 146                return -EIO;
 147
 148        time_ns = dt_ns;
 149
 150        /*
 151         * the speed as function of the temperature is approximately:
 152         *
 153         * speed = 331,5 + 0,6 * Temp
 154         *   with Temp in °C
 155         *   and speed in m/s
 156         *
 157         * use 343 m/s as ultrasonic speed at 20 °C here in absence of the
 158         * temperature
 159         *
 160         * therefore:
 161         *             time     343
 162         * distance = ------ * -----
 163         *             10^6       2
 164         *   with time in ns
 165         *   and distance in mm (one way)
 166         *
 167         * because we limit to 3 meters the multiplication with 343 just
 168         * fits into 32 bit
 169         */
 170        distance_mm = time_ns * 343 / 2000000;
 171
 172        return distance_mm;
 173}
 174
 175static int srf04_read_raw(struct iio_dev *indio_dev,
 176                            struct iio_chan_spec const *channel, int *val,
 177                            int *val2, long info)
 178{
 179        struct srf04_data *data = iio_priv(indio_dev);
 180        int ret;
 181
 182        if (channel->type != IIO_DISTANCE)
 183                return -EINVAL;
 184
 185        switch (info) {
 186        case IIO_CHAN_INFO_RAW:
 187                ret = srf04_read(data);
 188                if (ret < 0)
 189                        return ret;
 190                *val = ret;
 191                return IIO_VAL_INT;
 192        case IIO_CHAN_INFO_SCALE:
 193                /*
 194                 * theoretical maximum resolution is 3 mm
 195                 * 1 LSB is 1 mm
 196                 */
 197                *val = 0;
 198                *val2 = 1000;
 199                return IIO_VAL_INT_PLUS_MICRO;
 200        default:
 201                return -EINVAL;
 202        }
 203}
 204
 205static const struct iio_info srf04_iio_info = {
 206        .read_raw               = srf04_read_raw,
 207};
 208
 209static const struct iio_chan_spec srf04_chan_spec[] = {
 210        {
 211                .type = IIO_DISTANCE,
 212                .info_mask_separate =
 213                                BIT(IIO_CHAN_INFO_RAW) |
 214                                BIT(IIO_CHAN_INFO_SCALE),
 215        },
 216};
 217
 218static int srf04_probe(struct platform_device *pdev)
 219{
 220        struct device *dev = &pdev->dev;
 221        struct srf04_data *data;
 222        struct iio_dev *indio_dev;
 223        int ret;
 224
 225        indio_dev = devm_iio_device_alloc(dev, sizeof(struct srf04_data));
 226        if (!indio_dev) {
 227                dev_err(dev, "failed to allocate IIO device\n");
 228                return -ENOMEM;
 229        }
 230
 231        data = iio_priv(indio_dev);
 232        data->dev = dev;
 233
 234        mutex_init(&data->lock);
 235        init_completion(&data->rising);
 236        init_completion(&data->falling);
 237
 238        data->gpiod_trig = devm_gpiod_get(dev, "trig", GPIOD_OUT_LOW);
 239        if (IS_ERR(data->gpiod_trig)) {
 240                dev_err(dev, "failed to get trig-gpios: err=%ld\n",
 241                                        PTR_ERR(data->gpiod_trig));
 242                return PTR_ERR(data->gpiod_trig);
 243        }
 244
 245        data->gpiod_echo = devm_gpiod_get(dev, "echo", GPIOD_IN);
 246        if (IS_ERR(data->gpiod_echo)) {
 247                dev_err(dev, "failed to get echo-gpios: err=%ld\n",
 248                                        PTR_ERR(data->gpiod_echo));
 249                return PTR_ERR(data->gpiod_echo);
 250        }
 251
 252        if (gpiod_cansleep(data->gpiod_echo)) {
 253                dev_err(data->dev, "cansleep-GPIOs not supported\n");
 254                return -ENODEV;
 255        }
 256
 257        data->irqnr = gpiod_to_irq(data->gpiod_echo);
 258        if (data->irqnr < 0) {
 259                dev_err(data->dev, "gpiod_to_irq: %d\n", data->irqnr);
 260                return data->irqnr;
 261        }
 262
 263        ret = devm_request_irq(dev, data->irqnr, srf04_handle_irq,
 264                        IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
 265                        pdev->name, indio_dev);
 266        if (ret < 0) {
 267                dev_err(data->dev, "request_irq: %d\n", ret);
 268                return ret;
 269        }
 270
 271        platform_set_drvdata(pdev, indio_dev);
 272
 273        indio_dev->name = "srf04";
 274        indio_dev->dev.parent = &pdev->dev;
 275        indio_dev->info = &srf04_iio_info;
 276        indio_dev->modes = INDIO_DIRECT_MODE;
 277        indio_dev->channels = srf04_chan_spec;
 278        indio_dev->num_channels = ARRAY_SIZE(srf04_chan_spec);
 279
 280        return devm_iio_device_register(dev, indio_dev);
 281}
 282
 283static const struct of_device_id of_srf04_match[] = {
 284        { .compatible = "devantech,srf04", },
 285        {},
 286};
 287
 288MODULE_DEVICE_TABLE(of, of_srf04_match);
 289
 290static struct platform_driver srf04_driver = {
 291        .probe          = srf04_probe,
 292        .driver         = {
 293                .name           = "srf04-gpio",
 294                .of_match_table = of_srf04_match,
 295        },
 296};
 297
 298module_platform_driver(srf04_driver);
 299
 300MODULE_AUTHOR("Andreas Klinger <ak@it-klinger.de>");
 301MODULE_DESCRIPTION("SRF04 ultrasonic sensor for distance measuring using GPIOs");
 302MODULE_LICENSE("GPL");
 303MODULE_ALIAS("platform:srf04");
 304