#ifndef F_CPU
#define F_CPU 8000000UL
#endif
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <math.h>
/* Reference clock needs to be measured for the crystal per board (about 8MHz).
* It is then divided by 510.
* 8M / 510 = 15686.2740
*/
#define REFCLK 15686.274 // TODO: measure me and adjust
// output frequency
#define FREQ_MIN 0 // default: 0
#define FREQ_MAX 60 // default: 60
// pot values might not permit cranking full range so adjust here
#define POT_MAX 1023 // default: 1023
/*
* LUT for sin(t) generated (by spreadsheet) using this formula:
* x = ROUND( (255/2) * (1+SIN((2*PI()) * (t/256))) )
*
* where 0 <= t <= 255 ; keeping one period of sine in nvram
*
* Keeping LUT is way faster than calculating all the time
*/
const uint8_t sin256[] PROGMEM =
{
// 0 1 2 3 4 5 6 7 8 9 a b c d e f
128,131,134,137,140,143,146,149,152,155,158,162,165,167,170,173, // 00:0f
176,179,182,185,188,190,193,196,198,201,203,206,208,211,213,215, // 10:1f
218,220,222,224,226,228,230,232,234,235,237,238,240,241,243,244, // 20:2f
245,246,248,249,250,250,251,252,253,253,254,254,254,255,255,255, // 30:3f
255,255,255,255,254,254,254,253,253,252,251,250,250,249,248,246, // 40:4f
245,244,243,241,240,238,237,235,234,232,230,228,226,224,222,220, // 50:5f
218,215,213,211,208,206,203,201,198,196,193,190,188,185,182,179, // 60:6f
176,173,170,167,165,162,158,155,152,149,146,143,140,137,134,131, // 70:7f
128,124,121,118,115,112,109,106,103,100, 97, 93, 90, 88, 85, 82, // 80:8f
79, 76, 73, 70, 67, 65, 62, 59, 57, 54, 52, 49, 47, 44, 42, 40, // 90:9f
37, 35, 33, 31, 29, 27, 25, 23, 21, 20, 18, 17, 15, 14, 12, 11, // a0:af
10, 9, 7, 6, 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, // b0:bf
0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9, // c0:cf
10, 11, 12, 14, 15, 17, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35, // d0:df
37, 40, 42, 44, 47, 49, 52, 54, 57, 59, 62, 65, 67, 70, 73, 76, // e0:ef
79, 82, 85, 88, 90, 93, 97,100,103,106,109,112,115,118,121,124 // f0:ff
};
// phase offsets (n * 255 / 3)
#define A_OFFSET 0 // (0x00)
#define B_OFFSET 85 // (0x55)
#define C_OFFSET 170 // (0xaa)
int freq; // output frequency
const double TUN_K=( 4294967296.0 / REFCLK ); // tuning coefficient
// variables in ISR
volatile uint8_t icnt; //
volatile uint8_t icnt1; //
volatile uint8_t c4ms; // counter incremented every 4ms
volatile unsigned long phaccu; // phase accumulator
volatile unsigned long tune; // dds tuning word
void GPIO_SETUP(void);
void SETUP_TIMERS(void);
int main(void)
{
GPIO_SETUP();
sei();
SETUP_TIMERS();
while(1)
{
if(c4ms > 250)
{
c4ms = 0;
freq = 50;
TIMSK&=~(1<<TOIE1); // disable Timer1 interrupt
tune = TUN_K*freq; // update DDS tuning word
TIMSK|=(1<<TOIE1); // re-enable Timer2 interrupt
}
}
}
void GPIO_SETUP(void)
{
//OUTPUT PINS
DDRB|=(1<<PORTB5)|(1<<PORTB6)|(1<<PORTB7); //High Side PWM
DDRE|=(1<<PORTE3)|(1<<PORTE4)|(1<<PORTE5); //Low Side PWM
DDRD|=(1<<PORTD1); //Overcurrent LED
//INPUT PINS
DDRF&=~(1<<PORTF0); //ADC0
DDRF&=~(1<<PORTF1); //ADC1
DDRC&=~(1<<PORTC0); // PEDAL
DDRC&=~(1<<PORTC1); //MOD_SELECT
}
void SETUP_TIMERS(void)
{
TIMSK&=~(1<<TOIE0);
TIMSK&=~(1<<TOIE2);
/* Enable Timer 1
TIMSK|=(1<<TOIE1);
/* Enable Timer 3
ETIMSK|=(1<<TOIE3);
TCCR1B |=(1<<CS10);
TCCR1A|=(1<<WGM10);
TCCR1A |=(1<<COM1A1)|(1<<COM1B1)|(1<<COM1C1); // Clear OC1A on compare match -- and --
TCCR3B |=(1<<CS30);
TCCR3A|=(1<<WGM30);
TCCR3A |=(1<<COM3A1)|(1<<COM3A0)|(1<<COM3B1)|(1<<COM3B0)|(1<<COM3C1)|(1<<COM3C0);
}
/**
* Timer 1 interrupt - 15.6kHz ~= 64us
* Fout = (tuning word * REFCLK) / 2^32
* Runtime: 8us (incl stack push + pop)
*/
ISR(TIMER1_OVF_vect)
{
phaccu += tune; // soft DDS, phase accu with 32 bits
icnt = phaccu >> 24; // use upper 8 bits for phase accu as frequency information
// read byte from sin256 table using the phase offset as the address
// place into output compare registers
OCR1A = pgm_read_byte_near(sin256 + (uint8_t)(icnt + A_OFFSET));
OCR3A = pgm_read_byte_near(sin256 + (uint8_t)(icnt + A_OFFSET));
OCR1B = pgm_read_byte_near(sin256 + (uint8_t)(icnt + B_OFFSET));
OCR3B = pgm_read_byte_near(sin256 + (uint8_t)(icnt + B_OFFSET));
OCR1C = pgm_read_byte_near(sin256 + (uint8_t)(icnt + C_OFFSET));
OCR3C = pgm_read_byte_near(sin256 + (uint8_t)(icnt + C_OFFSET));
if (icnt1++ == 125) // increment variable c4ms every 4 milliseconds
{
c4ms++;
icnt1 = 0;
}
}