Serial.write blocking, causing jumps in analog read/write

Hi All,

I’m using the teensy 3.1 arduino board to produce control signals for an external VCO and digitize the resulting input.

Output signal is created in the loop function, incoming signal is recorded asynchronously using the ADC library in an interrupt function, where the data is also written out to serial port.

Everything seems to work ok as long as I’m not trying to read the data on the computer. If I do read the buffer on the computer the whole program stops waiting on the serial function. This is verified both on the recorded data on the computer which has jumps in the data, as well as on the output signal as verified by a scope.

This makes the device unusable for both purposes (signal output and input) as continuous record and transmit is crucial, I’m willing to reduce capture frequency and serial speed in exchange for a continuous signal.

I also tested doing everything in the main loop instead of an interrupt. This cleans up the receive signal which is now continuous, but the output signal is even more broken up.

Any ideas as to whether there is a solution to the problem and if so, what it is?

I attached the sketch.

Thanks

CoffeeCanRadarControler.ino (2.27 KB)

incoming signal is recorded asynchronously using the ADC library in an interrupt function, where the data is also written out to serial port.

I have not looked at your code but if you are doing what you say you are then you will have problems.

Serial.write() depends on interrupts
Interrupts are automatically disabled in an ISR
Hence you cannot use Serial.write() in an ISR

#include <ADC.h>
#include <ADC_Module.h>
#include <RingBuffer.h>
#include <RingBufferDMA.h>

// Simple DAC sine wave test on Teensy 3.1

#define WRITE_RESOLUTION 16
#define READ_RESOLUTION 16

#define RAMP_MAX ((1 << WRITE_RESOLUTION) - 5000)

unsigned int ledPin = 13;

int ramp_half_time = 10000; // Rise / fall time in usec

unsigned long serialRate = 100000 * 16; // Actual number shouldn't make a difference

elapsedMicros usec = 0;
int up = true;

// Output buffer
#define BUFSIZE 32
unsigned short databuf[BUFSIZE];
int bufpos = 3;
unsigned int start = 0;

ADC *adc = new ADC(); // adc object

#define INTERUPT_READ 0

void setup()
{
  pinMode(A14, OUTPUT);
  pinMode(A0, INPUT);
  pinMode(ledPin, OUTPUT);     

  analogWriteResolution(WRITE_RESOLUTION);

  adc->setConversionSpeed(ADC_MED_SPEED, ADC_0);
  adc->setSamplingSpeed(ADC_MED_SPEED, ADC_0);
  adc->setResolution(READ_RESOLUTION, ADC_0);
  adc->setAveraging(2, ADC_0);
  adc->startContinuous(A0, ADC_0);

#if INTERUPT_READ
  // If you enable interrupts, notice that the isr will read the result, so that isComplete() will return false (most of the time)
  adc->enableInterrupts(ADC_0); // enable interrupts BEFORE calling a measurement method
#endif

  databuf[0] = 0;
  databuf[1] = 0;

  Serial.begin(serialRate);

  digitalWrite(ledPin, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);               // wait for a second
  digitalWrite(ledPin, LOW);    // turn the LED off by making the voltage LOW
  delay(1000);               // wait for a second
  digitalWrite(ledPin, HIGH);   // turn the LED on (HIGH is the voltage level)
}

void loop()
{
#if !INTERUPT_READ
  if (adc->isComplete(ADC_0))
    ReadVal();
#endif

  if (usec >= ramp_half_time)
  {
    usec = 0;
    up = !up;
  }

  int val;

  if (up)
    val = round(float(usec) / ramp_half_time * RAMP_MAX);
  else
    val = round(float(ramp_half_time - float(usec)) / ramp_half_time * RAMP_MAX);

  analogWrite(A14, val);
}

void adc0_isr(void) {
  ReadVal();
}

void ReadVal()
{
  databuf[bufpos++] = adc->analogReadContinuous(ADC_0);

  if (bufpos >= BUFSIZE)
  {
    unsigned int stop = micros();
    databuf[2] = (unsigned short)(stop - start);
    start = stop;

    Serial.write((unsigned char *)databuf, BUFSIZE * sizeof(unsigned short));
    
    bufpos = 3;
  }
}

Thanks,

I moved the analogWrite and analogReadContinuous into interrupts with a ring buffer and moved the Serial.write into the loop and now I can get about 100KHz sampling rate and 80KHz transmit rate. It seems a bit wrong in terms of the sampling method but I guess it would take bypassing most of the arduino platform code to do better.

The read still doesn't seem to be completely stable in terms of quality but I'll have to look into that.