sample rates + Audio range waveforms from Teensy 3.1 DAC

Status
Not open for further replies.

doozle

New member
Hi I'm new to the Teensy and hardware in general. I'm trying synthesize simple waveforms from the Teensy 3.1 DAC pin. My constraints are pretty simple: audio range output with standard/common sample rates(44.1kHz)

For the moment, I'm only interested in sinewaves. I can create them without a problem, but I can't get the Teensy to output at the expected frequency. I think the problem is that the Teensy has trouble with the 44100 Hz sample rate. In my code below, I'm trying to output a 2000Hz sinewave, but when analyzed through an oscope, the frequency is measured at something around 909Hz; less than half the frequency.

Please see my code below:
each iteration of loop() outputs a sample. And my sample rate is determined by the value of an elaspedMicros object, the code waits until enough time has time has passed(22 microseconds for a 44.1kHz sample rate)

Code:
float phase = 0.0;
float twopi = 3.14159 * 2;
float freq  = 2000;           // output frequency, in Hz
float sr    = 44100;          // sample rate
float increment;              // To be added to phase during each iteration of loop
int srDelay;                    // time delay(in microseconds) between samples.  Determines sample rate
elapsedMicros usec = 0;    // type auto increases as time elapses.

// Calcuate increment for a given output frequency, and sample rate
float updateIncrement(float frequency, float sampleRate) {
  return twopi/(sampleRate/frequency);
}

// Calculate time delay between samples
float updateSRDelay(int sampleRate) { return (1.0/sr)*1000000.0; }

void setup() {
   analogWriteResolution(12);
   increment = updateIncrement(freq, sr);
   srDelay = updateSRDelay(sr);
}

void loop() {
  float val = sin(phase) * 2000.0 + 2047.0;    // maps sin() output range[-1,1] to unsigned 12bit range [0,4095]
  analogWrite(A14, (int)val);                       // output sample

  phase += increment;
  if (phase >= twopi)     // full cycle completed
    phase = 0;              // reset

  while (usec < srDelay);    // wait inbetween samples
  usec = usec - srDelay; 
}

I experimented by replacing the sinewave output code with simple alternating high voltage, and low voltage values(a square wave), and when analyzed, I successfully measured a consistent and expected sample rate (each sample lasted about 22 microseconds). Yet when outputting the sinewave, it seems as though each sample last about twice that length(about 50 microseconds per sample), and some samples last longer than others as well.

My questions:
Does anybody know if the use of an elaspedMicros object is the right way to manage a sample rate (I got this technique from an example on the pjrc.com Teensy 3.1 page)?

Is it possible that a software/memory buffer would help?

High frequencies also produce a a lot of stepping(the voltage gaps between samples) in the output. Does anybody know the right way to smooth this out?


Thanks!
 
Last edited:
I am aware of it, however right now I'm mostly interested in finding the right technique to do what I explained above. I'm sure the library is useful, and contains most of the answers I'm looking for, but it looks like a lot of code to read through. If there isn't a simpler way to achieve this, let me know, and I will read through through the library code. Right now this is mostly for own learning, than for any application. Thanks
 
I'm mostly interested in finding the right technique

Then you should definitely look at the audio library code. There are a LOT of files, you really you only need and synth_sine.cpp (efficiently creates the sinewave data) and output_dac.cpp (efficiently send to the DAC) from the library, and AudioStream.cpp (efficiently passes shared data blocks between objects) from the core library, and of course their respective header files.
 
As Paul said, it's best to study the audio library.

Some things that might be going wrong in your code:
if (phase >= twopi) // full cycle completed
phase = 0; // reset
should be:
if (phase >= twopi) // full cycle completed
phase -= twopi; // reset

else you'll have 'jumps' every cycle.

The reason you are having slow sinusoids is probably because the sin() function takes too long. There are many ways to improve this, but either a lookup table (e.g. every 0.01 radians), and/or interpolation would be much faster. Use your trigonometry identities to enhance the interpolation:
sin(a+b) = sin(a).cos(b)+cos(a).sin(b). When you take 'a' to be the index to the lookup table and 'b' to be the remainder, if 'b' is small, you can approximate cos(b) with 1, and sin(b) with just b.

Yes, the output will have steps -- you would need to filter the signal to get rid of the 44100 Hz (actually 22050 Hz) (and above) elements -- some resistors and capacitors would help; a good filter would require some opamp circuits.
 
Status
Not open for further replies.
Back
Top