# Generator for Sine and other wave forms

## The source

There are many ways to produce sine waves with Arduino. You can
1. produce the samples "just-in-time" calculating immediately before sending
2. calculate them during setup
3. precalculate and store in SRAM
4. in FLASH
5. in EEPROM
6. on SD card

## The hardware interface

To send a signal to an audio device there are various methods like 1-bit-audio or PWM.
We are going to use an 8 bit digital-to-analog converter (DAC) which needs 8 pins of the Arduino but makes handling much easier. Also, you don't have to to prepare much when sending sampled sound; just normalize it, save it in a "low-quality" format like "mono, 16,000 samples per second, 8-bit", and store it on a micro SD card.
By the way: to connect the SD card, you need another 4 pins. Often pins 10, 11, 12, and 13 are used to communicate with the SD card. If you still want to use the Serial terminal (RX and TX) you run out of digital pins. (You can use the analog pins as digital ones if you like.)
If you run out of port pins you can also decide to use only a 6 bit DAC by setting the lower 2 bits to zero. This will of course result in some more loss of quality.

If it is only a very short sample (<1 second) you could use your sound editor to save it in ASCII format as a text file and #include it in you FLASH memory. (If you reduce the sample rate to 8,000 samples per second you might be able to store and reproduce the numbers from zero to nine.) By doing this you don't even need an SD card.
So, for speed reasons we better not use the Arduino digitalWrite() command. Instead we look for pins mapped to one or two ports and use the PORT commands.

## Some math

The following formula is to be used:

vt = [ 127 * ( sin(2 * PI * f * t) + 1) ]

where f is the desired frequency and t is the time given in seconds.

The "+ 1" makes sure no negative values will occur. The "127 *" is to use the maximum range available. It is like the automatic gain control of your tape recorder - if you ever had one. The "[ ]" is to round the floating point number to an integer number.

If you decide for option 3. to 6. and are using a spreadsheet software like Microsoft Excel to precalculate the values you might use this formula:

(enter this in cell A1)
=INT ( 127 * ( SIN ( 2 * PI() * COLUMN() / 256 ) + 1 ) )

users of the german version might use:
=GANZZAHL ( 127 * ( SIN ( 2 * PI() * SPALTE() / 256 ) + 1 ) )

and copy this formula to the right until cell   IV1 . (So you filled 256 cells with the same formula).

You also can produce other signal forms as

 sawtooth PORTn = b++; triangle PORTn = b++ < 128 ? b : 255 - b; square wave PORTn b++ < 128 ? 0 : 255; random noise PORTn = random(256); voice message PORTn = msg[t]; DTMF signals PORTn = sin(f1*t)+sin(f2*t);
where PORTn is the port of your microcontroller you are using and b is the byte we are going to send.

If you want to send sampled sound to the DAC you can easily use a "*.wav" file coded as Windows PCM, 1 channel (mono), 22050 samples per second, 8 bits per sample (mostly called unsigned), and just skip the 44 byte header.

## Implementation

Of course you could send the values to the ADC port in the loop() procedure with a delay of your choice. But it is much better to install a timer interrupt routine to do this. Recommended frequencies are:
• 8,000 Hz, time = 125 μ seconds
• 22,050 Hz, time = 45.35 μ seconds

