Ardunio POV (Persistence of vision) Writer

just something to keep me busy :slight_smile:

using 24 LEDs with 3 74HC595 shift register it can create a text by quickly cycling through the individual lines of the text matrix.
the implementation depends on the SPI library (>=Arduino 0019)

the charset is 24x20/character optimized for this purpose.

i will integrate this into a big room fan to display the temperature and the current time.

result:

http://img831.imageshack.us/img831/1573/arduinorulz.jpg
Sorry for shaky image :slight_smile: not an easy task...

Bread Board Circuit:

http://img831.imageshack.us/img831/130/breadboardarduinoleds.jpg

Code for everybody free to use:

// POV (Persistence of vision) for ARDUINO
// 24 LEDs with 3 74HC595 Shift Register
// by Shaksbeer 2010

#include <SPI.h>
#include "char_matrix.h"

int latchPin = 8;

void setup() {
  SPI.begin();
  pinMode(latchPin, OUTPUT);

  Serial.begin(9600);
}

void loop() {
  char str[] = "ARDUINO RULZ ";
  showString(str, sizeof(str));
}

void showString( char* str, int len )
{
  for (int i=0; i<len; i++)
  {
    showChar( str[i] );
  }
  
}

void showChar(char chr)
{
  mychar* c;

  if (chr==' ')
  {
    digitalWrite(latchPin, 0);
    SPI.transfer(0x00);
    SPI.transfer(0x00);
    SPI.transfer(0x00);
    digitalWrite(latchPin, 1);
    delay(3);
    return;
  }
  
  c = getMyChar(chr);
  if (c==NULL)
    return;
  for (int j = 0; j < 20; j++) {
    digitalWrite(latchPin, 0);
    SPI.transfer((*c)[j][0]);
    SPI.transfer((*c)[j][1]);
    SPI.transfer((*c)[j][2]);
    digitalWrite(latchPin, 1);
    delayMicroseconds(500);
  }
}

mychar* getMyChar(char chr) {
  if (chr>=BIGCHARSTART && chr<=BIGCHAREND)
    return &bigchars[(int)(chr-BIGCHARSTART)];
  return NULL;
}

char_matrix.h (Letter A as example):

// 24x20 charset for ARDUINO 
// by shaksbeer 2010

#define BIGCHARSTART 65
#define BIGCHAREND 90

typedef unsigned char mychar[20][3];

mychar bigchars[] =
{
  // A = 65
  
  {
      {0,0,0},
      {248,0,0},
      {255,0,0},
      {255,224,0},
      {255,254,0},
      {15,255,192},
      {7,255,252},
      {7,159,255},
      {7,131,255},
      {7,128,63},
      {7,128,63},
      {7,131,255},
      {7,191,255},
      {7,255,254},
      {63,255,224},
      {255,254,0},
      {255,240,0},
      {255,0,0},
      {248,0,0},
      {128,0,0},
  }
};

EDIT: updated code.

Very interesting.

What is the display on - a fan?
I cant really see how the picture is seen from the breadboard LED's.

Could you provide a picture of the display mechanism?

the picture was made by moving the camera (pretty fast) across the board while taking the picture.

the circuit is not yet on a fan - more work to be done :slight_smile:

Cool project!

Project update :slight_smile:

  • added REED contact for timing (tested and working, not used for demo pics)
  • added MAX6657 (smBus/wire interface) for temperature monitoring
  • added potentiometer to regulate character width (char column speed)

breadboard:

running, shows current temp (Celcius) :

I had to make the font smaller, not enough RAM :wink:

Code: (feel free to use)

// POV (Persistence of vision) for ARDUINO
// 24 LEDs with 3 74HC595 Shift Register
// REED switch on digital port 3
// potentiometer on analog port 3
// MAX6657 temperature monitoring IC on WIRE interface
// by Shaksbeer 2010

#include <SPI.h>
#include <Wire.h>

#include "char_matrix_15.h"
#include "char_matrix_numbers_15.h"

#include "temperature_smb.h"

#define latchPin 8
#define potPin 2

#define interrupt_pin 3
#define interrupt_num 1

unsigned int micro_delay = 300;
unsigned int milli_delay = 0;

volatile byte int_flag = LOW;

byte temp = 20;

void setup() {
  SPI.begin();
  
  pinMode(latchPin, OUTPUT); 
  pinMode(interrupt_pin, INPUT);

  setup_max6657();
  
  attachInterrupt(interrupt_num, interrupt_callback, RISING );
}

