Generate a sawtooth wave with 8kHz frequency

Hi everyone,

I'm developping a bytebeat module with ArduinoUno and MCP4821 DAC.

User writes a formula, the formula is evaluated by the TinyExpr library and the result is sent through pwm to the DAC in order to have analogue values.

The reference formula in bytebeat is simply "t" which result to a sawtooth wave at 8kHz frequency.

T is a variable that never stop to increment between 0 and 255.

But now I'm a little confused, because when I send

for (int i = 0; i < 255; i++)
    {
      MCP.fastWriteA(i);
    }

To the DAC in the loop, the result frequency is ~260Hz and that's not enough to be eared.

I don't know where I have to search now in order to output the desired frequency.

Thanks !

The DAC expects a number as input, if I'm correct. Is the above code doing just this? Or is it a PWM value?

If PWM is what you're using on an analog pin, a PWM output of 255 means 5V almost constant (or 99% duty cycle), 128 means 50% duty cycle and so on. Arduino Uno has a PWM freq of about 590 Hz...

How long does it take for the fastWriteA function to execute?

Try a processor with a faster clock. The Uno is 16MHz, but the ESP boards are 80MHz.

@SteveMann
I tried with

for (int i = 0; i < 255; i++)
    {
      long a = micros();
      MCP.fastWriteA(i);
      long b = micros();
      Serial.println(b - a);
    }

and the result is 16.

@rohitbd
I'm sorry, I made a mistake, it's only a value in there, a i that increment to 255.

Full code

#include "Arduino.h"
#include "MCP_DAC.h"

uint16_t freq = 5;
uint32_t period = 0;
uint32_t halvePeriod = 0;

MCP4821 MCP;
uint16_t count;
uint32_t lastTime = 0;

void setup()
{
  Serial.begin(115200);

  MCP.begin(10); // select pin = 10
  MCP.fastWriteA(0);

  period = 1e6 / freq;
  halvePeriod = period / 2;

  while (1)
  {
    yield();
    uint32_t now = micros();

    uint32_t t = now % period;

    for (int i = 0; i < 255; i++)
    {
      long a = micros();
      MCP.fastWriteA(i);
      long b = micros();
      Serial.println(b - a);
    }
    // MCP.fastWriteA(t * 4095 / period);

    // Serial.print(t);
    // Serial.print('\t');
    // Serial.print(t * 4095 / period);
    // Serial.print('\t');
    // Serial.print(freq);
    // Serial.print('\t');
    // Serial.print(period);
    // Serial.print('\t');
    // Serial.print(halvePeriod);
    // Serial.println();
  }
}

void loop()
{
}

Use loop() instead of while in setup()

Does it change clock or cycle configuration ?

I tried nothing change

micros() returns an unsigned long. (I don't know if that would make any difference).

Also, doing floating point math, (b - a); in your code takes time. As does the Serial.Println() function.

Why are you making your own infinite loop in setup() instead of simply using loop()?

I take the code from an example, and it is written this way

Where?

It's the serial print that takes the time.

The DAC expects a value between 0 and 255, if I understood it correctly.

8kHz means a period of 1/8 millisecond.

Since there are 256 values, each value is spaced (1/8)/256 ms or approx. 488.28 microsecond apart.

In the loop write each value, then delay by 488.28 uS. See if this helps...

Maybe recheck your arithmetic...

Oh yeah 488.29 nanoseconds...

OK, long int is still an int.

Nope. MCP4821 is 12 bits DAC and its needed 16 bits data for work - read the datasheet.

Probably setting the DAC value is slow. To be sure, change the cycle by giving a larger increment.
Instead
for (int i = 0; i <255; i ++)
try it
for (int i = 0; i <255; i += 5)
and if the DAC frequency is about 5 times higher then the assumption is correct. If the project allows you, you can increase the step enough to achieve the required frequency. Or you can replace the microcontroller with a faster one as suggested by @SteveMann. The measured time of 16 microseconds corresponds to the result - 256 values each for 16 microseconds gives a frequency of about 244 Hz.

1 Like

You need to look at the MCP_DAC library to see what sort of data transfer it is doing. On the Uno, it may be using the default SPI connection at 100 kHz clock rate, or bit banged I/O. Those are slow, and very, very slow, respectively.

To send data faster, either modify the library or write your own SPI routines to make use of faster SPI data transfers.

Oh mate !

I replaced the for by:
for (int i = 0; i <4096; i += 512)
So the sawtooth is aliased, but I can reach 8kHz !

Any formula for calculating this frequency ? Because I get 8.44kHz and not 8Khz o'clock

Link to the cpp class : MCP_DAC/MCP_DAC.cpp at master · RobTillaart/MCP_DAC · GitHub

Any ressources in order to understand what u told me about SPI routines and transfer ?

The Arduino on line reference guide: SPI - Arduino Reference

The sawtooh generated :joy:

Try 'i + = 486' instead of 512. But it may not help, because the steps are very small - only 8. Try to change the upper value of 4096. 8 steps are only 12.5% resolution and you will hardly be able to adjust the frequency 8 kHz exactly.