Forum Index > Projects > Other projects
 Improved cylon firmware for minipov3
 |  Printable Version
By: Anonymous: Tim () on Tuesday, October 06 2009 @ 08:40 PM PDT (Read 7024 times)  
Anonymous: Tim

Here is my modified version of the Cylon minipov firmware for the minipov3. It has a few improvements:

- Up to 6 lights are on at a time, in varying intensities
- The intensity ramps up from off to full over 4 steps, and then takes 20 more steps to turn off
- During each of the 24 steps, pulse width modulation is used to create a different intensity
- Intensities are based on an exponential curve to compensate for the way our eyes operate
- I didn't like a constant side to side sweep so added a routine that makes it speed up gradually to a maximum, after which it gradually slows down

This thing looks great across a dark room

Free for your enjoyment!

PHP Formatted Code

// Originally by Evilmadscientist.com
//     http://www.evilmadscientist.com/article.php/minipovcylon
//
// Modified and copyright by Tim Charron September 2009
//
// Released under GPL

#include <avr/io.h>      // this contains all the IO port definitions
#include <util/delay.h>
#include <avr/interrupt.h>

#define TIMER1_PRESCALE_1 1
#define TIMER1_PRESCALE_8 2
#define TIMER1_PRESCALE_64 3
#define TIMER1_PRESCALE_256 4
#define TIMER1_PRESCALE_1024 5

// We use these macros because binary constants arent always supported. ugh.
#define HEX__(n) 0x##n##UL
#define B8__(x) ((x&0x0000000FLU)?1:0)  \
               +((x&0x000000F0LU)?2:0)  \
               +((x&0x00000F00LU)?4:0)  \
               +((x&0x0000F000LU)?8:0)  \
               +((x&0x000F0000LU)?16:0) \
               +((x&0x00F00000LU)?32:0) \
               +((x&0x0F000000LU)?64:0) \
               +((x&0xF0000000LU)?128:0)
#define B8(d) ((unsigned char)B8__(HEX__(d)))

// This function basically wastes time
void delay_ms( uint16_t milliseconds)
{
   for( ; milliseconds > 0; milliseconds--)
   {
      _delay_ms(1);
   }
}

 //Even More Basicer Version

const static int image[] = {
  B8(10000000),
  B8(01000000),
  B8(00100000),
  B8(00010000),
  B8(00001000),    
  B8(00000100),    
  B8(00000010),    
         
  B8(00000001),    
  B8(00000010),    
  B8(00000100),
  B8(00001000),
  B8(00010000),
  B8(00100000),
  B8(01000000),
 
// To avoid having to detect overflows in the PWM loop (since it references more than one image at a time), add the first 5 images to the end again
  B8(10000000),
  B8(01000000),
  B8(00100000),
  B8(00010000),
  B8(00001000),    
};
 
#define NUM_ELEM(x) (sizeof (x) / sizeof (*(x)))
int imagesize = NUM_ELEM(image);
int imagesize2 = NUM_ELEM(image)-5;

// this function is called when timer1 compare matches OCR1A
// Implements PWM fading from off to high in about 400 calls (4*my_timer)
//   Then each light fades back to off over about 2000 calls (20*my_timer)
// On each call, the LEDs are flipped either on or off so that their 'average' on time matches the desired luminosity.
// The weights are chosen based on
// This is called 8000 times per second, so we get

uint16_t i = 0;
uint8_t j = 0;
uint8_t j2 = 0;
uint8_t k=0;
uint16_t ii=0;

uint8_t threshold[6][4];
int my_timer=0;
int my_timer_dir=-1;
int my_timer_min=0;
int my_timer_max=0;
uint8_t thisimage=0;

SIGNAL( SIG_TIMER1_COMPA ) {

        // Show 6 images using PWM, at various levels of intensity
   thisimage=0;
   if (i%threshold[0][j2]==0) thisimage |= image[(j+5)];
   if (i%threshold[1][j2]==0) thisimage |= image[(j+4)];
   if (i%threshold[2][j2]==0) thisimage |= image[(j+3)];
   if (i%threshold[3][j2]==0) thisimage |= image[(j+2)];
   if (i%threshold[4][j2]==0) thisimage |= image[(j+1)];
   if (i%threshold[5][j2]==0) thisimage |= image[(j  )];
   PORTB=thisimage;
   
   // Every so often change the speed that we're looping at.  This gives a gradual change from min to max to min etc.
   // Set my_timer_min equal to my_timer_max to turn this off.
   i++;
   // Change the constant on the next line to affect rate of change of speed changes.  Try 100 to see the whole state change in about 3 seconds.
   if (i==35000) { // No more than 65535!
      i=0;
      if (my_timer_dir==-1) {
        ii=my_timer/20; // 5% changes see undetectable but fast enough
        if (ii==0) ii=1;
        my_timer=my_timer-ii;
        if (my_timer<=my_timer_min) my_timer_dir=1;
     } else {
        ii=my_timer/(20-1);  //( x == (x-x/n)*(1+1/(n-1))
        if (ii==0) ii=1;
        my_timer=my_timer+ii;
        if (my_timer>=my_timer_max) my_timer_dir=-1;
     }
   }

   if (i%my_timer==0) { // Figure out if we need to change states.
      if (++j2==4) { // Change this to 3 or 2 or 1 to use fewer intermediate states (change my_timer speeds commensurately)
         j2=0;
         if (++j >= imagesize2) j = 0;
      }
   }
}

