avrspiatmegamicrochipdigital-analog-converter

AVR - MCP4921, custom sinewave frequency


I'm trying to generate a sinewave of 40KHz from an avr micro-controller (atmega328p) interfaced with an MCP4921. I have an already prepared sinewave table generated online and i use the SPI protocol. Everything is working well except one slight problem. In the atmega328, i calculated the timer to generate an interrupt at 40KHz, and during each ISR call, i execute a SPI_Transmit() function. So i can come to frequency of the sinewave i want. Am stuck on how to get it right.

The SPI protocol is set to run at 8MHz because am using a 16MHz crystal and the max i can do is 8MHz.

AS far as the configuration of the timer are concerned, this is how i went about it,

#define TableSize = 256

const uint16_t sinewaveTable [TableSize] {
   0x800,0x832,0x864,0x896.... // I have 256 values in this table
}

void Setup_Timer()
{
    // Clear registers
    TCCR1A = 0;
    TCCR1B = 0;

    // CTC
    TCCR1B |= (1 << WGM12);

    // Set TOP Value (OCR1A) for 40kHz clock signal
    OCR1A = 199;

    // Prescaler 1
    TCCR1B |= (1 << CS10);

    // Output Compare Match A Interrupt Enable
    TIMSK1 |= (1 << OCIE1A);
}

And then my ISR is configured as follows

ISR(TIMER1_COMPA_vect) {
    // Chip select low
    CS_DAC_LOW();

    // concatenate command with sinewave value
    uint16_t cmd_and_value = (CMD << 12) | sinewaveTable[SineWave_TableCounter];

    // send the 
    SPI_MasterTransciever(cmd_and_value);
    SineWave_TableCounter++;

    if(SineWave_TableCounter>= TableSize) 
        SineWave_TableCounter= 0;

    CS_DAC_HIGH();
}

The only thing i think i'm sure about is that the final Timer frequency will have to be divided with the number of samples in the table to get the real frequency of the sinewave. That means that if i have lets say 40KHz and i have, then

results

Again, this is far from what i am anticipating. But then, should i sample at a NyQuist frequency of atleast 80KHz, although this too would give me

results

which is also still far from what i want. And thats why i am seeking any help or leads


Solution

  • You cannot generate a 40kHz sine wave, while sending to output 40k samples per second. To generate 40kHz wave you need output at least 80k samples (that what Nyquist frequency is about). But even then it will not be a sine wave, because the wave will be determined by two points without any intermediate.

    Since your DAC requires you to send 16 bit (that is 32 CPU cycles), and also requires you to toggle CS up and down (that is at least 2 more CPU cycles), the theoretical maximum sampling frequency you can achieve is 16 MHz / (32 + 2) = 470.6 ksamples per second. That is only 11.7 samples per one cycle of output sine wave. To do so you need to disable all interrupts and write directly to output registers, you need either a shorter sine table, or skip some values.

    In fact you can achieve any output frequency by skipping certain number of values in the sine table.