ADC
1. IIO Subsystem
The IIO (Industrial I/O) subsystem is an essential component of the Linux kernel, designed specifically for handling analog input and output devices in various industrial and embedded applications. These devices may include sensors, ADCs (Analog-to-Digital Converters), DACs (Digital-to-Analog Converters), and other analog signal processing equipment. The primary goal of the IIO subsystem is to provide a generic and unified framework to support various types of analog devices in Linux.
The IIO subsystem provides a standardized interface through the sysfs file system and user space, allowing users to easily access and configure analog devices. This enables applications to communicate with sensors and analog devices using standard Linux file I/O operations.
2. Reading ADC (Shell Script)
2.1 Pin Distribution
The development boards have ADC interfaces enabled by default, with a measurement range of 0V~1.8V.
- Luckfox Lyra Ultra/Ultra W Pin Diagram:

2.2 Device Directory
The /sys/bus/iio/devices directory contains subdirectories related to IIO devices detected by the system. Each subdirectory typically corresponds to a specific IIO device, with its name indicating the device type and number. To view the IIO devices present in the system, you can use the following command:
# ls /sys/bus/iio/devices/
iio:device0
2.3 Device Attributes
Each device subdirectory contains a set of attribute files used to retrieve and configure various parameters and states of the IIO device. For example, you can read these files to obtain sensor data, configure sampling rates, or set interrupt thresholds.
Viewing Device Attribute Files
# ls /sys/bus/iio/devices/iio\:device0/
of_node in_voltage_scale in_voltage0_raw power
name trigger buffer subsystem
uevent in_voltage1_raw dev scan_elementsAttribute Files,The main files used in this directory are
in_voltage0_raw,in_voltage1_raw, andin_voltage_scale.The
in_voltage0_rawfile contains the raw voltage value for ADC input channel 0, typically represented as an integer. You can read it using thecatcommand, for example:# cat /sys/bus/iio/devices/iio\:device0/in_voltage0_raw
1023The
in_voltage1_rawfile contains the raw voltage value for ADC input channel 1, also represented as an integer. You can read it using thecatcommand, for example:# cat /sys/bus/iio/devices/iio\:device0/in_voltage1_raw
1022The
in_voltage_scalefile contains a scaling factor used to convert raw voltage values into actual voltage values. You can read it using thecatcommand, for example:# cat /sys/bus/iio/devices/iio\:device0/in_voltage_scale
1.757812500
2.4 Write a shell script to get the voltage value
To obtain the actual voltage values of ADC channel 0 and channel 1, you can write a shell script to read the raw voltage values in_voltage0_raw, in_voltage1_raw and the scale factor in_voltage_scale, and then multiply the two raw voltage values by the scale factor, as shown below:
Open the shell script using vi editor.
# vi adc.shWriting Shell Scripts.
#!/bin/sh
echo "Press Ctrl+C to quit"
ADC_DIR="/sys/bus/iio/devices/iio:device0"
while true
do
scale_value=$(cat "$ADC_DIR/in_voltage_scale")
IN0_raw_value=$(cat "$ADC_DIR/in_voltage0_raw")
IN1_raw_value=$(cat "$ADC_DIR/in_voltage1_raw")
IN0_voltage=$(awk -v raw="$IN0_raw_value" -v scale="$scale_value" 'BEGIN { printf "%.6f\n", raw * scale / 1000 }')
IN1_voltage=$(awk -v raw="$IN1_raw_value" -v scale="$scale_value" 'BEGIN { printf "%.6f\n", raw * scale / 1000 }')
echo "IN0_Voltage: $IN0_voltage V,IN1_Voltage: $IN1_voltage V"
sleep 1
doneRun the Script:
# chmod 777 adc.sh
# ./adc.sh
Press Ctrl+C to quit
IN0_Voltage: 1.796484 V,IN1_Voltage: 1.798242 V
IN0_Voltage: 1.798242 V,IN1_Voltage: 1.798242 V
3. Read ADC (Python program)
In the previous article, we demonstrated how to use shell scripts to obtain the actual voltage value. Next, we will use Python programs to obtain voltage values.
3.1 Complete code
The following program can be used to obtain the voltage values of ADC channels 0 and 1.
import time
ADC_DIR = "/sys/bus/iio/devices/iio:device0"
def read_value(file_path):
with open(file_path, "r") as file:
return file.read().strip()
def main():
print("Press Ctrl+C to quit")
while True:
scale_value = float(read_value(f"{ADC_DIR}/in_voltage_scale"))
IN0_raw_value = float(read_value(f"{ADC_DIR}/in_voltage0_raw"))
IN1_raw_value = float(read_value(f"{ADC_DIR}/in_voltage1_raw"))
IN0_voltage = f"{IN0_raw_value * scale_value / 1000:.2f}"
IN1_voltage = f"{IN1_raw_value * scale_value / 1000:.2f}"
print(f"IN0_Voltage: {IN0_voltage} V, IN1_Voltage: {IN1_voltage} V")
time.sleep(1)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
pass
3.2 Reading voltage values
This code reads the scaling factor used for voltage value calculation and the raw voltage values of the two channels from the specified ADC device file.
scale_value = float(read_value(f"{ADC_DIR}/in_voltage_scale"))
IN0_raw_value = float(read_value(f"{ADC_DIR}/in_voltage0_raw"))
IN1_raw_value = float(read_value(f"{ADC_DIR}/in_voltage1_raw"))
3.3 Calculate voltage values
This section calculates the voltage values of two input channels, retains two decimal places and stores them in the form of strings in the IN0_voltage and IN1_voltage variables.
IN0_voltage = f"{IN0_raw_value * scale_value / 1000:.2f}"
IN1_voltage = f"{IN1_raw_value * scale_value / 1000:.2f}"
3.4 Run the program
Use the nano tool to create a py file in the terminal, paste and save the python program.
# nano adc.pyRun the Script:
# python3 adc.pyExperimental phenomenon, obtain ADC dual channel voltage value:

