Files
microchip-application-bmd38…/builtin_saadc.c
T
2024-06-27 21:30:30 +08:00

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,
};