## Generating Sine Waves

Who thought this would be hard?

I have spent a week trying to work this out. Here is the pseudo code so far:

Calculate the Mark and Space ratios:

- A= int(Fsample/Fmark+0.5)
- B= int(Fsample/Fpace+0.5)

Find the lowest common multiple N of A and B:

- N = LCM(A,B)

Where N is the sine (iSin[]) lookup array size.

To generated the sine wave step through the array using Phase:

- Phase=Phase +N/A, for the mark frequency

Note: B=N/A

So:

- Phase=Phase +B

or

- Phase=Phase +A, for the space frequency

The Phase needs to roll back to the start on overflow:

- if (Phase>=N) then Phase=Phase-N

The sine wave is:

- Sine=iSin[Phase]

To reduce the sine look up array size by a constant (C) we can divide the Phase by the constant (C):

- Sine=iSin[Phase/C]

or

- Sine=iSin[Phase>>M], as the maths is much faster

The sample frequency is restricted and depends on the Arduino clock (16 MHz), the pre-scaler (timer dependent) and the PWM type.

So I have spent a day trying different combinations of Fsample and M to minimise the array size and the error. Nothing great found!

---

I thinks I need help (at least my partner thinks so)!

I found a paper (http://www.analog.com/static/imported-files/tutorials/MT-085.pdf) that although a bit complicated, shows a way out of this mess:

Set N to a power of 2, say 65536 (an unsigned int):

- B=65536*Fmark/Fsample
- A=65536*Fspace/Fsample

Then phase is:

- Phase=Phase+B

or

- Phase=Phase+A

Choose a sine array size that is a power of 2 , say 64.

- Sine=iSin[Phase>>(16-6)]

or

- Sine=iSin[Phase>>10]

Phase rollover is implicit with an unsigned int.

Here is the code for the ISR:

```
// TRANSMIT:
// Phase Correct PWM frequency 31250 Hz
// Originate modem Mark frequency 1270 Hz (=2244)
// Originate modem Space frequency 1070 Hz (=2663)
// Answer modem Mark frequency 2225 Hz (=4666)
// Answer modem Space frequency 2025 Hz (=4247)
const byte iSin[64] = {
128,140,153,165,177,188,199,209,
218,226,234,240,245,250,253,254,
255,254,253,250,245,240,234,226,
218,209,199,188,177,165,153,140,
128,116,103, 91, 79, 68, 57, 47,
38, 30, 22, 16, 11, 6, 3, 2,
1, 2, 3, 6, 11, 16, 22, 30,
38, 47, 57, 68, 79, 91,103,116
};
volatile byte DataOut=0;
ISR(TIMER2_OVF_vect) {
static unsigned int phase=0;
OCR2A = iSin[(phase>>10)]; // Output on D11
if (true) { // Set true for Originate Modem
if (DataOut==0) {
phase+=2244; // Fspace (Originate modem)
} else {
phase+=2663; // Fmark (Originate modem)
}
} else {
if (DataOut==0) {
phase+=4247; // Fspace (Answer modem)
} else {
phase+=4666; // Fmark (Answer modem)
}
}
}
```

Now is that not sweet code!

---

I used Phase Correct PWM instead of Fast PWM as it generates less interrupts rather than the phase correct feature.

Final note, don't put the iSin array inside the ISR like I did! It took ages to workout why the PWM was off frequency!

**AlanX**

## Discussions

## Become a Hackaday.io Member

Create an account to leave a comment. Already have an account? Log In.