HDD persistence of vision display

Â
HDD POV hello 1234

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.

two SMD LEDs

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.

position of the hall sensor

A tiny piece of neodymium magnet (also salvaged from the HDD) is superglued on the bottom of the disk.

holes and glued neodymium magnet

Schematics:

HDD POV display schematics

AVR C Code:

/*
 * 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;
    }

}

Description

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.

Technology

Â