1

I'm generating a sine wave using a table, a PWM pin and an RC filter on the output. I would like to be able to control the frequency of the sine wave, how can I do that easily?

By the way, which is the maximum frequency achievable and why? As far as my understanding goes, the limit is given by the time arduino needs in order to run the loop() function.

This is the code I am using on arduino UNO. Please point it out if this is a horrible way to generate a sine wave with arduino, and if it is, feel free to provide a better solution.

const int8_t SIN1024_DATA [] = {... data table values (1024 values) ...};
int i = 0;
void setup()
{
  pinMode(9, OUTPUT);
}

void loop()
{
  digitalWrite(9, SIN1024_DATA[i]);
  i++;
  if(i == 1024){i = 0;}
}
1
  • 1
    I haven't seen this method of generating a sin-wave. Most I've seen use a PWM pin, and analogWrite. But to get a bit more performance out of this method, i'd wrap the code inside loop, in a while(true){ ... }. Making it a few cycles faster. Secondly, don't use digitalWrite, as it's quite slow. Either use direct port manipulation, or use something like digitalWriteFast.
    – Gerben
    Feb 13, 2017 at 19:08

1 Answer 1

2

First thing, you must find a way to output the samples at a steady rate. Your best bet is to use a timer. Ideally, you want to use the same timer used for the PWM output, in order to have them synchronized. See for example how it is done in the Mozzi sound synthesis library.

Then you can lower the frequency by repeatedly outputting the same value: if you output every value once, you get the “base” frequency (sample frequency ÷ 1024). If you output each value n times you get the base frequency divided by n. Conversely, you can get higher frequencies by skipping samples. For example, if you output only every other sample, you get twice the base frequency.

These two approaches can be conveniently combined in a very simple code: you keep a phase variable and, on every sample, you increment it by an amount proportional to the desired frequency. You then use the higher bits of the phase as an index into your array. The lower bits then serve only to increase the frequency resolution and allow frequencies lower than the base one. Here is a simple implementation:

const uint8_t pin = ...;
uint8_t frequency;

// Call periodically.
static inline void output_a_sample()
{
    static uint16_t phase;
    analogWrite(pin, SIN1024_DATA[phase>>6]);
    phase += frequency;
}

This lets you set the frequency in units of the sampling frequency ÷ 216, i.e. the base frequency ÷ 26.

Note that the phase here is 16-bits: 10 bits for the array index and 6 bits for the fractional part. Since all the bits are used, you do not need to handle the rollover: a uint16_t automatically rolls over modulo 216, which is just what you want here. You can use a 32-bit variable if you need more frequency resolution.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.