well, that was embarassing. made the make: blog and i lose a wire off my bodhilabs 3.3v boost supply and can't debug the color fading code. well, i could have, but i'd have been unable to test it. anyway, a little desoldering, a little soldering and a couple of volatile declarations later and all's well.
rather than try to explain how it works, i'll wait for questions.
Code: Select all
#include "avr/io.h" // i/o definitions
#include "avr/interrupt.h" // definitions for interrupts
#include "avr/pgmspace.h" // definitions for keeping data in program memory
#include "avr/sleep.h" // definitions for idling mcu
//
// mcu dependent definitions
// for mega168 leds are attached to portc, for tiny2313 to portb
//
#ifdef __AVR_ATmega168__
#define LRED PC0
#define LGRN PC1
#define LBLU PC2
#define RRED PC3
#define RGRN PC4
#define RBLU PC5
#define Timer1Mask TIMSK1
#define SleepModeIdle() SMCR &= ~((1 << SM2) | (1 << SM1) | (1 << SM0))
#define SleepEnable() SMCR |= (1 << SE)
#elif defined __AVR_ATtiny2313__
#define LRED PB0
#define LGRN PB1
#define LBLU PB2
#define RRED PB5
#define RGRN PB6
#define RBLU PB7
#define SleepModeIdle() MCUCR &= ~((1 << SM1) | (1 << SM0))
#define SleepEnable() MCUCR |= (1 << SE)
#define Timer1Mask TIMSK
#else
#error "Target must be ATMEGA168 or ATTINY2313"
#endif
struct notestep { // type for a 24 bit fixed point value
uint8_t integer;
uint16_t fraction;
};
//
// names for offset frequencies correspond to their positions in the following
// table, plus a value for the end of a tune
//
enum {DELTA2, DELTA3, THETA4, THETA5, THETA6, THETA7, ALPHA8, ALPHA9,
ALPHA10, ALPHA11, ALPHA12, ALPHA13, BETA14, BETA15, BETA16, BWEND};
//
// the constants used to build the note table are calculated by multiplying
// the note frequency by the size of the sinewave table, (256), then shifting
// that result 16 bits to the left, (i.e. multiply by 65536), to move the integer
// part and make room for a 16 bit fraction, and finally dividing the whole thing
// by the frequency of the interrupt that does the audio output, (15625Hz).
//
// the interrupt frequency is F_CPU / (256 (timer TOP value) * 2) (because timer counts
// up then down between interrupts).
//
// that's (note_freq * 256 * 65536) / (F_CPU / 512)
//
// which is (note_freq * 8589934592) / F_CPU
//
// what we wind up with is a table of 24 bit fixed point values, (8 bit integer
// and 16 bit fraction), that can be used as an increment to index through the
// sine wave table while the frequency is being played.
//
#define notecalc(s) {(uint8_t)(s>>16),(uint16_t)(s&0xffff)}
const struct notestep notetab[] PROGMEM = {
notecalc ((202ULL * 8589934592ULL) / (uint64_t)F_CPU), // delta
notecalc ((203ULL * 8589934592ULL) / (uint64_t)F_CPU),
notecalc ((204ULL * 8589934592ULL) / (uint64_t)F_CPU), // theta
notecalc ((205ULL * 8589934592ULL) / (uint64_t)F_CPU),
notecalc ((206ULL * 8589934592ULL) / (uint64_t)F_CPU),
notecalc ((207ULL * 8589934592ULL) / (uint64_t)F_CPU),
notecalc ((208ULL * 8589934592ULL) / (uint64_t)F_CPU), // alpha
notecalc ((209ULL * 8589934592ULL) / (uint64_t)F_CPU),
notecalc ((210ULL * 8589934592ULL) / (uint64_t)F_CPU),
notecalc ((211ULL * 8589934592ULL) / (uint64_t)F_CPU),
notecalc ((212ULL * 8589934592ULL) / (uint64_t)F_CPU),
notecalc ((213ULL * 8589934592ULL) / (uint64_t)F_CPU), // beta
notecalc ((214ULL * 8589934592ULL) / (uint64_t)F_CPU),
notecalc ((215ULL * 8589934592ULL) / (uint64_t)F_CPU),
notecalc ((216ULL * 8589934592ULL) / (uint64_t)F_CPU),
notecalc ((217ULL * 8589934592ULL) / (uint64_t)F_CPU)
};
//
// table of beat durations in 1/100 sec increments
//
enum {N1s, N5s, N10s, N15s, N20s, N25s, N30s, N60s};
#if F_CPU == 8000000
uint16_t durtab[] = { 100, 500, 1000, 1500, 2000, 2500, 3000, 6000 };
#elif F_CPU == 16000000
uint16_t durtab[] = { 200, 1000, 2000, 3000, 4000, 5000, 6000, 12000 };
#endif
#define BETA BETA14
#define ALPHA ALPHA11
#define THETA THETA7
#define DELTA DELTA2
uint8_t bwtab[] PROGMEM = {
BETA, N60s, ALPHA, N10s, BETA, N20s, ALPHA, N15s, BETA, N15s, ALPHA, N20s,
BETA, N10s, ALPHA, N30s, BETA, N5s, ALPHA, N60s, THETA, N10s, ALPHA, N30s,
THETA, N20s, ALPHA, N30s, THETA, N30s, ALPHA, N15s, THETA, N60s, ALPHA, N15s,
BETA, N1s, ALPHA, N15s, THETA, N60s, DELTA, N1s, THETA, N10s, DELTA, N1s,
THETA, N10s, DELTA, N1s, THETA, N30s, ALPHA, N15s, BETA, N1s, ALPHA, N15s,
THETA, N30s, ALPHA, N15s, BETA, N1s, ALPHA, N20s, BETA, N5s, ALPHA, N20s,
BETA, N15s, ALPHA, N15s, BETA, N20s, ALPHA, N10s, BETA, N25s, ALPHA, N5s,
BETA, N60s, BWEND, BWEND
};
const uint8_t sinetab[] PROGMEM = {
127,130,133,136,139,143,146,149,152,155,158,161,164,167,170,173,
176,178,181,184,187,190,192,195,198,200,203,205,208,210,212,215,
217,219,221,223,225,227,229,231,233,234,236,238,239,240,242,243,
244,245,247,248,249,249,250,251,252,252,253,253,253,254,254,254,
254,254,254,254,253,253,253,252,252,251,250,249,249,248,247,245,
244,243,242,240,239,238,236,234,233,231,229,227,225,223,221,219,
217,215,212,210,208,205,203,200,198,195,192,190,187,184,181,178,
176,173,170,167,164,161,158,155,152,149,146,143,139,136,133,130,
127,124,121,118,115,111,108,105,102, 99, 96, 93, 90, 87, 84, 81,
78, 76, 73, 70, 67, 64, 62, 59, 56, 54, 51, 49, 46, 44, 42, 39,
37, 35, 33, 31, 29, 27, 25, 23, 21, 20, 18, 16, 15, 14, 12, 11,
10, 9, 7, 6, 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0,
0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9,
10, 11, 12, 14, 15, 16, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35,
37, 39, 42, 44, 46, 49, 51, 54, 56, 59, 62, 64, 67, 70, 73, 76,
78, 81, 84, 87, 90, 93, 96, 99,102,105,108,111,115,118,121,124
};
//
// these data help us make our way through a song
//
volatile uint8_t* song_ptr; // pointer to position in song
volatile uint8_t song_flags; // some, er one flag, PLAY
#define S_PLAY 0
uint8_t song_note_tone; // note retrieved from song
uint8_t song_note_time; // whole note, half note, whatever from song
uint16_t song_note_duration; // count down actual duration of note in .01 sec
//
// these data are used for tone generation
//
struct notestep basestep = notecalc ((200ULL * 8589934592ULL) / (uint64_t)F_CPU);
volatile struct notestep offsetstep; // step size for offset frequency
struct notestep baseaccumulator; // base frequency phase accumulator
struct notestep offsetaccumulator; // offset frequency phase accumulator
uint8_t basephase; // base frequency phase
uint8_t offsetphase; // offset frequency phase
uint8_t basesample; // base frequency sample
uint8_t offsetsample; // offset frequency sample
//
// these data along with the base and offset phase values control the leds
//
uint8_t ledstate;
uint8_t laststate;
uint8_t portbuffer;
//
// colorfading datums
//
uint8_t iteration; // iteration counter for fading blue and green
uint8_t redcomp;
uint8_t greencomp;
uint8_t bluecomp;
volatile uint8_t red; // red intensity, 0-127, inclusive
volatile uint8_t green; // green intensity, 0-127, inclusive
volatile uint8_t blue; // blue intensity, 0-127, inclusive
//
// these data are used for time keeping
//
uint8_t intcount = 156; // count interrupts 156.25 = .01 sec
volatile uint8_t cents; // .01 sec elapsed, used to correct time
ISR (TIMER1_OVF_vect) {
if (song_flags & (1 << S_PLAY)) {
//
// first output sample
//
OCR1A = basesample;
OCR1B = offsetsample;
#ifdef __AVR_ATtiny2313__
PORTB = (PORTB & ((1 << PB3) | (1 << PB4))) | portbuffer;
#elif defined __AVR_ATmega168__
PORTC = portbuffer;
#endif
//
// do timing stuff
//
if (--intcount == 0) { // count interrupts
cents++; // increment elapsed .01 seconds
intcount = (cents & 0x03) ? 156 : 157;// ludicrous attention to precision
if (--song_note_duration == 0) { // note finished ?
song_note_tone = pgm_read_byte (song_ptr++) & 0x0f;
song_note_time = pgm_read_byte (song_ptr++) & 0x07;
song_note_duration = durtab[song_note_time];
if (song_note_tone < BWEND) {
offsetstep.integer = pgm_read_byte (&(notetab[song_note_tone].integer));
offsetstep.fraction = pgm_read_word (&(notetab[song_note_tone].fraction));
}
else {
song_flags &= ~(1 << S_PLAY);
offsetstep.integer = 0;
offsetstep.fraction = 0;
}
}
}
//
// now get next offset frequency sample
//
offsetaccumulator.fraction += offsetstep.fraction;
if (SREG & (1 << SREG_C)) // on carry
offsetaccumulator.integer++; // add 1 to integer part
offsetaccumulator.integer += offsetstep.integer;
offsetphase = offsetaccumulator.integer;// move integer part of accumulator to
// phase pointer
if (offsetaccumulator.fraction & 0x8000)// if accumulator fraction > 1/2
offsetphase++; // round up
offsetsample = pgm_read_byte(&(sinetab[offsetphase]));// get sample for next time.
//
// then get next base frequency sample
//
baseaccumulator.fraction += basestep.fraction;
if (SREG & (1 << SREG_C)) // on carry
baseaccumulator.integer++; // add 1 to integer part
baseaccumulator.integer += basestep.integer;
basephase = baseaccumulator.integer;// move integer part of accumulator to
// phase pointer
if (baseaccumulator.fraction & 0x8000)// if accumulator fraction > 1/2
basephase++; // round up
basesample = pgm_read_byte(&(sinetab[basephase]));// get sample for next time.
//
// figure out next led state for red
//
ledstate = (offsetphase - basephase) & 0x80;
if (laststate ^ ledstate) {
laststate = ledstate;
portbuffer ^= (1 << LRED) | (1 << RRED);
}
//
// do pwm for green and blue
//
if (iteration == 128) {
iteration = 0;
redcomp = red;
greencomp = green;
bluecomp = blue;
portbuffer |= (1 << LGRN) | (1 << LBLU) | (1 << RGRN) | (1 << RBLU);
}
// if (redcomp == iteration) portbuffer &= ~((1 << LRED) | (1 << RRED));
if (greencomp == iteration) portbuffer &= ~((1 << LGRN) | (1 << RGRN));
if (bluecomp == iteration) portbuffer &= ~((1 << LBLU) | (1 << RBLU));
iteration += 1;
}
}
void setup (void) {
//
// first turn off some stuff the arduino environment turns on by default
//
#ifdef Wiring_h
TCCR0B = 0; // stop timer 0
TCCR2B = 0; // stop timer 2
TIMSK0 = 0; // disable timer0 overflow interrupt
#endif
//
// then get the data direction registers set
//
DDRD = (1 << PD1); // PD0 is serial in, PD1 is serial out,
// PD2 is wakeup button
#ifdef __AVR_ATmega168__
DDRC = 0x3f; // PC0-5 are outputs for leds
DDRB = (1 << PB1) | (1 << PB2); // OC1A, OC1B are audio outputs
#elif defined __AVR_ATtiny2313__
DDRB = 0xff; // all outputs
#endif
//
// now set up timer1 - phase and frequency correct pwm mode 8
// frequency will be 8MHz / 1 / 256 / 2 = 15625Hz
// that's FCPU / prescaler / (TOP + 1) and then we divide by 2 because mode 8
// gives dual slope operation
//
TCCR1A = (1 << COM1A1) | (1 << COM1B1);// clear OC1A/B on upcount
// set OC1A/B on douncount
TCCR1B = (1 << WGM13) | (1 << CS10); // phase and frequency correct
// pwm, mode 8, prescale = 1
ICR1 = 255; // TOP = 255
//
// enable and disable some interrupts
//
Timer1Mask = (1 << TOIE1); // enable timer 1 overflow interrupt
}
uint8_t mycents;
uint8_t foo;
uint8_t inc;
#ifndef Wiring_h
int main (void) {
setup ();
sei ();
#else
void loop (void) {
#endif
green = 63; // turn on blue and green at 50%
blue = 0;
song_ptr = &bwtab[0]; // point to beginning of brainwave sequence
song_note_duration = 1; // kludge eliminates 655 second wait
song_flags |= (1 << S_PLAY); // start sequence
while (song_flags & (1 << S_PLAY)) { // let sequence run as long as it likes
if (cents != mycents) {
mycents = cents;
foo = (foo + 1) & 15;
if (foo == 0) { // this will update pwm values ca 6 times per second
if (green == 0) inc = 1;
else if (green == 63) inc = 255;
green += inc;
blue -= inc;
}
}
}
TCCR1B &= ~((1 << CS02) || (1 << CS01) || (1 << CS00));// stop timer 1
#ifdef __AVR_ATmega168__
PORTC = 0; // make sure leds are off
DDRC = 0; // make all PORTC pins inputs
#endif
PORTB = 0; // all off
DDRB = 0; // make all PORTB pins inputs
DDRD = 0; // make all PORTD pins inputs
SleepModeIdle();
SleepEnable();
sleep_cpu (); // zzzzzzzzzz
}
// ... ad nauseum