void loop() {
  char str[10] = "00 ";
  send_one_shot();
  temp = get_temp();
  num2str(temp, str, sizeof(str));
  if (int_flag==HIGH)
  {
    showString(str);
    int_flag = LOW;
  }
}

void interrupt_callback()
{
  int_flag = HIGH;
}

void read_poti_delay()
{
  micro_delay = analogRead(potPin);
  if (micro_delay<5)
    micro_delay=5;
    
  if (micro_delay>1000)
    milli_delay = (micro_delay-1000);
  else
    milli_delay = 0;
}

void showString( char *str )
{
  int i = 0;
  while (str[i]!=NULL)
  {
    read_poti_delay();
    showChar( str[i] );
    i++;
  }
}

void showChar(char chr)
{
  mychar* c;
 
  c = getMyChar(chr);

  if (chr==' ')
  {
    digitalWrite(latchPin, 0);
    SPI.transfer(0x00);
    SPI.transfer(0x00);
    SPI.transfer(0x00);
    digitalWrite(latchPin, 1);
    delay(1);
    return;
  }
  else if (c!=NULL)
  {
    for (int i=0;i<CHAR_WIDTH;i++)
    {
      digitalWrite(latchPin, 0);
      SPI.transfer( (*c)[i][0] );
      SPI.transfer( (*c)[i][1] );
      SPI.transfer( (*c)[i][2] );
      digitalWrite(latchPin, 1);
      if (milli_delay>0)
        delay(milli_delay);
      else
        delayMicroseconds(micro_delay);
    }  
  }
}

mychar* getMyChar(char c) {
  unsigned char chr = (unsigned char)c;
  if ( (chr>=BIGCHARSTART) && (chr<=BIGCHARSEND) )
    return (mychar*)bigchars[chr-BIGCHARSTART];
  else if ( (chr>=NUMCHARSTART) && (chr<=NUMCHARSEND) )
    return (mychar*)numchars[chr-NUMCHARSTART];
  /*
  if ((chr>=SPECIALCHARSTART) && (chr<=SPECIALCHARSEND) )
    return (mychar*)specialchars[chr-SPECIALCHARSTART];
    */
  return NULL;
}

void num2str(byte b, char* chr, byte max_size)
{
  chr[0] = '0'+ (b / 10);
  chr[1] = '0'+ (b % 10);
  return;
}

temperature_smb.h

// Functions for MAX6657 IC
// (c) by Shaksbeer 2010

#define MAX6657_ADDRESS 0b1001100

#define WRITE_CONFIGURATION_BYTE 0x09
#define READ_CONFIGURATION_BYTE 0x03

#define READ_STATUS_REGISTER 0x02

#define READ_INTERNAL_TEMPERATURE 0x00
#define READ_EXTERNAL_TEMPERATURE 0x01

#define INITIATE_ONE_SHOT 0x0F

void send_byte(byte b)
{
  Wire.beginTransmission(MAX6657_ADDRESS); 
  Wire.send(b);
  Wire.endTransmission();
}

void send_bytearray( byte* b, byte size)
{
  Wire.beginTransmission(MAX6657_ADDRESS);
  Wire.send(b,size);
  Wire.endTransmission();
}

byte send_command_read_byte( byte cmd )
{
  byte ret = 255;
  byte a;
  Wire.beginTransmission(MAX6657_ADDRESS); 
  Wire.send(cmd);
  Wire.endTransmission();

  Wire.requestFrom(MAX6657_ADDRESS, 1);
  a = Wire.available();
  if (a>0)
  {
    ret = Wire.receive();
  }
  return ret;
}

void setup_max6657()
{
  byte b[2];
  
  b[0] = WRITE_CONFIGURATION_BYTE;
  b[1] = 0b01000000;
  Wire.begin();
  delay(100);

  send_bytearray(b, 2);
}

byte read_temp()
{
  byte s=0b10000000;
  byte t=0;
  byte b;
  
  do {
    send_byte( INITIATE_ONE_SHOT );
    b = send_command_read_byte( READ_STATUS_REGISTER );
  } while (b>=128);
  t = send_command_read_byte(READ_INTERNAL_TEMPERATURE );
  return t;
}

void send_one_shot()
{
  send_byte( INITIATE_ONE_SHOT );
}

byte get_temp()
{
  return send_command_read_byte(READ_INTERNAL_TEMPERATURE );
}

Waiting for the ordered LEDs to arrive... can't wait to integrate everything into the fan :slight_smile: