adc:Add that ability to select an ADC

This commit is contained in:
David Sidrane
2019-08-06 10:46:37 -04:00
committed by Daniel Agar
parent 0ebb87357f
commit 890f805b37
4 changed files with 136 additions and 85 deletions
+24
View File
@@ -196,6 +196,30 @@
# error Unsuported BOARD_NUMBER_BRICKS number. # error Unsuported BOARD_NUMBER_BRICKS number.
#endif #endif
/* Historically PX4 used one ADC1 With FMUvnX this has changes.
* These defines maintain compatibility while allowing the
* new boards to override the ADC used from HW VER/REV and
* the system one.
*
* Depending on HW configuration (VER/REV POP options) hardware detection
* may or may NOT initialize a given ADC. SYSTEM_ADC_COUNT is used to size the
* singleton array to ensure this is only done once per ADC.
*/
#if !defined(HW_REV_VER_ADC_BASE)
# define HW_REV_VER_ADC_BASE STM32_ADC1_BASE
#endif
#if !defined(SYSTEM_ADC_BASE)
# define SYSTEM_ADC_BASE STM32_ADC1_BASE
#endif
#if SYSTEM_ADC_BASE == HW_REV_VER_ADC_BASE
# define SYSTEM_ADC_COUNT 1
#else
# define SYSTEM_ADC_COUNT 2
#endif
/* Choose the source for ADC_SCALED_V5_SENSE */ /* Choose the source for ADC_SCALED_V5_SENSE */
#if defined(ADC_5V_RAIL_SENSE) #if defined(ADC_5V_RAIL_SENSE)
#define ADC_SCALED_V5_SENSE ADC_5V_RAIL_SENSE #define ADC_SCALED_V5_SENSE ADC_5V_RAIL_SENSE
@@ -52,14 +52,14 @@
* boards may provide this function to allow complex version-ing. * boards may provide this function to allow complex version-ing.
* *
* Input Parameters: * Input Parameters:
* None. * base_address - base address of the ADC
* *
* Returned Value: * Returned Value:
* *
* OK, or -1 if the function failed. * OK, or -1 if the function failed.
*/ */
__EXPORT int board_adc_init(void); __EXPORT int board_adc_init(uint32_t base_address);
/************************************************************************************ /************************************************************************************
* Name: board_adc_sample * Name: board_adc_sample
@@ -68,14 +68,15 @@ __EXPORT int board_adc_init(void);
* boards provide this function to allow complex version-ing. * boards provide this function to allow complex version-ing.
* *
* Input Parameters: * Input Parameters:
* channel - The number of the adc channel to read. * base_address - base address of the ADC
* channel - The number of the adc channel to read.
* *
* Returned Value: * Returned Value:
* The ADC DN read for the channel or 0xffff if there * The ADC DN read for the channel or 0xffff if there
* is an error reading the channel. * is an error reading the channel.
*/ */
__EXPORT uint16_t board_adc_sample(unsigned channel); __EXPORT uint16_t board_adc_sample(uint32_t base_address, unsigned channel);
/************************************************************************************ /************************************************************************************
@@ -199,11 +199,11 @@ static int read_id_dn(int *id, uint32_t gpio_drive, uint32_t gpio_sense, int adc
/* Yes - Fire up the ADC (it has once control) */ /* Yes - Fire up the ADC (it has once control) */
if (board_adc_init() == OK) { if (board_adc_init(HW_REV_VER_ADC_BASE) == OK) {
/* Read the value */ /* Read the value */
for (unsigned av = 0; av < samples; av++) { for (unsigned av = 0; av < samples; av++) {
dn = board_adc_sample(adc_channel); dn = board_adc_sample(HW_REV_VER_ADC_BASE, adc_channel);
if (dn == 0xffff) { if (dn == 0xffff) {
break; break;
+105 -79
View File
@@ -73,31 +73,33 @@
* Register accessors. * Register accessors.
* For now, no reason not to just use ADC1. * For now, no reason not to just use ADC1.
*/ */
#define REG(_reg) (*(volatile uint32_t *)(STM32_ADC1_BASE + _reg)) #define REG(base, _reg) (*(volatile uint32_t *)((base) + (_reg)))
#define rSR(base) REG((base), STM32_ADC_SR_OFFSET)
#define rCR1(base) REG((base), STM32_ADC_CR1_OFFSET)
#define rCR2(base) REG((base), STM32_ADC_CR2_OFFSET)
#define rSMPR1(base) REG((base), STM32_ADC_SMPR1_OFFSET)
#define rSMPR2(base) REG((base), STM32_ADC_SMPR2_OFFSET)
#define rJOFR1(base) REG((base), STM32_ADC_JOFR1_OFFSET)
#define rJOFR2(base) REG((base), STM32_ADC_JOFR2_OFFSET)
#define rJOFR3(base) REG((base), STM32_ADC_JOFR3_OFFSET)
#define rJOFR4(base) REG((base), STM32_ADC_JOFR4_OFFSET)
#define rHTR(base) REG((base), STM32_ADC_HTR_OFFSET)
#define rLTR(base) REG((base), STM32_ADC_LTR_OFFSET)
#define rSQR1(base) REG((base), STM32_ADC_SQR1_OFFSET)
#define rSQR2(base) REG((base), STM32_ADC_SQR2_OFFSET)
#define rSQR3(base) REG((base), STM32_ADC_SQR3_OFFSET)
#define rJSQR(base) REG((base), STM32_ADC_JSQR_OFFSET)
#define rJDR1(base) REG((base), STM32_ADC_JDR1_OFFSET)
#define rJDR2(base) REG((base), STM32_ADC_JDR2_OFFSET)
#define rJDR3(base) REG((base), STM32_ADC_JDR3_OFFSET)
#define rJDR4(base) REG((base), STM32_ADC_JDR4_OFFSET)
#define rDR(base) REG((base), STM32_ADC_DR_OFFSET)
#define rSR REG(STM32_ADC_SR_OFFSET)
#define rCR1 REG(STM32_ADC_CR1_OFFSET)
#define rCR2 REG(STM32_ADC_CR2_OFFSET)
#define rSMPR1 REG(STM32_ADC_SMPR1_OFFSET)
#define rSMPR2 REG(STM32_ADC_SMPR2_OFFSET)
#define rJOFR1 REG(STM32_ADC_JOFR1_OFFSET)
#define rJOFR2 REG(STM32_ADC_JOFR2_OFFSET)
#define rJOFR3 REG(STM32_ADC_JOFR3_OFFSET)
#define rJOFR4 REG(STM32_ADC_JOFR4_OFFSET)
#define rHTR REG(STM32_ADC_HTR_OFFSET)
#define rLTR REG(STM32_ADC_LTR_OFFSET)
#define rSQR1 REG(STM32_ADC_SQR1_OFFSET)
#define rSQR2 REG(STM32_ADC_SQR2_OFFSET)
#define rSQR3 REG(STM32_ADC_SQR3_OFFSET)
#define rJSQR REG(STM32_ADC_JSQR_OFFSET)
#define rJDR1 REG(STM32_ADC_JDR1_OFFSET)
#define rJDR2 REG(STM32_ADC_JDR2_OFFSET)
#define rJDR3 REG(STM32_ADC_JDR3_OFFSET)
#define rJDR4 REG(STM32_ADC_JDR4_OFFSET)
#define rDR REG(STM32_ADC_DR_OFFSET)
#ifdef STM32_ADC_CCR #ifdef STM32_ADC_CCR
# define rCCR REG(STM32_ADC_CCR_OFFSET) # define rCCR(base) REG((base), STM32_ADC_CCR_OFFSET)
/* Assuming VDC 2.4 - 3.6 */ /* Assuming VDC 2.4 - 3.6 */
@@ -119,7 +121,7 @@
class ADC : public cdev::CDev class ADC : public cdev::CDev
{ {
public: public:
ADC(uint32_t channels); ADC(uint32_t base_address, uint32_t channels);
~ADC(); ~ADC();
virtual int init(); virtual int init();
@@ -138,8 +140,8 @@ private:
perf_counter_t _sample_perf; perf_counter_t _sample_perf;
unsigned _channel_count; unsigned _channel_count;
uint32_t _base_address;
px4_adc_msg_t *_samples; /**< sample buffer */ px4_adc_msg_t *_samples; /**< sample buffer */
orb_advert_t _to_system_power; orb_advert_t _to_system_power;
orb_advert_t _to_adc_report; orb_advert_t _to_adc_report;
@@ -164,10 +166,11 @@ private:
void update_adc_report(hrt_abstime now); void update_adc_report(hrt_abstime now);
}; };
ADC::ADC(uint32_t channels) : ADC::ADC(uint32_t base_address, uint32_t channels) :
CDev(ADC0_DEVICE_PATH), CDev(ADC0_DEVICE_PATH),
_sample_perf(perf_alloc(PC_ELAPSED, "adc_samples")), _sample_perf(perf_alloc(PC_ELAPSED, "adc_samples")),
_channel_count(0), _channel_count(0),
_base_address(base_address),
_samples(nullptr), _samples(nullptr),
_to_system_power(nullptr), _to_system_power(nullptr),
_to_adc_report(nullptr) _to_adc_report(nullptr)
@@ -209,75 +212,98 @@ ADC::~ADC()
} }
} }
int board_adc_init() int board_adc_init(uint32_t base_address)
{ {
static bool once = false; /* Perform ADC init once per ADC */
if (!once) { static uint32_t once[SYSTEM_ADC_COUNT] {};
once = true; uint32_t *free = nullptr;
/* do calibration if supported */ for (uint32_t i = 0; i < SYSTEM_ADC_COUNT; i++) {
#ifdef ADC_CR2_CAL if (once[i] == base_address) {
rCR2 |= ADC_CR2_CAL;
px4_usleep(100);
if (rCR2 & ADC_CR2_CAL) { /* This one was done already */
return -1;
return OK;
} }
/* Use first free slot */
if (free == nullptr && once[i] == 0) {
free = &once[i];
}
}
if (free == nullptr) {
/* ADC misconfigured SYSTEM_ADC_COUNT too small */;
PANIC();
}
*free = base_address;
/* do calibration if supported */
#ifdef ADC_CR2_CAL
rCR2(base_address) |= ADC_CR2_CAL;
px4_usleep(100);
if (rCR2(base_address) & ADC_CR2_CAL) {
return -1;
}
#endif #endif
/* arbitrarily configure all channels for 55 cycle sample time */ /* arbitrarily configure all channels for 55 cycle sample time */
rSMPR1 = 0b00000011011011011011011011011011; rSMPR1(base_address) = 0b00000011011011011011011011011011;
rSMPR2 = 0b00011011011011011011011011011011; rSMPR2(base_address) = 0b00011011011011011011011011011011;
/* XXX for F2/4, might want to select 12-bit mode? */ /* XXX for F2/4, might want to select 12-bit mode? */
rCR1 = 0; rCR1(base_address) = 0;
/* enable the temperature sensor / Vrefint channel if supported*/ /* enable the temperature sensor / Vrefint channel if supported*/
rCR2 = rCR2(base_address) =
#ifdef ADC_CR2_TSVREFE #ifdef ADC_CR2_TSVREFE
/* enable the temperature sensor in CR2 */ /* enable the temperature sensor in CR2 */
ADC_CR2_TSVREFE | ADC_CR2_TSVREFE |
#endif #endif
0; 0;
/* Soc have CCR */ /* Soc have CCR */
#ifdef STM32_ADC_CCR #ifdef STM32_ADC_CCR
# ifdef ADC_CCR_TSVREFE # ifdef ADC_CCR_TSVREFE
/* enable temperature sensor in CCR */ /* enable temperature sensor in CCR */
rCCR = ADC_CCR_TSVREFE | ADC_CCR_ADCPRE_DIV; rCCR(base_address) = ADC_CCR_TSVREFE | ADC_CCR_ADCPRE_DIV;
# else # else
rCCR = ADC_CCR_ADCPRE_DIV; rCCR(base_address) = ADC_CCR_ADCPRE_DIV;
# endif # endif
#endif #endif
/* configure for a single-channel sequence */ /* configure for a single-channel sequence */
rSQR1 = 0; rSQR1(base_address) = 0;
rSQR2 = 0; rSQR2(base_address) = 0;
rSQR3 = 0; /* will be updated with the channel each tick */ rSQR3(base_address) = 0; /* will be updated with the channel each tick */
/* power-cycle the ADC and turn it on */ /* power-cycle the ADC and turn it on */
rCR2 &= ~ADC_CR2_ADON; rCR2(base_address) &= ~ADC_CR2_ADON;
px4_usleep(10); px4_usleep(10);
rCR2 |= ADC_CR2_ADON; rCR2(base_address) |= ADC_CR2_ADON;
px4_usleep(10); px4_usleep(10);
rCR2 |= ADC_CR2_ADON; rCR2(base_address) |= ADC_CR2_ADON;
px4_usleep(10); px4_usleep(10);
/* kick off a sample and wait for it to complete */ /* kick off a sample and wait for it to complete */
hrt_abstime now = hrt_absolute_time(); hrt_abstime now = hrt_absolute_time();
rCR2 |= ADC_CR2_SWSTART; rCR2(base_address) |= ADC_CR2_SWSTART;
while (!(rSR & ADC_SR_EOC)) { while (!(rSR(base_address) & ADC_SR_EOC)) {
/* don't wait for more than 500us, since that means something broke - should reset here if we see this */ /* don't wait for more than 500us, since that means something broke - should reset here if we see this */
if ((hrt_absolute_time() - now) > 500) { if ((hrt_absolute_time() - now) > 500) {
return -1; return -1;
}
} }
} // once }
return OK; return OK;
} }
@@ -285,7 +311,7 @@ int board_adc_init()
int int
ADC::init() ADC::init()
{ {
int rv = board_adc_init(); int rv = board_adc_init(_base_address);
if (rv < 0) { if (rv < 0) {
PX4_DEBUG("sample timeout"); PX4_DEBUG("sample timeout");
@@ -475,22 +501,22 @@ ADC::update_system_power(hrt_abstime now)
#endif // BOARD_ADC_USB_CONNECTED #endif // BOARD_ADC_USB_CONNECTED
} }
uint16_t board_adc_sample(unsigned channel) uint16_t board_adc_sample(uint32_t base_address, unsigned channel)
{ {
/* clear any previous EOC */ /* clear any previous EOC */
if (rSR & ADC_SR_EOC) { if (rSR(base_address) & ADC_SR_EOC) {
rSR &= ~ADC_SR_EOC; rSR(base_address) &= ~ADC_SR_EOC;
} }
/* run a single conversion right now - should take about 60 cycles (a few microseconds) max */ /* run a single conversion right now - should take about 60 cycles (a few microseconds) max */
rSQR3 = channel; rSQR3(base_address) = channel;
rCR2 |= ADC_CR2_SWSTART; rCR2(base_address) |= ADC_CR2_SWSTART;
/* wait for the conversion to complete */ /* wait for the conversion to complete */
hrt_abstime now = hrt_absolute_time(); hrt_abstime now = hrt_absolute_time();
while (!(rSR & ADC_SR_EOC)) { while (!(rSR(base_address) & ADC_SR_EOC)) {
/* don't wait for more than 50us, since that means something broke - should reset here if we see this */ /* don't wait for more than 50us, since that means something broke - should reset here if we see this */
if ((hrt_absolute_time() - now) > 50) { if ((hrt_absolute_time() - now) > 50) {
@@ -499,7 +525,7 @@ uint16_t board_adc_sample(unsigned channel)
} }
/* read the result and clear EOC */ /* read the result and clear EOC */
uint16_t result = rDR; uint16_t result = rDR(base_address);
return result; return result;
} }
@@ -507,7 +533,7 @@ uint16_t
ADC::_sample(unsigned channel) ADC::_sample(unsigned channel)
{ {
perf_begin(_sample_perf); perf_begin(_sample_perf);
uint16_t result = board_adc_sample(channel); uint16_t result = board_adc_sample(_base_address, channel);
if (result == 0xffff) { if (result == 0xffff) {
PX4_ERR("sample timeout"); PX4_ERR("sample timeout");
@@ -563,7 +589,7 @@ adc_main(int argc, char *argv[])
{ {
if (g_adc == nullptr) { if (g_adc == nullptr) {
/* XXX this hardcodes the default channel set for the board in board_config.h - should be configurable */ /* XXX this hardcodes the default channel set for the board in board_config.h - should be configurable */
g_adc = new ADC(ADC_CHANNELS); g_adc = new ADC(SYSTEM_ADC_BASE, ADC_CHANNELS);
if (g_adc == nullptr) { if (g_adc == nullptr) {
errx(1, "couldn't allocate the ADC driver"); errx(1, "couldn't allocate the ADC driver");