Test Tone Generator, log frequency sweep, Grid Generator 250 kHz sample frequency

Status
Not open for further replies.

cebersp

Well-known member
Hi,
this might perhaps be interesting for some guys working on audio projects.

It is a testtone-generator which can do logarithmic frequency sweeps. If you have a 2-channel oscilloscope, then the second channel ist used for trigger and to display a logarithmic frequency grid, which is very handy to see the frequency response. See attachment for an example 80Hz to 10kHz.

In addition to a constant tone and two different sweeps there is a mode to test for compression. The volume of a 400Hz signal is modulated: low-high-low. This is interesting for Guitar Amps or effects.

It took me a while to get a high sample rate for good looking 20kHz. There is some compromise: At low frequency, the precision of the frequencies is not so good and there are some stops of the output between bursts of clear signal.

/*Potentiometer at A0 for selection of 4 different functions:
1. 400Hz constant Test Tone
2. Sweep for Guitar 80…10000Hz
3. Compresstest 400Hz; 1/4=>1=>1/4 Amplitude
4. Sweep for general use 20Hz … 20kHz

NF Output at A14, 12 bit
Grid Output with 2bit-Dac using digital outputs at 15 and 16 Trigger full 3.3V, Grid 0…1,65V
LED Pin 13 indicates Poti Mode
*/

Code:
#define VERSION "TeensyDac_J"
// for Teensy 3.2 09.05.2019 CWE
/*Potentiometer at A0 for selection of functions:
  1. 400Hz constant Test Tone
  2. Sweep for Guitar 80…10000Hz
  3. Compresstest 400Hz; 1/4=>1=>1/4 Amplitude
  4. Sweep for general use 20Hz … 20kHz

NF Output at A14, 12 bit
Grid Output with 2bit-Dac using digital outputs at 15 and 16 Trigger full 3.3V, Grid 0…1,65V
LED Pin 13 indicates Poti Mode
*/


#include <math.h>

float phase = 0.0;
float twopi = 3.14159 * 2;
elapsedMicros usec = 0;
const int ledPin = 13;
// #define gridPin A22
#define gridP1 15
#define gridP2 16
#define dacPin A14 //A21 for 3.5
#define potiPin A0 // For selection of modes

# define BUFLEN 16384


void startblink(int n)
{
  int i;
  for(i=0; i<n; i++)
  {
    digitalWrite(ledPin, HIGH);   // set the LED on
    delay(150);                  // wait for a second
    digitalWrite(ledPin, LOW);    // set the LED off
    delay(150);                  // wait for a second
  }
}

int bufmax;
short int buffer[BUFLEN+1];
void fillbuffer()
{
  int i=0;
  while(phase < twopi)
  {
      float val = sin(phase) * 2047.0; //+ 2050.0; // 0.1/1.17 f. 1000mV RMS
      //float val = 100.0/950.0*sin(phase) * 2000.0 + 2050.0; // f. 100mV RMS      
      buffer[i]= (int)val;
      phase = phase + twopi/(BUFLEN);
      i++;
  }
  float val = sin(phase) * 2047.0; //+ 2050.0;
  buffer[i]= (int)val;
}


unsigned long timeu;
float freq= 200.0;
float tcyc= 2.0;
float fsteps[30]= {10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0, \
                   200.0, 300.0, 400.0, 500.0, 600.0, 700.0, 800.0, 900.0, 1000.0, \
                   2000.0, 3000.0, 4000.0, 5000.0, 6000.0, 7000.0, 8000.0, 9000.0, 10000.0,20000.0};



void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(gridP1, OUTPUT);
  pinMode(gridP2, OUTPUT);    
  // pinMode(gridPin, OUTPUT);  
  digitalWrite(gridP1, 0);
  digitalWrite(gridP2, 0);  
  analogWriteResolution(12);
  //analogWrite(gridPin, 0);
  analogWrite(dacPin, 2048);      
  startblink(10);
  Serial.begin(115200);  
 
  fillbuffer();
  Serial.println(VERSION);
  Serial.println("Buffer gefuellt.");
  //startblink();
}



float frmin;
float frmax;
float twidth; //us

void loop() {
  int poti= analogRead(potiPin)*5/1024 + 1;
  Serial.println(poti);
  startblink(poti);
  
  if(poti==1) // Constant 400Hz
  {
    frmin= 400.0;
    frmax= 400.1;
    twidth= 2000000.0; //us
    sweep_C();    
  } 
  
  if(poti==2) // Spweep for Guitar
  {
    frmin= 80.0;
    frmax= 10000.0;
    twidth= 2000000.0; //us
    sweep_C();    
  }

  if(poti==3) // Compresstest 400Hz
  {
    comp_B();    
  } 

 
  if(poti==4) // Spweep for Audio
  {
    frmin= 20.0;
    frmax= 20000.0;
    twidth= 2000000.0; //us
    sweep_C();    
  }  

  //while(1);
}

