mirror of
https://github.com/apache/nuttx.git
synced 2026-05-20 20:44:39 +08:00
drivers/analog/hx711.c: Add driver for hx711 adc
Signed-off-by: Michał Łyszczek <michal.lyszczek@bofc.pl>
This commit is contained in:
committed by
Alan Carvalho de Assis
parent
b634798bd6
commit
84a2cab886
@@ -0,0 +1,220 @@
|
||||
=============================
|
||||
HX711 ADC DESIGNED FOR SCALES
|
||||
=============================
|
||||
|
||||
Driver contributed by Michał Łyszczek.
|
||||
|
||||
HX711 is a 24bit ADC (Analog Digital Converter) designed for weight scales.
|
||||
This chip can be very slow. With internal oscillator and RATE pin pulled
|
||||
down, it outputs only 10 samples per second. To not hog down CPU, driver
|
||||
uses interrupt to detect when chip is ready. This will make read(2) blocking,
|
||||
but system can do whatever it needs before chip is ready. Because of that
|
||||
driver does not fully follow ADC API, but rather standard character device
|
||||
(read only).
|
||||
|
||||
Values from tensometer can be easily read from shell with ``cat`` command
|
||||
|
||||
.. code-block::
|
||||
|
||||
cat /dev/hxx711_0
|
||||
|
||||
Altough it may be better to dump values with example ``hx711`` program,
|
||||
since ``cat`` will just read until the end of time, and if ctrl+c is
|
||||
not working, it will steal shell forever.
|
||||
|
||||
-------
|
||||
reading
|
||||
-------
|
||||
|
||||
Reading is done by calling standard, posix, read(2) function. Only one value
|
||||
can be returned with single call to read(2). But an averaging function can
|
||||
be enabled, so that driver will read N samples, average them, and then will
|
||||
return single averaged value.
|
||||
|
||||
This function accepts two types of buffer.
|
||||
|
||||
If buffer is of size ``sizeof(int32_t)`` a int32 value will be stored in
|
||||
a buffer. If buffer size of bigger than ``sizeof(int32_t)`` function will
|
||||
store string representation of values in passed buffer.
|
||||
|
||||
Simple code to read and print value may look like this
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
int fd;
|
||||
fd = open("/dev/hx711_0", O_RDONLY);
|
||||
|
||||
for (; ; )
|
||||
{
|
||||
int32_t value;
|
||||
value = read(fd, &value, sizeof(value));
|
||||
printf("Read: %"PRIi32"\n", value);
|
||||
}
|
||||
|
||||
-----
|
||||
ioctl
|
||||
-----
|
||||
|
||||
Since this chip (and driver) is designed for weight scale, kernel driver
|
||||
can provide some processing to make life easier for userspace code. These
|
||||
functions are implemented via ioctl(2) commands. In practice, non of these
|
||||
can be used, but if you just open driver and read it, you will get raw
|
||||
values from hx711 chip, which you will have to process yourself. If your
|
||||
needs are more standard, it's better to use kernel processing.
|
||||
|
||||
HX711_SET_AVERAGE
|
||||
-----------------
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
unsigned average = 5;
|
||||
ioctl(fd, HX711_SET_AVERAGE, average);
|
||||
|
||||
Driver will read this number of samples from hx711 and will return average
|
||||
value of them all. To avoid corrupted data due to integer overflow, max
|
||||
average value that can be set is 225. If you need to average more values
|
||||
you will need to write your own code for that.
|
||||
|
||||
HX711_SET_CHANNEL
|
||||
-----------------
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
char channel = 'a';
|
||||
ioctl(fd, HX711_SET_CHANNEL, channel);
|
||||
|
||||
HX711 has 2 channels, A and B, which can be swapped as necessary. Driver
|
||||
automatically performs dummy read, so that next call to read(2) will return
|
||||
value from new channel. When you switch to channel 'B', driver automatically
|
||||
changes gain to 32 (the only possible value). Going back to 'A' will set
|
||||
gain to 128.
|
||||
|
||||
HX711_SET_GAIN
|
||||
--------------
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
unsigned char gain = 128;
|
||||
ioctl(fd, HX711_SET_GAIN, gain);
|
||||
|
||||
Set gain. Channel 'A' supports gain "128" and "64". Channel 'B' has only
|
||||
one gain option - 32.
|
||||
|
||||
HX711_SET_VAL_PER_UNIT
|
||||
----------------------
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
int val_per_unit = 813;
|
||||
ioctl(fd, HX711_SET_VAL_PER_UNIT, val_per_unit);
|
||||
|
||||
Driver can perform calculations so that you can read physical values like
|
||||
grams, ounce or pounds, or your own artificial unit. You just need to specify
|
||||
what value from tensometer coresponds to one unit.
|
||||
|
||||
Say you have tensometer that has max value of 1'000'000. Value 100'000 means
|
||||
1kg and sensor is fully linear. If you want to get readings in kg, you would
|
||||
set ``val_per_unit`` to 100'000. If you wanted output in grams, it would be
|
||||
value of 100. To have tenths of grams precision, you would set it to 10.
|
||||
Driver does not care about unit, you just pick one and stick to it.
|
||||
|
||||
Note that driver can only return integers, so if you set it to return unit
|
||||
of kg, you will only get 1, 2, 3kg... and you won't be able to sense 0.5kg
|
||||
or 1.5kg. For that you would have to set value to 10'000, and driver would
|
||||
return you values of 15 (for 1.5kg) or 0.5 (for 0.5kg).
|
||||
|
||||
HX711_TARE
|
||||
----------
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
float precision = 0.1;
|
||||
ioctl(fd, HX711_TARE, &precision);
|
||||
|
||||
Every scale needs a tare function. Driver polls hx711 for some time, and if
|
||||
it detects that scale is stable state, ioctl(2) will return with success,
|
||||
and next read(2) call will take new tare value into consideration when
|
||||
returning readings. Scale is assumed to be stable when several consecutive
|
||||
readings are (min-max values) are within specified precition.
|
||||
|
||||
If ``HX711_SET_VAL_PER_UNIT`` was set prior to this, you can pass value
|
||||
in your unit. If you configured driver to work with grams, you can set
|
||||
this value to 0.1 (gram) or 5 (gram).
|
||||
|
||||
If driver cannot get stable reading within some time, it will return with
|
||||
ETIME errno set.
|
||||
|
||||
Important note, make sure you have set correct sign before taring, or
|
||||
else you will double your tare value instead of zeroing it!
|
||||
|
||||
HX711_SIGN
|
||||
----------
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
int sign = -1;
|
||||
ioctl(fd, HX711_SIGN, &sign);
|
||||
|
||||
If values from drivers go lower when mass on scale goes higher you can swap
|
||||
the sign. This may be necessary when tensometer was installed upside down.
|
||||
|
||||
---------------------
|
||||
hx711 example program
|
||||
---------------------
|
||||
|
||||
There is also companion program in Application Configuration ---> Examples
|
||||
called ``HX711 driver example``. Main purpose of this is to show how to
|
||||
use the driver, but it also is a very good tool for quickly debuging chip
|
||||
from the shell, as it can dump readings and set all options.
|
||||
|
||||
.. code-block::
|
||||
|
||||
-h print this help message
|
||||
-d<path> path to hx711 device, default: /dev/hx711_0
|
||||
-t<prec> tares the scale with specified precision, might take few seconds to complete.
|
||||
If you set value per unit, precision is in units, otherwise it's raw values.
|
||||
If units are used, float can be passed like 0.1
|
||||
-v<val> value read that coresponds to one unit. This value has to be
|
||||
calibrated first before it's known
|
||||
-s reverse sign, if values decreses when mass increases, pass this
|
||||
-D dumps current device settings (like, average, channel, gain etc.)
|
||||
-a<avg> set how many samples should be averaged before returning value,
|
||||
values [1..225] are valid
|
||||
-c<chan> set channel to read (either 'a' or 'b' is valid)
|
||||
-g<gain> set adc gain, for channel 'a' 64 and 128 are valid,
|
||||
for channel 'b', only 64 is valid
|
||||
-r<num> read this number of samples before exiting, samples will be printed
|
||||
on stdout as string, one sample per line
|
||||
|
||||
Set values are persistant, as in once set they are stored in driver and
|
||||
will be applied during execution of this program.
|
||||
|
||||
If you specify only <-a|-c|-g|-v|-t> without -r, program will set new parameters
|
||||
and exit. You can later call program again only with -r option to read
|
||||
samples with previously set values. You can also pass all of them in one call
|
||||
|
||||
To test if you require CONFIG_ADC_HX711_ADD_DELAY option set, run as:
|
||||
hx711 -a225 -r128
|
||||
This will load hx711 chip long enough to show any possible errors due to
|
||||
lack of added delay.
|
||||
|
||||
Program executes in order: set options, tare, dump, run, so if you specify all
|
||||
options, new settings will be applied, then new settings will be printed
|
||||
and at the end program will tare the scale and print samples
|
||||
|
||||
Examples:
|
||||
|
||||
Set hx711 settings for first chip and exit:
|
||||
hx711 -a32 -ca -g64
|
||||
|
||||
Dump chip settings from different chip
|
||||
hx711 -d/dev/hx711_2 -D
|
||||
|
||||
Read 10 samples with previously set hx711 settings
|
||||
hx711 -r10
|
||||
|
||||
Change channel and read 32 samples (average setting won't change):
|
||||
hx711 -cb -r32
|
||||
|
||||
Set value per unit, to get output in grams, and then tare with 10g precision
|
||||
hx711 -v 813 -t 10
|
||||
@@ -61,6 +61,7 @@ Character device drivers have these properties:
|
||||
contactless.rst
|
||||
crypto/index.rst
|
||||
efuse.rst
|
||||
hx711.rst
|
||||
i2s.rst
|
||||
input/index.rst
|
||||
ipcc.rst
|
||||
|
||||
@@ -91,6 +91,10 @@ if(CONFIG_ADC)
|
||||
if(CONFIG_ADC_LTC1867L)
|
||||
list(APPEND SRCS ltc1867l.c)
|
||||
endif()
|
||||
|
||||
if(CONFIG_ADC_HX711)
|
||||
list(APPEND SRCS hx711.c)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CONFIG_LMP92001)
|
||||
|
||||
@@ -192,6 +192,43 @@ endchoice
|
||||
|
||||
endif # ADC_MAX1161X
|
||||
|
||||
config ADC_HX711
|
||||
bool "Avia Semiconductor HX711 support"
|
||||
default n
|
||||
---help---
|
||||
Enable driver to support Avia Semiconductor HX711 ADC
|
||||
designed for weight scales.
|
||||
|
||||
Driver supports both 'a' and 'b' channels with 32, 64
|
||||
and 128 gain.
|
||||
|
||||
Driver does not support continuous read and is not buffered.
|
||||
Driver uses interrupts to not hog the CPU while waiting
|
||||
for hx711 to be ready.
|
||||
|
||||
if ADC_HX711
|
||||
|
||||
config ADC_HA711_ADD_DELAY
|
||||
bool "Add 1us delay between clock pulses"
|
||||
default y if BOARD_LOOPSPERMSEC >= 15000
|
||||
---help---
|
||||
HX711 requires about 1us between clock pulses to work.
|
||||
This is not an issue on slower chips, but faster chips
|
||||
will most likely try to clock HX711 too fast, which
|
||||
will result in data lose.
|
||||
|
||||
If this is enabled, code will insert 1us of delay to each
|
||||
clock change. Enable this only if you get data lose, or
|
||||
else you will just introduce unnecessary delay to your
|
||||
program.
|
||||
|
||||
Best way to know if you need this, is to compile
|
||||
HX711 demo program and run it. If there are no errors
|
||||
reported during runtime, you can turn this of. If you
|
||||
see communication errors, then you should enable this.
|
||||
|
||||
endif # ADC_HX711
|
||||
|
||||
endif # ADC
|
||||
|
||||
config COMP
|
||||
|
||||
@@ -103,6 +103,10 @@ endif
|
||||
ifeq ($(CONFIG_ADC_LTC1867L),y)
|
||||
CSRCS += ltc1867l.c
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_ADC_HX711),y)
|
||||
CSRCS += hx711.c
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_LMP92001),y)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,212 @@
|
||||
/****************************************************************************
|
||||
* include/nuttx/analog/hx711.h
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
#include <nuttx/compiler.h>
|
||||
#include <nuttx/irq.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
#define HX711_MAX_AVG_SAMPLES 225
|
||||
|
||||
/* ioctl requests ***********************************************************/
|
||||
|
||||
/* Set how many samples to read from hx711 to get a single averaged value.
|
||||
* Minimum value is 1. To prevent possible integer overflow, maximum value
|
||||
* is HX711_MAX_AVG_SAMPLES.
|
||||
*/
|
||||
|
||||
#define HX711_SET_AVERAGE 0
|
||||
|
||||
/* Set channel to use for next read() operation. Channels 'a' and 'b'
|
||||
* are available. Specify channel as 'a' character (0x61 hex)
|
||||
*/
|
||||
|
||||
#define HX711_SET_CHANNEL 1
|
||||
|
||||
/* Set gain to use for next read() operation. Channel 'b' only supports
|
||||
* gain of 32, and channel 'a' supports gain 128 and 64
|
||||
*/
|
||||
|
||||
#define HX711_SET_GAIN 2
|
||||
|
||||
/* Set what value coresponds to 1 unit. Takes integer.
|
||||
* If set to 0 (default) driver will return raw readings from
|
||||
* hx711 instead of calculated units.
|
||||
*/
|
||||
|
||||
#define HX711_SET_VAL_PER_UNIT 3
|
||||
|
||||
/* Depending on tensometer position, value will go higher or lower
|
||||
* (into negative values) when mass increases. If your sign does
|
||||
* not match, it can be changed by calling this.
|
||||
* 1 - no sign change (default)
|
||||
* -1 - sign will be changed
|
||||
*/
|
||||
|
||||
#define HX711_SET_SIGN 4
|
||||
|
||||
/* ioctl get functions */
|
||||
|
||||
/* Get current average, pass pointer to unsigned int type */
|
||||
|
||||
#define HX711_GET_AVERAGE 100
|
||||
|
||||
/* Get current channel, pass pointer to single char */
|
||||
|
||||
#define HX711_GET_CHANNEL 101
|
||||
|
||||
/* Get current gain, pass pointer to single unsignedchar */
|
||||
|
||||
#define HX711_GET_GAIN 102
|
||||
|
||||
/* Get current value per unit */
|
||||
|
||||
#define HX711_GET_VAL_PER_UNIT 103
|
||||
|
||||
/* Tare the scale. Accepts int value with desired precision.
|
||||
* If HX711_VAL_PER_UNIT was set earlier, you should pass value
|
||||
* in units, otherwise you need to pass raw value as read from hx711.
|
||||
* Takes pointer to a float value.
|
||||
*/
|
||||
|
||||
#define HX711_TARE 200
|
||||
|
||||
/****************************************************************************
|
||||
* Public Types
|
||||
****************************************************************************/
|
||||
|
||||
/* hx711 exposes 2 pins for communication. One is for data reading, and
|
||||
* second one is clock signal. This is similar to i2c but hx711 uses custom
|
||||
* protocol that is not compatible with i2c in any way.
|
||||
*
|
||||
* Platform code should provide these functions to manipulate these GPIOs
|
||||
*/
|
||||
|
||||
struct hx711_lower_s
|
||||
{
|
||||
/**************************************************************************
|
||||
* Name: clock_set
|
||||
*
|
||||
* Description:
|
||||
* Sets underlying GPIO pin according to val.
|
||||
*
|
||||
* Input Parameters:
|
||||
* val - set GPIO pin high (1) or low (0)
|
||||
* minor - hx711 device being manipulated
|
||||
*
|
||||
* Returned Value:
|
||||
* OK on success, or negated errno on failure
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
CODE int (*clock_set)(unsigned char minor, int val);
|
||||
|
||||
/**************************************************************************
|
||||
* Name: data_read
|
||||
*
|
||||
* Description:
|
||||
* Reads current value of data GPIO pin.
|
||||
*
|
||||
* Input Parameters:
|
||||
* minor - hx711 device being manipulated
|
||||
*
|
||||
* Returned Value:
|
||||
* For success, return 0 when GPIO is low, 1 when GPIO is high
|
||||
* or negated errno on failure.
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
CODE int (*data_read)(unsigned char minor);
|
||||
|
||||
/**************************************************************************
|
||||
* Name: cleanup
|
||||
*
|
||||
* Description:
|
||||
* This function is called when last instance of minor is closed and
|
||||
* unlinked from fs so that hx711 minor instance is no longer available.
|
||||
* Platform should free all resources it allocated to register the
|
||||
* device.
|
||||
*
|
||||
* This function does not have to be set, if there is nothing to clean.
|
||||
*
|
||||
* Input Parameters:
|
||||
* minor - hx711 instance being destroyed
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
CODE void (*cleanup)(unsigned char minor);
|
||||
|
||||
/**************************************************************************
|
||||
* Name: data_irq
|
||||
*
|
||||
* Description:
|
||||
* Setup (or tear down when handler is NULL) interrupt when data line
|
||||
* goes from HIGH to LOW state (falling edge).
|
||||
*
|
||||
* hx711 is slow, on internal oscillator and RATE=0 it takes 100ms to
|
||||
* sample a single reading. To avoid hogging CPU polling for data to
|
||||
* go down, driver will install interrupt handler before reading.
|
||||
* Once interrupt is received, driver will disable the handler.
|
||||
*
|
||||
* Input Parameters:
|
||||
* minor - hx711 device being manipulated
|
||||
* handler - function interrupt should call
|
||||
* arg - private data for handler, should be passed to handler
|
||||
*
|
||||
* Returned Value:
|
||||
* On successfull interrupt initialization 0 should be returned,
|
||||
* when there was failure initializing interrupt -1 shall be returned.
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
CODE int (*data_irq)(unsigned char minor, xcpt_t handler, void *arg);
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: hx711_register
|
||||
*
|
||||
* Description:
|
||||
* Register new hx711 device in /dev/hx711_%d. Multiple hx711 can be
|
||||
* supported by providing different minor number. When driver calls
|
||||
* platform specific function, minor number is passed back, so platform
|
||||
* can know which hx711 is manipulated.
|
||||
*
|
||||
* Input Parameters:
|
||||
* minor - unique number identifying hx711 chip.
|
||||
* lower - provided by platform code to manipulate hx711 with platform
|
||||
* dependant functions>
|
||||
*
|
||||
* Returned Value:
|
||||
* OK on success, or negated errno on failure
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int hx711_register(unsigned char minor, FAR struct hx711_lower_s *lower);
|
||||
Reference in New Issue
Block a user