- if 1
/*
larson.c
The Larson Scanner
Written by Windell Oskay, http:// www.evilmadscientist.com/
Copyright 2009 Windell H. Oskay
Distributed under the terms of the GNU General Public License, please see below.
An avr-gcc program for the Atmel ATTiny2313
Version 1.3 Last Modified: 2/8/2009.
Written for Evil Mad Science Larson Scanner Kit, based on the "ix" circuit board.
Improvements in v 1.3:
* EEPROM is used to *correctly* remember last speed & brightness mode.
Improvements in v 1.2:
* Skinny "eye" mode. Hold button at turn-on to try this mode. To make it default,
solder jumper "Opt 1." (If skinny mode is default, holding button will disable it temporarily.)
* EEPROM is used to remember last speed & brightness mode.
More information about this project is at
http://www.evilmadscientist.com/article.php/larsonkit
-------------------------------------------------
USAGE: How to compile and install
A makefile is provided to compile and install this program using AVR-GCC and avrdude.
To use it, follow these steps:
1. Update the header of the makefile as needed to reflect the type of AVR programmer that you use.
2. Open a terminal window and move into the directory with this file and the makefile.
3. At the terminal enter
make clean
make all
make install
4. Make sure that avrdude does not report any errors. If all goes well, the last few lines output by avrdude
should look something like this:
avrdude: verifying ...
avrdude: XXXX bytes of flash verified
avrdude: safemode: lfuse reads as 62
avrdude: safemode: hfuse reads as DF
avrdude: safemode: efuse reads as FF
avrdude: safemode: Fuses OK
avrdude done. Thank you.
If you a different programming environment, make sure that you copy over the fuse settings from the makefile.
-------------------------------------------------
This code should be relatively straightforward, so not much documentation is provided. If you'd like to ask
questions, suggest improvements, or report success, please use the evilmadscientist forum:
http://www.evilmadscientist.com/forum/
-------------------------------------------------
* /
- include
- include
- include
- include
- include
- define shortdelay(); \t\t\tasm("nop\
\\t" \\
"nop\
\\t");
- define TIMER1_PRESCALE_1 \t\t(1)
- define TIMER1_PRESCALE_8 \t\t(2)
- define TIMER1_PRESCALE_64 \t\t(3)
- define TIMER1_PRESCALE_256\t\t(4)
- define TIMER1_PRESCALE_1024\t(5)
uint16_t eepromWord __attribute__((section(".eeprom")));
int main (void)
{
\tuint8_t LEDs[[9]]; // Storage for current LED values
\t
\tint8_t eyeLoc[[5]]; // List of which LED has each role, leading edge through tail.
\tuint8_t LEDBright[[4]] = {1u,4u,2u,1u}; // Relative brightness of scanning eye positions
\t
\tint8_t j, m;
\t
\tuint8_t position, loopcount, direction;
\tuint8_t ILED, RLED, MLED;
\tuint8_t delaytime;
\t
\tuint8_t skinnyEye = 0;
\tuint8_t pt, debounce, speedLevel;
\tuint8_t \tUpdateConfig;
\tuint8_t BrightMode;
\tuint8_t debounce2, modeswitched;
\t
\tuint8_t CycleCountLow;
\tuint8_t LED0, LED1, LED2, LED3, LED4, LED5, LED6, LED7, LED8;
\t
\t//Initialization routine: Clear watchdog timer-- this can prevent several things from going wrong.\t\t
\tMCUSR &= 0xF7;\t\t//Clear WDRF Flag
\tWDTCSR\t= 0x18;\t\t//Set stupid bits so we can clear timer...
\tWDTCSR\t= 0x00;
\t//Data direction register: DDR's
\t//Port A: 0, 1 are inputs.\t
\t//Port B: 0-3 are outputs, B4 is an input.
\t//Port D: 1-6 are outputs, D0 is an input.
\t
\tDDRA = 0U;
\tDDRB = 15U;\t
\tDDRD = 126U;
\t
\tPORTA = 3;\t// Pull-up resistors enabled, PA0, PA1
\tPORTB = 16;\t// Pull-up resistor enabled, PA
\tPORTD = 0;
\t\t
TCCR1B = (1 << WGM12) | TIMER1_PRESCALE_1;
OCR1A = (uint16_t)800;
TIMSK |= 1 << OCIE1A; // Output Compare Interrupt Enable (timer 1, OCR1A)
\t/* Visualize outputs:
\t L to R:
\t D2 D3 D4 D5 D6 B0 B1 B2 B3\t
\t*/
\t
\t//multiplexed = LowPower;\t
\tdebounce = 0;
\tdebounce2 = 0;
\tloopcount = 254;
\tdelaytime = 0;
\t
\tdirection = 0;
\tposition = 0;
\tspeedLevel = 0; // Range: 1, 2, 3
\tBrightMode = 0;
\tCycleCountLow = 0;
\tUpdateConfig = 0;
\tmodeswitched = 0;
\t
\t
\tif ((PINA & 2) == 0)\t\t// Check if Jumper 1, at location PA1 is shorted
\t{
\t\t// Optional place to do something. :)
\t}
\t
\t
\t
\tif ((PINA & 1) == 0)\t\t// Check if Jumper 2, at location PA0 is shorted
\t{
\t\tskinnyEye = 1;
\t}\t
\t
\t
\tif ((PINB & 16) == 0)\t\t// Check if button pressed pressed down at turn-on
\t{\t\t
\t//Toggle Skinnymode
\t\tif (skinnyEye)
\t\t\tskinnyEye = 0;
\t\telse
\t\t\tskinnyEye = 1;
\t}
\t
\t
\t
\tif (skinnyEye){
\t\tLEDBright[[0]] = 0;
\t\tLEDBright[[1]] = 4;\t
\t\tLEDBright[[2]] = 1;
\t\tLEDBright[[3]] = 0;
\t}
\t
\t
\t//Check EEPROM values:
\t
\tpt = (uint8_t) (eeprom_read_word(&eepromWord)) ;
\tspeedLevel = pt >> 4;
\tBrightMode = pt & 1;
\tif (pt == 0xFF)
\t{
\t\tBrightMode = 0;
\t}
\t
\t
\tif (speedLevel > 3)
\t\tspeedLevel = 1;
\t
\tspeedLevel = 0;
\tif ((speedLevel == 2) || (speedLevel == 3)) {
\t\tdelaytime = 0;
\t}
\telse
\t{ speedlevel = 1;
\t\tdelaytime = 1;
\t}\t
\t
\t
\t
for (;;) // main loop
{
\tloopcount++;
\t
\tif (loopcount > delaytime)
\t{
\t\tloopcount = 0;
\t\t
\t\tCycleCountLow++;
\t\tif (CycleCountLow > 250)
\t\t\tCycleCountLow = 0;
\t\t
\t\t
\t\tif (UpdateConfig){\t\t// need to save configuration byte to eeprom
\t\t\tif (CycleCountLow > 100) // Avoid burning EEPROM in event of flaky power connection resets
\t\t\t{
\t\t\t\t
\t\t\t\tUpdateConfig = 0;
\t\t\t\tpt = (speedLevel << 4) | (BrightMode & 1);
\t\t\t\teeprom_write_word(&eepromWord, (uint8_t) pt);\t
\t\t\t\t// Note: this function causes a momentary brightness glitch while it writes the EEPROM.
\t\t\t\t// We separate out this section to minimize the effect.
\t\t\t}
\t\t\t
\t\t}\t
\t\t
\t\t
\tif ((PINB & 16) == 0)\t\t// Check for button press
\t{
\t\tdebounce2++;
\t\t
\t\tif (debounce2 > 100)
\t\t{
\t\t\tif (modeswitched == 0)
\t\t\t{
\t\t\t\tdebounce2 = 0;
\t\t\t\tUpdateConfig = 1;
\t\t\t\tswitch( BrightMode ) {
\t\t\t\t
\t\t\t\t\t\tcase 0:
\t\t\t\t\t\t\tBrightMode++;
\t\t\t\t\t\tcase 1:
\t\t\t\t\t\t\tBrightMode++;
\t\t\t\t\t\t\tbreak;
\t\t\t\t\t\tcase 2:
\t\t\t\t\t\t// off
\t\t\t\t\t\t\t// switch on POV
\t\t\t\t\t\t\t// wrap
\t\t\t\t\t\t\tBrightMode++;
\t\t\t\t\t\t\tbreak;
\t\t\t\t\t\tcase 3:
\t\t\t\t\t\t\t//sei(); // Set Enable Interrupts
\t\t\t\t\t\t\t// runs POV
\t\t\t\t\t\t\tBrightMode++;
\t\t\t\t\t\t\tbreak;
\t\t\t\t\t\tcase 4:
\t\t\t\t\t\t\tsei(); // Set Enable Interrupts
\t\t\t\t\t\t\tBrightMode++;
\t\t\t\t\t\t\t// runs POV
\t\t\t\t\t\t\tbreak;
\t\t\t\t\t\tcase 5:
\t\t\t\t\t\t\tcli(); // Set Enable Interrupts
\t\t\t\t\t\t\t// runs POV
\t\t\t\t\t\t\tBrightMode = 0 ;
\t\t\t\t\t\t\tbreak;
\t\t\t\t\t\t\t\t
\t\t\t\t\t\tdefault:
\t\t\t\t\t\t\t// interrupts off and wrap
\t\t\t\t\t\t\tBrightMode= 0;
\t\t\t\t\t\t\tbreak;
\t\t\t\t}
\t\t\t\t\t\t\t
\t\t\t\tmodeswitched = 1;
\t\t\t}
\t\t}
\t\telse {
\t\t\tdebounce = 1;\t\t// Flag that the button WAS pressed.
\t\t\tdebounce2++;\t
\t\t}
\t}\t
\telse{
\t\t
\t\tdebounce2 = 0;
\t\tmodeswitched = 0;
\t
\t\tif (debounce)
\t\t{ debounce = 0;
\t\t\tspeedLevel++;
\t\t\tUpdateConfig = 1;
\t\t\t
\t\t\tif ((speedLevel == 2) || (speedLevel == 3)) {
\t\t\t\tdelaytime = 0;
\t\t\t}
\t\t\telse
\t\t\t{ speedlevel = 1;
\t\t\t\tdelaytime = 1;
\t\t\t}
\t\t\t
\t\t\tdebounce = 0;
\t\t}
\t\t
\t\t\t}
\t
\t\tposition++;
\t\t
\t\tif (speedLevel == 3)
\t\t\tposition++;
\t\t
\t if (position >= 128)\t//was == 128
\t {
\t\t position = 0;
\t\t
\t if (direction == 0)
\t\t direction = 1;
\t else
\t\t direction = 0;
\t }
\t
\t\t \t\t
\tif (direction == 0) // Moving to right, as viewed from front.
\t{
\t\tILED = (15+position) >> 4;
\t\tRLED = (15+position) - (ILED << 4);
\t\tMLED = 15 - RLED; \t\t
\t\t}
else
\t{
\t\tILED = (127 - position) >> 4;
\t\tMLED = (127 - position) - (ILED << 4);
\t\tRLED = 15 - MLED;\t
\t}
\t\t
\t\tj = 0;
\t\twhile (j < 9) {
\t\t\tLEDs[[j]] = 0;
\t\t\tj++;
\t\t}
\t\t
\t\tj = 0;
\t\twhile (j < 5) {
\t\t\t
\t\t\tif (direction == 0)
\t\t\t m = ILED + (2 - j);\t// e.g., eyeLoc[[0]] = ILED + 2;
\t\t\telse
\t\t\t m = ILED + (j - 2); // e.g., eyeLoc[[0]] = ILED - 2;
\t\t\t
\t\t\tif (m > 8)
\t\t\t\tm -= (2 * (m - 8));
\t\t\t
\t\t\tif (m < 0)
\t\t\t\tm *= -1;
\t\t\t
\t\t\teyeLoc[[j]] = m;
\t\t\t
\t\t\tj++;
\t\t}
\t\t
\t\tj = 0;\t\t// For each of the eye parts...
\t\twhile (j < 4) {
\t\t\t
\t\t\tLEDs[eyeLoc[j]] += LEDBright[[j]]*RLED;\t\t\t
\t\t\tLEDs[eyeLoc[j+1]] += LEDBright[[j]]*MLED;\t\t\t
\t\t\t
\t\t\tj++;
\t\t}
\t
\t LED0 = LEDs[[0]];
LED1 = LEDs[[1]];
\t LED2 = LEDs[[2]];
\t LED3 = LEDs[[3]];
\t LED4 = LEDs[[4]];
\t LED5 = LEDs[[5]];
\t LED6 = LEDs[[6]];
\t LED7 = LEDs[[7]];
\t LED8 = LEDs[[8]];
\t}
\tif ( BrightMode > 3 ) {
\t\tcontinue;
\t}
\t
\tif (BrightMode == 0)
\t{\t\t//multiplexing routine: each led is on (1/9) of the time.
\t\t\t// -> Use much less power.
\t\tj = 0;
\t\twhile (j < 60)\t\t// Truncate brightness at a max value (60) in the interest of speed.
\t\t{
\t\t
\tif (LED0 > j)
\t\tPORTD = 4;
\t\t\telse\t
\t\t\tPORTD = 0;
\t\t\t
\tif (LED1 > j)
\t\tPORTD = 8;\t
\telse\t
\t\tPORTD = 0;\t
\t\t\t
\tif (LED2 > j)
\t\tPORTD = 16;
\telse\t
\t\tPORTD = 0;
\t\t\t
\tif (LED3 > j)
\t\tPORTD = 32;\t
\telse\t
\t\tPORTD = 0;
\t\t\t
\tif (LED4 > j)
\t\tPORTD = 64;
\telse\t
\t\tPORTD = 0;
\t\t
\tif (LED5 > j) {
\t\tPORTB = 17;\t
\t\tPORTD = 0;}
\t\t\telse\t{
\t\tPORTB = 16;
\tPORTD = 0;\t\t}
\t\t\t
\tif (LED6 > j)
\t\tPORTB = 18;\t
\telse\t
\t\tPORTB = 16;
\t\t\t
\tif (LED7 > j)
\t\tPORTB = 20;\t
\telse\t
\t\tPORTB = 16;
\t\t\t
\tif (LED8 > j)
\t\tPORTB = 24;\t
\telse\t
\t\tPORTB = 16;
\t\t
\t\tj++;
// \t\tif (speedLevel == 3)
// \t\t\tj++;
\t\t PORTB = 16;
\t}
\t
\t}
\t else
\t {\t\t// full power routine
\t\t
\t j = 0;
\t while (j < 70)
\t {
\t
\t pt = 0;\t
\t if (LED0 > j)
\t pt = 4;
\t if (LED1 > j)
\t pt |= 8;\t
\t if (LED2 > j)
\t pt |= 16;
\t if (LED3 > j)
\t pt |= 32;\t\t
\t if (LED4 > j)
\t pt |= 64;
\t
\t PORTD = pt;
\t shortdelay();
\t pt = 16;\t
\t if (LED5 > j)
\t pt |= 1;\t
\t if (LED6 > j)
\t pt |= 2;\t
\t if (LED7 > j)
\t pt |= 4;\t
\t if (LED8 > j)
\t pt |= 8;\t\t\t
\t
\t PORTB = pt;
\t shortdelay();
\t\t
\t j++;
// \t if (speedLevel == 3)
// \t j++;
\t }
\t\t
\t }
\t
\t
// Multiplexing routine: Each LED is on (1/9) of the time.
// -> Uses much less power.
\t}\t//End main loop
\treturn 0;
}
- define IS_BIT(a,p) (a&(1<// test the a variable p bit
- define SET_BIT(a,p) a |= (1<
// set in a the p bit
- define CLR_BIT(a,p) a &= ~(1<
// clear in a the p bit
- define LED0 \t\t( 4 )//1
- define LED1 \t\t( 3 )//2
- define LED2 \t\t( 2 )//3
- define LED3 \t\t( 1 )//4
- define LED4 \t\t( 0 )//5
// byte two
- define LED5 \t\t( 3 )
- define LED6 \t\t( 2 )
- define LED7 \t\t( 1 )
- define LED8 \t\t( 0 )
void delay_ms( uint16_t milliseconds)
{
for( ; milliseconds > 0; milliseconds--)
{
_delay_ms( 1);
}
}
// NSL Cylon
- define B9__(x) ((x&0x0000000FLU)?LED0:0) \\
+((x&0x000000F0LU)?LED1:0) \\
+((x&0x00000F00LU)?LED2:0) \\
+((x&0x0000F000LU)?LED3:0) \\
+((x&0x000F0000LU)?LED4:0) \\
+((x&0x00F00000LU)?LED5:0) \\
+((x&0x0F000000LU)?LED6:0) \\
+((x&0xF0000000LU)?LED7:0) \\
+((x&0xF00000000LU)?LED8:0)
- define _B9(d) ((uint16_t)B9__(HEX__(d)))
- define B9(d) 0b##d
const static uint16_t large_image[[]] PROGMEM ======
{
- if 1
\tB9(000000000),
\tB9(111111111),
\tB9(100000001),
\tB9(100000001),
\tB9(000000000),
\tB9(100000000),
\tB9(100000000),
\tB9(100000000),
\tB9(111111111),
\tB9(000000000),
\tB9(111110001),
\tB9(100010001),
\tB9(100010001),
\tB9(100011111),
\tB9(000000000),
\tB9(111111111),
\tB9(001000000),
\tB9(000111000),
\tB9(000000100),
\tB9(000000010),
\tB9(111111111),
\tB9(000000000),
\tB9(100000001),
\tB9(100000001),
\tB9(111111111),
\tB9(000000000),
\tB9(000000000),
\tB9(000000000),
// nullspace (backwards)
\tB9(100010001),
\tB9(100010001),
\tB9(100010001),
\tB9(111111111),
\tB9(000000000),
\tB9(100000001),
\tB9(100000001),
\tB9(111111111),
\tB9(000000000),
\tB9(111111111),
\tB9(000010001),
\tB9(000010001),
\tB9(111111111),
\tB9(000000000),
\tB9(000011111),
\tB9(000010001),
\tB9(000010001),
\tB9(111111111),
\tB9(000000000),
\tB9(111110001),
\tB9(100010001),
\tB9(100010001),
\tB9(100011111),
\tB9(000000000),
\tB9(100000000),
\tB9(100000000),
\tB9(111111111),
\tB9(000000000),
\tB9(100000000),
\tB9(100000000),
\tB9(111111111),
\tB9(000000000),
\tB9(111111111),
\tB9(100000000),
\tB9(100000000),
\tB9(111111111),
\tB9(000000000),
\tB9(111111111),
\tB9(010000000),
\tB9(001110000),
\tB9(000001100),
\tB9(000000010),
\tB9(111111111),
// \t0,0,0,0,
- else
// flickr
B9(010101010), B9(000000000),
B9(010101010), B9(000000000), B9(010101010), B9(101010101), B9(101010101), B9(101010101), B9(101010101),
B9(101010101), B9(010101010), B9(010101010), B9(010101010), B9(010101010), B9(010101010), B9(101010101), B9(101010101),
B9(101010101), B9(101010101), B9(101010101),
//
B9(000000000),
B9(000000000),
B9(101010101), B9(100000001),
B9(100000001), B9(000000000), B9(000000000), B9(100000000), B9(100000000), B9(100000000), B9(101010101), B9(000000000), B9(000000000), B9(101010001), B9(100010001), B9(100010001), B9(100010101), B9(000000000), B9(000000000), B9(101010101), B9(000000000), B9(001000000), B9(000010000), B9(000000100), B9(000000000), B9(101010101), B9(000000000), B9(000000000), B9(100000001), B9(100000001), B9(101010101),
B9(000000000),
B9(000000000),
B9(000000000), B9(000000000), B9(000000000), B9(000000000), B9(000101010), B9(000100010), B9(000100010), B9(000100010),
B9(010101010), B9(000000000), B9(000000000), B9(000000010), B9(000100010), B9(000100010), B9(000100010), B9(010101010), B9(000000000), B9(000000000), B9(010101010), B9(000000000), B9(000001000), B9(000000000), B9(000100000), B9(000000000), B9(000001000), B9(000000000), B9(010101010),
- endif
\t0,0,0,0,
\t65535
};
// special pointer for reading from ROM memory
PGM_P largeimage_p PROGMEM = (PGM_P)large_image;
- define NUM_ELEM(x) (sizeof (x) / sizeof (*(x)))
// this function is called when timer1 compare matches OCR1A
SIGNAL( SIG_TIMER1_COMPA ) {
\tstatic uint8_t j = 0;
\tuint8_t tmpout;
\tuint16_t tmp=0;
// reset counter
\tif (pgm_read_word(largeimage_p + j ) == 65535 ) {
\t\tj = 0;
\t}
\ttmp = pgm_read_word(largeimage_p + j);
\ttmpout = 0;
\tif( IS_BIT(tmp,0) ) SET_BIT(tmpout, LED0 );
\tif( IS_BIT(tmp,1) ) SET_BIT(tmpout, LED1 );
\tif( IS_BIT(tmp,2) ) SET_BIT(tmpout, LED2 );
\tif( IS_BIT(tmp,3) ) SET_BIT(tmpout, LED3 );
\tPORTB = ( tmpout ) >>1;
\ttmpout = 0;
\tif( IS_BIT(tmp,4) ) SET_BIT(tmpout, LED0 );
\tif( IS_BIT(tmp,5) ) SET_BIT(tmpout, LED5 );
\tif( IS_BIT(tmp,6) ) SET_BIT(tmpout, LED6 );
\tif( IS_BIT(tmp,7) ) SET_BIT(tmpout, LED7 );
\tif( IS_BIT(tmp,8) ) SET_BIT(tmpout, LED8 );
\tPORTD = tmpout<<2;
// word
\tj+=2;
}
- else
/*
larsonextend.c
The Larson Scanner -- Alternative version to allow scanner to run off the edge of the board.
It simulates one LED at brightness 4, followed by one LED of brightness 1, that moves across
the nine pixels, disappearing off either end of the board, before returning to scan in the other direction.
There is no longer any overlap of these "LEDs" at either end, but up to three LEDs may be lit at a time as
the head fades in and the tail fades out.
Also, some of the input and output values and pull-up resistors have been changed from the original program
in anticipation of future extensibility.
With the buttons linked between the units, it seems to be flakey, at best, to get an accurate button press
on multiple units at the same time, so that section is commented out below. Change the speed in the variable
declarations as you see fit. Also, since this program was originally written for a specific application
(a permanent installation in an enclosure), the unit was never really intended to change speeds. I tried to
get it to work like the original, but couldn't. Sorry, guys.
Original written by Windell Oskay, http:// www.evilmadscientist.com/
New alternative version written by John Breen III
Copyright 2009 Windell H. Oskay, 2010 John J. Breen III
Distributed under the terms of the GNU General Public License, please see below.
An avr-gcc program for the Atmel ATTiny2313
Based on Version 1.1_alt1, written by Windell Oskay
Version 1.1 Last Modified: 26-Sep-2010.
Written for Evil Mad Science Larson Scanner Kit, based on the "ix" circuit board.
More information about this project is at
http://www.evilmadscientist.com/article.php/larsonkit
-------------------------------------------------
USAGE: How to compile and install
A makefile is provided to compile and install this program using AVR-GCC and avrdude.
To use it, follow these steps:
1. Update the header of the makefile as needed to reflect the type of AVR programmer that you use.
2. Open a terminal window and move into the directory with this file and the makefile.
3. At the terminal enter
make clean
make all
make install
4. Make sure that avrdude does not report any errors. If all goes well, the last few lines output by avrdude
should look something like this:
avrdude: verifying ...
avrdude: XXXX bytes of flash verified
avrdude: safemode: lfuse reads as 62
avrdude: safemode: hfuse reads as DF
avrdude: safemode: efuse reads as FF
avrdude: safemode: Fuses OK
avrdude done. Thank you.
If you a different programming environment, make sure that you copy over the fuse settings from the makefile.
-------------------------------------------------
This code should be relatively straightforward, so not much documentation is provided. If you'd like to ask
questions, suggest improvements, or report success, please use the evilmadscientist forum:
http://www.evilmadscientist.com/forum/
-------------------------------------------------
* /
- include
- define shortdelay(); \t\t\tasm("nop\
\\t" \\
"nop\
\\t");
int main (void)
{
uint8_t LEDs[[9]]; // Storage for current LED values
uint8_t rightLED[[6]], leftLED[[6]]; // Storage for initialization visualization LEDs
\t
int8_t eyeLoc[[5]]; // List of which LED has each role, leading edge through tail.
uint8_t LEDBright[[4]] = {4u,2u,1u,1u}; // Relative brightness of scanning eye positions, head through tail
void delay_ms(uint8_t ms) {
return 0;
uint16_t delay_count = 100;
volatile uint16_t i;
while (ms != 0)
{
for (i=0; i != delay_count; i++);
ms--;
}
}
\t
int8_t j, k, m;
\t
uint8_t position, loopcount, direction, initloopcount, already_running, softbounce;
uint8_t runitout, d_base, a_base, d_mod, a_mod, far_left, far_right;
uint8_t ILED, RLED, MLED;\t// Eye position variables: Integer, Modulo, remainder
uint8_t delaytime;
uint8_t pt, debounce, speedLevel;
unsigned int debounce2, BrightMode;
\t
uint8_t LED0, LED1, LED2, LED3, LED4, LED5, LED6, LED7, LED8;
\t
// Initialization routine: Clear watchdog timer-- this can prevent several things from going wrong.\t\t
MCUSR &= 0xF7;\t\t//Clear WDRF Flag
WDTCSR\t= 0x18;\t\t//Set stupid bits so we can clear timer...
WDTCSR\t= 0x00;
// Data direction register: DDR's
// Port A: 1 is an output, A0 is an input.\t
// Port B: 0-3 are outputs, B4 is an input.\t
// Port D: 1-6 are outputs, D0 is an input.
\t
\tDDRA = 2U;
\tDDRB = 15U;\t
\tDDRD = 126U;
\t
\ta_base = 3; // set a base value (resting value) for PA, to keep things easy to modify
\td_base = 3; // set a base value (resting value) for PD, to keep things easy to modify
\t
\tPORTA = a_base;\t// Pull-up resistor enabled, PA0, Port A1 High
\tPORTB = 16;\t// Pull-up resistor enabled, PB4
\tPORTD = d_base; // Pull-up resistor enabled, PD0, Port D1 High
\t
\td_mod = 0;
\ta_mod = 0;
\t
/* Visualize outputs:
L to R:
D2 D3 D4 D5 D6 B0 B1 B2 B3\t
<-- A1 (out to left)
\t (out to right) D1 -->
* /
// Clear out all of the LED values to blank out the display
\tj = 0;
\twhile (j < 9) {
\t\tLEDs[[j]] = 0;
\t\tj++;
\t}
\t LED0 = LEDs[[0]];
LED1 = LEDs[[1]];
\t LED2 = LEDs[[2]];
\t LED3 = LEDs[[3]];
\t LED4 = LEDs[[4]];
\t LED5 = LEDs[[5]];
\t LED6 = LEDs[[6]];
\t LED7 = LEDs[[7]];
\t LED8 = LEDs[[8]];
\t
// multiplexed = LowPower;\t
\tdebounce = 1;
\tdebounce2 = 1;
\tloopcount = 254;
\tinitloopcount = 5;
\tdelaytime = 0;
\t
\tdirection = 0;
\tposition = 0;
\trunitout = 0;
\talready_running = 0;
\tsoftbounce = 0;
\tfar_left = 0;
\tfar_right = 0;
\tspeedLevel = 3; // Range: 1, 2, 3
\tBrightMode = 0;
if ((PINB & 16) == 0)\t\t// Check if button held on startup; used to verify wiring configuration
\t{ initloopcount = 0; // if so, set the startup loop counter to 0 so that we can watch the startup lights
\t softbounce = 1; // Also set the "soft-bounce" mode, which bounces back without leaving the edge, like the original
\t}\t
delay_ms(200);
PORTD = 1; // Pull D1 low, to trigger output on right side
delay_ms(10); // Delay to allow output to latch
if ((PIND & 1) == 0) // Check to see if D1 output has latched D0 input
\tfar_right = 1; //If D0 and D1 are connected, we're at the end of the chain; set far_right so we bounce back from this end
PORTD = d_base; // Set D1 high
PORTA = 1; // Pull A1 low, to trigger output on left side
delay_ms(10); // Delay to allow output to latch
if ((PINA & 1) == 0) // Check to see if A1 output has latched A0 input
\tfar_left = 1; //If A0 and A1 are connected, we're at the end of the chain; set far_left so we bounce back from this end
PORTA = a_base; // Set A1 high
// A little bit if visual verification to the user as to which way each end of the scanner is set
// (flash outwards if it's open-ended to go to the next scanner in the chain, flash inwards if it's the end and will bounce back
if (far_left) {
\tleftLED[[0]] = 7;
\tleftLED[[1]] = 11;
\tleftLED[[2]] = 19;
\tleftLED[[3]] = 19;
\tleftLED[[4]] = 19;
\tleftLED[[5]] = 19;
}
else {
\tleftLED[[0]] = 19;
\tleftLED[[1]] = 11;
\tleftLED[[2]] = 7;
\tleftLED[[3]] = 7;
\tleftLED[[4]] = 7;
\tleftLED[[5]] = 7;
}
if (far_right) {
\trightLED[[0]] = 24;
\trightLED[[1]] = 20;
\trightLED[[2]] = 18;
\trightLED[[3]] = 18;
\trightLED[[4]] = 18;
\trightLED[[5]] = 18;
}
else {
\trightLED[[0]] = 18;
\trightLED[[1]] = 20;
\trightLED[[2]] = 24;
\trightLED[[3]] = 24;
\trightLED[[4]] = 24;
\trightLED[[5]] = 24;
}
delay_ms(100);\t/* Allow all scanners to finish initializing before lighting any LEDs.
\t\t\t\t * This is necessary because the clock speeds on each chip only have a 10% tolerance, and initial
\t\t\t\t * testing showed that the tests to configure far_left and far_right were causing
\t\t\t\t * "faster" chips to get triggered by "slower" chips.
\t\t\t\t */
for (;;) // main loop
{
\tloopcount++;
\tif (loopcount > delaytime)
\t{
\t\tloopcount = 0;
\t\t
\tif ((PINB & 16) == 0) // Check for button press
\t{
\t\tif ((initloopcount >= 4) & (far_left == 1) & (already_running == 0))
\t\t{
\t\t\trunitout = 1;
\t\t\talready_running = 1;
\t\t\tdebounce = 0; // Start running the program, but only on the left-most unit.
\t\t}
\t\t
/*\tThis is the section from the original program to let the button change the speeds and brightness.
\t\tdebounce2++;
\t\t
\t\tif (debounce2 > 100)
\t\t{ debounce2 = 0;
\t\t\tdebounce = 0;
\t\t\t
\t\t if (BrightMode == 0)
\t\t\tBrightMode = 1;
\t\t else
\t\t\tBrightMode = 0;
\t\t\t
\t\t}
\t\t
\t\tif (debounce)
\t\t{
\t\t\tspeedLevel++;
\t\t\t
\t\t\tif ((speedLevel == 2) || (speedLevel == 3)) {
\t\t\t\tdelaytime = 0;
\t\t\t}
\t\t\telse
\t\t\t{ speedlevel = 1;
\t\t\t\tdelaytime = 1;
\t\t\t}
\t\t\t
\t\tdebounce = 0;
\t\t}
\t}\t
\telse{
\tdebounce = 1;
\tdebounce2 = 1;
\t}
* /\t
\t}
\t
\tif ((PINA & 1) == 0) // Check to see if display from the left has triggered to start
\t{
\t\tdirection = 0;
\t\trunitout = 1;
\t}
\t
\tif ((PIND & 1) == 0) // Check to see if display from the right has triggered to start
\t{
\t\tdirection = 1;
\t\trunitout = 1;
\t}
\t
\tif (runitout)
\t{
\t\tposition++;
\t\t
\t\tif (speedLevel == 3)
\t\t\tposition++;
\t\t
\t\tif ((softbounce == 1) & (((direction == 0) & (far_right == 1)) || ((direction == 1) & (far_left == 1))) & (position >= 224))
\t\t{ // this allows us to "soft-bounce" off the ends, like in the original program, without running off the edge
\t\t\tposition = 15;
\t\t\tif (direction == 0)
\t\t\t\tdirection = 1;
\t\t\telse
\t\t\t\tdirection = 0;
\t\t}
\t\t
\t\tif (position >= 240)\t// To allow for runoff at the ends; was '== 128'
\t\t{
\t\t\tposition = 0;
\t\t
\t\t\tif (direction == 0) {
\t\t\t\tdirection = 1; // we've reached the end, so go back in the other direction
\t\t\t\tif (far_right == 0) // If this isn't the end of the chain, we want to stop, and wait for a signal to go again
\t\t\t\t\trunitout = 0;
\t\t\t}
\t\t\telse {
\t\t\t\tdirection = 0;
\t\t\t\tif (far_left == 0) // If this isn't the end of the chain, we want to stop, and wait for a signal to go again
\t\t\t\t\trunitout = 0;
\t\t\t}
\t\t}
\t\t \t\t
\tif (direction == 0) // Moving to right, as viewed from front.
\t{
\t\tILED = (15+position) >> 4;
\t\tRLED = (15+position) - (ILED << 4);
\t\tMLED = 15 - RLED; \t\t
\t}
else
\t{
\t\tILED = (127 - position) >> 4;
\t\tMLED = (127 - position) - (ILED << 4);
\t\tRLED = 15 - MLED;\t
\t}
\t
\tif ((ILED == 10) & (direction == 0) & (far_right == 0))
\t\td_mod = 2; // If we're heading to the right, and we're not at the end of the chain, we want to trigger D1 to start the next scanner
\t\t
\tj = 0;
\twhile (j < 9) {
\t\tLEDs[[j]] = 0;
\t\tj++;
\t}
\t\t
\tj = 0;
\tif ((softbounce == 1) & (((direction == 0) & (far_right == 1)) || ((direction == 1) & (far_left == 1)))) {
\t\twhile (j < 5) {
\t\t\t
\t\t\tif (direction == 0)
\t\t\t m = ILED - (j + 1);
\t\t\t //m = ILED + (2 - j);\t// e.g., eyeLoc[[0]] = ILED + 2;
\t\t\telse
\t\t\t m = ILED + (j + 1);
\t\t\t //m = ILED + (j - 2); // e.g., eyeLoc[[0]] = ILED - 2;
\t\t\t
\t\t\tif ((direction == 0) & (m > 8))
\t\t\t\tm = 8;
\t\t\t
\t\t\tif ((direction == 1) & (m < 0))
\t\t\t\tm = 0;
\t\t\t
\t\t\teyeLoc[[j]] = m;
\t\t\t
\t\t\tj++;
\t\t}
\t}
\telse {
\t\twhile (j < 5)
\t\t{\t\t
\t\t\tif (direction == 0)
\t\t\t\tm = ILED - (j + 1);\t// e.g., eyeLoc[[0]] = ILED - 1;
\t\t\telse
\t\t\t\tm = ILED + (j + 1); // e.g., eyeLoc[[0]] = ILED + 1;
\t\t\t
\t\t\tif ((m == -1) & (direction == 1) & (far_left == 0))
\t\t\t\ta_mod = 2; // If we're heading to the left, and we're not at the end of the chain, we want to trigger A1 to start the next scanner
\t\t
\t\t\tif (m > 8)
\t\t\t\tm = -1; // If eye position is past the end of the board, don't light it; set to -1
\t\t\t\t\t\t
\t\t\tif (m < 0)
\t\t\t\tm = -1; // If eye position is past the end of the board, don't light it; set to -1
\t\t\t
\t\t\teyeLoc[[j]] = m;
\t\t\t
\t\t\tj++;
\t\t}
\t}
\t\t
\tj = 0;\t\t// For each of the eye parts...
\twhile (j < 4)
\t{\t\t
\t\tif (eyeLoc[[j]] >= 0)
\t\t\tLEDs[eyeLoc[j]] += LEDBright[[j]]*RLED;\t
\t\tif (eyeLoc[[j+1]] >= 0)
\t\t\tLEDs[eyeLoc[j+1]] += LEDBright[[j]]*MLED;
\t\tj++;
\t}
\t
\t LED0 = LEDs[[0]];
LED1 = LEDs[[1]];
\t LED2 = LEDs[[2]];
\t LED3 = LEDs[[3]];
\t LED4 = LEDs[[4]];
\t LED5 = LEDs[[5]];
\t LED6 = LEDs[[6]];
\t LED7 = LEDs[[7]];
\t LED8 = LEDs[[8]];
\t}
\telse if (initloopcount < 4 )
\t{
\t\tk = 0;
\t\twhile (k < 6) {
\t\t\tPORTD = leftLED[[k]];
\t\t\tPORTB = rightLED[[k]];
\t\t\tdelay_ms(1);
\t\t
\t\t\tPORTD = d_base;
\t\t\tPORTB = 16;
\t\t\tdelay_ms(29);
\t\t\tk++;
\t\t}
\t\tdelay_ms(100);
\t\tinitloopcount++;
\t}
\t}
\tif (runitout) {
\tif (BrightMode == 0)
\t{\t\t//multiplexing routine: each led is on (1/9) of the time.
\t\t\t// -> Use much less power.
\t\tj = 0;
\t\tPORTA = a_base - a_mod; // we set a_mod to correspond to A1's bit in PORTA; this makes it easier to change the pin configs later
\t\twhile (j < 60)\t\t// Truncate brightness at a max value (60) in the interest of speed.
\t\t{
\t\t
\tif (LED0 > j)
\t\tPORTD = 7 - d_mod; // we set d_mod to correspond to D1's bit in PORTD; this makes it easier to change the trigger pin configs later
\telse\t
\t\tPORTD = d_base - d_mod;
\t\t\t
\tif (LED1 > j)
\t\tPORTD = 11 - d_mod;\t
\telse\t
\t\tPORTD = d_base - d_mod;\t
\t\t\t
\tif (LED2 > j)
\t\tPORTD = 19 - d_mod;
\telse\t
\t\tPORTD = d_base - d_mod;
\t\t\t
\tif (LED3 > j)
\t\tPORTD = 35 - d_mod;\t
\telse\t
\t\tPORTD = d_base - d_mod;
\t\t\t
\tif (LED4 > j)
\t\tPORTD = 67 - d_mod;
\telse\t
\t\tPORTD = d_base - d_mod;
\t\t
\tif (LED5 > j) {
\t\tPORTB = 17;\t
\t\tPORTD = d_base - d_mod;}
\telse\t{
\t\tPORTB = 16;
\t\tPORTD = d_base - d_mod;}
\t\t\t
\tif (LED6 > j)
\t\tPORTB = 18;\t
\telse\t
\t\tPORTB = 16;
\t\t\t
\tif (LED7 > j)
\t\tPORTB = 20;\t
\telse\t
\t\tPORTB = 16;
\t\t\t
\tif (LED8 > j)
\t\tPORTB = 24;\t
\telse\t
\t\tPORTB = 16;
\t\t
\t\tj++;
// \t\tif (speedLevel == 3)
// \t\t\tj++;
\t\t PORTB = 16;
\t}
\t
\td_mod = 0;
\ta_mod = 0;
\t}
\t else
\t {\t\t// full power routine
\t
\t PORTA = a_base - a_mod;
\t j = 0;
\t while (j < 70)
\t {
\t
\t
\t pt = d_base - d_mod;\t
\t if (LED0 > j)
\t pt |= 4;
\t if (LED1 > j)
\t pt |= 8;\t
\t if (LED2 > j)
\t pt |= 16;
\t if (LED3 > j)
\t pt |= 32;\t\t
\t if (LED4 > j)
\t pt |= 64;
\t
\t PORTD = pt;
\t shortdelay();
\t pt = 16;\t
\t if (LED5 > j)
\t pt |= 1;\t
\t if (LED6 > j)
\t pt |= 2;\t
\t if (LED7 > j)
\t pt |= 4;\t
\t if (LED8 > j)
\t pt |= 8;\t\t\t
\t
\t PORTB = pt;
\t shortdelay();
\t\t
\t j++;
// \t if (speedLevel == 3)
// \t j++;
\t }
\t
\t d_mod = 0; // we want to stop triggering D1, so set the modifier of PORTD back to 0
\t a_mod = 0; // we want to stop triggering A1, so set the modifier of PORTA back to 0
\t
\t }
\t}
\t
// Multiplexing routine: Each LED is on (1/9) of the time.
// -> Uses much less power.
PORTA = a_base - a_mod;
\t}\t//End main loop
\treturn 0;
}
- endif
\t