iio: adc: hx711: Add IIO driver for AVIA HX711
Subsystems (2)
Andreas Klinger
maintainerPATCH 2/2 iio: adc: hx711: Add IIO driver for AVIA HX711
This is the IIO driver for AVIA HX711 ADC which ist mostly used in weighting cells.
The protocol is quite simple and using GPIO's:
One GPIO is used as clock (SCK) while another GPIO is read (DOUT)
--- drivers/iio/adc/Kconfig | 13 +++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/hx711.c | 269 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 283 insertions(+) create mode 100644 drivers/iio/adc/hx711.c
drivers/iio/adc/Kconfig +13 -0
@@ -205,6 +205,19 @@ config HI8435 This driver can also be built as a module. If so, the module will be called hi8435. +config HX711 + tristate "AVIA HX711 ADC for weight cells" + depends on GPIOLIB + help + If you say yes here you get support for AVIA HX711 ADC which is used + for weight cells + + This driver uses two GPIO's, one for setting the clock and the other + one for getting the data + + This driver can also be built as a module. If so, the module will be + called hx711. + config INA2XX_ADC tristate "Texas Instruments INA2xx Power Monitors IIO driver" depends on I2C && !SENSORS_INA2XX
drivers/iio/adc/Makefile +1 -0
@@ -21,6 +21,7 @@ obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o obj-$(CONFIG_HI8435) += hi8435.o +obj-$(CONFIG_HX711) += hx711.o obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
drivers/iio/adc/hx711.c new file +269 -0
269 lines not on this page · raw message
Signature · 6 lines
2.1.4 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 3 lines
This is the IIO driver for AVIA HX711 ADC which ist mostly used in weighting cells.
comments below
Quoted 1 line
The protocol is quite simple and using GPIO's:
GPIOs
Quoted 16 lines
One GPIO is used as clock (SCK) while another GPIO is read (DOUT)
Signed-off-by Andreas Klinger <ak-n176/[email protected]>--- drivers/iio/adc/Kconfig | 13 +++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/hx711.c | 269 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 283 insertions(+) create mode 100644 drivers/iio/adc/hx711.cdrivers/iio/adc/Kconfig +0
-0@@ -205,6 +205,19 @@ config HI8435 This driver can also be built as a module. If so, the module will be
drivers/iio/adc/Kconfig config HI8435 · line 215
Quoted 10 lines
drivers/iio/adc/Kconfig +8
-0called hi8435. +config HX711 + tristate "AVIA HX711 ADC for weight cells" + depends on GPIOLIB + help + If you say yes here you get support for AVIA HX711 ADC which is used + for weight cells + + This driver uses two GPIO's, one for setting the clock and the other
GPIOs
Quoted 49 lines
drivers/iio/adc/Kconfig +5
-0+ one for getting the data + + This driver can also be built as a module. If so, the module will be + called hx711. + config INA2XX_ADC tristate "Texas Instruments INA2xx Power Monitors IIO driver" depends on I2C && !SENSORS_INA2XXdrivers/iio/adc/Makefile +1
-0@@ -21,6 +21,7 @@ obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o obj-$(CONFIG_HI8435) += hi8435.o +obj-$(CONFIG_HX711) += hx711.o obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o obj-$(CONFIG_LP8788_ADC) += lp8788_adc.odrivers/iio/adc/hx711.c new file +23
-0@@ -0,0 +1,269 @@ +/* +* HX711: analog to digital converter for weight sensor module +* +* Copyright (c) Andreas Klinger <ak-n176/[email protected]> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +*/ +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h>
drivers/iio/adc/hx711.c line 33
Quoted 10 lines
drivers/iio/adc/hx711.c new file +10
-0+#include <linux/property.h> +#include <linux/slab.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/iio/iio.h> + +#define HX711_GAIN_32 2/* gain = 32 for channel B */ +#define HX711_GAIN_64 3/* gain = 64 for channel A */ +#define HX711_GAIN_128 1/* gain = 128 for channel A */ +
only one newline here please
drivers/iio/adc/hx711.c line 37
Quoted 4 lines
drivers/iio/adc/hx711.c new file +4
-0+ +struct hx711_data { +struct device *dev; +dev_t devt;
devt is not used, delete
Quoted 4 lines
drivers/iio/adc/hx711.c new file +4
-0+struct gpio_desc *gpiod_sck; +struct gpio_desc *gpiod_dout; +int gain_pulse; +struct mutex lock;
drivers/iio/adc/hx711.c line 51
Quoted 10 lines
drivers/iio/adc/hx711.c new file +10
-0+}; + +static void hx711_reset (struct hx711_data *hx711_data) +{ +int val; +int i; + + val =gpiod_get_value (hx711_data->gpiod_dout); +if (val) { +dev_warn (hx711_data->dev,"RESET-HX711\n" );
message needed?
drivers/iio/adc/hx711.c line 61
Quoted 10 lines
drivers/iio/adc/hx711.c new file +10
-0+ +gpiod_set_value (hx711_data->gpiod_sck,1 ); +udelay (80 ); +gpiod_set_value (hx711_data->gpiod_sck,0 ); + +for (i =0 ; i <1000 ; i++) { + val =gpiod_get_value (hx711_data->gpiod_dout); +if (!val) +break ; +/* sleep at least 1 ms*/
add space before */
drivers/iio/adc/hx711.c line 63
Quoted 2 lines
drivers/iio/adc/hx711.c new file +2
-0+msleep (1 ); + }
can this fail? what is it takes longer than 1000 * 1ms?
Quoted 15 lines
drivers/iio/adc/hx711.c new file +15
-0+ } +} + +static int hx711_cycle (struct hx711_data *hx711_data) +{ +int val; + +/* if preempted for more then 60us while SCK is high: +* hx711 is going in reset +* ==> measuring is false +*/ +preempt_disable (); +gpiod_set_value (hx711_data->gpiod_sck,1 ); + val =gpiod_get_value (hx711_data->gpiod_dout); +gpiod_set_value (hx711_data->gpiod_sck,0 );
drivers/iio/adc/hx711.c line 88
Quoted 10 lines
drivers/iio/adc/hx711.c new file +10
-0+preempt_enable (); + +return val; +} + +static unsigned int hx711_read (struct hx711_data *hx711_data) +{ +int i; +int ret; +int wert =0 ;
val or value maybe?
should be unsigned int to match the return type of the function
Quoted 8 lines
drivers/iio/adc/hx711.c new file +8
-0+ +hx711_reset (hx711_data); + +for (i =0 ; i <24 ; i++) { + wert <<=1 ; + ret =hx711_cycle (hx711_data); +if (ret) + wert++;
drivers/iio/adc/hx711.c line 106
Quoted 10 lines
drivers/iio/adc/hx711.c new file +10
-0+ } + + wert ^=0x800000 ; + +for (i =0 ; i < hx711_data->gain_pulse; i++) + ret =hx711_cycle (hx711_data); + +return wert; +} +
delete newline
Quoted 6 lines
drivers/iio/adc/hx711.c new file +6
-0+ +static int hx711_read_raw (struct iio_dev *iio_dev, +const struct iio_chan_spec *chan, +int *val,int *val2,long mask) +{ +struct hx711_data *hx711_data =iio_priv (iio_dev);
drivers/iio/adc/hx711.c line 122
Quoted 10 lines
drivers/iio/adc/hx711.c new file +10
-0+int ret; + +switch (mask) { +case IIO_CHAN_INFO_RAW : +switch (chan->type) { +case IIO_VOLTAGE : +mutex_lock (&hx711_data->lock); + *val = (int )hx711_read (hx711_data); +mutex_unlock (&hx711_data->lock); + ret =IIO_VAL_INT ;
return IIO_VAL_INT;
drivers/iio/adc/hx711.c line 132
Quoted 10 lines
drivers/iio/adc/hx711.c new file +10
-0+break ; +default : +return -EINVAL ; + } +return ret; +default : +return -EINVAL ; + } + +return ret;
delete, dead code
drivers/iio/adc/hx711.c line 142
Quoted 10 lines
drivers/iio/adc/hx711.c new file +10
-0+} + +static const struct iio_info hx711_iio_info = { + .driver_module =THIS_MODULE , + .read_raw = hx711_read_raw, +}; + +static const struct iio_chan_spec hx711_chan_spec[] = { + { .type =IIO_VOLTAGE , + .channel =0 ,
.channel = 0 not needed, there is only one channel
drivers/iio/adc/hx711.c line 145
Quoted 3 lines
drivers/iio/adc/hx711.c new file +3
-0+ .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW), + .scan_type = {
scan_type not needed, driver does not support buffered reads
Quoted 39 lines
drivers/iio/adc/hx711.c new file +39
-0+ .sign = 'u', + .realbits = 24, + .storagebits = 32, + .shift = 0, + } + }, +}; + +static int hx711_probe (struct platform_device *pdev) +{ +struct device *dev = &pdev->dev; +struct device_node *node = dev->of_node; +struct hx711_data *hx711_data =NULL ; +struct iio_dev *iio; +int ret =0 , ival; + + iio =devm_iio_device_alloc (dev,sizeof (struct hx711_data )); +if (!iio) { +dev_err (dev,"failed to allocate IIO device\n" ); +return -ENOMEM ; + } + + hx711_data =iio_priv (iio); + hx711_data->dev = dev; + +mutex_init (&hx711_data->lock); + + hx711_data->gpiod_sck =devm_gpiod_get (dev,"sck" ,GPIOD_OUT_HIGH ); +if (IS_ERR (hx711_data->gpiod_sck)) { + ret =PTR_ERR (hx711_data->gpiod_sck); + goto err; + } + + hx711_data->gpiod_dout =devm_gpiod_get (dev,"dout" ,GPIOD_OUT_HIGH ); +if (IS_ERR (hx711_data->gpiod_dout)) { + ret =PTR_ERR (hx711_data->gpiod_dout); + goto err; + } +
drivers/iio/adc/hx711.c line 194
Quoted 10 lines
drivers/iio/adc/hx711.c new file +10
-0+ ret =of_property_read_u32 (node,"gain" , &ival); +if (!ret) { +switch (ival) { +case 32 : + hx711_data->gain_pulse =HX711_GAIN_32 ; +break ; +case 64 : + hx711_data->gain_pulse =HX711_GAIN_64 ; +break ; +case 128 :
merge default and case 128?
Quoted 21 lines
drivers/iio/adc/hx711.c new file +21
-0+ hx711_data->gain_pulse =HX711_GAIN_128 ; +break ; +default : + hx711_data->gain_pulse =HX711_GAIN_128 ; + } + }else + hx711_data->gain_pulse =HX711_GAIN_128 ; +dev_dbg (hx711_data->dev,"gain: %d\n" , hx711_data->gain_pulse); + + ret =gpiod_direction_input (hx711_data->gpiod_dout); +if (ret <0 ) { +dev_err (hx711_data->dev,"gpiod_direction_input: %d\n" , ret); + goto err; + } + + ret =gpiod_direction_output (hx711_data->gpiod_sck,0 ); +if (ret <0 ) { +dev_err (hx711_data->dev,"gpiod_direction_output: %d\n" , ret); + goto err; + } +
drivers/iio/adc/hx711.c line 225
Quoted 10 lines
drivers/iio/adc/hx711.c new file +10
-0+platform_set_drvdata (pdev, iio); + + iio->name = pdev->name; + iio->dev.parent = &pdev->dev; + iio->info = &hx711_iio_info; + iio->modes =INDIO_DIRECT_MODE ; + iio->channels = hx711_chan_spec; + iio->num_channels =ARRAY_SIZE (hx711_chan_spec); + +dev_err (hx711_data->dev,"initialized\n" );
excessive logging, please remove
drivers/iio/adc/hx711.c line 230
Quoted 5 lines
drivers/iio/adc/hx711.c new file +5
-0+ +return devm_iio_device_register (dev, iio); + +err: +return ret;
just return directly without goto?
drivers/iio/adc/hx711.c line 235
Quoted 5 lines
drivers/iio/adc/hx711.c new file +5
-0+} + + +static int hx711_suspend (struct device *dev) +{
pointless, just don't do PM support
Quoted 35 lines
drivers/iio/adc/hx711.c new file +34
-0+return 0 ; +} + +static int hx711_resume (struct device *dev) +{ +return 0 ; +} + +static SIMPLE_DEV_PM_OPS(hx711_pm_ops, hx711_suspend, hx711_resume); + + +static const struct of_device_id of_hx711_match[] = { + { .compatible ="avia,hx711" , }, + {}, +}; + +MODULE_DEVICE_TABLE (of, of_hx711_match); + +static struct platform_driver hx711_driver = { + .probe = hx711_probe, + .driver = { + .name ="hx711-gpio" , + .pm = &hx711_pm_ops, + .of_match_table = of_hx711_match, + }, +}; + +module_platform_driver (hx711_driver); + +MODULE_AUTHOR ("Andreas Klinger <ak-n176/[email protected]>" ); +MODULE_DESCRIPTION ("HX711 bitbanging driver - ADC for weight cells" ); +MODULE_LICENSE ("GPL" ); +MODULE_ALIAS ("platform:hx711-gpio" ); +
Signature · 2 lines
Peter Meerwald-Stadler +43-664-2444418 (mobile)
Quoted 2 lines · Andreas Klinger
This is the IIO driver for AVIA HX711 ADC which ist mostly used in weighting cells.
First off cool that this is finally getting a driver... I'll have to get the SparkFun breakout and really cheap scale to test :).
Quoted 93 lines
The protocol is quite simple and using GPIO's:
One GPIO is used as clock (SCK) while another GPIO is read (DOUT)Signed-off-by Andreas Klinger <ak-n176/[email protected]>--- drivers/iio/adc/Kconfig | 13 +++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/hx711.c | 269 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 283 insertions(+) create mode 100644 drivers/iio/adc/hx711.cdrivers/iio/adc/Kconfig +13
-0@@ -205,6 +205,19 @@ config HI8435 This driver can also be built as a module. If so, the module will be called hi8435. +config HX711 + tristate "AVIA HX711 ADC for weight cells" + depends on GPIOLIB + help + If you say yes here you get support for AVIA HX711 ADC which is used + for weight cells + + This driver uses two GPIO's, one for setting the clock and the other + one for getting the data + + This driver can also be built as a module. If so, the module will be + called hx711. + config INA2XX_ADC tristate "Texas Instruments INA2xx Power Monitors IIO driver" depends on I2C && !SENSORS_INA2XXdrivers/iio/adc/Makefile +1
-0@@ -21,6 +21,7 @@ obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o obj-$(CONFIG_HI8435) += hi8435.o +obj-$(CONFIG_HX711) += hx711.o obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o obj-$(CONFIG_LP8788_ADC) += lp8788_adc.odrivers/iio/adc/hx711.c new file +39
-0@@ -0,0 +1,269 @@ +/* +* HX711: analog to digital converter for weight sensor module +* +* Copyright (c) Andreas Klinger <ak-n176/[email protected]> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +*/ +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/slab.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/iio/iio.h> + +#define HX711_GAIN_32 2/* gain = 32 for channel B */ +#define HX711_GAIN_64 3/* gain = 64 for channel A */ +#define HX711_GAIN_128 1/* gain = 128 for channel A */ + + +struct hx711_data { +struct device *dev; +dev_t devt; +struct gpio_desc *gpiod_sck; +struct gpio_desc *gpiod_dout;
drivers/iio/adc/hx711.c line 49
Quoted 10 lines
drivers/iio/adc/hx711.c new file +10
-0+int gain_pulse; +struct mutex lock; +}; + +static void hx711_reset (struct hx711_data *hx711_data) +{ +int val; +int i; + + val =gpiod_get_value (hx711_data->gpiod_dout);
could move the val assignment here to the initialization don't think it will hit 80 chars
drivers/iio/adc/hx711.c line 50
Quoted 1 line
drivers/iio/adc/hx711.c new file +1
-0+if (val) {
move "int i" here to avoid compiler initialization warnings
drivers/iio/adc/hx711.c line 54
Quoted 4 lines
drivers/iio/adc/hx711.c new file +4
-0+dev_warn (hx711_data->dev,"RESET-HX711\n" ); + +gpiod_set_value (hx711_data->gpiod_sck,1 ); +udelay (80 );
IIRC this chip has quite a bit of latency thresholds, can't use usleep_range? Single core embedded systems could have an issue with continuous polling.
Quoted 38 lines
drivers/iio/adc/hx711.c new file +38
-0+gpiod_set_value (hx711_data->gpiod_sck,0 ); + +for (i =0 ; i <1000 ; i++) { + val =gpiod_get_value (hx711_data->gpiod_dout); +if (!val) +break ; +/* sleep at least 1 ms*/ +msleep (1 ); + } + } +} + +static int hx711_cycle (struct hx711_data *hx711_data) +{ +int val; + +/* if preempted for more then 60us while SCK is high: +* hx711 is going in reset +* ==> measuring is false +*/ +preempt_disable (); +gpiod_set_value (hx711_data->gpiod_sck,1 ); + val =gpiod_get_value (hx711_data->gpiod_dout); +gpiod_set_value (hx711_data->gpiod_sck,0 ); +preempt_enable (); + +return val; +} + +static unsigned int hx711_read (struct hx711_data *hx711_data) +{ +int i; +int ret; +int wert =0 ; + +hx711_reset (hx711_data); + +for (i =0 ; i <24 ; i++) {
drivers/iio/adc/hx711.c line 102
Quoted 10 lines
drivers/iio/adc/hx711.c new file +10
-0+ wert <<=1 ; + ret =hx711_cycle (hx711_data); +if (ret) + wert++; + } + + wert ^=0x800000 ; + +for (i =0 ; i < hx711_data->gain_pulse; i++) + ret =hx711_cycle (hx711_data);
What is ret used for here?
Quoted 170 lines
drivers/iio/adc/hx711.c new file +167
-1+ +return wert; +} + + +static int hx711_read_raw (struct iio_dev *iio_dev, +const struct iio_chan_spec *chan, +int *val,int *val2,long mask) +{ +struct hx711_data *hx711_data =iio_priv (iio_dev); +int ret; + +switch (mask) { +case IIO_CHAN_INFO_RAW : +switch (chan->type) { +case IIO_VOLTAGE : +mutex_lock (&hx711_data->lock); + *val = (int )hx711_read (hx711_data); +mutex_unlock (&hx711_data->lock); + ret =IIO_VAL_INT ; +break ; +default : +return -EINVAL ; + } +return ret; +default : +return -EINVAL ; + } + +return ret; +} + +static const struct iio_info hx711_iio_info = { + .driver_module =THIS_MODULE , + .read_raw = hx711_read_raw, +}; + +static const struct iio_chan_spec hx711_chan_spec[] = { + { .type =IIO_VOLTAGE , + .channel =0 , + .info_mask_separate = +BIT (IIO_CHAN_INFO_RAW ), + .scan_type = { + .sign ='u' , + .realbits =24 , + .storagebits =32 , + .shift =0 , + } + }, +}; + +static int hx711_probe (struct platform_device *pdev) +{ +struct device *dev = &pdev->dev; +struct device_node *node = dev->of_node; +struct hx711_data *hx711_data =NULL ; +struct iio_dev *iio; +int ret =0 , ival; + + iio =devm_iio_device_alloc (dev,sizeof (struct hx711_data )); +if (!iio) { +dev_err (dev,"failed to allocate IIO device\n" ); +return -ENOMEM ; + } + + hx711_data =iio_priv (iio); + hx711_data->dev = dev; + +mutex_init (&hx711_data->lock); + + hx711_data->gpiod_sck =devm_gpiod_get (dev,"sck" ,GPIOD_OUT_HIGH ); +if (IS_ERR (hx711_data->gpiod_sck)) { + ret =PTR_ERR (hx711_data->gpiod_sck); + goto err; + } + + hx711_data->gpiod_dout =devm_gpiod_get (dev,"dout" ,GPIOD_OUT_HIGH ); +if (IS_ERR (hx711_data->gpiod_dout)) { + ret =PTR_ERR (hx711_data->gpiod_dout); + goto err; + } + + ret =of_property_read_u32 (node,"gain" , &ival); +if (!ret) { +switch (ival) { +case 32 : + hx711_data->gain_pulse =HX711_GAIN_32 ; +break ; +case 64 : + hx711_data->gain_pulse =HX711_GAIN_64 ; +break ; +case 128 : + hx711_data->gain_pulse =HX711_GAIN_128 ; +break ; +default : + hx711_data->gain_pulse =HX711_GAIN_128 ; + } + }else + hx711_data->gain_pulse =HX711_GAIN_128 ; +dev_dbg (hx711_data->dev,"gain: %d\n" , hx711_data->gain_pulse); + + ret =gpiod_direction_input (hx711_data->gpiod_dout); +if (ret <0 ) { +dev_err (hx711_data->dev,"gpiod_direction_input: %d\n" , ret); + goto err; + } + + ret =gpiod_direction_output (hx711_data->gpiod_sck,0 ); +if (ret <0 ) { +dev_err (hx711_data->dev,"gpiod_direction_output: %d\n" , ret); + goto err; + } + +platform_set_drvdata (pdev, iio); + + iio->name = pdev->name; + iio->dev.parent = &pdev->dev; + iio->info = &hx711_iio_info; + iio->modes =INDIO_DIRECT_MODE ; + iio->channels = hx711_chan_spec; + iio->num_channels =ARRAY_SIZE (hx711_chan_spec); + +dev_err (hx711_data->dev,"initialized\n" ); + +return devm_iio_device_register (dev, iio); + +err: +return ret; +} + + +static int hx711_suspend (struct device *dev) +{ +return 0 ; +} + +static int hx711_resume (struct device *dev) +{ +return 0 ; +} + +static SIMPLE_DEV_PM_OPS(hx711_pm_ops, hx711_suspend, hx711_resume); + + +static const struct of_device_id of_hx711_match[] = { + { .compatible ="avia,hx711" , }, + {}, +}; + +MODULE_DEVICE_TABLE (of, of_hx711_match); + +static struct platform_driver hx711_driver = { + .probe = hx711_probe, + .driver = { + .name ="hx711-gpio" , + .pm = &hx711_pm_ops, + .of_match_table = of_hx711_match, + }, +}; + +module_platform_driver (hx711_driver); + +MODULE_AUTHOR ("Andreas Klinger <ak-n176/[email protected]>" ); +MODULE_DESCRIPTION ("HX711 bitbanging driver - ADC for weight cells" ); +MODULE_LICENSE ("GPL" ); +MODULE_ALIAS ("platform:hx711-gpio" ); + --2.1.4
Quoted 4 lines
--
To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Andreas Klinger
maintainerHello Matt,
thank you for your suggestions. See below.
Matt Ranostay <matt.ranostay-OWPKS81ov/FWk0Htik3J/[email protected]> schrieb am Sat, 17. Dec 00:53:
Quoted 105 lines · Andreas Klinger
Quoted 2 lines
This is the IIO driver for AVIA HX711 ADC which ist mostly used in weighting cells.
First off cool that this is finally getting a driver... I'll have to get the SparkFun breakout and really cheap scale to test :).
Quoted 98 lines
The protocol is quite simple and using GPIO's:
One GPIO is used as clock (SCK) while another GPIO is read (DOUT)Signed-off-by Andreas Klinger <ak-n176/[email protected]>--- drivers/iio/adc/Kconfig | 13 +++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/hx711.c | 269 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 283 insertions(+) create mode 100644 drivers/iio/adc/hx711.cdrivers/iio/adc/Kconfig +13
-0@@ -205,6 +205,19 @@ config HI8435 This driver can also be built as a module. If so, the module will be called hi8435. +config HX711 + tristate "AVIA HX711 ADC for weight cells" + depends on GPIOLIB + help + If you say yes here you get support for AVIA HX711 ADC which is used + for weight cells + + This driver uses two GPIO's, one for setting the clock and the other + one for getting the data + + This driver can also be built as a module. If so, the module will be + called hx711. + config INA2XX_ADC tristate "Texas Instruments INA2xx Power Monitors IIO driver" depends on I2C && !SENSORS_INA2XXdrivers/iio/adc/Makefile +1
-0@@ -21,6 +21,7 @@ obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o obj-$(CONFIG_HI8435) += hi8435.o +obj-$(CONFIG_HX711) += hx711.o obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o obj-$(CONFIG_LP8788_ADC) += lp8788_adc.odrivers/iio/adc/hx711.c new file +44
-0@@ -0,0 +1,269 @@ +/* +* HX711: analog to digital converter for weight sensor module +* +* Copyright (c) Andreas Klinger <ak-n176/[email protected]> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +*/ +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/slab.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/iio/iio.h> + +#define HX711_GAIN_32 2/* gain = 32 for channel B */ +#define HX711_GAIN_64 3/* gain = 64 for channel A */ +#define HX711_GAIN_128 1/* gain = 128 for channel A */ + + +struct hx711_data { +struct device *dev; +dev_t devt; +struct gpio_desc *gpiod_sck; +struct gpio_desc *gpiod_dout; +int gain_pulse; +struct mutex lock; +}; + +static void hx711_reset (struct hx711_data *hx711_data)
Quoted 9 lines
drivers/iio/adc/hx711.c line 49
Quoted 5 lines
drivers/iio/adc/hx711.c new file +5
-0+{ +int val; +int i; + + val =gpiod_get_value (hx711_data->gpiod_dout);could move the val assignment here to the initialization don't think it will hit 80 chars
I'll change this.
Quoted 4 lines
drivers/iio/adc/hx711.c line 50
Quoted 1 line
drivers/iio/adc/hx711.c new file +1
-0+if (val) {move "int i" here to avoid compiler initialization warnings
Quoted 9 lines
drivers/iio/adc/hx711.c line 54
Quoted 4 lines
drivers/iio/adc/hx711.c new file +4
-0+dev_warn (hx711_data->dev,"RESET-HX711\n" ); + +gpiod_set_value (hx711_data->gpiod_sck,1 ); +udelay (80 );IIRC this chip has quite a bit of latency thresholds, can't use
usleep_range? Single core embedded systems could have an issue with continuous polling.
That's a good suggestion. We don't need udelay so i'll change to usleep_range for the next version
Quoted 224 lines
Quoted 38 lines
drivers/iio/adc/hx711.c new file +38
-0+gpiod_set_value (hx711_data->gpiod_sck,0 ); + +for (i =0 ; i <1000 ; i++) { + val =gpiod_get_value (hx711_data->gpiod_dout); +if (!val) +break ; +/* sleep at least 1 ms*/ +msleep (1 ); + } + } +} + +static int hx711_cycle (struct hx711_data *hx711_data) +{ +int val; + +/* if preempted for more then 60us while SCK is high: +* hx711 is going in reset +* ==> measuring is false +*/ +preempt_disable (); +gpiod_set_value (hx711_data->gpiod_sck,1 ); + val =gpiod_get_value (hx711_data->gpiod_dout); +gpiod_set_value (hx711_data->gpiod_sck,0 ); +preempt_enable (); + +return val; +} + +static unsigned int hx711_read (struct hx711_data *hx711_data) +{ +int i; +int ret; +int wert =0 ; + +hx711_reset (hx711_data); + +for (i =0 ; i <24 ; i++) {drivers/iio/adc/hx711.c line 102
Quoted 10 lines
drivers/iio/adc/hx711.c new file +10
-0+ wert <<=1 ; + ret =hx711_cycle (hx711_data); +if (ret) + wert++; + } + + wert ^=0x800000 ; + +for (i =0 ; i < hx711_data->gain_pulse; i++) + ret =hx711_cycle (hx711_data);What is ret used for here?
Quoted 174 lines
drivers/iio/adc/hx711.c new file +167
-1+ +return wert; +} + + +static int hx711_read_raw (struct iio_dev *iio_dev, +const struct iio_chan_spec *chan, +int *val,int *val2,long mask) +{ +struct hx711_data *hx711_data =iio_priv (iio_dev); +int ret; + +switch (mask) { +case IIO_CHAN_INFO_RAW : +switch (chan->type) { +case IIO_VOLTAGE : +mutex_lock (&hx711_data->lock); + *val = (int )hx711_read (hx711_data); +mutex_unlock (&hx711_data->lock); + ret =IIO_VAL_INT ; +break ; +default : +return -EINVAL ; + } +return ret; +default : +return -EINVAL ; + } + +return ret; +} + +static const struct iio_info hx711_iio_info = { + .driver_module =THIS_MODULE , + .read_raw = hx711_read_raw, +}; + +static const struct iio_chan_spec hx711_chan_spec[] = { + { .type =IIO_VOLTAGE , + .channel =0 , + .info_mask_separate = +BIT (IIO_CHAN_INFO_RAW ), + .scan_type = { + .sign ='u' , + .realbits =24 , + .storagebits =32 , + .shift =0 , + } + }, +}; + +static int hx711_probe (struct platform_device *pdev) +{ +struct device *dev = &pdev->dev; +struct device_node *node = dev->of_node; +struct hx711_data *hx711_data =NULL ; +struct iio_dev *iio; +int ret =0 , ival; + + iio =devm_iio_device_alloc (dev,sizeof (struct hx711_data )); +if (!iio) { +dev_err (dev,"failed to allocate IIO device\n" ); +return -ENOMEM ; + } + + hx711_data =iio_priv (iio); + hx711_data->dev = dev; + +mutex_init (&hx711_data->lock); + + hx711_data->gpiod_sck =devm_gpiod_get (dev,"sck" ,GPIOD_OUT_HIGH ); +if (IS_ERR (hx711_data->gpiod_sck)) { + ret =PTR_ERR (hx711_data->gpiod_sck); + goto err; + } + + hx711_data->gpiod_dout =devm_gpiod_get (dev,"dout" ,GPIOD_OUT_HIGH ); +if (IS_ERR (hx711_data->gpiod_dout)) { + ret =PTR_ERR (hx711_data->gpiod_dout); + goto err; + } + + ret =of_property_read_u32 (node,"gain" , &ival); +if (!ret) { +switch (ival) { +case 32 : + hx711_data->gain_pulse =HX711_GAIN_32 ; +break ; +case 64 : + hx711_data->gain_pulse =HX711_GAIN_64 ; +break ; +case 128 : + hx711_data->gain_pulse =HX711_GAIN_128 ; +break ; +default : + hx711_data->gain_pulse =HX711_GAIN_128 ; + } + }else + hx711_data->gain_pulse =HX711_GAIN_128 ; +dev_dbg (hx711_data->dev,"gain: %d\n" , hx711_data->gain_pulse); + + ret =gpiod_direction_input (hx711_data->gpiod_dout); +if (ret <0 ) { +dev_err (hx711_data->dev,"gpiod_direction_input: %d\n" , ret); + goto err; + } + + ret =gpiod_direction_output (hx711_data->gpiod_sck,0 ); +if (ret <0 ) { +dev_err (hx711_data->dev,"gpiod_direction_output: %d\n" , ret); + goto err; + } + +platform_set_drvdata (pdev, iio); + + iio->name = pdev->name; + iio->dev.parent = &pdev->dev; + iio->info = &hx711_iio_info; + iio->modes =INDIO_DIRECT_MODE ; + iio->channels = hx711_chan_spec; + iio->num_channels =ARRAY_SIZE (hx711_chan_spec); + +dev_err (hx711_data->dev,"initialized\n" ); + +return devm_iio_device_register (dev, iio); + +err: +return ret; +} + + +static int hx711_suspend (struct device *dev) +{ +return 0 ; +} + +static int hx711_resume (struct device *dev) +{ +return 0 ; +} + +static SIMPLE_DEV_PM_OPS(hx711_pm_ops, hx711_suspend, hx711_resume); + + +static const struct of_device_id of_hx711_match[] = { + { .compatible ="avia,hx711" , }, + {}, +}; + +MODULE_DEVICE_TABLE (of, of_hx711_match); + +static struct platform_driver hx711_driver = { + .probe = hx711_probe, + .driver = { + .name ="hx711-gpio" , + .pm = &hx711_pm_ops, + .of_match_table = of_hx711_match, + }, +}; + +module_platform_driver (hx711_driver); + +MODULE_AUTHOR ("Andreas Klinger <ak-n176/[email protected]>" ); +MODULE_DESCRIPTION ("HX711 bitbanging driver - ADC for weight cells" ); +MODULE_LICENSE ("GPL" ); +MODULE_ALIAS ("platform:hx711-gpio" ); + --2.1.4
--
To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html