Coder Social home page Coder Social logo

arduino_advancedanalog's Introduction

〰️ Arduino AdvancedAnalog Library

The Arduino AdvancedAnalog library is designed to offer high performance DAC/ADC applications on boards based on the STM32H7 microcontroller.

📖 For more information about this library please read the documentation here.

arduino_advancedanalog's People

Contributors

aentinger avatar dependabot[bot] avatar facchinm avatar iabdalkader avatar jmdodd95682 avatar karlsoderby avatar leonardocavagnis avatar per1234 avatar tfry-git avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

arduino_advancedanalog's Issues

Doc missing for AdvancedADCDual

Before releasing the new version (1.4.0):

  • Update api.md with information on the new begin() and AdvancedADCDual.
  • Update docs/readme.md with an explanation and example for AdvancedADCDual.

Number of ADC channels arbitrarily limited

These lines of code:

ADC_REGULAR_RANK_1, ADC_REGULAR_RANK_2, ADC_REGULAR_RANK_3, ADC_REGULAR_RANK_4, ADC_REGULAR_RANK_5

and

#define AN_MAX_ADC_CHANNELS (5)

(also referenced, here:

"A maximum of 5 channels can be sampled successively.");
)

limit multi-channel sampling to a sequence of max length 5. The STM32H7 actually supports 16 channels (accordingly #defines up to ADC_REGULAR_RANK_16 exist), and arguably the feature is most useful when more than a handful of pins are involved.

Now, I am not quite sure what is the long term plan for cross-hardware support in this library. Depending on that, the proper fix will either be a plain adjustment of the values, or a small mess of #ifdefs.

DMA DAC intialization is unstable

While trying to port the Mozzi (Sound Synthesis) library to the Arduino Giga, I notice that DMA driven DAC seems to be quite fragile to set up, in some cases the DAC will simply fail to start.

Testcase: I can trip up the DAC_Sine_wave example by as little as uncommenting the Serial.begin() line. Now the very same sketch will work some but not all of the time (try resetting a couple of times).

After lots of trial and error, I think I have pinned this down to the double buffered dma setup around here:

hal_dma_enable_dbm(&descr->dma, descr->dmabuf[0]->data(), descr->dmabuf[1]->data());

Quite obviously, first starting the DAC, then pausing it to enable double buffering, then restarting it seems like a hack (if justified) in the first place.

I managed to arrive at a robust sketch, by essentially copying the HAL_DAC_Start_DMA implementation from the HAL, but modifying it to set up double buffering right away (settings the correct flags, addresses, and starting DMA with HAL_DMAEx_MultiBufferStart_IT). Of course that makes for a very verbose solution. License-wise, I think it should be ok, as the HAL library appears to be BSD'ed.

Would the above make for an acceptable PR, do you have another idea on attacking the problem, or am I the only one to experience it?

DAC maximum Voltage is low (0.33V)

Hello,

Using the Arduino Giga R1 WiFi and the pin DAC0, I tried to generate an analog signal but the maximum Voltage of this signal is ~330 mV. I used the function AdvancedDAC.write() and a sample code provided in this link and set the value as the maximum of the resolution(for example 255 for 8-bit resolution):

link from the arduino tutorial

Is it possible to have an analog output of 3.3V from the Arduino Giga R1 WiFi?

Giga R1 Wifi Sample Rate Slower than expected

I'm new to Giga R1, so I've been trying out a few simple things. I'm trying to sample live audio via ADC.
I'm using the Arduino_AdvancedAnalog library. I'm seeing that the sample rate is about 4 times slower than I would expect.
I have an external strobe connected to a digital pin so I can measure the rate on a logic analyzer (roughly).

I programmed a single ADC:

adc1.begin(AN_RESOLUTION_16, 32000, 1, 32)

Which I THINK means 16-bit resolution, 32KHz sample rate, 1 sample per channel, 32-deep FIFO.

Then I loop on reading this as fast as I can. Obviously I'm outstripping the FIFO and exposing the underlying sample rate. The adc1.read() is blocking, waiting for the DMA interrupt most of the time. What I see is that the adc sample is actually only being sampled twice every 250us. Two quick samples, and then 250us waiting, and two more samples...etc. So, kind of like
8KHz sampling. Not sure I understand what's going on here.

I tried different resolutions, different FIFO depths and the number of samples per channel. Nothing seems to budge this off this behavior (except moving the samples per channel to 16...which increases the period to 500us).

If I increase the sample rate to 48000, this seems to increase the sample rate as expected by 33%, but it is still slow by a factor or 4x.
Is this expected? I've pasted my code below. If I'm doing something stupid (always a possibility) I would appreciate being told what
it is. Thanks in advance.

#include <Arduino_AdvancedAnalog.h>

#define OUTPUT_PIN 3

AdvancedADC adc1(A0);
uint64_t last_millis = 0;

uint16_t data_Buffer[1024];
uint16_t data_ptr = 0;

void setup() {
  Serial.begin(9600);

  // Resolution, sample rate, number of samples per channel, queue depth.
  if (!adc1.begin(AN_RESOLUTION_16, 32000, 1, 32)) {
    Serial.println("Failed to start analog acquisition!");
    while (1)
      ;
  }

  pinMode(OUTPUT_PIN, OUTPUT);
  delay(2000);
}

void loop() {
  data_ptr = 0;
  while (data_ptr < 1024) {
    digitalWrite(OUTPUT_PIN, 1);
    SampleBuffer buf = adc1.read();
    digitalWrite(OUTPUT_PIN, 0);
    data_Buffer[data_ptr] = buf[0];
    buf.release();
    data_ptr++;
  }
  for (uint16_t i = 0; i < 1024; i++) {
    Serial.println(data_Buffer[i]);
  }
  while (true) {
    digitalWrite(OUTPUT_PIN, 1);
  }
}

DAC Output Hangs With No Available DMA Buffer

I am trying to take an analog signal in, convert to digital using ADC, and then send it out through the DAC. This is on a Arduino Giga R1.
I have the ADC setup with 16b resolution, 96KSamp/sec, and 1024-samples per buffer, and a 8-deep queue of buffers.
On the DAC size I have 12b resolution, 96KSamp/sec, 1024-samples per buffer and a 8-deep queue of buffers.
I know its hanging on the DAC output because I already validated that the ADC is working correctly with no hangs. Also, I added an output pin (D3) which toggles during each ADC/DAC pass. It goes high on the ADC, and low after the DAC. After example N+1 loops, where N is the queue-depth, it will hang on the N+2 loop with the D3 pin high, meaning it is not able to get an available DAC buffer.
I've written a minimal code example that shows the hang. I'm trying to figure out what I'm doing wrong here. This is a pretty basic operation. I think, somehow, the AdvancedDAC.write() operation is not freeing the dma buffer, so it runs out of buffers. I am using A2 for ADC, and A13 (DAC1) for DAC. It could be something obvious I've missed. Any help would be greatly appreciated.

#include <Arduino_AdvancedAnalog.h>
#include <Arduino.h>

AdvancedADC adc_input(A2);
AdvancedDAC dac_output(A13);

#define DBG_OUTPUT_PIN 3 //Digital Debug Output Pin
#define LED_OFF 1
#define LED_ON 0

#define DEBUG 0

const uint32_t SAMPLE_RATE=96000;
const uint32_t BUFFER_SIZE=1024;
const uint32_t SAMPLE_PERIOD=(uint32_t)((1.0/96000.0)*1000000.0);

int16_t resolution16b=4; //4=16,3=14,2=12,1=10,8=0
int16_t resolution12b=2;

int32_t io_buffer[1024];

void setup() 
{ 
  Serial.begin(9600);
  delay(1000);
  
  pinMode(DBG_OUTPUT_PIN,OUTPUT);
  digitalWrite(DBG_OUTPUT_PIN,0);
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN,LED_ON);
  int32_t result=adc_input.begin(resolution16b, SAMPLE_RATE, BUFFER_SIZE, 8); //16b res, 96KS/s,1024 samples,4-deep queue
  result=dac_output.begin(resolution12b, SAMPLE_RATE, BUFFER_SIZE, 8);//12b res, 96KS/s,1024 samples,4-deep queue
}

