Skip to content

Add support for dual ADC mode. #65

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions examples/Advanced/ADC_Dual_Mode/ADC_Dual_Mode.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#include <Arduino_AdvancedAnalog.h>

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();
}
}
95 changes: 87 additions & 8 deletions src/AdvancedADC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -115,9 +129,9 @@ DMABuffer<Sample> &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;
Expand Down Expand Up @@ -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; i<n_channels; i++) {
for (size_t j=0; j<AN_ARRAY_SIZE(adc_pin_alt); j++) {
Expand Down Expand Up @@ -188,6 +203,8 @@ int AdvancedADC::begin(uint32_t resolution, uint32_t sample_rate, size_t n_sampl
if (descr->pool == nullptr) {
return 0;
}

// Allocate the two DMA buffers used for double buffering.
descr->dmabuf[0] = descr->pool->allocate();
descr->dmabuf[1] = descr->pool->allocate();

Expand All @@ -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.
Expand Down
29 changes: 25 additions & 4 deletions src/AdvancedADC.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();
};

Expand Down
9 changes: 9 additions & 0 deletions src/HALConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
1 change: 1 addition & 0 deletions src/HALConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -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__
Loading