The display is lit by two high power SMD LEDs, each creating a display area of 30 x 8 pixel (5 letters). These are masked from each other's light and the non-lit area with thin plastic inserts.
A hall sensor and a magnet is responsible for synchronizing the LEDs with the rotation of the platter. The sensor is fixed on a small wooden adapter below the disk.
A tiny piece of neodymium magnet (also salvaged from the HDD) is superglued on the bottom of the disk.
/*
* main.c
*
* Created on: 2011.02.15.
* Author: Antok Adam
*/
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>
#include "font5x7.h"
#define ROLLTIME 500;
#define CURSORTIME 5000;
#define LED_PORT PORTB
#define LED_DDR DDRB
#define LED1 (1<<3)
#define LED2 (1<<4)
#define DB_CHECKS 10
#define DB_PINS PIND
#define ENC_J (1<<4)
#define ENC_B (1<<5)
#define ENC_K (1<<6)
//void debounce(void);
uint8_t state[DB_CHECKS];
uint8_t db_index;
uint8_t debouncedState;
uint8_t btnFlags;
uint8_t nextchar;
int8_t rollamount = 0;
int8_t rollcnt = 0;
uint16_t rolltimer = 0;
uint16_t cursortimer = 0;
int8_t cursor = 0;
uint8_t cursormode = 0;
uint8_t message[10];
volatile int8_t sor = 0;
volatile int8_t sor2 = 0;
volatile int8_t oszlop = 0;
volatile int8_t oszlop2 = 0;
volatile uint8_t debounceflag = 0;
volatile unsigned char pixelbuffer[60];
int main(void){
LED_DDR = LED1 | LED2;
PORTD = 0xFF;
MCUCR = (1<<ISC01);
GIMSK = (1<<INT0);
TCCR1B = (1<<WGM12)|(1<<CS10);
OCR1A = 746;
TCCR0A = (1<<WGM01);
TCCR0B = (1<<CS02);
OCR0A = 2;
TIMSK = (1<<OCIE1A)|(1<<OCIE0A);
sei();
//eeprom_write_block((const void*)&message, (void*)0,10);
eeprom_read_block((void*)&message,(const void*)0,10);
nextchar = message[cursor];
for (uint8_t c=0;c<10;c++){
for (uint8_t i=0;i<5;i++){
pixelbuffer[c*6+i] = (pgm_read_byte(&Font5x7[((message[c] - 0x20) * 5) + i]));
}
}
debouncedState = DB_PINS;
for(uint8_t i=0; i<DB_CHECKS;i++){
state[i] = DB_PINS;
}
while(1){
if (debounceflag){
debounce();
debounceflag = 0;
}
if (btnFlags){
if (btnFlags & ENC_J){
if (cursormode){
rollamount++;
} else {
printcursor(cursor,0);
if (++cursor==11){
cursor = 0;
eeprom_write_block((const void*)&message, (void*)0,10);
}
nextchar = message[cursor];
}
}
if (btnFlags & ENC_B){
if (cursormode){
rollamount--;
} else {
printcursor(cursor,0);
if (--cursor==-1) cursor = 9;
nextchar = message[cursor];
}
}
if (btnFlags & ENC_K){
cursormode ^= 0x01;
}
btnFlags = 0;
}
if (rollamount > 0 && rollcnt==0){
++nextchar;
message[cursor] = nextchar;
rollcnt=8;
rolltimer = ROLLTIME;
rollamount--;
}
if (rollamount < 0 && rollcnt==0){
--nextchar;
message[cursor] = nextchar;
rollcnt=-8;
rolltimer = ROLLTIME;
rollamount++;
}
if (rollcnt && !rolltimer){
printchar(nextchar,cursor);
rolltimer = ROLLTIME;
}
if (!cursortimer && cursor != 10){
printcursor(cursor,2);
cursortimer = CURSORTIME;
}
}
}
void printchar(uint8_t c, uint8_t h){
if (rollcnt>0){
rollcnt--;
for (uint8_t i=0;i<5;i++){
pixelbuffer[h*6+i] &= ~0x80;
pixelbuffer[h*6+i] = pixelbuffer[h*6+i]<<1;
pixelbuffer[h*6+i] |= (pgm_read_byte(&Font5x7[((c - 0x20) * 5) + i]))>>rollcnt;
}
}
if (rollcnt<0){
rollcnt++;
for (uint8_t i=0;i<5;i++){
pixelbuffer[h*6+i] &= ~0x80;
pixelbuffer[h*6+i] = pixelbuffer[h*6+i]>>1;
pixelbuffer[h*6+i] |= (pgm_read_byte(&Font5x7[((c - 0x20) * 5) + i]))<<(-rollcnt);
}
}
}
void printcursor(uint8_t h, uint8_t mode){
for (uint8_t i=0;i<5;i++){
if (mode==0)
pixelbuffer[h*6+i] &= ~0x80;
else if (mode==1)
pixelbuffer[h*6+i] |= 0x80;
else
pixelbuffer[h*6+i] ^= 0x80;
}
}
ISR(INT0_vect){
TCNT1 = 600;
sor = 4;
oszlop = 17;
LED_PORT &= ~(LED1|LED2);
}
ISR(TIMER0_COMPA_vect){
debounceflag = 1;
if (rolltimer)
rolltimer--;
if (cursortimer)
cursortimer--;
}
ISR(TIMER1_COMPA_vect){
if (--oszlop == -1){
if (++sor == 8){
sor = 0;
}
oszlop=29;
}
if (pixelbuffer[oszlop]&(1<<sor)){
LED_PORT |= LED1;
} else {
LED_PORT &= ~LED1;
}
sor2 = sor+1;
if (sor2 == 8)
sor2=0;
if (pixelbuffer[oszlop+30]&(1<<sor2)){
LED_PORT |= LED2;
} else {
LED_PORT &= ~LED2;
}
}
void debounce(void) {
uint8_t i,j;
state[db_index] = DB_PINS;
j=0xff;
for(i=0; i<DB_CHECKS;i++){
j=j & state[i];
}
if ((debouncedState ^ j) & ENC_B){
if (((debouncedState >> 1) ^ state[db_index]) & ENC_J) {
btnFlags |= ENC_J;
} else {
btnFlags |= ENC_B;
}
}
if (((debouncedState ^ j) & ENC_K) && (j & ENC_K)){
// gomb
btnFlags |= ENC_K;
}
debouncedState = j;
db_index++;
if (db_index>=DB_CHECKS){
db_index=0;
}
}
The first time I saw a hard-drive based persistance of vision toy I knew I need to do something similar. The internet was already full of working examples, tutorials, tips and tricks, but I thought I need something new.
So I came up with the idea of creating a graphic display instead.