Using the SAMA5D2-compatible ADC device


This page describes how to test the features of the SAMA5D2-compatible ADC device. This driver uses the Industrial Input/Output (IIO) subsystem.

Documentation for the IIO subsystem is available in the kernel source code directory: drivers/staging/iio/Documentation and Documentation/iio.


The driver (features are added little by little) for the SAMA5D2-compatible ADC device is available on:

Feature Matrix

Mainline kernel 4.6 Mainline kernel 4.9 Mainline kernel 4.14
ADC/Software trigger yes yes yes
Hardware external trigger no no yes

The ADC drivers is automatically selected if you use sama5_defconfig. Otherwise, check you have set CONFIG_AT91_SAMA5D2_ADC.

To check that ADC driver is correctly loaded:

# dmesg|grep at91-sama5d2_adc
at91-sama5d2_adc fc030000.adc: version: 800
# ls /sys/bus/iio/devices/iio\:device0
dev                         in_voltage2_raw           in_voltage8_raw
in_voltage-voltage_scale    in_voltage3_raw           in_voltage9_raw
in_voltage0-voltage1_raw    in_voltage4-voltage5_raw  in_voltage_scale
in_voltage0_raw             in_voltage4_raw           name
in_voltage10-voltage11_raw  in_voltage5_raw           of_node
in_voltage10_raw            in_voltage6-voltage7_raw  power
in_voltage11_raw            in_voltage6_raw           sampling_frequency
in_voltage1_raw             in_voltage7_raw           subsystem
in_voltage2-voltage3_raw    in_voltage8-voltage9_raw  uevent

TIP Tips: The ADC controller doesn't manage its pins in the same way as other ones. PIOs can't be assigned to it. It will automatically use them (if not assigned to another peripheral) when enabling the corresponding channel. To reserve PIOs for the ADC, you could declare them as GPIO in the device tree. By doing this, if there is a conflict with another peripheral, you'll be warned. You should also disable pull-up and pull-down to have coherent measurement when the PIO is not used by the ADC device. Here is an exemple from at91-sama5d2_xplained.dts for AD4 signal:

