plain text pov - no message generator required
Moderators: adafruit_support_bill, adafruit

plain text pov - no message generator required

by tldr on Sat Mar 30, 2013 1:29 am

here is some pov code that is able to display multiple messages and does not require a message generator.

place the strings you wish to display in the text array. at the end of each line put a null character. your messages will be displayed one line at a time with a pause of approximately 1/2 a second between lines.

this program will only display ascii text. and only characters between 32 and 127.

there used to be GPL text in the comment at the beginning of the code, but the spam filter found the language objectionable.

Code: Select all | TOGGLE FULL SIZE
/*

*/

#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 pauseAfter = 200;   // then wait this long before next line
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) {
  PORTB = buffB;      // ouput one column
  if (c) {         // not at end of string
    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 (pauseAfter) {   // wait a little before next line
    pauseAfter--;
  }
  else {
    c = 0xff;         // force acquisition of 1st char of next line
    glyfColumn = 5;      // ditto
    pauseAfter = 200;
    if (textPtr >= (char*)(text + sizeof(text))) {   // if there's no more text
      textPtr = text;                             // return to beginning
    }
  }
}

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/256 prescale, mode 7, top = 39, 400.64Hz, enable interrupt
//
  TCCR0A = (1 << WGM01) | (1 << WGM00);      // select fast pwm w/ top = OCR0A
  TCCR0B = (1 << WGM02)  | (1 << CS02);   // and prescale = 256
  OCR0A = 39;           // gives interrupt freq ca 400Hz
  TIMSK = (1 << TOIE0);      // enable overflow interrupt
  sei ();         // enable interrupts
  MCUCR = (1 << SE);      // enable idle sleep mode
  while (1) {
    sleep_cpu ();
  }
 }


actually, since this code was written for my own use, there is some missing bounds checking and it will attempt to display any character with a value between 1 and 255. this will probably result in parts of the program itself being displayed. an enterprising individual could take advantage of that to display some graphics, but then said individual would probably need a message generating program to produce the data for that.
Last edited by tldr on Thu Apr 24, 2014 9:00 am, edited 2 times in total.
User avatar
tldr
 
Posts: 466
Joined: Thu Aug 30, 2012 1:34 am

Re: plain text pov - no message generator required

by adafruit_support_bill on Sat Mar 30, 2013 6:43 am

Nicely done! Thanks for posting. :)
User avatar
adafruit_support_bill
 
Posts: 31079
Joined: Sat Feb 07, 2009 10:11 am

Re: plain text pov - no message generator required

by mpv4gb on Tue Apr 22, 2014 10:55 pm

Hey, I'm a total newbie building a POV rig, and you code is such a windfall!! Thank you so very much for sharing.
I have a few questions:

1. Is this code for the Arduino IDE or for Processing?

2. How does the code adjust for varying rotation speeds? I didn't notice any parts that ask for timing or Hall Effect data.

3. What are the "interrupts" doing here? I've never encountered them before. Perhaps that's what's controlling speed?

Thank you again!!

—Matt
mpv4gb
 
Posts: 2
Joined: Thu Feb 27, 2014 5:34 pm

Re: plain text pov - no message generator required

by tldr on Wed Apr 23, 2014 8:30 am

mpv4gb wrote:1. Is this code for the Arduino IDE or for Processing?


this is just plain old garden variety c, written to be compiled with avr-gcc. avr-gcc is the program used to compile arduino sketches, but this is not an arduino sketch. it was written for the adafruit minipov3.

mpv4gb wrote:2. How does the code adjust for varying rotation speeds? I didn't notice any parts that ask for timing or Hall Effect data.


it doesn't. the minipov is designed to be waved at the end of a human arm and the speed is hardcoded. i used the speeds that ladyada used in her original code. there is a sensor input on the board, however, and controlling the speed would not be difficult. there ought to be code for that in this forum.

mpv4gb wrote:3. What are the "interrupts" doing here? I've never encountered them before. Perhaps that's what's controlling speed?


yep. the code displays a single column of the text, then goes to sleep, to be awakened by the interrupt 1/400th of a second later.

there is no reason this cannot be ported to an arduino. timer0 on an arduino is used to for timing, so using it would disable things like millis and delay. there are two other timers available.

i may port this myself. my aunt's been in the hospital for awhile and i've been thinking of throwing together a pov toy, since my geeky endeavors seem to bring her a bit of amusement.

gotta go to work.
User avatar
tldr
 
Posts: 466
Joined: Thu Aug 30, 2012 1:34 am

Re: plain text pov - no message generator required

by tldr on Thu Apr 24, 2014 8:59 am

i have edited the original post because there was an error in the code. i was sticking a top value of 10000 into an 8 bit register. wouldn't fit. not even close. so i changed the prescaler to 256, and used a top value of 39. this should give a display frequency of 8000000/2/256/39=400.64.

sorry for any confusion. my project used a frequency of about 48Hz and uv leds to write on a wall covered in fluorescent paint and i was waving the thing around very slowly.
User avatar
tldr
 
Posts: 466
Joined: Thu Aug 30, 2012 1:34 am