plain text pov - no message generator required

MiniPOV4 and previous versions

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
tldr
 
Posts: 466
Joined: Thu Aug 30, 2012 1:34 am

plain text pov - no message generator required

Post by tldr »

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

/*

*/

#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
adafruit_support_bill
 
Posts: 88086
Joined: Sat Feb 07, 2009 10:11 am

Re: plain text pov - no message generator required

Post by adafruit_support_bill »

Nicely done! Thanks for posting. :)

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

Re: plain text pov - no message generator required

Post by mpv4gb »

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

tldr
 
Posts: 466
Joined: Thu Aug 30, 2012 1:34 am

Re: plain text pov - no message generator required

Post by tldr »

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.

tldr
 
Posts: 466
Joined: Thu Aug 30, 2012 1:34 am

Re: plain text pov - no message generator required

Post by tldr »

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
jacobkeyes1
 
Posts: 11
Joined: Fri Oct 23, 2015 8:01 pm

Re: plain text pov - no message generator required

Post by jacobkeyes1 »

tldr wrote: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.
That sounds amazing! do you have any pictures, or can you explain a bit more on how it worked?

User avatar
mtbfzero
 
Posts: 16
Joined: Sun Mar 29, 2015 11:28 am

Re: plain text pov - no message generator required

Post by mtbfzero »

this was it. minipov with ir leds. phosphorescent paint.

https://www.youtube.com/watch?v=DunP3g0_7sg

but to get this behavior, the interrupt frequency has to be slowed way down, so main() must be changed as follows...

Code: Select all

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) | (1 << CS00);   // and prescale = 1024
  OCR0A = 81;           // gives interrupt freq ca 48Hz
  TIMSK = (1 << TOIE0);      // enable overflow interrupt
  sei ();         // enable interrupts
  MCUCR = (1 << SE);      // enable idle sleep mode
  while (1) {
    sleep_cpu ();
  }
 }

User avatar
jacobkeyes1
 
Posts: 11
Joined: Fri Oct 23, 2015 8:01 pm

Re: plain text pov - no message generator required

Post by jacobkeyes1 »

thanks for the code, appreciate it. My son will love this!
Do you have a link to the paint you used (or know the manufacturer), and will any UV LED do or did you find it only worked with specific ones?

User avatar
jacobkeyes1
 
Posts: 11
Joined: Fri Oct 23, 2015 8:01 pm

Re: plain text pov - no message generator required

Post by jacobkeyes1 »

Actually did you just use these LEDs?
https://www.adafruit.com/products/1793

Can you just replace them in the MiniPOV3 without changing the resistor values? I tried looking at the datasheets to figure this out but I am still pretty new to this.

thanks

User avatar
mtbfzero
 
Posts: 16
Joined: Sun Mar 29, 2015 11:28 am

Re: plain text pov - no message generator required

Post by mtbfzero »

the two aa batteries that the minipov3 is designed to use may not provide a high enough voltage to power those leds. might work. if 2 aa's will work you might just use wire jumpers in place of the resistors. or you could go with 3 aa's to get 4.5 volts in which case you'd want (4.5-3.4)/.02 = 55 ohms, so a 56 ohm resistor. i like using those lipo packs for charging phones and whatnot. (5-3.4)/.02 = 80 ohms, so you could use 82 ohm resistors. of course if your resistors have a 5% tolerance, your 82 ohm resistors might actually be 76 ohm resistors. (5-3.4)/76 = .021 amps which is above the recommended but below the maximum current for those leds.

oops. now i went and gave myself a headache.

User avatar
jacobkeyes1
 
Posts: 11
Joined: Fri Oct 23, 2015 8:01 pm

Re: plain text pov - no message generator required

Post by jacobkeyes1 »

thanks. super helpful post showing the calculations. Now I can do it myself.

Locked
Please be positive and constructive with your questions and comments.

Return to “MiniPOV”