The frequency is determined by the formula --

frequency = sample rate / number of samples per cycle

The size of the wavetable and the number of bits per sample is
determined by the acceptable signal-to-error noise ratio (SNR). In
this example the wavetable contains 64 bytes (one byte for each
sample). The DAC on the NB1A is an 8 bit DAC. The SNR is 30dB. If a
table size of 2048 bytes is used then the SNR would be 54.7 (Moore
1988).
For this example a sample rate of 3750Hz was chosen. If all 64 samples are output at the sample rate the frequency will be ≈ 60Hz (3750/64). This corresponds to a phase increment of one. Every 266μS the phase accumulator is incremented by one and a sample is output. For higher output frequencies the phase increment would be greater than one and samples would be skipped. For lower frequencies the phase increment would be less than one and samples would be repeated. To determine the phase increment required to generate a specific frequency --

phase increment = frequency * sine table length / sample rate

struct nco_struct { union { struct { unsigned UNUSED1 : 16; unsigned UNUSED2 : 9; unsigned round : 1; unsigned address : 6; } acc_bits; unsigned long acc_long; } acc; unsigned long inc; unsigned char control; // control byte for the TLV5620 };

`acc.acc_bits.address`

). The seventh bit
(`acc.acc_bits.round`

) is used to round up the address
value (use of the rounding bit in the phase accumulator improves the
SNR by ≈ 6dB (Moore 1988) . The lower 23 bits are used in the
phase calculation but are ignored in the address calculation. If the
wavetable size is changed then the size of
`acc.acc_bits.address`

, `acc.acc_bits.UNUSED2`

and `acc.acc_bits.UNUSED1`

can be changed.
. . #define nco_addr(i) (nco[i].acc.acc_bits.address) #define nco_round(i) (nco[i].acc.acc_bits.round) . . unsigned char addr; . . for (i = 0; i < NCO_NUM_CHS; i++) { nco_inc_phase_acc(i); addr = nco_addr(i); if (nco_round(i)) { addr += 1; addr &= NCO_ADDR_MASK; } // // update DAC channel i // . . }

With the sample rate set to 3750Hz and a wavetable size of 64 samples a 100Hz output contains 37 samples per cycle. The 60Hz output contains 58 samples per cycle.

#include <avr/interrupt.h> #include <avr/io.h> // Numerically Controlled Oscillator (NCO) // Uses the quad DAC on the NB1A to create a four channel numerically // controlled oscillator. extern "C" { #include <NCO.h> } #define UC_MOSI PB3 #define UC_MISO PB4 #define UC_SCK PB5 volatile unsigned char update_dac = 0; void setup() { // Compensating for the 12MHz XTAL // 12800 = (16/12) * 9600 // 25600 = (16/12) * 19200 Serial.begin(12800); PORTB |= (1<<NCO_DAC_LOAD) | (1<<NCO_DAC_LATCH); DDRB |= (1<<UC_MOSI) | (0<<UC_MISO) | (1<<UC_SCK) | (1<<NCO_DAC_LOAD) | (1<<NCO_DAC_LATCH); DDRD |= (1<<PD7); // Setup the SPI control register (SPCR) // and the status register (SPSR) // Page 174 ATmega328P Datasheet Rev 8025I-AVR-02/09 // SPIE = 1 SPI interrupts enabled // SPE = 1 SPI enabled // DORD = 0 Data Order MSB First (=1 LSB First) // MSTR = 1 Master mode (=0 Slave) // CPOL = 0 SCK low when idle (=1 SCK high when idle) // CPHA = 1 Sample on leading edge (=1 falling edge) // SPR1, SPR2 = 0 fosc/4 (= 1 fosc/16) // (= 2 fosc/64) // (= 3 fosc/128) // SPSR is read only except for the double speed bit // SPI2X = 1 double speed SPCR = (0<<SPIE) | (1<<SPE) | (1<<MSTR) | (1<<CPHA); SPSR = (1<<SPI2X); // page 158 // Clear timer on Compare Match (CTC) Mode // Counter (TCNT2) counts up to OCR2A and is is cleared // when TCNT2 == OCR2A use the OCF2A flag to generate an interrupt. // Each interrupt occurs at a frequency of = fclk_io / N * (1+OCR2A) // N is 1,8,32,64,256,1024 // fclk_io = 12MHz for the NB1A // With N = 256 the frequency range is 23437.5Hz (OCR2A = 0) // to 91.55Hz (OCR2A = 255) // (OCR2A, Freq) = (2, 7812Hz), (3, 5859Hz) // TCCR2A Timer/Counter Control Register A // COM2A1 = 0 Normal port operation for OC2A // COM2A0 = 0 // COM2B1 = x // COM2B0 = x // BIT3 // BIT2 // WGM21 = 1 WGM2x are the waveform generation bits for // WGM20 = 0 channel the counter-timer 2. There are three // bits WGM21 and WMG21 are in TCCRA and // WGM22 is in TCCR2B TCCR2A |= (1<<WGM21); TCCR2A &= ~((1<<COM2A1) | (1<<COM2A0) | (1<<WGM20)); // TCCR2B // FOC2A = x Force Output Compare (not used) // FOC2B = x Force Output Compare (not used) // BIT5 // BIT4 // WGM22 = 0 CTC (WGM21 and WGM20 are in TCCR2A) // CS22 = 1 Prescaler = 32 (NCO_CLOCK_DIV32) // CS21 = 0 // CS20 = 0 TCCR2B = NCO_CLOCK_DIV32; // For this NCO the prescaler is set to divide by 32. // Setting OCR2A to NCO_OCR2A (99) gives a sampling // frequency of 3750 OCR2A = NCO_OCR2A; TIMSK2 |= (1<<OCIE2A); nco_init(); nco_set_freq(0, 60); nco_set_freq(1, 100); nco_set_freq(2, 300); nco_set_freq(3, 200); sei(); } void loop() { unsigned char pt; if (update_dac) { nco_update(); update_dac = 0; } } ISR(TIMER2_COMPA_vect) { #if DEBUG if (PIND & (1<<PD7)) PORTD &= ~(1<<PD7); else PORTD |= (1<<PD7); #endif update_dac++; }

Moore, F. Richard 1988 "Table Lookup Noise for Sinusoidal Digital Oscillators"