void loop()
{
  if(adc_input.available())
  {
    digitalWrite(DBG_OUTPUT_PIN,1);
    SampleBuffer buf = adc_input.read();
    for(uint16_t i=0;i<BUFFER_SIZE;i++)
    {
        io_buffer[i]=(int32_t)buf[i]>>4;//Reducing precision to 12-bits
    }
    buf.release();

    if (dac_output.available()) 
    {
        SampleBuffer bufout = dac_output.dequeue();
        
        for(uint16_t i=0;i<BUFFER_SIZE;i++)
        {
            bufout[i]=(uint16_t)io_buffer[i];
        }
        dac_output.write(bufout);
        digitalWrite(3,0);
    }
  }
}

Added dual-mode support for ADC.

After reading the 3500-page reference manual (nicely written but so much detail...so much...) for the STM32H7xx
I found how to enable the dual-mode.

I modified the begin() and start() routines and added a few new members to the AdvancedADC class to allow using dual-mode.
This gives the optimal synchronization between two sampled inputs. I'm seeing between 0 and 1us skew, which is pretty good for a 1M samp/sec configuration.

It does have some restrictions. You need to always use ADC1 and 2 (dual mode is only supported in HW for ADC 1 and 2). Also, you need to configure the AdvancedADC instance to explicitly selected dual mode. I tried to keep it as general purpose as possible.