int main(void) {

  DDRB = 0xFF;       // set port B to output only
  PORTB = 0; // turn off all LEDs

  /*
    the frequency of the interrupt overflow is determined by the
    prescaler and overflow value.
    freq = clock_frequency / ( 2 * prescaler * overflow_val)
    where prescaler can be 1, 8, 64, 256, or 1024
    clock_freq is 8MHz
    and overflow_val is 16bit

    the overflow value is placed in OCR1A, the prescale is set in TCCR1B
    so for example:
    A good POV frequency is around 400Hz
    desired freq = 400Hz
    clock freq = 8MHz
    8MHz / (400Hz * 2) = 10000
    since 10000 is less than 655536 (largest 16 bit number)
    OCR1A = 10000 and the prescale is 1
  */

   // Overall effect here is that lights pulses take on intensities of inverse of:
   // off 148 20 7 3 1 1 1 2 3 4 5 6 7 9 12 16 20 26 33 43 55 70 90 116 off

   // In a larger program this could go in the program rather than using up RAM.
   threshold[0][0]=148;  // exp(5)
   threshold[1][0]=1;    // exp(0)
   threshold[2][0]=3;    // exp(1)
   threshold[3][0]=7;    // exp(2)
   threshold[4][0]=20;   // exp(3)
   threshold[5][0]=55;   // exp(4)

   threshold[0][1]=20;   // exp(3)
   threshold[1][1]=1;    // exp(0*0.75+0.25*1)
   threshold[2][1]=4;    // exp(1*0.75+0.25*2)
   threshold[3][1]=9;    // exp(2*0.75+0.25*3)
   threshold[4][1]=26;   // exp(3*0.75+0.25*4)
   threshold[5][1]=70;   // exp(4*0.75+0.25*5)

   threshold[0][2]=7;    // exp(2)
   threshold[1][2]=1;    // exp(0*0.50+0.50*1)
   threshold[2][2]=5;    // exp(1*0.50+0.50*2)
   threshold[3][2]=12;   // exp(2*0.50+0.50*3)
   threshold[4][2]=33;   // exp(3*0.50+0.50*4)
   threshold[5][2]=90;   // exp(4*0.50+0.50*5)

   threshold[0][3]=3;    // exp(1)
   threshold[1][3]=2;    // exp(0*0.25+0.75*1)
   threshold[2][3]=6;    // exp(1*0.25+0.75*2)
   threshold[3][3]=16;   // exp(2*0.25+0.75*3)
   threshold[4][3]=43;   // exp(3*0.25+0.75*4)
   threshold[5][3]=116;  // exp(4*0.25+0.75*5)

   uint16_t ms=500; //determines how often the interrupt is called

   TCCR1B = (1 << WGM12) | TIMER1_PRESCALE_1;
   OCR1A = (uint16_t) ms;

   TIMSK |= 1 << OCIE1A;   // Output Compare Interrupt Enable (timer 1, OCR1A)

   sei();                 // Set Enable Interrupts

   //OCR1A 500 and prescale 1 gives an overall interrupt calling frequency of 8000hz
   //At (for example) my_timer=100, with 4 states (the threshold states above), the 'eye'
   //   progresses 20 lights per second, or about 2.5 sweeps per second

   my_timer_min=50; // 50-250 seem reasonable (gives 1 to 5 sweeps per second)
   my_timer_max=250;
   my_timer=my_timer_max;
   while (1) delay_ms(3000);
}
 





       
   
By: Windell (offline) on Tuesday, October 06 2009 @ 09:06 PM PDT  
Windell

Thanks, Tim!

I've linked to this forum post from our original project.


Windell H. Oskay
drwho(at)evilmadscientist.com
http://www.evilmadscientist.com/

Forum Evil Scientist
Evil Scientist

Status: offline

Registered: 06/15/06
Posts: 1932
Sunnyvale, CA

Profile Email Website  
   
By: Anonymous: squall_line () on Monday, April 12 2010 @ 08:20 PM PDT  
Anonymous: squall_line

One of my first thoughts when I ordered the Larson Scanner was that the new cylon models have that sinusoidal-type scan pattern. I'll have to try this out once I get mine built (and get my USBTinyISP built, of course) and see if it's what I had in mind.

Thanks for posting, and my apologies for the necro-bump.





       
   
By: squall_line (offline) on Tuesday, April 13 2010 @ 11:47 AM PDT  
squall_line

The more I thought about this, the more I realised that this is a post about the miniPOV3, not the Larson Scanner, but I think both use the ATTiny2313, so the code should be somewhat portable. Even if it isn't, I should be able to figure it out.

As you were...


Forum Mad Scientist
Mad Scientist

Status: offline

Registered: 04/13/10
Posts: 96
Iowa, USA

Profile Email    
   
By: mightyohm (offline) on Friday, June 03 2011 @ 01:25 PM PDT  
mightyohm

Tim,

Can you explain how the exponential brightness matrix works? What function did you use to create the matrix?


Forum Apprentice
Apprentice

Status: offline

Registered: 11/12/08
Posts: 1

Profile Email    
   



 All times are PDT. The time is now 02:16 PM.
Normal Topic Normal Topic
Locked Topic Locked Topic
Sticky Topic Sticky Topic
New Post New Post
Sticky Topic W/ New Post Sticky Topic W/ New Post
Locked Topic W/ New Post Locked Topic W/ New Post
View Anonymous Posts 
Able to Post 
Filtered HTML Allowed 
Censored Content 

Evil Mad Scientist Forum Archives — Read only!

Please visit our new forums for new discussions.


DIY Hardware for Electronic Art


The Original Egg-Bot Kit


Octolively
Interactive LED kits


Meggy Jr RGB
LED matrix game
development kit.


Business-card sized
AVR target boards


Peggy 2
LED Pegboard kits

My Account






Lost your password?