This chapter introduces the GK7206 ADC (analog-to-digital converter) sample, sample_lsadc. It reads ADC channel data through the /dev/lsadc device node and supports both single-step and continuous scan modes.
The source lives in sample/lsadc/ in the SDK. It is a simple peripheral access example that shows how to talk to a hardware driver through the Linux character-device interface.
| Parameter | Value |
|---|---|
Device node |
/dev/lsadc |
Scan modes |
single-step, continuous |
Sampling interval |
1 s (adjustable) |
Sample count |
20 (default) |
Memory use |
minimal |
Kernel module |
requires xm_lsadc.ko |
sample/lsadc/
├── Makefile # build script
└── sample_lsadc.c # main program
Before building this application, make sure the following are ready:
xm_lsadc.ko must be loaded# enter the sample directory
cd <SDK_PATH>/sample
# lsadc is not in the top-level objects, build it separately
make -C lsadc clean && make -C lsadc
Tip
The lsadc sample is not listed in the top-level Makefile
objects, so it must be built separately.
# transfer to the board over SCP
scp sample/lsadc/sample_lsadc root@<board-IP>:/tmp/
# or download it over TFTP
tftp -g -r sample_lsadc <board-IP>
Note
The binary is very small, so it can run straight from
/tmp.
# make it executable
chmod +x /tmp/sample_lsadc
# load the kernel module (needed on first run)
insmod /opt/ko/xm_lsadc.ko
# run the application (interactive)
./sample_lsadc
=============== LSADC TEST ===============
0:single step scan mode
1:continuous scan mode
please select the mode:
After choosing a channel:
please select the channel:
0
get value:1234,chn[0]
get value:1235,chn[0]
get value:1233,chn[0]
get value:1236,chn[0]
get value:1234,chn[0]
get value:1237,chn[0]
get value:1232,chn[0]
get value:1238,chn[0]
get value:1235,chn[0]
get value:1239,chn[0]
get value:1233,chn[0]
get value:1236,chn[0]
get value:1234,chn[0]
get value:1237,chn[0]
get value:1232,chn[0]
get value:1238,chn[0]
get value:1235,chn[0]
get value:1239,chn[0]
get value:1233,chn[0]
get value:1236,chn[0]
Expected behaviour:
get value:<ADC value>,chn[<channel>] 20 timesusleep(1000*1000))After choosing a channel:
please select the channel:
0
get value:1234,chn[0]
get value:1235,chn[0]
get value:1233,chn[0]
get value:1236,chn[0]
get value:1234,chn[0]
get value:1237,chn[0]
get value:1232,chn[0]
get value:1238,chn[0]
get value:1235,chn[0]
get value:1239,chn[0]
get value:1233,chn[0]
get value:1236,chn[0]
get value:1234,chn[0]
get value:1237,chn[0]
get value:1232,chn[0]
get value:1238,chn[0]
get value:1235,chn[0]
get value:1239,chn[0]
get value:1233,chn[0]
get value:1236,chn[0]
Expected behaviour:
get value:<ADC value>,chn[<channel>] 20 timesCtrl+Cfail to open file:/dev/lsadc
Cause: the xm_lsadc.ko kernel module is not loaded. Fix: insmod /opt/ko/xm_lsadc.ko
adc model select error.
Cause: invalid mode argument. Fix: enter 0 or 1.
please select the mode: 0
please select the channel: 0
get value:1234,chn[0]
get value:1235,chn[0]
...
please select the mode: 1
please select the channel: 0
get value:1234,chn[0]
get value:1235,chn[0]
...
The sample uses a simple interactive design:
int main(void)
{
int mode, chn;
// show the menu
printf("=============== LSADC TEST ===============\n");
printf("0:single step scan mode\n");
printf("1:continuous scan mode\n");
printf("please select the mode:\n");
// read the user input
scanf("%d", &mode);
// pick the scan mode from mode
switch(mode) {
case 0:
printf("please select the channel:\n");
scanf("%d", &chn);
sample_lsadc_single_step_scan_mode(mode, chn);
break;
case 1:
printf("please select the channel:\n");
scanf("%d", &chn);
sample_lsadc_continuous_scan_mode(mode, chn);
break;
default:
printf("invalid mode\n");
break;
}
return 0;
}
Single-step mode runs the full start -> read -> stop sequence for every sample:
xmedia_s32 sample_lsadc_single_step_scan_mode(lsadc_scan_mode mode, xmedia_s32 chn)
{
int i, value;
int fd = open(DEV_FILE, O_RDWR); // open /dev/lsadc
if (fd < 0) {
fprintf(stderr, "fail to open file:%s\n", DEV_FILE);
return -1;
}
// select the scan mode
if(ioctl(fd, LSADC_IOC_MODEL_SEL, &mode) < 0) {
fprintf(stderr, "adc model select error.\n");
goto exit;
}
// enable the channel
if(ioctl(fd, LSADC_IOC_CHN_ENABLE, &chn) < 0) {
fprintf(stderr, "enable chn %d error.\n", chn);
goto exit;
}
// capture 20 times in a loop
for(i = 0; i < 20; i++) {
// start the ADC
if(ioctl(fd, LSADC_IOC_START) < 0) {
fprintf(stderr, "start lsadc error.\n");
goto exit;
}
// read the channel value
value = ioctl(fd, LSADC_IOC_GET_CHNVAL, &chn);
printf("get value:%d,chn[%d]\n", value, chn);
usleep(1000 * 1000); // wait 1 second
// stop the ADC
if(ioctl(fd, LSADC_IOC_STOP) < 0) {
fprintf(stderr, "stop lsadc error.\n");
}
}
exit:
// release resources
if(ioctl(fd, LSADC_IOC_STOP) < 0) {
fprintf(stderr, "stop lsadc error.\n");
}
if(ioctl(fd, LSADC_IOC_CHN_DISABLE, &chn) < 0) {
fprintf(stderr, "disable chn %d error.\n", chn);
}
close(fd);
return 0;
}
Continuous mode starts the ADC once and keeps reading:
xmedia_s32 sample_lsadc_continuous_scan_mode(lsadc_scan_mode mode, xmedia_s32 chn)
{
int i, value;
int fd = open(DEV_FILE, O_RDWR);
if (fd < 0) {
fprintf(stderr, "fail to open file:%s\n", DEV_FILE);
return -1;
}
// select the scan mode
if(ioctl(fd, LSADC_IOC_MODEL_SEL, &mode) < 0) {
fprintf(stderr, "adc model select error.\n");
goto exit;
}
// enable the channel
if(ioctl(fd, LSADC_IOC_CHN_ENABLE, &chn) < 0) {
fprintf(stderr, "enable chn %d error.\n", chn);
goto exit;
}
// start the ADC once
if(ioctl(fd, LSADC_IOC_START) < 0) {
fprintf(stderr, "start lsadc error.\n");
goto exit;
}
// read data in a loop
for(i = 0; i < 20; i++) {
value = ioctl(fd, LSADC_IOC_GET_CHNVAL, &chn);
printf("get value:%d,chn[%d]\n", value, chn);
usleep(1000 * 1000); // wait 1 second
}
exit:
// release resources
if(ioctl(fd, LSADC_IOC_STOP) < 0) {
fprintf(stderr, "stop lsadc error.\n");
}
if(ioctl(fd, LSADC_IOC_CHN_DISABLE, &chn) < 0) {
fprintf(stderr, "disable chn %d error.\n", chn);
}
close(fd);
return 0;
}
The sample uses these IOCTL commands to talk to the driver:
| Command | Function | Argument |
|---|---|---|
LSADC_IOC_MODEL_SEL |
select the scan mode | 0=single-step, 1=continuous |
LSADC_IOC_CHN_ENABLE |
enable a channel | channel number |
LSADC_IOC_CHN_DISABLE |
disable a channel | channel number |
LSADC_IOC_START |
start the ADC | none |
LSADC_IOC_STOP |
stop the ADC | none |
LSADC_IOC_GET_CHNVAL |
read the channel value | channel number |
The ADC returns an integer, usually in the range 0~4095 (12-bit ADC). The value is proportional to the input voltage:
voltage = (ADC value / 4095) x reference voltage
For example, with a 3.3 V reference and an ADC reading of 2048, the input voltage is about:
voltage = (2048 / 4095) x 3.3V ~ 1.65V
The standard Linux character-device pattern:
// open the device
int fd = open("/dev/lsadc", O_RDWR);
// ioctl control
ioctl(fd, LSADC_IOC_MODEL_SEL, &mode);
// close the device
close(fd);
Check the return value of every IOCTL call:
if(ioctl(fd, LSADC_IOC_START) < 0) {
fprintf(stderr, "start lsadc error.\n");
goto exit; // jump to the cleanup code
}
Use goto for unified resource cleanup:
exit:
// disable the channel
if(ioctl(fd, LSADC_IOC_CHN_DISABLE, &chn) < 0) {
fprintf(stderr, "disable chn %d error.\n", chn);
}
// close the device
close(fd);
return 0;
}
| Problem | Likely cause | Fix |
|---|---|---|
fail to open file:/dev/lsadc |
device node missing | check whether xm_lsadc.ko is loaded |
adc model select error |
invalid mode argument | check that mode is 0 or 1 |
enable chn X error |
invalid channel | check that the channel number is in range |
| ADC value always 0 | input not connected, or voltage too low | check the wiring |
| ADC value always 4095 | input above the reference voltage | check the input voltage range |