I would be happy to share my code if you're interested. I can do a pull_request.

Programmable Sample Time parameter for faster sample rates

In HALConfig.cpp, the hal_adc_config() routine currently has the Sample Time (sConfig.SamplingTime) hard coded to a value of
ADC_SAMPLETIME_8CYCLES_5 (8.5 clocks).

According to the datasheet, a value of ADC_SAMPLETIME_2CYCLES_5 (2.5 clocks) is legal. This increases the maximum sample rate from 1.8 MS/s to 2.9 MS/s.

Due to concerns about operating conditions (temperature, voltage etc), it would NOT be a good idea to simply change the hard-coded value from 8.5 clocks to 2.5 clocks. Instead, a new defaulted argument can be added to hal_adc_config() which can be set to SAMPLE_SLOW or SAMPLE_FAST to enable switching between 8.5 clocks and 2.5 clocks as the user prefers based on their operating conditions and needs. The default would be SAMPLE_SLOW to retain backward compatibility.

Adding Interleaved Sampling for 2X Sample Rate improvement

Looking through the Reference Manual for the STM32H7xx I see there is another Dual mode called Interleaved. It seems likely that by putting an input signal into two Analog Input pins (e.g. A0 and A1) and enabling Dual interleaved mode it will be possible to sample at twice the current maximum rate. So, instead of say 2.9MS/sec we can reach 5.8MS/sec at 16b resolution.

Also, one can also enable a single DMA to handle this dual interleaved data so that all the captured data needs to re-arranging once retrieved from the DMA buffers.

I'm going to try some experiments and see if it works.

ADC read speed

First, great job with this library. I wouldn't necessarily call my post an issue, but since you say the ADC is running the "slowest clock", I have to ask.

I'm using your library with an Arduino Portenta H7 lite and reading two ADC channels simultaneously at 100,000 sample pairs/sec, 16-bit resolution, with what appears to be high accuracy. Here's the setup:
adc.begin(AN_RESOLUTION_16, 400000, 4, 128)

That's as fast as I need for sure, but I'm intrigued with the idea of faster based on your "slowest clock" comment. So, how much faster could you make it?

Also, could you shed some light on how the parameter queue depth affects the speed/accuracy? (I found this which answers an earlier question I had about the sample per channel parameter: https://www.st.com/resource/en/application_note/an5354-getting-started-with-the-stm32h7-series-mcu-16bit-adc-stmicroelectronics.pdf).

Sampling with very high frequencies did not work as expected.

First of all, thank you for this great library.

Sampling with very high frequencies did not work as expected.

According to the documentation for the STM32H7, sampling rates of > 2 MHz should be possible. I have read that you are working with a slow timer.

In my case I need at least 4 ADC channels with a sampling rate of at least 8bit / 600'000Hz each. The problem is that I should work with ultrasonic receivers that receive at a frequency of 200KHz. In order to analyze the received data correctly, the triple sampling rate is the lower limit.

So far I have managed to sample at 70KHz per channel, distributed over two ADCs with 2 channels each. As soon as I go higher (80KHz) the program no longer starts.

Is this due to this "slow timer" or are my requirements too high?
It's been about 30 years since I wrote anything in C++ or assembler, but with a few hints I might be able to adapt the code myself.

Synchronized ADC Capture

AdvancedADC gives you two options for capturing two analog signals simultaneously. The first is to bind both analog input pins to the same AdvancedADC instance. This works great, but sadly, means your sample rate is cut in half. The sample_rate provided applies to the sampling of both inputs. So a 1M Samp./sec rate really means each is sampled at 500K Samp./sec. If you trying to push the limits of sample rate, this is not a great solution.

The second way is to bind each analog input to a different AdvancedADC. The library will automatically assign the two pins to two different ADC blocks. The sample rate of each will be maximum. Very nice. However, this creates another problem. The begin() routine takes approximately 100 us on GIGA R1 to initialize and start the ADC capture. So if I say something like:

adc_input[0].load(res, sample_rate, num_samples, QUEUE_DEPTH,1,&(probes[0]));
adc_input[1].load(res, sample_rate, num_samples, QUEUE_DEPTH,1,&(probes[1])));

What happens is that the second ADC input capture is delayed by about 100us. So the two signals are not synchronized. If you're trying to capture two signals with reasonable synchronization, that is not going to work. I analyzed the timing of each line of code in the begin() routine and found that two lines are causing almost all the delay:

descr->pool = new DMABufferPool<Sample>(n_samples, n_channels, n_buffers);

and

if (hal_adc_config(&descr->adc, ADC_RES_LUT[resolution], descr->tim_trig, adc_pins, n_channels) < 0) {
return 0;
}

I have a solution. I created two new routines in AdvancedADC.cpp I call load() and fire().
load() is identical to the begin() routine up to a point, including the long-delay lines above. It then returns. Essentially load() allocates the DMA and ADC etc, but does not start the capture. The fire() routine (which only takes about 4 us) does the rest of
what was in begin() and starts the capture. This is working and providing a synchronization error of about 4-6 us. Not bad.

This is probably NOT the solution that someone else would prefer, but it works for me. Now I have full sample-rate and okay synchronization between two or three analog inputs.

I guess I would say the issue I have is that begin() takes too long (106 us!). Either this needs to be shortened significantly or the idea of the load() and fire() would also work.

A0 and A1 not working in M4?

When I run Arduino_AdvancedAnalog/examples/Advanced/ADC_Multi_Channel/ADC_Multi_Channel.ino in the M4 co-processor of my Portenta H7, it only works for channels A4 to A7. According to the documentation ADC1 should also work with A0 and A1. Why are A0 and A1 not working? They do work in the main M7 core.

I have used these codes:
M7:

void setup() {
  bootM4();
  Serial.begin(9600);
}
void loop() {
  Serial.println("M7");
  digitalWrite(LEDG, LOW);
  delay(100);
  digitalWrite(LEDG, HIGH);
  delay(2000);
}

M4:

#include <Arduino_AdvancedAnalog.h>

AdvancedADC adc(A0); // not working with A0 or A1. It does work with A4 to A7
uint64_t last_millis = 0;

void setup() {
  Serial1.begin(9600);

  // Resolution, sample rate, number of samples per channel, queue depth.
  if (!adc.begin(AN_RESOLUTION_16, 16000, 32, 128)) {
      Serial1.println("Failed to start analog acquisition!");
      while (1);
  }
}

void loop() {
  if (adc.available()) {
      SampleBuffer buf = adc.read();
      // Process the buffer.
      if ((millis() - last_millis) > 20) {
        Serial1.println(buf[0]);   // Sample from first channel
        Serial1.println(buf[1]);   // Sample from second channel
        last_millis = millis();
      }
      // Release the buffer to return it to the pool.
      buf.release();
  }
}

Programming >2M Samples/sec on DAC seems to cause Giga board to hang

I've been able to reproduce this a couple of times. If I take the example program for generating a sinewave and change the frequency to 48000*lut_size (about 3M samples per second), the program compiles and uploads with no error. About two seconds later the board disconnects from the USB serial port and is no longer recognizable by my PC. I have to double-tap the reset and reload a program in order to get out of this. I see no "SOS" pattern on the LED. Just silence.

What is the maximum samples per second for the DAC outputs? Obviously for audio 2M samples per second is plenty. I was just wondering. If there is a hard limit, it might be good to either put a check in the code for values > 2M or at least something in the documentations saying "Don't do that".

I'm including the failing code below, but its just the sinewave example with the frequency increased to 48000*lut_size.

// This example outputs a 32KHz sine wave on A12/DAC1.
#include <Arduino_AdvancedAnalog.h>

AdvancedDAC dac1(A13);

uint16_t lut[] = {
    0x0800,0x08c8,0x098f,0x0a52,0x0b0f,0x0bc5,0x0c71,0x0d12,0x0da7,0x0e2e,0x0ea6,0x0f0d,0x0f63,0x0fa7,0x0fd8,0x0ff5,
    0x0fff,0x0ff5,0x0fd8,0x0fa7,0x0f63,0x0f0d,0x0ea6,0x0e2e,0x0da7,0x0d12,0x0c71,0x0bc5,0x0b0f,0x0a52,0x098f,0x08c8,
    0x0800,0x0737,0x0670,0x05ad,0x04f0,0x043a,0x038e,0x02ed,0x0258,0x01d1,0x0159,0x00f2,0x009c,0x0058,0x0027,0x000a,
    0x0000,0x000a,0x0027,0x0058,0x009c,0x00f2,0x0159,0x01d1,0x0258,0x02ed,0x038e,0x043a,0x04f0,0x05ad,0x0670,0x0737
};

static size_t lut_size = sizeof(lut) / sizeof(lut[0]);

void setup() {
    Serial.begin(9600);
    if (!dac1.begin(AN_RESOLUTION_12, 48000 * lut_size, 64, 128)) {
        Serial.println("Failed to start DAC1 !");
        while (1);
    }
}

void loop() {
    static size_t lut_offs = 0;
    if (dac1.available()) {
        // Get a free buffer for writing.
        SampleBuffer buf = dac1.dequeue();
        // Write data to buffer.
        for (size_t i=0; i<buf.size(); i++, lut_offs++) {
            buf[i] =  lut[lut_offs % lut_size];
        }
        // Write the buffer to DAC.
        dac1.write(buf);
    }
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.