CodeSnippets/Peripherals/ADC/AdvancedFunctions/advanced_functions.c (237 lines of code) (raw):
/* Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License. */
// Code Snippet: Read ADC channel using Advanced Functions (Linux ioctls)
// This code snippet demonstrates how to read a value from the ADC
// Potentiometer controller using ioctl system calls, and displays the
// value in volts.
// To read a value from an ADC channel, the application manifest
// (https://learn.microsoft.com/azure-sphere/app-development/app-manifest)
// must enable the peripheral. To enable this capability, copy the
// lines in the Capabilities section of AdvancedFunctions/app_manifest.json
// into your application manifest file.
#include <string.h>
#include <applibs/adc.h>
#include <applibs/log.h>
static int OpenAdc(ADC_ControllerId adcControllerId);
static int GetSampleBitCount(int adcControllerFd, ADC_ChannelId channelId,
struct iio_ioctl_chan_spec_buffer *channelSpecBuffer);
static int GetPropertyIndex(const struct iio_ioctl_chan_spec *channelSpec,
const char *propertyName);
static int GetExtInfo(int adcControllerFd, ADC_ChannelId channelId,
unsigned int extendedPropertyIndex, char *data, size_t length);
static int SetReferenceVoltage(int adcControllerFd, ADC_ChannelId channelId,
struct iio_ioctl_chan_spec_buffer *channelSpecBuffer,
float referenceVoltage);
static int PollAdc(int adcControllerFd, ADC_ChannelId channelId, uint32_t *outSampleValue);
static int SetExtInfo(int adcControllerFd, ADC_ChannelId channelId,
unsigned int extendedPropertyIndex, const char *data, size_t length);
static int GetChannelSpecification(int adcControllerFd, ADC_ChannelId channelId,
struct iio_ioctl_chan_spec_buffer *channelSpecBuffer);
static int ReadAdcChannel(void);
// The maximum voltage.
static const float sampleMaxVoltage = 2.5f;
// ADC device path.
static const char adcPath[] = "/dev/adc";
// Channel specification.
static struct iio_ioctl_chan_spec_buffer channelSpecBuffer;
/// <summary>
/// Reads the value from the ADC channel and displays the value in volts.
/// </summary>
/// <returns>0 on success, else -1.</returns>
static int ReadAdcChannel(void)
{
int adcControllerFd = -1;
int sampleBitCount = -1;
int retVal = -1;
// Open the ADC.
adcControllerFd = OpenAdc(SAMPLE_POTENTIOMETER_ADC_CONTROLLER);
if (adcControllerFd == -1) {
Log_Debug("ERROR: OpenAdc failed.\n");
goto cleanup;
}
// Get the specification for the channel.
int result = GetChannelSpecification(adcControllerFd, SAMPLE_POTENTIOMETER_ADC_CHANNEL,
&channelSpecBuffer);
if (result == -1) {
Log_Debug("ERROR: GetChannelSpecification failed.\n");
goto cleanup;
}
// Get the number of bits in the sample.
sampleBitCount =
GetSampleBitCount(adcControllerFd, SAMPLE_POTENTIOMETER_ADC_CHANNEL, &channelSpecBuffer);
if (sampleBitCount == -1) {
Log_Debug("ERROR: GetSampleBitCount failed.\n");
goto cleanup;
}
// Set the reference voltage.
result = SetReferenceVoltage(adcControllerFd, SAMPLE_POTENTIOMETER_ADC_CHANNEL,
&channelSpecBuffer, sampleMaxVoltage);
if (result == -1) {
Log_Debug("ERROR: SetReferenceVoltage failed.\n");
goto cleanup;
}
// Poll the ADC to read the voltage.
uint32_t value;
result = PollAdc(adcControllerFd, SAMPLE_POTENTIOMETER_ADC_CHANNEL, &value);
if (result == -1) {
Log_Debug("ERROR: PollAdc failed.\n");
goto cleanup;
}
// Calculate voltage.
float maxSample = (float)((1 << sampleBitCount) - 1);
float voltage = ((float)value * sampleMaxVoltage) / maxSample;
Log_Debug("The out sample value is %.3f V.\n", voltage);
retVal = 0;
cleanup:
// Close the file descriptor.
if (adcControllerFd != -1) {
int result = close(adcControllerFd);
if (result != 0) {
Log_Debug("ERROR: Could not close ADC fd: %s (%d).\n", strerror(errno), errno);
}
}
return retVal;
}
/// <summary>
/// Helper function to open the ADC.
/// </summary>
/// <param name="adcControllerId">Id of the ADC controller.</param>
/// <returns>Value of fd if succesful, -1 on error</returns>
static int OpenAdc(ADC_ControllerId adcControllerId)
{
// Format the ADC path.
char path[32];
int len = snprintf(path, sizeof(path), "%s%u", adcPath, adcControllerId);
if (len < 0 || len >= sizeof(path)) {
Log_Debug("ERROR: Cannot format ADC path to buffer.\n");
return -1;
}
int fd = open(path, O_RDWR | O_CLOEXEC, 0);
if (fd == -1) {
if (errno == ENOENT) {
Log_Debug("ERROR: open failed with error: %s (%d)\n", strerror(errno), errno);
}
return -1;
}
return fd;
}
/// <summary>
/// Helper function to gets the specification of the channel and stores the specification
/// in channelSpecBuffer parameter.
/// </summary>
/// <param name="adcControllerFd">The ADC file descriptor.</param>
/// <param name="channelId">The channel id of the ADC.</param>
/// <param name="channelSpecBuffer">The channel spec buffer.</param>
/// <returns>0 if succesful, -1 on error</returns>
static int GetChannelSpecification(int adcControllerFd, ADC_ChannelId channelId,
struct iio_ioctl_chan_spec_buffer *channelSpecBuffer)
{
struct iio_ioctl_chan_spec_buffer_size channelSpecBufferSize = {
.size = sizeof(channelSpecBufferSize), .index = (int)channelId, .total_size = 0};
int ret = ioctl(adcControllerFd, (int)IIO_GET_CHANNEL_SPEC_BUFFER_TOTAL_SIZE_IOCTL,
&channelSpecBufferSize);
if (ret < 0) {
Log_Debug(
"ERROR: ioctl call failed with error \"%s (%d)\" for request "
"IIO_GET_CHANNEL_SPEC_BUFFER_TOTAL_SIZE_IOCTL.\n",
strerror(errno), errno);
return -1;
}
channelSpecBuffer->size = sizeof(*channelSpecBuffer);
channelSpecBuffer->total_size = channelSpecBufferSize.total_size;
channelSpecBuffer->index = (int)channelId;
channelSpecBuffer->channel = NULL;
ret = ioctl(adcControllerFd, (int)IIO_GET_CHANNEL_SPEC_BUFFER_IOCTL, channelSpecBuffer);
if (ret < 0) {
Log_Debug(
"ERROR: ioctl call failed with error \"%s (%d)\" for request "
"IIO_GET_CHANNEL_SPEC_BUFFER_IOCTL.\n",
strerror(errno), errno);
return -1;
}
return 0;
}
/// <summary>
/// Helper function to get the index of the specified property name.
/// </summary>
/// <param name="channelSpec">Pointer to channel specification struct</param>
/// <param name="propertyName">Name of the property</param>
/// <returns>Index of the property if succesful, -1 on error</returns>
static int GetPropertyIndex(const struct iio_ioctl_chan_spec *channelSpec, const char *propertyName)
{
if (!channelSpec) {
Log_Debug("ERROR: Channel specification input parameter is NULL.\n");
return -1;
}
int propertyIndex = 0;
const struct iio_ioctl_chan_spec_ext_info *extendedChannelSpecInfo = channelSpec->ext_info;
while (extendedChannelSpecInfo) {
if (extendedChannelSpecInfo->name &&
strcmp(propertyName, extendedChannelSpecInfo->name) == 0) {
return propertyIndex;
}
++propertyIndex;
extendedChannelSpecInfo = extendedChannelSpecInfo->next;
}
Log_Debug("ERROR: Failed to retrieve property index for property name %s.\n", propertyName);
return -1;
}
/// <summary>
/// Helper function to get the extended channel information.
/// </summary>
/// <param name="adcControllerFd">The ADC file descriptor.</param>
/// <param name="propertyName">The channel id of the ADC.</param>
/// <param name="extendedPropertyIndex">The index of the property.</param>
/// <param name="data">Buffer to hold the extended information.</param>
/// <param name="length">Length of the buffer.</param>
/// <returns>0 if succesful, -1 on error</returns>
static int GetExtInfo(int adcControllerFd, ADC_ChannelId channelId,
unsigned int extendedPropertyIndex, char *data, size_t length)
{
struct iio_ioctl_read_chan_ext_info readExtendedChannelInfo = {
.size = sizeof(readExtendedChannelInfo),
.channel_index = channelId,
.info_index = extendedPropertyIndex,
.buffer = data,
.length = length};
int ret =
ioctl(adcControllerFd, (int)IIO_READ_CHANNEL_EXT_INFO_IOCTL, &readExtendedChannelInfo);
if (ret < 0) {
Log_Debug(
"ERROR: ioctl call failed with error \"%s (%d)\" for request "
"IIO_READ_CHANNEL_EXT_INFO_IOCTL.\n",
strerror(errno), errno);
return -1;
}
return 0;
}
/// <summary>
/// Helper function to set the extended channel information.
/// </summary>
/// <param name="adcControllerFd">The ADC file descriptor.</param>
/// <param name="propertyName">The channel id of the ADC.</param>
/// <param name="extendedPropertyIndex">The index of the property.</param>
/// <param name="data">Buffer holding the extended information to be set.</param>
/// <param name="length">Length of the buffer.</param>
/// <returns>0 if succesful, -1 on error</returns>
static int SetExtInfo(int adcControllerFd, ADC_ChannelId channelId,
unsigned int extendedPropertyIndex, const char *data, size_t length)
{
if (extendedPropertyIndex < 0) {
Log_Debug("ERROR: SetExtInfo: Invalid extended property index.\n");
return -1;
}
struct iio_ioctl_write_chan_ext_info writeExtendedChannelInfo = {
.size = sizeof(writeExtendedChannelInfo),
.channel_index = channelId,
.info_index = extendedPropertyIndex,
.buffer = data,
.length = length};
int ret =
ioctl(adcControllerFd, (int)IIO_WRITE_CHANNEL_EXT_INFO_IOCTL, &writeExtendedChannelInfo);
if (ret < 0) {
Log_Debug(
"ERROR: ioctl call failed with error \"%s (%d)\" for request "
"IIO_WRITE_CHANNEL_EXT_INFO_IOCTL.\n",
strerror(errno), errno);
return -1;
}
return 0;
}
/// <summary>
/// Helper function to get the number of bits in the sample.
/// </summary>
/// <param name="adcControllerFd">The ADC file descriptor.</param>
/// <param name="propertyName">The channel id of the ADC.</param>
/// <param name="iio_ioctl_chan_spec_buffer">The channel spec buffer.</param>
/// <returns>Number of bits if succesful, -1 on error</returns>
static int GetSampleBitCount(int adcControllerFd, ADC_ChannelId channelId,
struct iio_ioctl_chan_spec_buffer *channelSpecBuffer)
{
// Buffer to hold decimal representation of 4-byte integer value and null terminator.
char dataBuffer[12];
int res = GetExtInfo(adcControllerFd, channelId,
(unsigned int)GetPropertyIndex(channelSpecBuffer->channel, "current_bits"),
dataBuffer, sizeof(dataBuffer));
if (res < 0) {
Log_Debug("ERROR: Failed to retrieve extended channel information for channelId = %u.\n",
channelId);
return -1;
}
if (sizeof(dataBuffer) <= strnlen(dataBuffer, sizeof(dataBuffer))) {
Log_Debug("ERROR: Extended channel information received is invalid for channelId = %d.\n",
channelId);
return -1;
}
int numberOfBits = atoi(dataBuffer);
return numberOfBits;
}
/// <summary>
/// Helper function to set the reference voltage.
/// </summary>
/// <param name="adcControllerFd">The ADC file descriptor.</param>
/// <param name="propertyName">The channel id of the ADC.</param>
/// <param name="channelSpecBuffer">The channel spec buffer.</param>
/// <param name="referenceVoltage">The reference voltage to set.</param>
/// <returns>0 if succesful, -1 on error</returns>
static int SetReferenceVoltage(int adcControllerFd, ADC_ChannelId channelId,
struct iio_ioctl_chan_spec_buffer *channelSpecBuffer,
float referenceVoltage)
{
// Buffer to hold single precision floating point and null terminator.
char dataBuffer[12];
int length = snprintf(dataBuffer, sizeof(dataBuffer), "%.3f", referenceVoltage);
if (length < 0 || length >= sizeof(dataBuffer)) {
Log_Debug("ERROR: Cannot write reference voltage to buffer.\n");
return -1;
}
int res =
SetExtInfo(adcControllerFd, channelId,
(unsigned int)GetPropertyIndex(channelSpecBuffer->channel, "reference_voltage"),
dataBuffer, (size_t)(length + 1));
if (res < 0) {
Log_Debug("ERROR: Failed to set extended channel information for channelId = %u.\n",
channelId);
return -1;
}
return 0;
}
/// <summary>
/// Helper function to poll the ADC.
/// </summary>
/// <param name="adcControllerFd">The ADC file descriptor.</param>
/// <param name="propertyName">The channel id of the ADC.</param>
/// <param name="outSampleValue">Holds the sampled value.</param>
/// <returns>0 if succesful, -1 on error</returns>
static int PollAdc(int adcControllerFd, ADC_ChannelId channelId, uint32_t *outSampleValue)
{
if (adcControllerFd < 0) {
Log_Debug("ERROR: Invalid file descriptor.\n");
return -1;
}
struct iio_ioctl_raw_channel_info rawChannelInfo = {
.size = sizeof(rawChannelInfo), .index = (int)(channelId), .mask = IIO_IOCTL_CHAN_INFO_RAW};
int result = ioctl(adcControllerFd, (int)IIO_READ_RAW_CHANNEL_INFO_IOCTL, &rawChannelInfo);
if (result < 0) {
Log_Debug(
"ERROR: ioctl call failed with error \"%s (%d)\" for request "
"IIO_READ_RAW_CHANNEL_INFO_IOCTL.\n",
strerror(errno), errno);
return -1;
}
*outSampleValue = (uint32_t)(rawChannelInfo.val);
return 0;
}