Skip to content

Instantly share code, notes, and snippets.

@MZachmann
Last active June 3, 2024 11:45
Show Gist options
  • Save MZachmann/14117d5af65e7b22c9761dd41c175627 to your computer and use it in GitHub Desktop.
Save MZachmann/14117d5af65e7b22c9761dd41c175627 to your computer and use it in GitHub Desktop.
Analog input for Zephyr
#include "AnalogIn.h"
#include <drivers/adc.h>
#include <string.h>
// Simple analog input method
// this just reads a sample then waits then returns it
// ADC Sampling Settings
// doc says that impedance of 800K == 40usec sample time
#define ADC_DEVICE_NAME DT_LABEL(DT_ALIAS(adcctrl))
#define ADC_RESOLUTION 10
#define ADC_GAIN ADC_GAIN_1_6
#define ADC_REFERENCE ADC_REF_INTERNAL
#define ADC_ACQUISITION_TIME ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 40)
#define BUFFER_SIZE 6
static bool _IsInitialized = false;
static uint8_t _LastChannel = 250;
static int16_t m_sample_buffer[BUFFER_SIZE];
// the channel configuration with channel not yet filled in
static struct adc_channel_cfg m_1st_channel_cfg = {
.gain = ADC_GAIN,
.reference = ADC_REFERENCE,
.acquisition_time = ADC_ACQUISITION_TIME,
.channel_id = 0, // gets set during init
.differential = 0,
#if CONFIG_ADC_CONFIGURABLE_INPUTS
.input_positive = 0, // gets set during init
#endif
};
// return device* for the adc
static const struct device* getAdcDevice(void)
{
return device_get_binding(ADC_DEVICE_NAME);
}
// initialize the adc channel
static const struct device* init_adc(int channel)
{
int ret;
const struct device *adc_dev = getAdcDevice();
if(_LastChannel != channel)
{
_IsInitialized = false;
_LastChannel = channel;
}
if ( adc_dev != NULL && !_IsInitialized)
{
// strangely channel_id gets the channel id and input_positive gets id+1
m_1st_channel_cfg.channel_id = channel;
#if CONFIG_ADC_CONFIGURABLE_INPUTS
m_1st_channel_cfg.input_positive = channel+1,
#endif
ret = adc_channel_setup(adc_dev, &m_1st_channel_cfg);
if(ret != 0)
{
//LOG_INF("Setting up of the first channel failed with code %d", ret);
adc_dev = NULL;
}
else
{
_IsInitialized = true; // we don't have any other analog users
}
}
memset(m_sample_buffer, 0, sizeof(m_sample_buffer));
return adc_dev;
}
// ------------------------------------------------
// read one channel of adc
// ------------------------------------------------
static int16_t readOneChannel(int channel)
{
const struct adc_sequence sequence = {
.options = NULL, // extra samples and callback
.channels = BIT(channel), // bit mask of channels to read
.buffer = m_sample_buffer, // where to put samples read
.buffer_size = sizeof(m_sample_buffer),
.resolution = ADC_RESOLUTION, // desired resolution
.oversampling = 0, // don't oversample
.calibrate = 0 // don't calibrate
};
int ret;
int16_t sample_value = BAD_ANALOG_READ;
const struct device *adc_dev = init_adc(channel);
if (adc_dev)
{
ret = adc_read(adc_dev, &sequence);
if(ret == 0)
{
sample_value = m_sample_buffer[0];
}
}
return sample_value;
}
// ------------------------------------------------
// high level read adc channel and convert to float voltage
// ------------------------------------------------
float AnalogRead(int channel)
{
int16_t sv = readOneChannel(channel);
if(sv == BAD_ANALOG_READ)
{
return sv;
}
// Convert the result to voltage
// Result = [V(p) - V(n)] * GAIN/REFERENCE / 2^(RESOLUTION)
int multip = 256;
// find 2**adc_resolution
switch(ADC_RESOLUTION)
{
default :
case 8 :
multip = 256;
break;
case 10 :
multip = 1024;
break;
case 12 :
multip = 4096;
break;
case 14 :
multip = 16384;
break;
}
// the 3.6 relates to the voltage divider being used in my circuit
float fout = (sv * 3.6 / multip);
return fout;
}
#ifndef ANALOG_IN_H
#define ANALOG_IN_H
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
float AnalogRead(int channel);
#define BAD_ANALOG_READ -123
#ifdef __cplusplus
}
#endif
#endif
@MZachmann
Copy link
Author

Hi, Where can i find the "AnalogIn.h" file?

Hi, I've added what I think is a useful AnalogIn.h file. It's very short. Please take a look at the article: https://medium.com/home-wireless/reading-analog-data-in-zephyr-with-a-nrf52840-e05dae326b9b for lots of explanation.

Mark

@myst729
Copy link

myst729 commented Jun 3, 2024

Is it possible to run Zephyr with analog multiplexer (e.g. CD74HC4067)?

@MZachmann
Copy link
Author

Zephyr can easily contain code to have the processor control an analog mux but I don't know of any prebuilt libraries to do that within it. Simply decide which gpio ports are used to control the mux and write C/C++ to switch them.

@myst729
Copy link

myst729 commented Jun 3, 2024

I'm new to Zephyr, guess I need some time to learn its syntax. It seems weird to me at the moment. Writing C/C++ is ok for me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment