Another easy way to create a clean and stable sin wave is to use a DAC. I tried it with a
MAX5712, a 12 bit DAC, which can be directly connected to the Arduino with SPI. This is a test with a Mega2560, which allows up to 24 kHz samplerate, here with 20 kHz samplerate, generating a sin wave of 943 Hz:
Only one small 6 pin IC, two capacitors and one resistor are needed, and you can change the frequency in software, and switch it on and off without more external parts. You can even program it in a way, that it switches it off at zero crossover and starts at zero (1/2 VDD with the current circuit), to avoid any clicks, or add an envelope. If you need more power for the output, you can still use your LM386. Depending on the output frequency and the samplerate, you need to adjust the lowpass filter with R1/C1. Unlike most audio DACs, the output of the MAX5712 can be DC. This is the filtered output for 0.1 Hz, only one variable change in the Arduino sketch:
Below is the Arduino source code. The output is generated in an interrupt, you can use the loop for other things like reading switches or analog values, blinking LEDs etc. But the interrupt uses timer 1, which means you can't use the Arduino PWM functions anymore. For low frequencies, you can even use the sinf function for calculating the DAC value in realtime, no need for the big table, but this works only up to 5 kHz samplerate. I tried this with a STM32F4 discovery kit, which has an ARM microcontroller, and it is no problem to calculate the sinf function @48 kHz samplerate in stereo and 24 bit in realtime, if running with 168 MHz CPU clock.
/**
* Generates a sin 943 Hz wave with a MAX5712, connected with SPI. Tested with a Mega2560 Arduino.
* Samplerate works up to 24 kHz.
* SCK: pin 52
* MOSI: pin 51
*/
#include "SPI.h"
// pin numbers
const int ledPin = 13;
const int csPin = 12;
// signal generator
const float samplerate = 20000.0f;
const float frequency = 943.0f;
uint32_t increment = 1024.0f / samplerate * frequency * 4194304; // multiplicator for 22 bit shift
uint32_t sinIndex = 0;
uint16_t nextValue = 0;
// 1024 values sin table, 12 bit unsigned
const uint16_t sinTable[] = {
0x0800, 0x080c, 0x0819, 0x0825, 0x0832, 0x083e, 0x084b, 0x0857, 0x0864, 0x0870, 0x087d, 0x088a, 0x0896, 0x08a3, 0x08af, 0x08bc,
0x08c8, 0x08d5, 0x08e1, 0x08ee, 0x08fa, 0x0907, 0x0913, 0x091f, 0x092c, 0x0938, 0x0945, 0x0951, 0x095d, 0x096a, 0x0976, 0x0983,
0x098f, 0x099b, 0x09a7, 0x09b4, 0x09c0, 0x09cc, 0x09d8, 0x09e5, 0x09f1, 0x09fd, 0x0a09, 0x0a15, 0x0a21, 0x0a2e, 0x0a3a, 0x0a46,
0x0a52, 0x0a5e, 0x0a6a, 0x0a76, 0x0a82, 0x0a8e, 0x0a99, 0x0aa5, 0x0ab1, 0x0abd, 0x0ac9, 0x0ad4, 0x0ae0, 0x0aec, 0x0af8, 0x0b03,
0x0b0f, 0x0b1a, 0x0b26, 0x0b32, 0x0b3d, 0x0b48, 0x0b54, 0x0b5f, 0x0b6b, 0x0b76, 0x0b81, 0x0b8d, 0x0b98, 0x0ba3, 0x0bae, 0x0bb9,
0x0bc4, 0x0bd0, 0x0bdb, 0x0be6, 0x0bf0, 0x0bfb, 0x0c06, 0x0c11, 0x0c1c, 0x0c27, 0x0c31, 0x0c3c, 0x0c47, 0x0c51, 0x0c5c, 0x0c66,
0x0c71, 0x0c7b, 0x0c86, 0x0c90, 0x0c9a, 0x0ca4, 0x0caf, 0x0cb9, 0x0cc3, 0x0ccd, 0x0cd7, 0x0ce1, 0x0ceb, 0x0cf5, 0x0cff, 0x0d08,
0x0d12, 0x0d1c, 0x0d25, 0x0d2f, 0x0d39, 0x0d42, 0x0d4b, 0x0d55, 0x0d5e, 0x0d67, 0x0d71, 0x0d7a, 0x0d83, 0x0d8c, 0x0d95, 0x0d9e,
0x0da7, 0x0db0, 0x0db9, 0x0dc1, 0x0dca, 0x0dd3, 0x0ddb, 0x0de4, 0x0dec, 0x0df5, 0x0dfd, 0x0e05, 0x0e0e, 0x0e16, 0x0e1e, 0x0e26,
0x0e2e, 0x0e36, 0x0e3e, 0x0e45, 0x0e4d, 0x0e55, 0x0e5d, 0x0e64, 0x0e6c, 0x0e73, 0x0e7b, 0x0e82, 0x0e89, 0x0e90, 0x0e97, 0x0e9f,
0x0ea6, 0x0eac, 0x0eb3, 0x0eba, 0x0ec1, 0x0ec8, 0x0ece, 0x0ed5, 0x0edb, 0x0ee2, 0x0ee8, 0x0eee, 0x0ef5, 0x0efb, 0x0f01, 0x0f07,
0x0f0d, 0x0f13, 0x0f18, 0x0f1e, 0x0f24, 0x0f2a, 0x0f2f, 0x0f35, 0x0f3a, 0x0f3f, 0x0f45, 0x0f4a, 0x0f4f, 0x0f54, 0x0f59, 0x0f5e,
0x0f63, 0x0f67, 0x0f6c, 0x0f71, 0x0f75, 0x0f7a, 0x0f7e, 0x0f83, 0x0f87, 0x0f8b, 0x0f8f, 0x0f93, 0x0f97, 0x0f9b, 0x0f9f, 0x0fa3,
0x0fa6, 0x0faa, 0x0fae, 0x0fb1, 0x0fb4, 0x0fb8, 0x0fbb, 0x0fbe, 0x0fc1, 0x0fc4, 0x0fc7, 0x0fca, 0x0fcd, 0x0fcf, 0x0fd2, 0x0fd5,
0x0fd7, 0x0fda, 0x0fdc, 0x0fde, 0x0fe0, 0x0fe2, 0x0fe5, 0x0fe6, 0x0fe8, 0x0fea, 0x0fec, 0x0fee, 0x0fef, 0x0ff1, 0x0ff2, 0x0ff3,
0x0ff5, 0x0ff6, 0x0ff7, 0x0ff8, 0x0ff9, 0x0ffa, 0x0ffb, 0x0ffb, 0x0ffc, 0x0ffd, 0x0ffd, 0x0ffe, 0x0ffe, 0x0ffe, 0x0ffe, 0x0ffe,
0x0fff, 0x0ffe, 0x0ffe, 0x0ffe, 0x0ffe, 0x0ffe, 0x0ffd, 0x0ffd, 0x0ffc, 0x0ffb, 0x0ffb, 0x0ffa, 0x0ff9, 0x0ff8, 0x0ff7, 0x0ff6,
0x0ff5, 0x0ff3, 0x0ff2, 0x0ff1, 0x0fef, 0x0fee, 0x0fec, 0x0fea, 0x0fe8, 0x0fe6, 0x0fe5, 0x0fe2, 0x0fe0, 0x0fde, 0x0fdc, 0x0fda,
0x0fd7, 0x0fd5, 0x0fd2, 0x0fcf, 0x0fcd, 0x0fca, 0x0fc7, 0x0fc4, 0x0fc1, 0x0fbe, 0x0fbb, 0x0fb8, 0x0fb4, 0x0fb1, 0x0fae, 0x0faa,
0x0fa6, 0x0fa3, 0x0f9f, 0x0f9b, 0x0f97, 0x0f93, 0x0f8f, 0x0f8b, 0x0f87, 0x0f83, 0x0f7e, 0x0f7a, 0x0f75, 0x0f71, 0x0f6c, 0x0f67,
0x0f63, 0x0f5e, 0x0f59, 0x0f54, 0x0f4f, 0x0f4a, 0x0f45, 0x0f3f, 0x0f3a, 0x0f35, 0x0f2f, 0x0f2a, 0x0f24, 0x0f1e, 0x0f18, 0x0f13,
0x0f0d, 0x0f07, 0x0f01, 0x0efb, 0x0ef5, 0x0eee, 0x0ee8, 0x0ee2, 0x0edb, 0x0ed5, 0x0ece, 0x0ec8, 0x0ec1, 0x0eba, 0x0eb3, 0x0eac,
0x0ea6, 0x0e9f, 0x0e97, 0x0e90, 0x0e89, 0x0e82, 0x0e7b, 0x0e73, 0x0e6c, 0x0e64, 0x0e5d, 0x0e55, 0x0e4d, 0x0e45, 0x0e3e, 0x0e36,
0x0e2e, 0x0e26, 0x0e1e, 0x0e16, 0x0e0e, 0x0e05, 0x0dfd, 0x0df5, 0x0dec, 0x0de4, 0x0ddb, 0x0dd3, 0x0dca, 0x0dc1, 0x0db9, 0x0db0,
0x0da7, 0x0d9e, 0x0d95, 0x0d8c, 0x0d83, 0x0d7a, 0x0d71, 0x0d67, 0x0d5e, 0x0d55, 0x0d4b, 0x0d42, 0x0d39, 0x0d2f, 0x0d25, 0x0d1c,
0x0d12, 0x0d08, 0x0cff, 0x0cf5, 0x0ceb, 0x0ce1, 0x0cd7, 0x0ccd, 0x0cc3, 0x0cb9, 0x0caf, 0x0ca4, 0x0c9a, 0x0c90, 0x0c86, 0x0c7b,
0x0c71, 0x0c66, 0x0c5c, 0x0c51, 0x0c47, 0x0c3c, 0x0c31, 0x0c27, 0x0c1c, 0x0c11, 0x0c06, 0x0bfb, 0x0bf0, 0x0be6, 0x0bdb, 0x0bd0,
0x0bc4, 0x0bb9, 0x0bae, 0x0ba3, 0x0b98, 0x0b8d, 0x0b81, 0x0b76, 0x0b6b, 0x0b5f, 0x0b54, 0x0b48, 0x0b3d, 0x0b32, 0x0b26, 0x0b1a,
0x0b0f, 0x0b03, 0x0af8, 0x0aec, 0x0ae0, 0x0ad4, 0x0ac9, 0x0abd, 0x0ab1, 0x0aa5, 0x0a99, 0x0a8e, 0x0a82, 0x0a76, 0x0a6a, 0x0a5e,
0x0a52, 0x0a46, 0x0a3a, 0x0a2e, 0x0a21, 0x0a15, 0x0a09, 0x09fd, 0x09f1, 0x09e5, 0x09d8, 0x09cc, 0x09c0, 0x09b4, 0x09a7, 0x099b,
0x098f, 0x0983, 0x0976, 0x096a, 0x095d, 0x0951, 0x0945, 0x0938, 0x092c, 0x091f, 0x0913, 0x0907, 0x08fa, 0x08ee, 0x08e1, 0x08d5,
0x08c8, 0x08bc, 0x08af, 0x08a3, 0x0896, 0x088a, 0x087d, 0x0870, 0x0864, 0x0857, 0x084b, 0x083e, 0x0832, 0x0825, 0x0819, 0x080c,
0x0800, 0x07f3, 0x07e6, 0x07da, 0x07cd, 0x07c1, 0x07b4, 0x07a8, 0x079b, 0x078f, 0x0782, 0x0775, 0x0769, 0x075c, 0x0750, 0x0743,
0x0737, 0x072a, 0x071e, 0x0711, 0x0705, 0x06f8, 0x06ec, 0x06e0, 0x06d3, 0x06c7, 0x06ba, 0x06ae, 0x06a2, 0x0695, 0x0689, 0x067c,
0x0670, 0x0664, 0x0658, 0x064b, 0x063f, 0x0633, 0x0627, 0x061a, 0x060e, 0x0602, 0x05f6, 0x05ea, 0x05de, 0x05d1, 0x05c5, 0x05b9,
0x05ad, 0x05a1, 0x0595, 0x0589, 0x057d, 0x0571, 0x0566, 0x055a, 0x054e, 0x0542, 0x0536, 0x052b, 0x051f, 0x0513, 0x0507, 0x04fc,
0x04f0, 0x04e5, 0x04d9, 0x04cd, 0x04c2, 0x04b7, 0x04ab, 0x04a0, 0x0494, 0x0489, 0x047e, 0x0472, 0x0467, 0x045c, 0x0451, 0x0446,
0x043b, 0x042f, 0x0424, 0x0419, 0x040f, 0x0404, 0x03f9, 0x03ee, 0x03e3, 0x03d8, 0x03ce, 0x03c3, 0x03b8, 0x03ae, 0x03a3, 0x0399,
0x038e, 0x0384, 0x0379, 0x036f, 0x0365, 0x035b, 0x0350, 0x0346, 0x033c, 0x0332, 0x0328, 0x031e, 0x0314, 0x030a, 0x0300, 0x02f7,
0x02ed, 0x02e3, 0x02da, 0x02d0, 0x02c6, 0x02bd, 0x02b4, 0x02aa, 0x02a1, 0x0298, 0x028e, 0x0285, 0x027c, 0x0273, 0x026a, 0x0261,
0x0258, 0x024f, 0x0246, 0x023e, 0x0235, 0x022c, 0x0224, 0x021b, 0x0213, 0x020a, 0x0202, 0x01fa, 0x01f1, 0x01e9, 0x01e1, 0x01d9,
0x01d1, 0x01c9, 0x01c1, 0x01ba, 0x01b2, 0x01aa, 0x01a2, 0x019b, 0x0193, 0x018c, 0x0184, 0x017d, 0x0176, 0x016f, 0x0168, 0x0160,
0x0159, 0x0153, 0x014c, 0x0145, 0x013e, 0x0137, 0x0131, 0x012a, 0x0124, 0x011d, 0x0117, 0x0111, 0x010a, 0x0104, 0x00fe, 0x00f8,
0x00f2, 0x00ec, 0x00e7, 0x00e1, 0x00db, 0x00d5, 0x00d0, 0x00ca, 0x00c5, 0x00c0, 0x00ba, 0x00b5, 0x00b0, 0x00ab, 0x00a6, 0x00a1,
0x009c, 0x0098, 0x0093, 0x008e, 0x008a, 0x0085, 0x0081, 0x007c, 0x0078, 0x0074, 0x0070, 0x006c, 0x0068, 0x0064, 0x0060, 0x005c,
0x0059, 0x0055, 0x0051, 0x004e, 0x004b, 0x0047, 0x0044, 0x0041, 0x003e, 0x003b, 0x0038, 0x0035, 0x0032, 0x0030, 0x002d, 0x002a,
0x0028, 0x0025, 0x0023, 0x0021, 0x001f, 0x001d, 0x001a, 0x0019, 0x0017, 0x0015, 0x0013, 0x0011, 0x0010, 0x000e, 0x000d, 0x000c,
0x000a, 0x0009, 0x0008, 0x0007, 0x0006, 0x0005, 0x0004, 0x0004, 0x0003, 0x0002, 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0002, 0x0003, 0x0004, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009,
0x000a, 0x000c, 0x000d, 0x000e, 0x0010, 0x0011, 0x0013, 0x0015, 0x0017, 0x0019, 0x001a, 0x001d, 0x001f, 0x0021, 0x0023, 0x0025,
0x0028, 0x002a, 0x002d, 0x0030, 0x0032, 0x0035, 0x0038, 0x003b, 0x003e, 0x0041, 0x0044, 0x0047, 0x004b, 0x004e, 0x0051, 0x0055,
0x0059, 0x005c, 0x0060, 0x0064, 0x0068, 0x006c, 0x0070, 0x0074, 0x0078, 0x007c, 0x0081, 0x0085, 0x008a, 0x008e, 0x0093, 0x0098,
0x009c, 0x00a1, 0x00a6, 0x00ab, 0x00b0, 0x00b5, 0x00ba, 0x00c0, 0x00c5, 0x00ca, 0x00d0, 0x00d5, 0x00db, 0x00e1, 0x00e7, 0x00ec,
0x00f2, 0x00f8, 0x00fe, 0x0104, 0x010a, 0x0111, 0x0117, 0x011d, 0x0124, 0x012a, 0x0131, 0x0137, 0x013e, 0x0145, 0x014c, 0x0153,
0x0159, 0x0160, 0x0168, 0x016f, 0x0176, 0x017d, 0x0184, 0x018c, 0x0193, 0x019b, 0x01a2, 0x01aa, 0x01b2, 0x01ba, 0x01c1, 0x01c9,
0x01d1, 0x01d9, 0x01e1, 0x01e9, 0x01f1, 0x01fa, 0x0202, 0x020a, 0x0213, 0x021b, 0x0224, 0x022c, 0x0235, 0x023e, 0x0246, 0x024f,
0x0258, 0x0261, 0x026a, 0x0273, 0x027c, 0x0285, 0x028e, 0x0298, 0x02a1, 0x02aa, 0x02b4, 0x02bd, 0x02c6, 0x02d0, 0x02da, 0x02e3,
0x02ed, 0x02f7, 0x0300, 0x030a, 0x0314, 0x031e, 0x0328, 0x0332, 0x033c, 0x0346, 0x0350, 0x035b, 0x0365, 0x036f, 0x0379, 0x0384,
0x038e, 0x0399, 0x03a3, 0x03ae, 0x03b8, 0x03c3, 0x03ce, 0x03d8, 0x03e3, 0x03ee, 0x03f9, 0x0404, 0x040f, 0x0419, 0x0424, 0x042f,
0x043b, 0x0446, 0x0451, 0x045c, 0x0467, 0x0472, 0x047e, 0x0489, 0x0494, 0x04a0, 0x04ab, 0x04b7, 0x04c2, 0x04cd, 0x04d9, 0x04e5,
0x04f0, 0x04fc, 0x0507, 0x0513, 0x051f, 0x052b, 0x0536, 0x0542, 0x054e, 0x055a, 0x0566, 0x0571, 0x057d, 0x0589, 0x0595, 0x05a1,
0x05ad, 0x05b9, 0x05c5, 0x05d1, 0x05de, 0x05ea, 0x05f6, 0x0602, 0x060e, 0x061a, 0x0627, 0x0633, 0x063f, 0x064b, 0x0658, 0x0664,
0x0670, 0x067c, 0x0689, 0x0695, 0x06a2, 0x06ae, 0x06ba, 0x06c7, 0x06d3, 0x06e0, 0x06ec, 0x06f8, 0x0705, 0x0711, 0x071e, 0x072a,
0x0737, 0x0743, 0x0750, 0x075c, 0x0769, 0x0775, 0x0782, 0x078f, 0x079b, 0x07a8, 0x07b4, 0x07c1, 0x07cd, 0x07da, 0x07e6, 0x07f3,
};
// send a word to the DAC
void sendDacWord(uint16_t word)
{
digitalWrite(csPin, LOW);
SPI.transfer(word >> 8);
SPI.transfer(word & 0xff);
digitalWrite(csPin, HIGH);
}
void setup()
{
// init pins
pinMode(ledPin, OUTPUT);
pinMode(csPin, OUTPUT);
digitalWrite(csPin, HIGH);
// init SPI
SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
SPI.begin(); // additional begin is required, otherwise the second call to SPI.transfer hangs
// send wakeup command to DAC
sendDacWord(0xf000);
// initialize timer1
noInterrupts(); // disable all interrupts
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
OCR1A = 16000000.0f / samplerate; // compare match register for IRQ with selected samplerate
TCCR1B |= (1 << WGM12); // CTC mode
TCCR1B |= (1 << CS10); // no prescaler
TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
interrupts(); // enable all interrupts
}
// timer 1 interrupt
ISR(TIMER1_COMPA_vect)
{
sendDacWord(nextValue & 0x0fff);
nextValue = sinTable[sinIndex >> 22];
sinIndex += increment;
}
void loop()
{
digitalWrite(ledPin, HIGH);
delay(500);
digitalWrite(ledPin, LOW);
delay(500);
}
Code to generate the sin table:
#!/usr/bin/python
import math
import sys
length = 1024
for i in xrange(length):
s = math.sin(2 * math.pi * i / length) * 2047 + 2048
sys.stdout.write("0x" + format(int(s), '04x') + ", ")
sys.stdout.flush()
if i % 16 == 15: print("")