pinctrl_adc_default: adc_default {
        pinmux = <PIN_PD23__GPIO>;
For example, pins PD20 and PD21 which are connected to ADC input channels 1 and 2 respectively are also used for i2c0 bus. i2c0 bus will not function if the ADC will request these pins for conversion.

Software triggers

Unsigned single-ended channel conversion


  • Connect a waveform or DC generator to the pin 4 of J8 connector called A3. You can also use a resistor bridge as the image below shows

Test procedure

  • request the conversion:
# cat /sys/bus/iio/devices/iio\:device0/in_voltage4_raw 
  • request the scale:
# cat /sys/bus/iio/devices/iio\:device0/in_voltage_scale 
  • check the conversion by multiplying the conversion value with the scale:
3693 x 0.8 = 2954.4 mV

Signed differential channel conversion


Use A3 (AD4) and A6 (AD5) from J8 connector

Test procedure

  • request the conversion:
# cat /sys/bus/iio/devices/iio\:device0/ in_voltage4-voltage5_raw
  • request the scale:
# cat /sys/bus/iio/devices/iio\:device0/in_voltage-voltage_scale
  • check the conversion by multiplying the conversion value with the scale:
-396 x 1.6 = -633,6 mV
  • you can compare it manually with the help of single ended conversions:
# cat /sys/bus/iio/devices/iio\:device0/in_voltage4_raw
# cat /sys/bus/iio/devices/iio\:device0/in_voltage5_raw 
# cat /sys/bus/iio/devices/iio\:device0/in_voltage_scale 

(1237-2031) x 0.8 = -635.2 mV

Hardware triggers

Hardware triggers are an operating mode of the ADC where conversions are no longer triggered by the software but directly by the hardware. For the SAMA5D2, currently there is one single type of hardware trigger, the external pin AD31, which on the Xplained board is connected on J17 pin 33 (labeled D52 on the PCB). Support for the hardware external trigger has been in kernel mainline since kernel 4.14 and in Linux4SAM releases since 5.7.

How it works

The external trigger is directly connected to the ADC inside the SoC. The ADC will monitor this pin once it's configured to do so. Once this pin will have edges (from logical 1 to logical 0 or the other way around), these will be detected by the ADC. Inside the device tree, one can configure if the ADC should react on rising edge (0->1), falling edge (1->0) or both type of edges. If an edge is detected, the ADC will start the conversion on all the enabled channels. As opposed to the software trigger which is done on demand by the user with a command (userspace process requesting it ), the hardware trigger and conversion occurs independently on the user. Thus, all the conversion data has to be stored in some buffer, for later retrieval by the userspace. This is why we need to setup a buffer and have an interface to read the data from, if we want to use the hardware trigger.


IIO configuration of hardware triggers is also done through sysfs.

Important folders in the iio:deviceX directory are:

  • buffer
  • trigger
  • scan_elements

The buffer directory contains 3 files:

  • enabled : get and set the state of the buffer
  • length : get and set the length of the buffer.

The trigger directory contains only one file, current_trigger, that reports and get the trigger in use with the driver. It also refers to the triggerX directories in /sys/bus/iio/devices/

Finally, scan_elements exposes 3 files per channel:

  • in_voltageX_en : is this channel enabled?
  • in_voltageX_index : index of this channel in the buffer's chunks
  • in_voltageX_type : How the ADC stores its data. Reading this file should return you a weird looking string. How to interpret it is explained in the Going Further section

It also contains the same information for the timestamp, under the in_timestamp_* files.

How to set it up

Basically, what you should do for launching the hardware triggers is :

Set up the channels in use (you can enable any combination of the channels you want)

# echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_voltage0_en
# echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_voltage1_en
# echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_voltage2_en
# echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_voltage3_en
Set up the trigger we want to use (it must be the name of one of the triggers present in iio/devices).

TIP With device tree, name of the device may look similar to "fc030000.adc-dev0-external_rising" instead of simply "at91_sama5d2-adc": the name sysfs file is there to inform you

# echo "fc030000.adc-dev0-external_rising" > /sys/bus/iio/devices/iio:device0/trigger/current_trigger
# echo "fc030000.adc-dev0-external_falling" > /sys/bus/iio/devices/iio:device0/trigger/current_trigger
for instance.

TIP Depending on device tree configuration edge type, the trigger name will reflect the edge type that it detects.

Set up the buffer length (Maximum number of data kept in the buffer, if we reach this number, the older data will be dropped)

# echo 100 > /sys/bus/iio/devices/iio:device0/buffer/length
Enable the capture
# echo 1 > /sys/bus/iio/devices/iio:device0/buffer/enable

Now, all the captures are exposed in the character device /dev/iio:device0, with the following format

8 bytes 8 bytes
Chan 0 Chan 1 Chan 2 Chan 3 Timestamp
2 bytes 2 bytes 2 bytes 2 bytes

This means that you can poll() or select() on it to wait for data to arrive, and get them by reading the file, just like you would do with any other file.

And to stop the capture, just disable the buffer

# echo 0 > /sys/bus/iio/devices/iio:device0/buffer/enable

Testing the buffer

In the IIO documentation there is a tool that allows to do quick tests on the buffer from the command-line. The source code is located in tools/iio/generic_buffer.c. First, we need to compile it

arm-linux-gnueabi-gcc --static generic_buffer.c -o generic_buffer


<path_to_cross-compiler/cross-compiler-prefix->-gcc --static generic_buffer.c -o generic_buffer

Then copy the generic_buffer program on your board.

Below is an example of how to use this tool :

First, load the driver:

# modprobe at91-sama5d2_adc
Then enable the channels:
# echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_voltage1_en
# echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_voltage3_en

Finally, the generic_buffer tool does all the "enable" and "disable" actions for you. You will only need to specify the IIO driver and the trigger you want to use (fc030000.adc and for the trigger fc030000.adc-dev0-external_rising for example). You can also specify the buffer length to use (2 in this example). The default value is 128.

# ./generic_buffer -n fc030000.adc -t fc030000.adc-dev0-external_rising -l 2
iio device number being used is 0
iio trigger number being used is 0
/sys/bus/iio/iio iio/iio:device0 fc030000.adc-dev0-external_rising:

1681.420898 3296.777344
1841.748047 3296.777344
1841.748047 3296.777344

While running, it displays values that are stored in the buffer. So using the setup above, we have the following trace:

Channel 1 Channel 3
Sample 1 1681.420898 3296.777344
Sample 2 1841.748047 3296.777344
Sample 3 1841.748047 3296.777344  

Going further

Understanding the in_voltageX_type file

Reading the in_voltageX_type file should return you something like: le:u10/16>>0

  • le represents the endianness, here little endian
  • u is the sign of the value returned. It could be either u (for unsigned) or s (for signed)
  • 10 is the number of relevant bits of information
  • 16 is the actual number of bits used to store the datum
  • 0 is the number of right shifts needed.
Boards Sama5d27Som1EK, Sama5d2PtcEK, Sama5d2Xplained
Components Kernel, linux-4.1-at91, linux-4.4-at91, linux-4.9-at91
Summary Using the SAMA5D2-compatible ADC device
Topic attachments
I Attachment Action Size Date Who Comment
JPEGjpg 20151215_174632.jpg manage 3082.9 K 2016-04-26 - 15:18 LudovicDesroches  
r12 - 03 Apr 2018 - 13:26:41 - LudovicDesroches
Linux & Open Source related information for AT91 Smart ARM Microcontrollers

Copyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.

Linux® is the registered trademark of Linus Torvalds in the U.S. and other countries.

Atmel® and others, are registered trademarks or trademarks of Atmel Corporation or its subsidiaries. This site is powered by the TWiki collaboration platform

ARM® and others are registered trademarks or trademarks of ARM Ltd. Other terms and product names may be trademarks of others.

Ideas, requests, contributions ? Connect to LinksToCommunities page.

Syndicate this siteRSS ATOM