342 lines
10 KiB
C
342 lines
10 KiB
C
#include "builtin_saadc.h"
|
|
#include "nrf_log.h"
|
|
#include "nrf_saadc.h"
|
|
|
|
#define VERF (0.60)
|
|
|
|
static uint32_t m_gain = NRF_SAADC_GAIN1_6;
|
|
static int read(uint32_t channel, int32_t *adc_val);
|
|
static int init(void)
|
|
{
|
|
/* enable ssadc */
|
|
NRF_SAADC->ENABLE = 1;
|
|
/* stop ssadc */
|
|
NRF_SAADC->EVENTS_STOPPED = 0;
|
|
NRF_SAADC->TASKS_STOP = 1;
|
|
do {
|
|
} while (NRF_SAADC->EVENTS_STOPPED == 0);
|
|
/* config resolution */
|
|
NRF_SAADC->RESOLUTION = SAADC_RESOLUTION_VAL_14bit;
|
|
/* auto calibration ssadc */
|
|
NRF_SAADC->EVENTS_CALIBRATEDONE = 0;
|
|
NRF_SAADC->TASKS_CALIBRATEOFFSET = 1;
|
|
do {
|
|
} while (NRF_SAADC->EVENTS_CALIBRATEDONE == 0);
|
|
/* disable ssadc */
|
|
NRF_SAADC->ENABLE = 0;
|
|
/* stop ssadc */
|
|
NRF_SAADC->EVENTS_STOPPED = 0;
|
|
NRF_SAADC->TASKS_STOP = 1;
|
|
do {
|
|
} while (NRF_SAADC->EVENTS_STOPPED == 0);
|
|
return 0;
|
|
}
|
|
|
|
static int reset(void)
|
|
{
|
|
// Do nothing...
|
|
return 0;
|
|
}
|
|
|
|
static float adc_convert_milivolt(uint16_t range_sel, int32_t val_14bit, bool is_diff)
|
|
{
|
|
float volt;
|
|
float gain;
|
|
float vref;
|
|
uint32_t m = is_diff ? 1 : 0;
|
|
|
|
switch (range_sel)
|
|
{
|
|
case NRF_SAADC_GAIN1_6:
|
|
gain = 1.0 / 6.0;
|
|
vref = VERF;
|
|
break;
|
|
case NRF_SAADC_GAIN1_5:
|
|
gain = 1.0 / 5.0;
|
|
vref = VERF;
|
|
break;
|
|
case NRF_SAADC_GAIN1_4:
|
|
gain = 1.0 / 4.0;
|
|
vref = VERF;
|
|
break;
|
|
case NRF_SAADC_GAIN1_3:
|
|
gain = 1.0 / 3.0;
|
|
vref = VERF;
|
|
break;
|
|
case NRF_SAADC_GAIN1_2:
|
|
gain = 1.0 / 2.0;
|
|
vref = VERF;
|
|
break;
|
|
case NRF_SAADC_GAIN1:
|
|
gain = 1.0 / 1.0;
|
|
vref = VERF;
|
|
break;
|
|
case NRF_SAADC_GAIN2:
|
|
gain = 2.0;
|
|
vref = VERF;
|
|
break;
|
|
case NRF_SAADC_GAIN4:
|
|
gain = 4.0;
|
|
vref = VERF;
|
|
break;
|
|
}
|
|
|
|
// differential V(P) = RESULT * (REFERENCE / GAIN/) / 2(RESOLUTION - 1) - V(N)
|
|
// single end V(P) = RESULT * (REFERENCE / GAIN/) / 2(RESOLUTION - 0)
|
|
|
|
volt = ((float)val_14bit * 1000.0) * (vref / gain) / (0x01 << (14 - m));
|
|
|
|
return volt;
|
|
}
|
|
|
|
static int read(uint32_t channel, int32_t *adc_val)
|
|
{
|
|
/* disable ssadc */
|
|
NRF_SAADC->ENABLE = 0;
|
|
/* stop ssadc */
|
|
NRF_SAADC->EVENTS_STOPPED = 0;
|
|
NRF_SAADC->TASKS_STOP = 1;
|
|
do {
|
|
} while (NRF_SAADC->EVENTS_STOPPED == 0);
|
|
/*
|
|
ref: p.381, nrf52840_PS_v1.1.pdf
|
|
Note: Oversampling should only be used when a single input channel is enabled, as averaging is
|
|
performed over all enabled channels.
|
|
*/
|
|
NRF_SAADC->OVERSAMPLE = NRF_SAADC_OVERSAMPLE_DISABLED;
|
|
/* config analog input */
|
|
NRF_SAADC->CH[0].PSELP = NRF_SAADC_INPUT_AIN0 + channel;
|
|
NRF_SAADC->CH[0].PSELN = NRF_SAADC_INPUT_DISABLED;
|
|
NRF_SAADC->CH[0].CONFIG =
|
|
(NRF_SAADC_RESISTOR_DISABLED << SAADC_CH_CONFIG_RESP_Pos) |
|
|
(NRF_SAADC_RESISTOR_DISABLED << SAADC_CH_CONFIG_RESN_Pos) |
|
|
(m_gain << SAADC_CH_CONFIG_GAIN_Pos) |
|
|
(NRF_SAADC_REFERENCE_INTERNAL << SAADC_CH_CONFIG_REFSEL_Pos) |
|
|
(NRF_SAADC_ACQTIME_40US << SAADC_CH_CONFIG_TACQ_Pos) |
|
|
(NRF_SAADC_MODE_SINGLE_ENDED << SAADC_CH_CONFIG_MODE_Pos) |
|
|
(NRF_SAADC_BURST_DISABLED << SAADC_CH_CONFIG_BURST_Pos);
|
|
/* enable ssadc */
|
|
NRF_SAADC->ENABLE = 1;
|
|
/* read single channel */
|
|
uint16_t val = 0;
|
|
NRF_SAADC->RESULT.PTR = (uint32_t)&val;
|
|
NRF_SAADC->RESULT.MAXCNT = 1;
|
|
NRF_SAADC->EVENTS_END = 0;
|
|
NRF_SAADC->TASKS_SAMPLE = 1;
|
|
NRF_SAADC->TASKS_START = 1;
|
|
do {
|
|
} while (NRF_SAADC->EVENTS_END == 0);
|
|
/* disable ssadc */
|
|
NRF_SAADC->ENABLE = 0;
|
|
/* stop ssadc */
|
|
NRF_SAADC->TASKS_STOP = 1;
|
|
/* copy value */
|
|
*adc_val = val;
|
|
adc_convert_milivolt(m_gain, *adc_val, 0);
|
|
return 0;
|
|
}
|
|
|
|
static int read_channels(uint32_t *p_channel, int32_t *adc_val, uint32_t count)
|
|
{
|
|
/* disable ssadc */
|
|
NRF_SAADC->ENABLE = 0;
|
|
/* stop ssadc */
|
|
NRF_SAADC->EVENTS_STOPPED = 0;
|
|
NRF_SAADC->TASKS_STOP = 1;
|
|
do {
|
|
} while (NRF_SAADC->EVENTS_STOPPED == 0);
|
|
/*
|
|
ref: p.381, nrf52840_PS_v1.1.pdf
|
|
Note: Oversampling should only be used when a single input channel is enabled, as averaging is
|
|
performed over all enabled channels.
|
|
*/
|
|
NRF_SAADC->OVERSAMPLE = NRF_SAADC_OVERSAMPLE_DISABLED;
|
|
/* config analog inputs */
|
|
for (uint32_t i = 0; i < COUNTOF(NRF_SAADC->CH); i++)
|
|
{
|
|
if (i < count)
|
|
{
|
|
NRF_SAADC->CH[i].PSELP = NRF_SAADC_INPUT_AIN0 + p_channel[i];
|
|
NRF_SAADC->CH[i].PSELN = NRF_SAADC_INPUT_DISABLED;
|
|
NRF_SAADC->CH[i].CONFIG =
|
|
(NRF_SAADC_RESISTOR_DISABLED << SAADC_CH_CONFIG_RESP_Pos) |
|
|
(NRF_SAADC_RESISTOR_DISABLED << SAADC_CH_CONFIG_RESN_Pos) |
|
|
(m_gain << SAADC_CH_CONFIG_GAIN_Pos) |
|
|
(NRF_SAADC_REFERENCE_INTERNAL << SAADC_CH_CONFIG_REFSEL_Pos) |
|
|
(NRF_SAADC_ACQTIME_40US << SAADC_CH_CONFIG_TACQ_Pos) |
|
|
(NRF_SAADC_MODE_SINGLE_ENDED << SAADC_CH_CONFIG_MODE_Pos) |
|
|
(NRF_SAADC_BURST_DISABLED << SAADC_CH_CONFIG_BURST_Pos);
|
|
}
|
|
else
|
|
{
|
|
NRF_SAADC->CH[i].PSELP = NRF_SAADC_INPUT_DISABLED;
|
|
NRF_SAADC->CH[i].PSELN = NRF_SAADC_INPUT_DISABLED;
|
|
NRF_SAADC->CH[i].CONFIG = 0;
|
|
}
|
|
}
|
|
/* enable ssadc */
|
|
NRF_SAADC->ENABLE = 1;
|
|
/* start convert */
|
|
uint16_t val[16] = { 0 };
|
|
NRF_SAADC->RESULT.PTR = (uint32_t)&val[0];
|
|
NRF_SAADC->RESULT.MAXCNT = count;
|
|
NRF_SAADC->EVENTS_END = 0;
|
|
NRF_SAADC->TASKS_SAMPLE = 1;
|
|
NRF_SAADC->TASKS_START = 1;
|
|
do {
|
|
} while (NRF_SAADC->EVENTS_END == 0);
|
|
/* disable ssadc */
|
|
NRF_SAADC->ENABLE = 0;
|
|
/* stop ssadc */
|
|
NRF_SAADC->TASKS_STOP = 1;
|
|
/* copy values */
|
|
for (uint32_t i = 0; i < count; i++)
|
|
{
|
|
adc_val[i] = val[i];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int read_channels_ex(uint32_t *p_channel, int32_t *adc_val, uint32_t count, void (*preliminary_callback)(void))
|
|
{
|
|
/* disable ssadc */
|
|
NRF_SAADC->ENABLE = 0;
|
|
/* stop ssadc */
|
|
NRF_SAADC->EVENTS_STOPPED = 0;
|
|
NRF_SAADC->TASKS_STOP = 1;
|
|
do {
|
|
} while (NRF_SAADC->EVENTS_STOPPED == 0);
|
|
/*
|
|
ref: p.381, nrf52840_PS_v1.1.pdf
|
|
Note: Oversampling should only be used when a single input channel is enabled, as averaging is
|
|
performed over all enabled channels.
|
|
*/
|
|
NRF_SAADC->OVERSAMPLE = NRF_SAADC_OVERSAMPLE_DISABLED;
|
|
/* config analog inputs */
|
|
for (uint32_t i = 0; i < COUNTOF(NRF_SAADC->CH); i++)
|
|
{
|
|
if (i < count)
|
|
{
|
|
NRF_SAADC->CH[i].PSELP = NRF_SAADC_INPUT_AIN0 + p_channel[i];
|
|
NRF_SAADC->CH[i].PSELN = NRF_SAADC_INPUT_DISABLED;
|
|
NRF_SAADC->CH[i].CONFIG =
|
|
(NRF_SAADC_RESISTOR_DISABLED << SAADC_CH_CONFIG_RESP_Pos) |
|
|
(NRF_SAADC_RESISTOR_DISABLED << SAADC_CH_CONFIG_RESN_Pos) |
|
|
(m_gain << SAADC_CH_CONFIG_GAIN_Pos) |
|
|
(NRF_SAADC_REFERENCE_INTERNAL << SAADC_CH_CONFIG_REFSEL_Pos) |
|
|
(NRF_SAADC_ACQTIME_40US << SAADC_CH_CONFIG_TACQ_Pos) |
|
|
(NRF_SAADC_MODE_SINGLE_ENDED << SAADC_CH_CONFIG_MODE_Pos) |
|
|
(NRF_SAADC_BURST_DISABLED << SAADC_CH_CONFIG_BURST_Pos);
|
|
}
|
|
else
|
|
{
|
|
NRF_SAADC->CH[i].PSELP = NRF_SAADC_INPUT_DISABLED;
|
|
NRF_SAADC->CH[i].PSELN = NRF_SAADC_INPUT_DISABLED;
|
|
NRF_SAADC->CH[i].CONFIG = 0;
|
|
}
|
|
}
|
|
/* enable ssadc */
|
|
NRF_SAADC->ENABLE = 1;
|
|
/* disable irq */
|
|
__disable_irq();
|
|
/* do preliminary job */
|
|
if (preliminary_callback)
|
|
{
|
|
preliminary_callback();
|
|
}
|
|
/* start convert */
|
|
int16_t val[COUNTOF(NRF_SAADC->CH)] = { 0 };
|
|
NRF_SAADC->RESULT.PTR = (uint32_t)&val[0];
|
|
NRF_SAADC->RESULT.MAXCNT = count;
|
|
NRF_SAADC->EVENTS_END = 0;
|
|
NRF_SAADC->TASKS_SAMPLE = 1;
|
|
NRF_SAADC->TASKS_START = 1;
|
|
/* enabl irq */
|
|
__enable_irq();
|
|
do {
|
|
} while (NRF_SAADC->EVENTS_END == 0);
|
|
/* disable ssadc */
|
|
NRF_SAADC->ENABLE = 0;
|
|
/* stop ssadc */
|
|
NRF_SAADC->TASKS_STOP = 1;
|
|
/* copy values */
|
|
for (uint32_t i = 0; i < count; i++)
|
|
{
|
|
adc_val[i] = val[i];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int gain(adc_gain_t gain)
|
|
{
|
|
int ret;
|
|
switch (gain)
|
|
{
|
|
case GAIN_1P000:
|
|
m_gain = NRF_SAADC_GAIN1_6;
|
|
ret = 0;
|
|
break;
|
|
case GAIN_1P200:
|
|
m_gain = NRF_SAADC_GAIN1_5;
|
|
ret = 0;
|
|
break;
|
|
case GAIN_1P500:
|
|
m_gain = NRF_SAADC_GAIN1_4;
|
|
ret = 0;
|
|
break;
|
|
case GAIN_2P000:
|
|
m_gain = NRF_SAADC_GAIN1_3;
|
|
ret = 0;
|
|
break;
|
|
case GAIN_3P000:
|
|
m_gain = NRF_SAADC_GAIN1_2;
|
|
ret = 0;
|
|
break;
|
|
case GAIN_6P000:
|
|
m_gain = NRF_SAADC_GAIN1;
|
|
ret = 0;
|
|
break;
|
|
case GAIN_12P000:
|
|
m_gain = NRF_SAADC_GAIN2;
|
|
ret = 0;
|
|
break;
|
|
case GAIN_24P000:
|
|
m_gain = NRF_SAADC_GAIN4;
|
|
ret = 0;
|
|
break;
|
|
default:
|
|
m_gain = SAADC_CH_CONFIG_GAIN_Gain1_6;
|
|
ret = -1;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int read_multiple_milivolt_ex(uint32_t *p_channels, float *p_val, uint32_t count, void(*preliminary_action))
|
|
{
|
|
int32_t int_val[16];
|
|
double f_val[16];
|
|
|
|
if (read_channels_ex(p_channels, int_val, count, preliminary_action) != 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
p_val[i] = adc_convert_milivolt(m_gain, int_val[i], false);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
const adc_drv_if_t builtin_saadc = {
|
|
.init = init,
|
|
.reset = reset,
|
|
.read = read,
|
|
.gain = gain,
|
|
.read_multiple_channels = read_channels,
|
|
.read_multiple_channels_ex = read_channels_ex,
|
|
.read_multiple_milivolt_ex = read_multiple_milivolt_ex,
|
|
};
|