ADC
Update time:2018-04-13 Views:2759
1 Introduction
AIO-3288J development board has three types of AD interfaces: High-speed ADC Stream Interface, Temperature Sensor and Successive Approximation Register. This article will introduce how to configure these ADCs to work properly.
Linux kernel controls the ADC via industrial I/O subsystem, which is intended to provide support for ADC or DAC devices.
The related data structure and configuration are shown as below.
2 Data Structure
2.1 struct iio_channel
struct iio_channel { struct iio_dev *indio_dev;// Device on which the channel exists const struct iio_chan_spec *channel;//Full description of the channel void *data;// Data about the channel used by consumer };
2.2 struct iio_dev
This structure mainly describes the industrial I/O device, as defined below:
struct iio_dev { int id; int modes; int currentmode; struct device dev; struct iio_event_interface *event_interface; struct iio_buffer *buffer; struct list_head buffer_list; int scan_bytes; struct mutex mlock; const unsigned long *available_scan_masks; unsigned masklength; const unsigned long *active_scan_mask; bool scan_timestamp; unsigned scan_index_timestamp; struct iio_trigger *trig; struct iio_poll_func *pollfunc; struct iio_chan_spec const *channels; int num_channels; struct list_head channel_attr_list; struct attribute_group chan_attr_group; const char *name; const struct iio_info *info; struct mutex info_exist_lock; const struct iio_buffer_setup_ops *setup_ops; struct cdev chrdev; #define IIO_MAX_GROUPS 6 const struct attribute_group *groups[IIO_MAX_GROUPS + 1]; int groupcounter; unsigned long flags; #if defined(CONFIG_DEBUG_FS) struct dentry *debugfs_dentry; unsigned cached_reg_addr; #endif };
2.3 struct iio_chan_spec
iio_chan_spec is the specification of a single channel:
struct iio_chan_spec { enum iio_chan_type type; int channel; int channel2; unsigned long address; int scan_index; struct { char sign; u8 realbits; u8 storagebits; u8 shift; enum iio_endian endianness; } scan_type; long info_mask; long info_mask_separate; long info_mask_shared_by_type; long event_mask; const struct iio_chan_spec_ext_info *ext_info; const char *extend_name; const char *datasheet_name; unsigned modified:1; unsigned indexed:1; unsigned output:1; unsigned differential:1; };
3 Configuration Steps
3.1 Create DTS Node
The DTS device node of the ADC is already defined in file kernel/arch/arm/boot/dts/rk3288.dtsi:
adc:adc@ff100000 { compatible = "rockchip,saradc"; reg = <0xff100000 0x100>; interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>; #io-channel-cells = <1>; io-channel-ranges; rockchip,adc-vref = <1800>; clock-frequency = <1000000>; clocks = <&clk_saradc>, <&clk_gates7 1>; clock-names = "saradc", "pclk_saradc"; status = "disabled"; };
You just need to add the channel node and change its status to "okay" in file kernel/arch/arm/boot/dts/firefly-rk3288.dts, as shown below:
&adc { status = "okay"; adc_test{ compatible = "rockchip,adc_test"; io-channels = <&adc 0>; }; };
3.2 Match DTS Node in Driver
Define an array of struct of_device_id:
static const struct of_device_id of_XXX_match[] = { { .compatible = "rockchip,adc_test" }, { /* Sentinel */ } };
Note: the compatible attribute must match the devcie node’s compatible, otherwise it will not work.
Fill the .of_match_table member of platform_driver with of_XXX_match:
static struct platform_driver XXX_driver = { .probe = ..., .remove = ..., .driver = { .name = "..", .owner = THIS_MODULE, #ifdef CONFIG_OF.of_match_table = of_XXX_match, #endif }, };
3.3 Get Device Channel
struct iio_channel *chan; chan = iio_channel_get(&pdev->dev, NULL);
Note: iio_channel_get gets the channel from the pdev which is paramter of the probe function shown below:
static int XXX_probe(struct platform_device *pdev);
3.4 Read Raw Data from Channel
int val,ret; ret = iio_read_channel_raw(chan, &val);
Call iio_read_channel_raw to read the raw data from the channel and store the result in val parameter.
3.5 Convert the Raw Data to Voltage
Use the standard voltage to convert the raw data to the result voltage. The equation is shown as below:
Vref / (2^n-1) = Vresult / raw
Note:
Vref is the standard voltage
n is the AD conversion accuracy, such as 10 bits or 12 bits.
Vresult is the conversion result of the voltage.
raw is the raw data read from function iio_read_channel_raw.
For example, the standard voltage is 1.8V, n is 10 bits, raw data is 568. The conversion expression is:
Vresult = (1800mv * 568) / 1023;
4 ADC API
/** * iio_channel_get() - get description of all that is needed to access channel. * @dev: Pointer to consumer device. Device name must match * the name of the device as provided in the iio_map * with which the desired provider to consumer mapping * was registered. * @consumer_channel: Unique name to identify the channel on the consumer * side. This typically describes the channels use within * the consumer. E.g. 'battery_voltage' */ struct iio_channel *iio_channel_get(struct device *dev, const char *consumer_channel);
/** * iio_channel_release() - release channels obtained via iio_channel_get * @chan: The channel to be released. */ void iio_channel_release(struct iio_channel *chan);
/** * iio_read_channel_raw() - read from a given channel * @chan: The channel being queried. * @val: Value read back. * * Note raw reads from iio channels are in adc counts and hence * scale will need to be applied if standard units required. */ int iio_read_channel_raw(struct iio_channel *chan, int *val);