ADC
Update time:2018-04-13 Views:1497
Introduction
ROC-RK3328-CC development board has two types of AD interfaces: Temperature Sensor(TS-ADC) and SAR-ADC(Successive Approximation Register):
TS-ADC(Temperature Sensor):Embedded 2 channel TS-ADC in RK3328, TS-ADC clock must be less than 800KHZ
SAR-ADC(Successive Approximation Register):6-channel single-ended 10-bit SAR analog-to-digital converter,SAR-ADC clock must be less than 13MHZ
Linux kernel controls the ADC via industrial I/O subsystem, which is intended to provide support for ADC or DAC devices. Take the SAR-ADC as an example to introduce the basic configuration of the ADC method.
Data Structure
iio_channel structural morphology
struct iio_channel { struct iio_dev *indio_dev; const struct iio_chan_spec *channel; void *data; };
iio_dev structural morphology
The structure is mainly used to describe the equipment belonging to the IO port, which is defined as follows:
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 };
iio_chan_spec structural morphology
The structure is mainly used to describe the attributes of a single channel, which is defined as follows:
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; };
DTS Config
Config DTS Node
The DTS device node of the ADC is already defined in file kernel/arch/arm64/boot/dts/rk322xh.dtsi, as follows:
adc: adc@ff280000 { compatible = "rockchip,saradc"; reg = <0x0 0xff280000 0x0 0x100>; interrupts = <GIC_SPI 80 IRQ_TYPE_LEVEL_HIGH>; #io-channel-cells = <1>; io-channel-ranges; rockchip,adc-vref = <1800>; clock-frequency = <1000000>; clocks = <&clk_gates2 14>, <&clk_gates17 15>; clock-names = "saradc", "pclk_saradc"; status = "disabled"; };
To begin with, you need to add resource description to the dts (Device Tree) file:
kernel/arch/arm64/boot/dts/rk3328-roc-cc.dts: &adc { status = "okay"; rk_key: rockchip-key { compatible = "rockchip,key"; io-channels = <&adc 0>; status = "okay"; vol-up-key { linux,code = <113>; label = "F12"; rockchip,adc_value = <4>; }; power-key { linux,code = <116>; label = "POWER"; rockchip,adc_value = <4>; }; }; };
Here to apply for the ADC zero channel.
Match the DTS node in the driver file
The of_device_id structure array is defined in the driver file kernel/drivers/input/keyboard/rk_keys.c:
static const struct of_device_id rk_key_match[] = { { .compatible = "rockchip,key", .data = NULL}, {}, };
Next, fill the .of_match_table member of platform_driver with firefly_adc_match:
static struct platform_driver keys_device_driver = { .probe = keys_probe, .remove = keys_remove, .driver = { .name = "rk-keypad", .owner = THIS_MODULE, .of_match_table = rk_key_match, #ifdef CONFIG_PM .pm = &keys_pm_ops, #endif } };
Driving instructions
Get the AD channel
struct iio_channel *chan; //Define IIO channel structure chan = iio_channel_get(&pdev->dev, NULL); //Get IIO channel structure
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);
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.
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;
Interface specification
/** * 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);