#include #include #include #define TRUE (1==1) #define FALSE (!TRUE) prog_char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; prog_char smallFont[] = { 0x00, 0x00, 0x06, 0x5f, 0x06, 0x00, /* ! */ 0x00, 0x07, 0x03, 0x00, 0x07, 0x03, /* " */ 0x00, 0x24, 0x7e, 0x24, 0x7e, 0x24, /* # */ 0x00, 0x24, 0x2b, 0x6a, 0x12, 0x00, /* $ */ 0x00, 0x63, 0x13, 0x08, 0x64, 0x63, /* % */ 0x00, 0x36, 0x49, 0x56, 0x20, 0x50, /* & */ 0x00, 0x00, 0x07, 0x03, 0x00, 0x00, /* ' */ 0x00, 0x00, 0x3e, 0x41, 0x00, 0x00, /* ( */ 0x00, 0x00, 0x41, 0x3e, 0x00, 0x00, /* ) */ 0x00, 0x08, 0x3e, 0x1c, 0x3e, 0x08, /* * */ 0x00, 0x08, 0x08, 0x3e, 0x08, 0x08, /* + */ 0x00, 0x00, 0xe0, 0x60, 0x00, 0x00, /* , */ 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, /* - */ 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, /* . */ 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, /* / */ 0x00, 0x3e, 0x51, 0x49, 0x45, 0x3e, /* 0 */ 0x00, 0x00, 0x42, 0x7f, 0x40, 0x00, /* 1 */ 0x00, 0x62, 0x51, 0x49, 0x49, 0x46, /* 2 */ 0x00, 0x22, 0x49, 0x49, 0x49, 0x36, /* 3 */ 0x00, 0x18, 0x14, 0x12, 0x7f, 0x10, /* 4 */ 0x00, 0x2f, 0x49, 0x49, 0x49, 0x31, /* 5 */ 0x00, 0x3c, 0x4a, 0x49, 0x49, 0x30, /* 6 */ 0x00, 0x01, 0x71, 0x09, 0x05, 0x03, /* 7 */ 0x00, 0x36, 0x49, 0x49, 0x49, 0x36, /* 8 */ 0x00, 0x06, 0x49, 0x49, 0x29, 0x1e, /* 9 */ 0x00, 0x00, 0x6c, 0x6c, 0x00, 0x00, /* : */ 0x00, 0x00, 0xec, 0x6c, 0x00, 0x00, /* ; */ 0x00, 0x08, 0x14, 0x22, 0x41, 0x00, /* < */ 0x00, 0x24, 0x24, 0x24, 0x24, 0x24, /* = */ 0x00, 0x00, 0x41, 0x22, 0x14, 0x08, /* > */ 0x00, 0x02, 0x01, 0x59, 0x09, 0x06, /* ? */ 0x00, 0x3e, 0x41, 0x5d, 0x55, 0x1e, /* @ */ 0x00, 0x7e, 0x11, 0x11, 0x11, 0x7e, /* A */ 0x00, 0x7f, 0x49, 0x49, 0x49, 0x36, /* B */ 0x00, 0x3e, 0x41, 0x41, 0x41, 0x22, /* C */ 0x00, 0x7f, 0x41, 0x41, 0x41, 0x3e, /* D */ 0x00, 0x7f, 0x49, 0x49, 0x49, 0x41, /* E */ 0x00, 0x7f, 0x09, 0x09, 0x09, 0x01, /* F */ 0x00, 0x3e, 0x41, 0x49, 0x49, 0x7a, /* G */ 0x00, 0x7f, 0x08, 0x08, 0x08, 0x7f, /* H */ 0x00, 0x00, 0x41, 0x7f, 0x41, 0x00, /* I */ 0x00, 0x30, 0x40, 0x40, 0x40, 0x3f, /* J */ 0x00, 0x7f, 0x08, 0x14, 0x22, 0x41, /* K */ 0x00, 0x7f, 0x40, 0x40, 0x40, 0x40, /* L */ 0x00, 0x7f, 0x02, 0x04, 0x02, 0x7f, /* M */ 0x00, 0x7f, 0x02, 0x04, 0x08, 0x7f, /* N */ 0x00, 0x3e, 0x41, 0x41, 0x41, 0x3e, /* O */ 0x00, 0x7f, 0x09, 0x09, 0x09, 0x06, /* P */ 0x00, 0x3e, 0x41, 0x51, 0x21, 0x5e, /* Q */ 0x00, 0x7f, 0x09, 0x09, 0x19, 0x66, /* R */ 0x00, 0x26, 0x49, 0x49, 0x49, 0x32, /* S */ 0x00, 0x01, 0x01, 0x7f, 0x01, 0x01, /* T */ 0x00, 0x3f, 0x40, 0x40, 0x40, 0x3f, /* U */ 0x00, 0x1f, 0x20, 0x40, 0x20, 0x1f, /* V */ 0x00, 0x3f, 0x40, 0x3c, 0x40, 0x3f, /* W */ 0x00, 0x63, 0x14, 0x08, 0x14, 0x63, /* X */ 0x00, 0x07, 0x08, 0x70, 0x08, 0x07, /* Y */ 0x00, 0x71, 0x49, 0x45, 0x43, 0x00, /* Z */ 0x00, 0x00, 0x7f, 0x41, 0x41, 0x00, /* [ */ 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, /* \ */ 0x00, 0x00, 0x41, 0x41, 0x7f, 0x00, /* ] */ 0x00, 0x04, 0x02, 0x01, 0x02, 0x04, /* ^ */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, /* _ */ 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, /* ` */ 0x00, 0x20, 0x54, 0x54, 0x54, 0x78, /* a */ 0x00, 0x7f, 0x44, 0x44, 0x44, 0x38, /* b */ 0x00, 0x38, 0x44, 0x44, 0x44, 0x28, /* c */ 0x00, 0x38, 0x44, 0x44, 0x44, 0x7f, /* d */ 0x00, 0x38, 0x54, 0x54, 0x54, 0x08, /* e */ 0x00, 0x08, 0x7e, 0x09, 0x09, 0x00, /* f */ 0x00, 0x18, 0xa4, 0xa4, 0xa4, 0x7c, /* g */ 0x00, 0x7f, 0x04, 0x04, 0x78, 0x00, /* h */ 0x00, 0x00, 0x00, 0x7d, 0x40, 0x00, /* i */ 0x00, 0x40, 0x80, 0x84, 0x7d, 0x00, /* j */ 0x00, 0x7f, 0x10, 0x28, 0x44, 0x00, /* k */ 0x00, 0x00, 0x00, 0x7f, 0x40, 0x00, /* l */ 0x00, 0x7c, 0x04, 0x18, 0x04, 0x78, /* m */ 0x00, 0x7c, 0x04, 0x04, 0x78, 0x00, /* n */ 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, /* o */ 0x00, 0xfc, 0x44, 0x44, 0x44, 0x38, /* p */ 0x00, 0x38, 0x44, 0x44, 0x44, 0xfc, /* q */ 0x00, 0x44, 0x78, 0x44, 0x04, 0x08, /* r */ 0x00, 0x08, 0x54, 0x54, 0x54, 0x20, /* s */ 0x00, 0x04, 0x3e, 0x44, 0x24, 0x00, /* t */ 0x00, 0x3c, 0x40, 0x20, 0x7c, 0x00, /* u */ 0x00, 0x1c, 0x20, 0x40, 0x20, 0x1c, /* v */ 0x00, 0x3c, 0x60, 0x30, 0x60, 0x3c, /* w */ 0x00, 0x6c, 0x10, 0x10, 0x6c, 0x00, /* x */ 0x00, 0x9c, 0xa0, 0x60, 0x3c, 0x00, /* y */ 0x00, 0x64, 0x54, 0x54, 0x4c, 0x00, /* z */ 0x00, 0x08, 0x3e, 0x41, 0x41, 0x00, /* { */ 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, /* | */ 0x00, 0x00, 0x41, 0x41, 0x3e, 0x08, /* } */ 0x00, 0x02, 0x01, 0x02, 0x01, 0x00 /* ~ */ }; #define MCU_MEGAHERTZ (8) #define SCREEN_WIDTH (256) #define SCREEN_WIDTH_EXP (8) #define FRAME_WIDTH (128) #define FRAMES_IN_SCREEN (2) volatile uint8_t g_timer0StartValue = 0x80; volatile uint8_t g_column; volatile uint8_t g_frames; volatile uint8_t g_buffer_offset; volatile uint8_t g_buffer_remaining; volatile uint8_t g_rbuf[FRAME_WIDTH]; volatile uint8_t g_gbuf[FRAME_WIDTH]; volatile uint8_t g_bbuf[FRAME_WIDTH]; volatile uint16_t g_program_counter = 0; static void stopTimer0(void) { outp(0, TCCR0); } static void resetTimer0(void) { outp(g_timer0StartValue, TCNT0); } #define NO_LATCH_BIT (0) #define RED_LATCH_BIT (3) #define GREEN_LATCH_BIT (4) #define BLUE_LATCH_BIT (5) #define COLUMN_PORT_LOW PORTC #define COLUMN_PORT_HIGH PORTD #define LATCH_PORT PORTD static void setColumnLights(void) { uint8_t c; c = g_rbuf[g_column]; outp(c, COLUMN_PORT_LOW); if (c & BV(6)) { sbi(COLUMN_PORT_HIGH, 6); } else { cbi(COLUMN_PORT_HIGH, 6); } if (c & BV(7)) { sbi(COLUMN_PORT_HIGH, 7); } else { cbi(COLUMN_PORT_HIGH, 7); } sbi(LATCH_PORT, RED_LATCH_BIT); cbi(LATCH_PORT, RED_LATCH_BIT); c = g_gbuf[g_column]; outp(c, COLUMN_PORT_LOW); if (c & BV(6)) { sbi(COLUMN_PORT_HIGH, 6); } else { cbi(COLUMN_PORT_HIGH, 6); } if (c & BV(7)) { sbi(COLUMN_PORT_HIGH, 7); } else { cbi(COLUMN_PORT_HIGH, 7); } sbi(LATCH_PORT, GREEN_LATCH_BIT); cbi(LATCH_PORT, GREEN_LATCH_BIT); c = g_bbuf[g_column]; outp(c, COLUMN_PORT_LOW); if (c & BV(6)) { sbi(COLUMN_PORT_HIGH, 6); } else { cbi(COLUMN_PORT_HIGH, 6); } if (c & BV(7)) { sbi(COLUMN_PORT_HIGH, 7); } else { cbi(COLUMN_PORT_HIGH, 7); } sbi(LATCH_PORT, BLUE_LATCH_BIT); cbi(LATCH_PORT, BLUE_LATCH_BIT); } static void extinguishColumnLights(void) { outp(0, COLUMN_PORT_LOW); cbi(COLUMN_PORT_HIGH, 6); cbi(COLUMN_PORT_HIGH, 7); sbi(LATCH_PORT, RED_LATCH_BIT); cbi(LATCH_PORT, RED_LATCH_BIT); sbi(LATCH_PORT, GREEN_LATCH_BIT); cbi(LATCH_PORT, GREEN_LATCH_BIT); sbi(LATCH_PORT, BLUE_LATCH_BIT); cbi(LATCH_PORT, BLUE_LATCH_BIT); } /* Move to next column in frame, overflowing to next frame if necessary. * If we've finished the last frame, put the timer to sleep. */ static void advanceColumnMarker(void) { if (++g_column >= FRAME_WIDTH) { g_column = 0; } if (0 == --g_buffer_remaining) { ++g_frames; g_buffer_remaining = FRAME_WIDTH; } } void SIG_OVERFLOW0(void) __attribute__ ((interrupt)); void SIG_OVERFLOW0(void) { /* The check for frame overflow is here instead of advanceColumnMarker * because we want to extinguish the lights _after_ the final column has * had a full phase. If we shut down the timer and the lights right after * setting the final column, then we'd never see the final column. */ if (g_frames < FRAMES_IN_SCREEN) { resetTimer0(); setColumnLights(); advanceColumnMarker(); } else { extinguishColumnLights(); stopTimer0(); } } static void startTimer0(void) { outp(BV(CS00) + BV(CS01), TCCR0); /* CK/64 */ } static void adjustAndResetColumnTimer(void) { uint16_t elapsed; elapsed = __inw_atomic(TCNT1L); __outw_atomic(0, TCNT1L); /* Adjust the column interval only if this looks like a reasonable * cycle (roughly, mcu's CK/Hz / CK/64 / 30fps). Otherwise there * might be some ruined frames because of Hall sensor bouncing. */ if (elapsed > (MCU_MEGAHERTZ * 500) ) { uint8_t elapsed8; elapsed = elapsed >> SCREEN_WIDTH_EXP; elapsed8 = (uint8_t)elapsed; g_timer0StartValue = 255 - elapsed8; } } /* The Hall sensor tripped. Start at column 0 again; restart the * column timer, which may have fallen asleep; and start the rotation * counter all over again. */ void SIG_INTERRUPT0(void) __attribute__ ((interrupt)); void SIG_INTERRUPT0(void) { g_column = g_buffer_offset; g_buffer_remaining = FRAME_WIDTH; g_frames = 0; startTimer0(); adjustAndResetColumnTimer(); } /* This counter never stops. */ static void startCounter1(void) { outp(BV(CS10) + BV(CS11), TCCR1B); /* CK/64 */ } static void enableTimer0Overflow(void) { timer_enable_int(BV(TOIE0)); } static void enableExternalInterrupt0(void) { outp(BV(ISC01), MCUCR); /* falling edge of INT0 triggers interrupt. */ enable_external_int(BV(INT0)); } void emit_character(char c, uint8_t col_num, uint8_t color_mask) { uint8_t bitmap[6]; uint8_t i; if (c >= 33) { PGM_P sfp = smallFont + (c - 33) * 6; memcpy_P(bitmap, sfp, 6); } else { bitmap[0] = bitmap[1] = bitmap[2] = bitmap[3] = bitmap[4] = bitmap[5] = 0; } for (i = 0; i < 6; i++) { uint8_t column = bitmap[i]; if ((1 & color_mask)) { g_rbuf[col_num + i] = column; } else { g_rbuf[col_num + i] = 0; } if ((2 & color_mask)) { g_gbuf[col_num + i] = column; } else { g_gbuf[col_num + i] = 0; } if ((4 & color_mask)) { g_bbuf[col_num + i] = column; } else { g_bbuf[col_num + i] = 0; } } } static void emit_string(uint8_t *s, uint8_t offset) { uint8_t color_mask = 1; uint8_t i = 0; while ('\0' != *s) { emit_character(*s, i + offset, color_mask); i += 6; color_mask++; if (color_mask > 7) { color_mask = 1; } s++; } } static void dumpHex(uint8_t *s, uint8_t n) { uint8_t color_mask = 1; uint8_t i = 0; while (n-- > 0) { uint8_t temp_char; uint8_t binary_digit = *s; memcpy_P(&temp_char, &hexDigits[binary_digit >> 4], 1); emit_character(temp_char, i, color_mask); i += 6; memcpy_P(&temp_char, &hexDigits[binary_digit & 0x0f], 1); emit_character(temp_char, i, color_mask); i += 6; color_mask++; if (color_mask > 7) { color_mask = 1; } s++; } } //0123456789012345678901234567890123456789012 uint8_t msg[22] = "The Quoting, Whirled "; static void fillFrameBuffer(uint8_t which_word) { switch (which_word) { case 0: /* 123456789012345678901 */ emit_string("test test test test ", 0); emit_string(msg, 0); break; case 1: /* 123456789012345678901 */ emit_string("aaabbbcccdddeeefffggg", 0); break; case 2: /* 123456789012345678901 */ emit_string("!@#$%^&*()!@#$%^!@#$%", 0); break; } } static void shiftVirtualBufferStart(uint8_t forward, uint8_t rate) { if (forward) { g_buffer_offset++; if (g_buffer_offset > FRAME_WIDTH) { g_buffer_offset = 0; } } else { if (0 == g_buffer_offset) { g_buffer_offset = FRAME_WIDTH - 1; } else { g_buffer_offset--; } } } static void setLatch(uint8_t bits) { uint8_t qv = bits; outp(qv, COLUMN_PORT_LOW); if (64 & qv) { sbi(COLUMN_PORT_HIGH, 6); } else { cbi(COLUMN_PORT_HIGH, 6); } if (128 & qv) { sbi(COLUMN_PORT_HIGH, 7); } else { cbi(COLUMN_PORT_HIGH, 7); } } static void clearLatch(void) { outp(0, COLUMN_PORT_LOW); cbi(COLUMN_PORT_HIGH, 6); cbi(COLUMN_PORT_HIGH, 7); } static void lightPixels(uint8_t color, uint8_t pixels) { if (color & 1) { setLatch(pixels); } else { clearLatch(); } sbi(LATCH_PORT, RED_LATCH_BIT); cbi(LATCH_PORT, RED_LATCH_BIT); if (color & 2) { setLatch(pixels); } else { clearLatch(); } sbi(LATCH_PORT, GREEN_LATCH_BIT); cbi(LATCH_PORT, GREEN_LATCH_BIT); if (color & 4) { setLatch(pixels); } else { clearLatch(); } sbi(LATCH_PORT, BLUE_LATCH_BIT); cbi(LATCH_PORT, BLUE_LATCH_BIT); } static void doDelay(uint8_t c) { uint8_t i, j, k; for (i = 0; i < c; i++) { for (j = 0; j < 32; j++) { for (k = 0; k < 32; k++) { } } } } #define STARTUP_SEQUENCE_DELAY (32) static void doStartupSequence(void) { uint8_t c; for (c = 1; c < 8; c++) { uint8_t n = 2; for (n = 0; n < 2; n++) { int8_t q; for (q = 0; q < 8; q++) { lightPixels(c, (uint8_t)BV(q)); doDelay(STARTUP_SEQUENCE_DELAY); } for (; q >= 0; q--) { lightPixels(c, (uint8_t)BV(q)); doDelay(STARTUP_SEQUENCE_DELAY); } } } for (c = 0; c < 8; c++) { lightPixels(c, (uint8_t)0xff); doDelay(255); } for (c = 0; c < 8; c++) { extinguishColumnLights(); doDelay(255); lightPixels(7, (uint8_t)0xff); doDelay(255); } extinguishColumnLights(); } enum { MODE_IDLE, MODE_STATIC, MODE_SCROLL_FORWARD, MODE_SCROLL_BACKWARD, MODE_RAINBOW_CYCLE, MODE_INVERTING }; static void handleModeIdle(uint8_t delay) { doDelay(delay); } #define SPI_SS (2) #define SPI_MOSI (3) #define SPI_MISO (4) #define SPI_SCK (5) /* Enable eight output pins and three latch pins. * Also MOSI, SCK, and SS. */ static void enablePins(void) { outp(BV(PC0) + BV(PC1) + BV(PC2) + BV(PC3) + BV(PC4) + BV(PC5), DDRC); outp(BV(PD3) + BV(PD4) + BV(PD5) + BV(PD6) + BV(PD7), DDRD); outp(BV(SPI_SCK) + BV(SPI_MOSI) + BV(SPI_SS), DDRB); /* Pull up inputs, and set outputs high. */ outp(0xff, PORTB); outp(0xff, PORTC); outp(0xff, PORTD); } #define SPI_CMD_WRSR (1) #define SPI_CMD_WRIT (2) #define SPI_CMD_READ (3) #define SPI_CMD_WRDI (4) #define SPI_CMD_RDSR (5) #define SPI_CMD_WREN (6) static void eeprom_enable(void) { cbi(PORTB, SPI_SS); // set SS low (enable) } static void eeprom_disable(void) { sbi(PORTB, SPI_SS); // set SS low (enable) } static void spi_init(void) { eeprom_disable(); outp(BV(MSTR), SPCR); } static void spi_enable(void) { eeprom_disable(); sbi(SPCR, SPE); sbi(SPCR, MSTR); } static void spi_disable(void) { eeprom_disable(); cbi(SPCR, SPE); } static void loop_until_set(uint8_t port, uint8_t val) { volatile uint8_t byte; uint8_t thebit = BV(val); do { byte = inp(SPSR); } while (0 == (thebit & byte)); } static uint8_t spi_both_ways(uint8_t d) { outp(d, SPDR); loop_until_set(SPSR, SPIF); return inp(SPDR); } static void handleModeStatic(uint8_t which_word) { uint8_t i; uint8_t c[10] = { 0xde, 0xad, 0xbe, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xff }; c[0] = 0xdf; dumpHex(c, 10); spi_enable(); for (i = 0; i < 10; i++) { eeprom_enable(); spi_both_ways(SPI_CMD_READ); spi_both_ways(0x00); // high byte of addr spi_both_ways(i); // low byte of addr c[i] = spi_both_ways(0xff); eeprom_disable(); dumpHex(c, 10); } spi_disable(); dumpHex(c, 10); } static void writeSomeStuff(void) { uint8_t i; volatile uint8_t status; uint8_t c = 0; uint8_t j = 0; spi_enable(); lightPixels(1, (uint8_t)BV(0)); eeprom_enable(); spi_both_ways(SPI_CMD_WREN); eeprom_disable(); lightPixels(4, (uint8_t)BV(1)); eeprom_enable(); spi_both_ways(SPI_CMD_WRSR); spi_both_ways(0); // allow everything eeprom_disable(); doDelay(255); lightPixels(1, (uint8_t)BV(1)); for (i = 0; i < 10; i++) { lightPixels(2, (uint8_t)BV(2)); eeprom_enable(); spi_both_ways(SPI_CMD_WREN); eeprom_disable(); eeprom_enable(); spi_both_ways(SPI_CMD_WRIT); spi_both_ways(0x00); // high byte of addr spi_both_ways(i); // low byte of addr spi_both_ways('a' + i); eeprom_disable(); doDelay(255); lightPixels(1, (uint8_t)BV(3)); #if 0 doDelay(1); #else do { eeprom_enable(); spi_both_ways(SPI_CMD_RDSR); // RDSR status = spi_both_ways(0xff); eeprom_disable(); j++; if (0 == j) { c++; if (c > 3) { c = 0; } } lightPixels(c, (uint8_t)status); } while (1 == (BV(0) & status)); #endif lightPixels(2, (uint8_t)BV(4)); } eeprom_enable(); spi_both_ways(SPI_CMD_WRDI); eeprom_disable(); spi_disable(); lightPixels(2, (uint8_t)BV(5)); } static uint8_t fetchNextMode(uint8_t *x, uint8_t *y, uint8_t *z) { g_program_counter; return MODE_STATIC; } int main(void) { enablePins(); spi_init(); doStartupSequence(); writeSomeStuff(); doStartupSequence(); enableTimer0Overflow(); enableExternalInterrupt0(); startCounter1(); sei(); fillFrameBuffer(0); { uint8_t mode; uint8_t x, y, z; for (;;) { mode = fetchNextMode(&x, &y, &z); switch (mode) { case MODE_IDLE: handleModeIdle(x); break; case MODE_STATIC: // handleModeStatic(x); do { } while (TRUE); break; case MODE_SCROLL_FORWARD: shiftVirtualBufferStart(TRUE, 128); break; case MODE_SCROLL_BACKWARD: shiftVirtualBufferStart(FALSE, 128); break; case MODE_RAINBOW_CYCLE: break; case MODE_INVERTING: break; default: break; } } } }