works pretty well. has it's own font built in and room for about 950 characters of message. as presented here, there is an output bug at the end of the second line, but i reckon i'll work on that in the morning.safety director wrote:WARNING - do not look directly at uv leds. they will mess you up.
meanwhile, i can wave it around and wipe poetry onto a piece of butcher paper painted with fluorescent paint.
here's some not very good video of the chemical pov running the above code. the light does persist a little longer than it appears to in the video, but i think it will work better when i replace the current limiting resistors.
thanks to omar khayyam and edward fitzgerald for the poem.
Code: Select all
#include "avr/io.h" // i/o definitions
#include "avr/interrupt.h" // definitions for interrupts
#include "avr/pgmspace.h" // definitions for data in program memory
#include "avr/sleep.h" // definitions for idling mcu
#define GLYFWIDTH 5
//
// data in program memory
//
// foo - avrobjdump trashes the first symbol after the interrupt vectors,
// so we'll just feed it this.
// text - display text is a single string with lines delimited with nulls
// povGlyf - character bitmaps. each character is 5 bytes, one per column of
// of a 5x8 cell. low order bit is the top.
int foo PROGMEM = 666;
char text[] PROGMEM = "The Moving Finger writes; and, having writ,\0"
"Moves on; nor all your Piety nor Wit\0"
"Shall lure it back to cancel half a Line,\0"
"Nor all your Tears wash out a Word of it.";
uint8_t povGlyf[] PROGMEM = {
/* */ 0x00,0x00,0x00,0x00,0x00, /* ! */ 0x00,0x00,0x5f,0x00,0x00,
/* " */ 0x00,0x07,0x00,0x07,0x00, /* # */ 0x14,0x7f,0x14,0x7f,0x14,
/* $ */ 0x24,0x2a,0x7f,0x2a,0x12, /* % */ 0x23,0x13,0x08,0x64,0x62,
/* & */ 0x36,0x49,0x55,0x22,0x50, /* ' */ 0x00,0x05,0x03,0x00,0x00,
/* ( */ 0x00,0x1c,0x22,0x41,0x00, /* ) */ 0x00,0x41,0x22,0x1c,0x00,
/* * */ 0x08,0x2a,0x1c,0x2a,0x08, /* + */ 0x08,0x08,0x3e,0x08,0x08,
/* , */ 0x00,0x50,0x30,0x00,0x00, /* - */ 0x08,0x08,0x08,0x08,0x08,
/* . */ 0x00,0x60,0x60,0x00,0x00, /* / */ 0x20,0x10,0x08,0x04,0x02,
/* 0 */ 0x3e,0x51,0x49,0x45,0x3e, /* 1 */ 0x00,0x42,0x7f,0x40,0x00,
/* 2 */ 0x42,0x61,0x51,0x49,0x46, /* 3 */ 0x21,0x41,0x45,0x4b,0x31,
/* 4 */ 0x18,0x14,0x12,0x7f,0x10, /* 5 */ 0x27,0x45,0x45,0x45,0x39,
/* 6 */ 0x3c,0x4a,0x49,0x49,0x30, /* 7 */ 0x01,0x71,0x09,0x05,0x03,
/* 8 */ 0x36,0x49,0x49,0x49,0x36, /* 9 */ 0x06,0x49,0x49,0x29,0x1e,
/* : */ 0x00,0x36,0x36,0x00,0x00, /* ; */ 0x00,0x56,0x36,0x00,0x00,
/* < */ 0x00,0x08,0x14,0x22,0x41, /* = */ 0x14,0x14,0x14,0x14,0x14,
/* > */ 0x41,0x22,0x14,0x08,0x00, /* ? */ 0x02,0x01,0x51,0x09,0x06,
/* @ */ 0x32,0x49,0x79,0x41,0x3e, /* A */ 0x7e,0x11,0x11,0x11,0x7e,
/* B */ 0x7f,0x49,0x49,0x49,0x36, /* C */ 0x3e,0x41,0x41,0x41,0x22,
/* D */ 0x7f,0x41,0x41,0x22,0x1c, /* E */ 0x7f,0x49,0x49,0x49,0x41,
/* F */ 0x7f,0x09,0x09,0x01,0x01, /* G */ 0x3e,0x41,0x41,0x51,0x32,
/* H */ 0x7f,0x08,0x08,0x08,0x7f, /* I */ 0x00,0x41,0x7f,0x41,0x00,
/* J */ 0x20,0x40,0x41,0x3f,0x01, /* K */ 0x7f,0x08,0x14,0x22,0x41,
/* L */ 0x7f,0x40,0x40,0x40,0x40, /* M */ 0x7f,0x02,0x04,0x02,0x7f,
/* N */ 0x7f,0x04,0x08,0x10,0x7f, /* O */ 0x3e,0x41,0x41,0x41,0x3e,
/* P */ 0x7f,0x09,0x09,0x09,0x06, /* Q */ 0x3e,0x41,0x51,0x21,0x5e,
/* R */ 0x7f,0x09,0x19,0x29,0x46, /* S */ 0x46,0x49,0x49,0x49,0x31,
/* T */ 0x01,0x01,0x7f,0x01,0x01, /* U */ 0x3f,0x40,0x40,0x40,0x3f,
/* V */ 0x1f,0x20,0x40,0x20,0x1f, /* W */ 0x7f,0x20,0x18,0x20,0x7f,
/* X */ 0x63,0x14,0x08,0x14,0x63, /* Y */ 0x03,0x04,0x78,0x04,0x03,
/* Z */ 0x61,0x51,0x49,0x45,0x43, /* [ */ 0x00,0x00,0x7f,0x41,0x41,
/* \ */ 0x02,0x04,0x08,0x10,0x20, /* ] */ 0x41,0x41,0x7f,0x00,0x00,
/* ^ */ 0x04,0x02,0x01,0x02,0x04, /* _ */ 0x40,0x40,0x40,0x40,0x40,
/* ` */ 0x00,0x01,0x02,0x04,0x00, /* a */ 0x20,0x54,0x54,0x54,0x78,
/* b */ 0x7f,0x48,0x44,0x44,0x38, /* c */ 0x38,0x44,0x44,0x44,0x20,
/* d */ 0x38,0x44,0x44,0x48,0x7f, /* e */ 0x38,0x54,0x54,0x54,0x18,
/* f */ 0x08,0x7e,0x09,0x01,0x02, /* g */ 0x08,0x14,0x54,0x54,0x3c,
/* h */ 0x7f,0x08,0x04,0x04,0x78, /* i */ 0x00,0x44,0x7d,0x40,0x00,
/* j */ 0x20,0x40,0x44,0x3d,0x00, /* k */ 0x00,0x7f,0x10,0x28,0x44,
/* l */ 0x00,0x41,0x7f,0x40,0x00, /* m */ 0x7c,0x04,0x18,0x04,0x78,
/* n */ 0x7c,0x08,0x04,0x04,0x78, /* o */ 0x38,0x44,0x44,0x44,0x38,
/* p */ 0x7c,0x14,0x14,0x14,0x08, /* q */ 0x08,0x14,0x14,0x18,0x7c,
/* r */ 0x7c,0x08,0x04,0x04,0x08, /* s */ 0x48,0x54,0x54,0x54,0x20,
/* t */ 0x04,0x3f,0x44,0x40,0x20, /* u */ 0x3c,0x40,0x40,0x20,0x7c,
/* v */ 0x1c,0x20,0x40,0x20,0x1c, /* w */ 0x3c,0x40,0x30,0x40,0x3c,
/* x */ 0x44,0x28,0x10,0x28,0x44, /* y */ 0x0c,0x50,0x50,0x50,0x3c,
/* z */ 0x44,0x64,0x54,0x4c,0x44, /* { */ 0x00,0x08,0x36,0x41,0x00,
/* | */ 0x00,0x00,0x7f,0x00,0x00, /* } */ 0x00,0x41,0x36,0x08,0x00,
/* ~ */ 0x08,0x08,0x2a,0x1c,0x08, /* */ 0x08,0x1c,0x2a,0x08,0x08
};
//
// data in sram
//
char c = 0; // character being output
uint8_t pauseBefore = 95; // pause at the end of each line this long
uint8_t blink = 1; // then turn on indicator for this long
uint8_t pauseAfter = 48; // then wait this long before next line
volatile uint8_t run = 1; // main program loop runs while (run);
uint8_t buffB; // hold PORTB output 'til next interrupt
char* textPtr = text; // pointer into display text
uint8_t* glyfPtr; // pointer into character bitmap table
uint8_t glyfColumn; // how far we are through the current character
ISR (TIMER0_OVF_vect) {
if (c) { // not at end of string
PORTB = buffB; // ouput one column
if (glyfColumn++ < GLYFWIDTH) {
buffB = pgm_read_byte (glyfPtr++);
}
else { // we've output all 5 columns of character
buffB = 0; // prepare to put space between characters
c = pgm_read_byte (textPtr++); // get next character
if (c) c -= 31; // if it's not null, map to glyph array
glyfPtr = (uint8_t*)((povGlyf - 5) + ((int)c << 2) + (int)c);
glyfColumn = 0; // start counting at first column
}
}
else if (pauseBefore) { // pause before blinking green led
pauseBefore--;
}
else if (blink) { // turn on green led
blink--;
PORTD |= (1 << PD2);
}
else if (pauseAfter) { // pause after blinking green led
PORTD &= ~(1 << PD2);
pauseAfter--;
}
else if (textPtr < (char*)(text + sizeof(text))) {
c = pgm_read_byte (textPtr++);
pauseBefore = 95;
blink = 1;
pauseAfter = 48;
}
else {
run = 0;
}
}
int main (void) {
//
// set up i/o ports
//
PORTD = 0x00; // PD2 out
PORTB = 0x00; // all out
DDRD = (1 << PD2); // indicator led
DDRB = 0xff; // pov display
//
// setup timer 0, 1/1024 prescale, mode 7, 48Hz, enable interrupt
//
TCCR0A = (1 << WGM01) | (1 << WGM00); // select fast pwm w/ top = OCR0A
TCCR0B = (1 << WGM02) | (1 << CS02) | (1 << CS00); // and prescale = 1024
OCR0A = 163; // gives interrupt freq ca 48Hz
TIMSK = (1 << TOIE0); // enable overflow interrupt
sei (); // enable interrupts
MCUCR = (1 << SE); // enable idle sleep mode
while (run) {
sleep_cpu ();
}
TCCR0B = 0x00; // stop timer
DDRB = 0;
DDRD = 0;
PORTB = 0;
PORTD = 0;
MCUCR = (1 << SE) | (1 << SM0); // enable shutdown sleep mode
sleep_cpu (); // zzzzzzzzzzzz
}