|
|||||||
![]() |
Forum Index > Projects > Other projects | ||
Improved cylon firmware for minipov3 |
|||
| | | Printable Version |
|
Anonymous: Tim | ||||||||
|
Here is my modified version of the Cylon minipov firmware for the minipov3. It has a few improvements: 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); } |
|
||||||||
|
|||||||||
|
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/ |
![]() Evil Scientist ![]() Status: offline
Registered: 06/15/06 |
||||||||
|
|||||||||
|
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. |
|
||||||||
|
|||||||||
|
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. |
![]() Mad Scientist Status: offline
Registered: 04/13/10 |
||||||||
|
|||||||||
|
mightyohm | ||||||||
|
Tim, |
![]() Apprentice Status: offline
Registered: 11/12/08 |
||||||||
|
|||||||||
|
|
| All times are PDT. The time is now 07:48 PM. |
|
|
Octolively
Interactive LED kits
Meggy Jr RGB
LED matrix game
development kit.
Business-card sized
AVR target boards
Peggy 2
LED Pegboard kits