That was a fun little experiment. I got a 6 sine waves out of an Arduino Nano, 50 Hz (or very close to that), phase adjustable with steps of 0.72 degrees, and with just two extra components per output: an RC filter of a 330Ω resistor and a 10 µF capacitor, values as calculated on this site. Easier than anticipated, to be honest.
The loop() function is empty. This is where e.g. a Serial input could be read for phase shifting, and the variable phaseShift adjusted accordingly. The values set in this code are just an example. As the whole thing is interrupt driven - at 25k Hz the phase is updated to the next, everything else is in hardware - there should be ample processing power left for this.
Code:
// Sine wave generator.
// 50 Hz; 500 steps per full sine wave; 25,000 total steps per second.
// 640 ticks per step.
// PWM frequency: at least 25 kHz.
// OC0A Arduino pin 6
// OC0B Arduino pin 5
// OC1A Arduino pin 9
// OC1B Arduino pin 10
// OC2A Arduino pin 11
// OC2B Arduino pin 3
const byte sine[500] = {127, 128, 130, 131, 133, 134, 136, 138, 139, 141, 142, 144, 146, 147, 149, 150, 152, 153, 155, 157, 158, 160, 161, 163, 164, 166, 167, 169, 170, 172, 173, 175, 176, 178, 179, 181, 182, 183, 185, 186, 188, 189, 190, 192, 193, 195, 196, 197, 199, 200, 201, 202, 204, 205, 206, 207, 209, 210, 211, 212, 213, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 235, 236, 237, 238, 239, 239, 240, 241, 241, 242, 243, 243, 244, 245, 245, 246, 246, 247, 247, 248, 248, 249, 249, 250, 250, 250, 251, 251, 251, 252, 252, 252, 252, 252, 253, 253, 253, 253, 253, 253, 253, 253, 253, 254, 253, 253, 253, 253, 253, 253, 253, 253, 253, 252, 252, 252, 252, 252, 251, 251, 251, 250, 250, 250, 249, 249, 248, 248, 247, 247, 246, 246, 245, 245, 244, 243, 243, 242, 241, 241, 240, 239, 239, 238, 237, 236, 235, 235, 234, 233, 232, 231, 230, 229, 228, 227, 226, 225, 224, 223, 222, 221, 220, 219, 218, 217, 216, 215, 213, 212, 211, 210, 209, 207, 206, 205, 204, 202, 201, 200, 199, 197, 196, 195, 193, 192, 190, 189, 188, 186, 185, 183, 182, 181, 179, 178, 176, 175, 173, 172, 170, 169, 167, 166, 164, 163, 161, 160, 158, 157, 155, 153, 152, 150, 149, 147, 146, 144, 142, 141, 139, 138, 136, 134, 133, 131, 130, 128, 127, 125, 123, 122, 120, 119, 117, 115, 114, 112, 111, 109, 107, 106, 104, 103, 101, 100, 98, 96, 95, 93, 92, 90, 89, 87, 86, 84, 83, 81, 80, 78, 77, 75, 74, 72, 71, 70, 68, 67, 65, 64, 63, 61, 60, 58, 57, 56, 54, 53, 52, 51, 49, 48, 47, 46, 44, 43, 42, 41, 40, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 18, 17, 16, 15, 14, 14, 13, 12, 12, 11, 10, 10, 9, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 3, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 10, 10, 11, 12, 12, 13, 14, 14, 15, 16, 17, 18, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 40, 41, 42, 43, 44, 46, 47, 48, 49, 51, 52, 53, 54, 56, 57, 58, 60, 61, 63, 64, 65, 67, 68, 70, 71, 72, 74, 75, 77, 78, 80, 81, 83, 84, 86, 87, 89, 90, 92, 93, 95, 96, 98, 100, 101, 103, 104, 106, 107, 109, 111, 112, 114, 115, 117, 119, 120, 122, 123, 125};
volatile uint16_t phaseShift[6]; // Phase shift: 0 = 0 deg; 250 = 180 deg; 499 = almost 360 deg.
void setup() {
pinMode(3, OUTPUT);
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(9, OUTPUT);
pinMode(10, OUTPUT);
pinMode(11, OUTPUT);
// Set up the timers for sufficiently high PWM output.
// Timer0: fast PWM mode, non-inverting.
// Prescaler = 1 so f(PWM) = 62.5 kHz (note: this messes up millis() which now runs 64x too fast).
TCCR0A = bit(COM0A1) | bit(COM0B1) | bit(WGM01) | bit(WGM00);
TCCR0B = bit(CS00);
// Timer1: fast PWM mode, non-inverting, prescaler = 1, TOP = 639 (frequency: 25 kHz for update on timer overflow interrupt).
// Use IRC1 as top, leaving OCR1A free for PWM. Sets ICF1 interrupt flag upon reaching TOP.
TCCR1A = bit(COM1A1) | bit(COM1B1) | bit(WGM11);
TCCR1B = bit(WGM13) | bit (WGM12) | bit(CS10);
TCCR1C = 0;
ICR1 = 639;
// Enable the overflow interrupt.
TIMSK1 = bit(TOIE1);
// Timer2: fast PWM mode, non-inverting.
// Prescaler = 1 so f(PWM) = 62.5 kHz.
TCCR2A = bit(COM2A1) | bit(COM2B1) | bit(WGM21) | bit(WGM20);
TCCR2B = bit(CS20);
// Set some random values for the phase shift.
phaseShift[0] = 0;
phaseShift[1] = 100;
phaseShift[2] = 120;
phaseShift[3] = 30;
phaseShift[4] = 90;
phaseShift[5] = 450;
}
void loop() {
}
volatile uint16_t sineIndex;
ISR(TIMER1_OVF_vect) {
uint16_t i = sineIndex; // Local copy for increased efficiency.
OCR0A = sine[i + phaseShift[0]];
OCR0B = sine[i + phaseShift[1]];
OCR1A = sine[i + phaseShift[2]]; // Will produce a smaller wave amplitude as timer counts 0-639, not 0-255.
OCR1B = sine[i + phaseShift[3]]; // To get full swing, add second lookup table with values 0-638 instead.
OCR2A = sine[i + phaseShift[4]];
OCR2B = sine[i + phaseShift[5]];
i++;
if (i == 500) {
i = 0;
}
sineIndex = i;
}
Schematic used (for each pin):
Note: depending on what you want to do with this signal you may want to pass it through an OpAmp to buffer it, add an offset, or decrease the amplitude with a voltage divider. I did not experiment with the RC values, the cap value I think is way bigger than needed.