4. Read ADC (C program)
In the previous article, we demonstrated how to use Shell scripts and Python programs to obtain the actual voltage value. In addition, we can also use C library functions or system calls to read device files to achieve the goal of obtaining voltage values. Please note that in order to run the program on a specific embedded system, it is usually necessary to compile the code using a cross-compilation tool to generate an executable file that can be executed on the target development board. Next, let's explore the specific implementation steps together.
4.1 Complete code
The following program can be used to obtain the voltage values of ADC channel 0 and channel 1.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main() {
printf("Press Ctrl+C to quit\n");
const char *adc_dir = "/sys/bus/iio/devices/iio:device0";
char in_voltage0_raw_path[256];
char in_voltage1_raw_path[256];
char in_voltage_scale_path[256];
sprintf(in_voltage0_raw_path, "%s/in_voltage0_raw", adc_dir);
sprintf(in_voltage1_raw_path, "%s/in_voltage1_raw", adc_dir);
sprintf(in_voltage_scale_path, "%s/in_voltage_scale", adc_dir);
FILE *scale_file = fopen(in_voltage_scale_path, "r");
FILE *in0_raw_file = fopen(in_voltage0_raw_path, "r");
FILE *in1_raw_file = fopen(in_voltage1_raw_path, "r");
while (1) {
char buffer[32];
fseek(scale_file, 0, SEEK_SET);
fseek(in0_raw_file, 0, SEEK_SET);
fseek(in1_raw_file, 0, SEEK_SET);
if (scale_file && in0_raw_file && in1_raw_file) {
fgets(buffer, sizeof(buffer), scale_file);
float scale = strtof(buffer, NULL);
fgets(buffer, sizeof(buffer), in0_raw_file);
int in0_raw_value = atoi(buffer);
fgets(buffer, sizeof(buffer), in1_raw_file);
int in1_raw_value = atoi(buffer);
float in0_voltage = (in0_raw_value * scale) / 1000.0;
float in1_voltage = (in1_raw_value * scale) / 1000.0;
printf("IN0 Voltage: %.6f V, IN1 Voltage: %.6f V\n", in0_voltage, in1_voltage);
}
sleep(1);
}
fclose(scale_file);
fclose(in0_raw_file);
fclose(in1_raw_file);
return 0;
}
4.2 Opening files
This code defines variables related to file paths, dynamically constructs three file paths based on adc_dir through sprintf, and opens these three files in read-only mode, which are used to read the original voltage value and scale factor of the dual channels of the ADC device.
const char *adc_dir = "/sys/bus/iio/devices/iio:device0";
char in_voltage0_raw_path[256];
char in_voltage1_raw_path[256];
char in_voltage_scale_path[256];
sprintf(in_voltage0_raw_path, "%s/in_voltage0_raw", adc_dir);
sprintf(in_voltage1_raw_path, "%s/in_voltage1_raw", adc_dir);
sprintf(in_voltage_scale_path, "%s/in_voltage_scale", adc_dir);
FILE *scale_file = fopen(in_voltage_scale_path, "r");
FILE *in0_raw_file = fopen(in_voltage0_raw_path, "r");
FILE *in1_raw_file = fopen(in_voltage1_raw_path, "r");
4.3 Calculating voltage values
This code implements a loop that reads the raw voltage values and scaling factors of the dual channels of the ADC device once a second, and then calculates and displays the actual voltage value, retaining six decimal places. File operations use fseek to ensure that data is read from the beginning of the file each loop.
while (1) {
char buffer[32];
fseek(scale_file, 0, SEEK_SET);
fseek(in0_raw_file, 0, SEEK_SET);
fseek(in1_raw_file, 0, SEEK_SET);
if (scale_file && in0_raw_file && in1_raw_file) {
fgets(buffer, sizeof(buffer), scale_file);
float scale = strtof(buffer, NULL);
fgets(buffer, sizeof(buffer), in0_raw_file);
int in0_raw_value = atoi(buffer);
fgets(buffer, sizeof(buffer), in1_raw_file);
int in1_raw_value = atoi(buffer);
float in0_voltage = (in0_raw_value * scale) / 1000.0;
float in1_voltage = (in1_raw_value * scale) / 1000.0;
printf("IN0 Voltage: %.6f V, IN1 Voltage: %.6f V\n", in0_voltage, in1_voltage);
}
sleep(1);
}
4.4 Cross-compilation
Cross-compile and run the program. For setting up the cross-compilation environment, please refer to the "Program Compilation" or "GPIO" section.
Run the program. Modify the operation permissions of the
adcfile and run the program.
# chmod 777 adc
# ./adc
- Experimental phenomenon. Get the ADC dual-channel voltage value:
