arduino-libraries / Arduino_AdvancedAnalog

Advanced Analog Library
GNU Lesser General Public License v2.1
7 stars 5 forks source link

DAC Output Hangs With No Available DMA Buffer #40

Closed jmdodd95682 closed 1 year ago

jmdodd95682 commented 1 year ago

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);
    }
  }
}
jmdodd95682 commented 1 year ago

I found a fix for this problem. If I add "buffout.release();" after the "dac_output.write(bufout);" it seems to work. It at least does not hang. I haven't checked the output yet. None of the examples for this library seem to require this "release()" after a "write", so maybe I've had some other issue with my code?

jmdodd95682 commented 1 year ago

Turns out buffout.release() after write does not really fix the problem. It just masks the fact that the DAC itself has stopped. Looking into it, the hang usually occurs right at the start. It begins sending out the first buffer and stops about half-way through. Of course the program continues to push new buffers into the queue until it fills. The bufout.release() was simply throwing away entries in the queue to make room for new ones. The underlying problem is that the DAC hangs.

This DAC hang is intermittent. If you hit reset enough times it will eventually work, and continue working. I've tested it for hours and there is no subsequent hang if you get past the first buffer.

I've tried various hacks to see if I can work around it with no luck:

I think there is a fundamental problem with how the MBED OS interacts with the DMA engine and DAC which is causing some problem, but I don't have enough visibility to tell what it is.

jmdodd95682 commented 1 year ago

By the way, I also verified this failure in the base example code "DAC Sinewave". It will run usually, but will hang on occasion with the DAC output low. And sometimes it fails with the same buffer being repeated forever, even though the queue is full. This is hard to see since the buffer is one full period of a sinewave, and therefore every buffer looks the same. But with some debug hooks you can see the queue fills and blocks forever and yet the output continues. I call this DAC Zombie failure.

jmdodd95682 commented 1 year ago

The latest repo for the library appears to fix all of the strange behaviors I was seeing with the DAC. I've tested it a number of times, and there are no hangs or DAC Zombie failures. I think my issue is resolved.