To produce sine waves with Arduino we take this square wave generator, set it to the desired frequency and use either this or that digital to analog converter.
The picture above was taken from an oscilloscope using a six bit digital-to-analog converter and a sample rate of 8000 samples per second (this is what most PC sound cards offer as "telephone quality"). If you don't want the steps go for an analog oscillator. If you just want smaller steps you have to increase the sample rate and use more bits. You also have to find an Arduino port where you have access to all the bits (if you want to use port D you will loose the TxD and RxD pins which are used for the Serial terminal) or you have to combine the bits of two ports. We are using the pins d2 to d7 which belong to port D, and the pins d8 and d9 which belong to port B. Unfortunately, it is not possible to write to both port at the same time. But the delay is so short that it makes no difference.

 ```#define FILENAME "dac8.ino - 9.3.2014" #include  /*  DAC connected to PORT B and D /* Ports B & D: data              d7 d6 d5 d4 d3 d2 d1 d0 pin   13 12 11 10  9  8  7  6  5  4  3  2  1  0 PORTB             d1 d0 PORTD                   d7 d6 d5 d4 d3 d2 */ #define type 1 /* 1 sine      1000 Hz 2 sawtooth   122 Hz 3 triangle    31 Hz 4 square wave 61 Hz 5 random noise 6 voice msg 7 DTMF "1" (697 Hz and 1209 Hz) as an example (actually, the ATmega328 is a bit too slow for it. Reduce tcnt2 to 70) */ // SD card: const int chipSelect = 10; // SD card boolean sd_card = false; File fileHandle; boolean eof; boolean done = false; char* filename = "welcome.wav"; // Timer-2: const byte prescaler = 2; // 1:1, 2:8, 3:32, 4:64, 5:128, 6:256, 7:1024 const byte tcnt2 = 131; // 16000 Hz float t = 0; // seconds float dt = 1.0 / 16000; // seconds float f = 1000; // Hz float f1 = 697; // DTMF frequencies float f2 = 1209; byte sinetab[256]; void setup() {   Serial.begin(9600);   Serial.println(FILENAME);   Serial.print("type ");   Serial.println(type);   if (type == 6) {     Serial.println("Initializing SD card...");     // ------------ SD card ---------------:     pinMode(chipSelect, OUTPUT);     if (!SD.begin(chipSelect)) {       Serial.println("Card failed, or not present");       return;     }     sd_card = true;     Serial.println("card initialized.");     openFile(filename);   }   if ((type == 1) || (type == 7))     for (int i = 0; i < 256; i++)       sinetab[i] = 127 * (sin(2 * PI * i / 256) + 1);   // ------------- DAC port pins --------:   Serial.println("output: PORTB and PORTD");   delay(100);   DDRB = B00000011 | DDRB; // remaining bits used for SD card   DDRD = B11111100;   // ------------- Timer ----------------:   setupTimer();   DDRC =   1; } void openFile(char* s) {   fileHandle.close();   fileHandle = SD.open(s);   if (fileHandle != 0) eof = false;   fileHandle.seek(44); // skip normal header } long count = 0; long time = millis() + 1000; void loop() {   while (millis() < time); // wait   time = millis() + 1000;   Serial.println(count); // I am alive   // the values printed prove: some of the interrupts are skipped   count = 0; } //========================================================================= ISR(TIMER2_OVF_vect) {   // all the work is done in here:   TCNT2 = tcnt2;    byte b;   switch (type) {     case 1: b = sine(t); break; // sine     case 2: b = sawtooth(t); break; // sawtooth     case 3: b = triangle(t); break; // triangle     case 4: b = squareWave(t); break; // square wave     case 5: b = randomNoise(t); break; // random noise     case 6: b = voiceMsg(t); break; // voice msg     case 7: b = dtmf(t); break; // DTMF "1" (697Hz and 1209Hz)   }   sendByte(b);   t = t + dt;   count++;   TIFR2 = 0x00;  } //========================================================================= void sendByte(byte b) {       byte d;       // b 7 6 5 4 3 2 1 0       // PORTB - - - - - - ^ ^       // PORTD ^ ^ ^ ^ ^ ^ - -       d = b << 2;       b = (b >> 6) | (PORTB & B11111100);       PORTB = b; // nearly no time between the PORT commands       PORTD = d; } //========================================================================= byte frac(float x) { // not a standard C function   return 256 * (x - floor(x)); } byte sine(float t) {   //return 127 * ( sin(2 * PI * f * t) + 1); // too slow!!!   return sinetab[ frac(f * t) ]; } byte sawtooth(float t) {   static byte b;   return b++; } byte triangle(float t) {   static byte b;   static int8_t dir = 1; // toggle +/-1   b = b + dir;   if ((b == 0) || (b == 255)) dir = -dir;   return b; } byte squareWave(float t) {   static byte b;   return b++ < 128 ? 0 : 255; } byte randomNoise(float t) {   return random(256); } byte voiceMsg(float t) {   byte b = 128;   if (!done) {     if (fileHandle) {       b = fileHandle.read();       eof = !fileHandle.available();       if (eof) done = true;     }   }   return b; } byte dtmf(float t) {   //return 63 * ( sin( 2 * PI * f1 * t) + sin( 2 * PI * f2 * t) + 2);   byte s1 = sinetab[ frac(f1 * t) ] / 2;   byte s2 = sinetab[ frac(f2 * t) ] / 2;   return s1 + s2; }   //========================================================================= void setupTimer() {   int prescalers[] = {0,1,8,32,64,128,256,1024}; // see doc8271.pdf, p. 164   TIMSK2 = 0x01;        // Timer2 INT Reg: Timer2 Overflow Interrupt Enable   TCCR2A = 0x00;        // Timer2 Control Reg A: Wave Gen Mode normal   TCCR2B = prescaler;   // Timer2 Control Reg B: Timer Prescaler set to 2   float clk = F_CPU / prescalers[prescaler];   float t1 = 1 / clk;   float t2 = (256 - tcnt2) * t1;   float f = 1 / t2;   Serial.print("cpu frequency [Hz]: ");   Serial.println(F_CPU);   Serial.print("clock frequency [Hz]: ");   Serial.println(clk);   Serial.print("ISR frequency [Hz]: ");   Serial.println(f); } ```

contact: nji(at)gmx.de