diff --git a/examples/Advanced/ADC_Dual_Mode/ADC_Dual_Mode.ino b/examples/Advanced/ADC_Dual_Mode/ADC_Dual_Mode.ino new file mode 100644 index 0000000..433285c --- /dev/null +++ b/examples/Advanced/ADC_Dual_Mode/ADC_Dual_Mode.ino @@ -0,0 +1,42 @@ +#include + +AdvancedADC adc1(A0, A1); +AdvancedADC adc2(A2, A3); +AdvancedADCDual adc_dual(adc1, adc2); +uint64_t last_millis = 0; + +void setup() { + Serial.begin(9600); + while (!Serial) { + } + + // Resolution, sample rate, number of samples per channel, queue depth. + if (!adc_dual.begin(AN_RESOLUTION_16, 16000, 32, 32)) { + Serial.println("Failed to start analog acquisition!"); + while (1); + } +} + +void loop() { + if (adc1.available()) { + SampleBuffer buf1 = adc1.read(); + SampleBuffer buf2 = adc2.read(); + + // Process the buffer. + if (millis() - last_millis > 1) { + Serial.println(buf1.timestamp()); // Print buffer timestamp + Serial.println(buf1[0]); // Print sample from first channel + Serial.println(buf1[1]); // Print sample from second channel + + Serial.println(buf2.timestamp()); // Print buffer timestamp + Serial.println(buf2[0]); // Print sample from first channel + Serial.println(buf2[1]); // Print sample from second channel + + last_millis = millis(); + } + + // Release the buffer to return it to the pool. + buf1.release(); + buf2.release(); + } +} diff --git a/src/AdvancedADC.cpp b/src/AdvancedADC.cpp index 49f7e23..5e68273 100644 --- a/src/AdvancedADC.cpp +++ b/src/AdvancedADC.cpp @@ -97,6 +97,20 @@ static void dac_descr_deinit(adc_descr_t *descr, bool dealloc_pool) { } } +int AdvancedADC::id() { + if (descr) { + ADC_TypeDef *adc = descr->adc.Instance; + if (adc == ADC1) { + return 1; + } else if (adc == ADC2) { + return 2; + } else if (adc == ADC3) { + return 3; + } + } + return -1; +} + bool AdvancedADC::available() { if (descr != nullptr) { return descr->pool->readable(); @@ -115,9 +129,9 @@ DMABuffer &AdvancedADC::read() { return NULLBUF; } -int AdvancedADC::begin(uint32_t resolution, uint32_t sample_rate, size_t n_samples, size_t n_buffers) { +int AdvancedADC::begin(uint32_t resolution, uint32_t sample_rate, size_t n_samples, size_t n_buffers, bool start) { + ADCName instance = ADC_NP; - // Sanity checks. if (resolution >= AN_ARRAY_SIZE(ADC_RES_LUT) || (descr && descr->pool)) { return 0; @@ -159,6 +173,7 @@ int AdvancedADC::begin(uint32_t resolution, uint32_t sample_rate, size_t n_sampl // Configure ADC pins. pinmap_pinout(adc_pins[0], PinMap_ADC); + uint8_t ch_init = 1; for (size_t i=1; ipool == nullptr) { return 0; } + + // Allocate the two DMA buffers used for double buffering. descr->dmabuf[0] = descr->pool->allocate(); descr->dmabuf[1] = descr->pool->allocate(); @@ -212,27 +229,89 @@ int AdvancedADC::begin(uint32_t resolution, uint32_t sample_rate, size_t n_sampl hal_dma_enable_dbm(&descr->dma, descr->dmabuf[0]->data(), descr->dmabuf[1]->data()); HAL_NVIC_EnableIRQ(descr->dma_irqn); - // Init, config and start the ADC timer. + if (start) { + return this->start(sample_rate); + } + + return 1; +} + +int AdvancedADC::start(uint32_t sample_rate){ + // Initialize and configure the ADC timer. hal_tim_config(&descr->tim, sample_rate); + + // Start the ADC timer. Note, if dual ADC mode is enabled, + // this will also start ADC2. if (HAL_TIM_Base_Start(&descr->tim) != HAL_OK) { return 0; } - + return 1; } -int AdvancedADC::stop() -{ +int AdvancedADC::stop() { dac_descr_deinit(descr, true); return 1; } -AdvancedADC::~AdvancedADC() -{ +void AdvancedADC::clear() { + if (descr && descr->pool) { + descr->pool->flush(); + } +} + +size_t AdvancedADC::channels() { + return n_channels; +} + +AdvancedADC::~AdvancedADC() { dac_descr_deinit(descr, true); } +int AdvancedADCDual::begin(uint32_t resolution, uint32_t sample_rate, size_t n_samples, size_t n_buffers) { + // The two ADCs must have the same number of channels. + if (adc1.channels() != adc2.channels()) { + return 0; + } + + // Configure the ADCs. + if (!adc1.begin(resolution, sample_rate, n_samples, n_buffers, false)) { + return 0; + } + + if (!adc2.begin(resolution, sample_rate, n_samples, n_buffers, false)) { + adc1.stop(); + return 0; + } + + // Note only ADC1 (master) and ADC2 can be used in dual mode. + if (adc1.id() != 1 || adc2.id() != 2) { + adc1.stop(); + adc2.stop(); + return 0; + } + + // Enable dual ADC mode. + hal_adc_enable_dual_mode(true); + + // Start ADC1, note ADC2 is also automatically started. + return adc1.start(sample_rate); +} + +int AdvancedADCDual:: stop() { + adc1.stop(); + adc2.stop(); + // Disable dual mode. + hal_adc_enable_dual_mode(false); + return 1; +} + +AdvancedADCDual::~AdvancedADCDual() { + stop(); +} + extern "C" { + void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *adc) { adc_descr_t *descr = adc_descr_get(adc->Instance); // NOTE: CT bit is inverted, to get the DMA buffer that's Not currently in use. diff --git a/src/AdvancedADC.h b/src/AdvancedADC.h index 994687c..498b9eb 100644 --- a/src/AdvancedADC.h +++ b/src/AdvancedADC.h @@ -45,20 +45,41 @@ class AdvancedADC { AdvancedADC(): n_channels(0), descr(nullptr) { } ~AdvancedADC(); + int id(); bool available(); SampleBuffer read(); - int begin(uint32_t resolution, uint32_t sample_rate, size_t n_samples, size_t n_buffers); - int begin(uint32_t resolution, uint32_t sample_rate, size_t n_samples, size_t n_buffers, - size_t n_pins, pin_size_t *pins) { + int begin(uint32_t resolution, uint32_t sample_rate, size_t n_samples, + size_t n_buffers, bool start=true); + int begin(uint32_t resolution, uint32_t sample_rate, size_t n_samples, + size_t n_buffers, size_t n_pins, pin_size_t *pins, bool start=true) { if (n_pins > AN_MAX_ADC_CHANNELS) { n_pins = AN_MAX_ADC_CHANNELS; } for (size_t i = 0; i < n_pins; ++i) { adc_pins[i] = analogPinToPinName(pins[i]); } + n_channels = n_pins; - return begin(resolution, sample_rate, n_samples, n_buffers); + return begin(resolution, sample_rate, n_samples, n_buffers, start); } + int start(uint32_t sample_rate); + int stop(); + void clear(); + size_t channels(); +}; + +class AdvancedADCDual { + private: + AdvancedADC &adc1; + AdvancedADC &adc2; + size_t n_channels; + + public: + AdvancedADCDual(AdvancedADC &adc1_in, AdvancedADC &adc2_in): + n_channels(0), adc1(adc1_in), adc2(adc2_in) { + } + ~AdvancedADCDual(); + int begin(uint32_t resolution, uint32_t sample_rate, size_t n_samples, size_t n_buffers); int stop(); }; diff --git a/src/HALConfig.cpp b/src/HALConfig.cpp index f62e1be..eb17c86 100644 --- a/src/HALConfig.cpp +++ b/src/HALConfig.cpp @@ -223,6 +223,15 @@ int hal_adc_config(ADC_HandleTypeDef *adc, uint32_t resolution, uint32_t trigger return 0; } +int hal_adc_enable_dual_mode(bool enable) { + if (enable) { + LL_ADC_SetMultimode(__LL_ADC_COMMON_INSTANCE(ADC1), LL_ADC_MULTI_DUAL_REG_SIMULT); + } else { + LL_ADC_SetMultimode(__LL_ADC_COMMON_INSTANCE(ADC1), LL_ADC_MULTI_INDEPENDENT); + } + return 0; +} + int hal_i2s_config(I2S_HandleTypeDef *i2s, uint32_t sample_rate, uint32_t mode, bool mck_enable) { // Set I2S clock source. RCC_PeriphCLKInitTypeDef pclk_init = {0}; diff --git a/src/HALConfig.h b/src/HALConfig.h index 7ab6711..a7cc540 100644 --- a/src/HALConfig.h +++ b/src/HALConfig.h @@ -30,6 +30,7 @@ void hal_dma_enable_dbm(DMA_HandleTypeDef *dma, void *m0 = nullptr, void *m1 = n void hal_dma_update_memory(DMA_HandleTypeDef *dma, void *addr); int hal_dac_config(DAC_HandleTypeDef *dac, uint32_t channel, uint32_t trigger); int hal_adc_config(ADC_HandleTypeDef *adc, uint32_t resolution, uint32_t trigger, PinName *adc_pins, uint32_t n_channels); +int hal_adc_enable_dual_mode(bool enable); int hal_i2s_config(I2S_HandleTypeDef *i2s, uint32_t sample_rate, uint32_t mode, bool mck_enable); #endif // __HAL_CONFIG_H__