int iold=0, iakt, ifrequenz, imode=1, ifakt, irepeats=100, ideltat;
unsigned long oldu, aktu;
float frfaktor;

void fastsample(void)
{
  // ifrequenz=20000;
  ifakt= ifrequenz*BUFLEN/(1000000/4);
  iakt=0;
  oldu=micros()/4;
  for(int i=0; i<irepeats; i++)
  {  
    do
    {
      if(imode==1) analogWrite(dacPin, 2048 + buffer[iakt]);
      else analogWrite(dacPin, 2048 + buffer[iakt]/4);
      aktu=micros()/4;
      iakt+=(aktu-oldu)*ifakt;
      oldu=aktu;
    } while(iakt<BUFLEN);
    iakt-=BUFLEN;
  }
  analogWrite(dacPin, 2048);
}


void comp_B() // Compressortest
{
  ifrequenz=400;
  imode = 0; 
  irepeats= 266;
  fastsample();
  imode=1;
  //analogWrite(gridPin, 4095);  //Trigger
  digitalWrite(gridP1, 1);
  digitalWrite(gridP2, 1);   
  fastsample();
  imode=0;
  //analogWrite(gridPin, 0);  //Trigger
  digitalWrite(gridP2, 0);
  digitalWrite(gridP1, 0);  
  fastsample();  
}

void sweep_C()
{
  int fstep=0;

  unsigned long int tistart;
  int gridstat;
  
  float ffaktor= (log10(frmax)-log10(frmin))/twidth;
  float log10frmin= log10(frmin);
  freq= frmin;
  
  
  //analogWrite(gridPin, 4095);  //Trigger
  digitalWrite(gridP1, 1);
  digitalWrite(gridP2, 1);  
  gridstat=1;
  
  tistart= micros();
  oldu= tistart;
  imode=1;
  iakt=0;
  iold=0;
  
  
  while(freq<frmax)
  {
    //frfaktor= freq*BUFLEN/1000000.0;    
    //Serial.println(freq);
    ifrequenz=freq;
   
    while(freq>=fsteps[fstep]) // Next grid line
    {
      if(gridstat<1)
      {
          //analogWrite(gridPin, 2048);
          digitalWrite(gridP1, 1);
          digitalWrite(gridP2, 0);            
          gridstat=1;
      }
      else
      {
          //analogWrite(gridPin, 0); 
          digitalWrite(gridP1, 0);
          digitalWrite(gridP2, 0); 
          gridstat=0;        
      }
      //Serial.println("Grid");
      fstep++;
    }

    // fastsample();
    
    if(freq<100.0) irepeats= 1;
    else if(freq<1000.0) irepeats= 5;
    else if(freq<5000.0) irepeats= 10;   
    else if(freq<10000.0) irepeats= 30;
    else irepeats=100; 

    fastsample();

    float fhelp= ffaktor*(micros()-tistart);
    fhelp += log10frmin;
    freq= pow(10,fhelp); 
  }
  //analogWrite(gridPin, 0);  
  digitalWrite(gridP1, 0);
  digitalWrite(gridP2, 0);
  gridstat=0;
  imode=0;
  analogWrite(dacPin,2048);
  delay(1000);
}

The hardware is pretty simple.

Suggestions for additional modes?

Enjoy!
Christof
 

Attachments

  • Teensy DAC Testtone Generator.JPG
    Teensy DAC Testtone Generator.JPG
    111.1 KB · Views: 135
  • NewFile14.jpg
    NewFile14.jpg
    91 KB · Views: 73
Last edited:
Linear sweep! Itvs very convinient for calculating spectra since the energy per unit bandwidth is constant, contrarry to log sweep.
 
and the analog output of the 3.6 can go beyond 1MHz.. for audio thats to much, but for ultrasonic ranging or sonar it might be interesting. Even for extended amplifier testing it might be fun to be able to test a few octaves above 20k
 
Hi tschrama,

thanks for the replies! Linear sweep would be very easy to implement.

As the sample frequency is 250kHz, you could go well over 20kHz. This high frequency signal would not look very much like a sine.
Along the signal path of a guitar effect (not at the output) I found that high frequencies have a high gain factor. This led to the effect, that the high frequency content of a signal, which was not a nice looking clear sine, got amplified very much. So I decided, that a "good clean" sine is important.


For higher sample rate there seem to be limits using the easy Arduino environment. Calling function seems to have a very large overhead which consumes a lot of cycles. But I did not investigate this in more depth. I did not even block interrupts.


Christof
 
Status
Not open for further replies.
Back
Top