Noise on DAC (A14) output Teensy 3.1.

Status
Not open for further replies.

hillig

New member
First off, I apologize if I am posting this in the wrong location as I am new to the forum.

I am planning to use the Teensy 3.1 to modulate a VCO tuning pin. Using the default DAC configuration supplied by the library code I was able to provide fairly coarse modulation. This produces a very clean output. At the hardware level, A14 is connected to a 2-pole LC filter with a corner frequency of about 20kHz which then feeds the VCO tuning pin.

I only need a signal with a DC level up to a few hundred mV and an AC component of around 6mV or so. I thought I would change the DAC reference from DACREF_2 (external AREF at 3.3V) to DACREF_1 which would supply an internally generated 1.2V reference and produce finer DAC step size. This did in fact change the DAC resolution but the odd thing is now there seems to be a significant amount of noise on the DAC output. There are 2 components to the noise. First is a ~6mV sine wave at ~33kHz. The second is a periodic event which appears to be some coupled switching every 3ms. The 6mV noise causes undesirable noise in my spectrum and can't be filtered because it's on the order of what I need to use for modulation. The switching noise could be snubbed with some sort of bandpass filter arrangement but I have not attempted to address this yet. What I am hoping is there is a firmware solution :D.

The code is relatively simple. It creates an interval timer that at the moment just toggles a variable state which CAN be, but is currently not, used to create a high or low key for a binary FSK type modulation. The interval time is the modulation rate. Serial is used to change the DC offset for the DAC output and an LED is toggled when each serial DC value is processed. The reference voltage bit(6) in DAC0_C0 is cleared after the library call to analogWrite() so no changes to library code needed to be made. This might be the source of the trouble. See code below.

Any thoughts would be greatly appreciated.
Thanks, Mark

Code:
#define COUNT_LIM 4096

//Global vars
int inputValue = 0;             // an int to hold incoming data
String inputString = "";        // a string to hold incoming data
boolean valueComplete = false;  // whether the string is complete
int fskState = LOW;

// Create an IntervalTimer object 
IntervalTimer myTimer;

void setup()   
{                
  // Initialize the DAC output pins
  analogWriteResolution(12);

  // Setup the Teensy LED as and output
  pinMode(13, OUTPUT);
  
  //Initialize the USB UART serial at 9600 baud with a RX buffer of 10 chars
  Serial.begin(9600);
  inputString.reserve(10);

// 1/8212.28 = 1.217688632146005737748834671979e-4
//  myTimer.begin(event_timer, 122);  // ~4106 baud
  myTimer.begin(event_timer, 30);     // Trying a higher data rate
}

// Loop forever
void loop()                     
{
  if(Serial.available())
    serialEvent();
    
  if(valueComplete)
  {
    //Toggle the LED
    digitalWrite(13, !digitalRead(13));
       
    // Convert the string to an int
    inputValue = inputString.toInt();

    //Limit the input value to 12 bits
    if(inputValue > COUNT_LIM)
      inputValue = COUNT_LIM;
    
    //Send back the new value for confirmation.
    Serial.println(inputValue);

    //Clear the flag and received string
    valueComplete = false;
    inputString = "";
  }
}

// Handle serial reception
void serialEvent() 
{
    // get the new byte:
    char inChar = (char)Serial.read();
    inputString += inChar;    
    
    //Build the decimal value of the string
    inputValue += (unsigned int)inChar;

    // if the incoming character is a newline, set a flag
    // so the main loop can do something about it:
    if (inChar == '\n') {
      valueComplete = true;
    }
}

// Generates the modulation clock
void event_timer(void) 
{
  if (fskState == LOW) 
  {
    fskState = HIGH;
  } 
  else 
  {
    fskState = LOW;
  }
//  analogWrite(A14, inputValue + (fskState));
  analogWrite(A14, inputValue);
  DAC0_C0 &= 0b10111111;  //uses 1.2V reference for DAC instead of 3.3V
}
 
So in writing the previous post I got a hint as to what was happening. After a little review of the LIB code and rewriting of my code it works much better. in setup() I initialize the DAC as before by setting the resolution but now call the lib function to set the initial DAC output which in turn configures the DAC as the lib code intends. I then clear the DACRFS bit just once. Note that the 3*fskState provides the AC voltage component needed to generate the FSK frequency deviation I am after. This will likely be parametrized at some point.

In setup():
Code:
// Initialize the DAC output pins
  analogWriteResolution(12);
  analogWrite(A14, 0);  //Set the DAC output to 0.
  DAC0_C0 &= 0b10111111;  //uses 1.2V reference for DAC instead of 3.3V

In interval timer ISR:
Code:
//analogWrite(A14, inputValue);
  //DAC0_C0 &= 0b10111111;  //uses 1.2V reference for DAC instead of 3.3V
  *(int16_t *)&(DAC0_DAT0L) = inputValue + (fskState * 3); //Assignment borrowed from lib

And a shot of the final product. Sorry my scope does not have sufficient resolution to show off the tuning waveform but it's pretty boring anyway.
FSK spectrum.jpg
 
Yes, you only want to tell it to use the internal reference once, not each time you output a value. Changing reference triggers a recalibration. I'm not sure though if repeatedly telling it to use the internal reference causes any actual change or is just ignored.

Incidentally, Serial over USB ignores the baud rate; you get 12Mbits/s. The UARTs are on Serial1, Serial2 and Serial3.
 
Status
Not open for further replies.
Back
Top