How to use analog input (ADC) on NuttX RTOS
In all previous tutorial about NuttX we used some type of digital interface (I2C, SPI, etc), so today we will see how to use an analog interface to read some analog sensor or other analog device. In fact we live in an analog world and sometimes we need to read some analog voltage to make sense of some external analog signal. Today we will see how to use the ADC (Analog Digital Converter) from Raspberry Pi Pico RP2040 to read analog voltages.
Quick Links
- Part 1: Getting Started with (Apache) NuttX RTOS - Part 1
- Part 2: Getting Started with (Apache) NuttX RTOS Part 2 - Looking Inside and Creating Your Customized Image
- Part 3: Getting Started with NuttX RTOS on Three Low Cost Boards
- Part 4: Using GPIO in (Apache) NuttX RTOS
- Part 5: Using (Apache) NuttX USERLED Subsystem
- Part 6: Using (Apache) NuttX Buttons Subsystem
- Part 7: How to use I2C devices in (Apache) NuttX: Scanning for Devices
- Part 8: How to use I2C devices in (Apache) NuttX: Adding support for an I2C device in your board
- Part 9: How to use SPI devices in NuttX RTOS
- Part 10: How to use analog input (ADC) on NuttX RTOS
First let me explain what is an ADC (Analog to Digital Converter). An ADC is basically a peripheral that "translate" analog voltage to a digital value (a number) representing that voltage. Let's suppose we have an ADC able to read 0V to 3.3V, so it could display a value from 0 to 1023 (assuming a 10-bit ADC) to represent this range of voltage. So, when you put 0V in the analog input pin your ADC will report the value 0 and when you put 3.3V it will return 1023. And any other value between 0V e 3.3V will return a proportional value between 0 and 1023. There are more details involved, but you got the idea.
Nowadays almost all microcontrollers have some ADC peripheral internally and it can read analog inputs with different bit resolutions (RP2040 has a 12-bit ADC resolution). The ADC's resolution is only a small part of its characteristic. There are many details related to ADC that will interfere to its "quality", such as its bandwidth, quantization error, nonlinearity error, signal-to-noise ratio (SNR), accuracy, etc. The wikipedia is a good starting point to understanding more about ADC intricacies.
The RP2040 has a 12-bit ADC with 4 externally accessible channels in the GPIO26, GPIO27, GPIO28 and GPIO29 pins and an internal channel connected temperature sensor inside the chip.
Note: The RP2040's ADC has a small issue that we need to address, there are some spikes at 512, 1536, 2560, and 3584 counts, more information here. Hopefully we expect these issues to be fixed in their new RP2350.
Now that you know what is an ADC, let's use it in NuttX RTOS.
The internal ADC peripheral driver is located at arch/arm/src/rp2040/rp2040_adc.c.
The board initialization for this driver is located at boards/arm/rp2040/common/src/rp2040_common_bringup.c. And as you can see inside this file, it initialize the ADC this way:
ret = rp2040_adc_setup("/dev/adc0", ADC_0, ADC_1, ADC_2, ADC_3, ADC_TEMP); if (ret != OK) { syslog(LOG_ERR, "Failed to initialize ADC Driver: %d\n", ret); }
Basically this function will create a /dev/adc0 device file that will be used by our application (in this case the apps/examples/adc application). Note that ADC_0 to ADC_3 and ADC_TEMP are just macros that represent if we are enabling (boolean true) or disabling (boolean false) that channel.
At this point you already got a basic understanding of ADC and how it is used on NuttX.
So let's configure our Raspberry Pi Pico board on NuttX to use ADC input:
$ cd nuttxspace/nuttx $ make distclean $ ./tools/configure.sh raspberrypi-pico:usbnsh
Run make menuconfig to select the ADC peripheral, the driver and the application:
$ make menuconfig
Enter inside "System Type" and enable the ADC support, Include ADC device driver and channel 0:
In this tutorial we are interested to read only the ADC channel 0, if you want to read more channels, you can enable it.
Return to the main menu and enter inside "Device Drivers" and enable "Analog Device(ADC/DAC) Support" and inside it confirm that Analog-to-Digital Conversion is enabled and buffer size and poll waiters are configured as shown here:
Finally return to the main menu and enter inside "Application Configuration --->", "Examples --->" and enable the ADC example:
That is it, now you can exit, save, compile and flash it in your board, as you did in the previous articles:
$ export PICO_SDK_PATH=~/pico-sdk $ make -j ... LD: nuttx Generating: nuttx.uf2 picotool uf2 convert --quiet -t elf nuttx nuttx.uf2; Done.
Press and hold the BOOTSEL of your board and plug the USB in the computer, then a virtual disk will appear, just copy the created nuttx.uf2 to this disk to flash NuttX on your board.
A good way to test an ADC input is using a potentiometer with central tap connected to the analog input pin and the side taps connected to GND and 3.3V this way:
If you doesn't have a breadboard you can connect the potentiometer using some dupont wires this way:
Now using minicom, picocom or any serial terminal to connect to /dev/ttyACM0 and press ENTER three times to start the "nsh>" and run the adc application:
You can pass the number of sampling you want to read.
Note: the returned read value is high because NuttX converts the read input to 32-bit!
That's all, I hope you enjoyed this tutorial and start using NuttX on your projects.
- Comments
- Write a Comment Select to add a comment
To post reply to a comment, click on the 'reply' button attached to each comment. To post a new comment (not a reply to a comment) check out the 'Write a Comment' tab at the top of the comments.
Please login (on the right) if you already have an account on this platform.
Otherwise, please use this form to register (free) an join one of the largest online community for Electrical/Embedded/DSP/FPGA/ML engineers: