Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // this is the code for the 8 LED, Tiny85 blinkie developed for Duckon's Free For All build-a-blinkie
- // PWM code and excellent comments taken straight from http://matt16060936.blogspot.com/2012/04/attiny-pwm.html
- //
- // John Ridley
- // 2014-02-05
- // http://johnridley.blogspot.com
- // effective brightness levels are 0, powers of 2 up through 128. 255 isn't really effectively brigher than 128, save battery power.
- #define F_CPU 2000000
- #include <avr/io.h>
- #include <avr/sleep.h>
- #include <avr/interrupt.h>
- #include <avr/pgmspace.h>
- #include <util/delay.h>
- // button tracking
- int button_down_count = 0;
- // Modes: 0 = rotation, higher than that = run one pattern
- volatile int button_mode = 1; // this is changed by the button within the ISR
- volatile int current_mode = 1; // this follows button_mode when the main code reacts
- int max_mode = 2;
- // this is used for timing patterns. It gets incremented every time the display driver
- // completes a cycle
- // about 132 per second
- volatile int loops = 0;
- // display buffer
- volatile unsigned char displaybuffer[9];
- // some forward declarations
- void power_off(void);
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // pattern subroutines here
- // call ExitCheck often, leave when it returns 1
- // set loops to 0 before beginning a function that will call this
- int ExitCheck()
- {
- if (button_mode != current_mode)
- return 1;
- if (loops > 8000)
- return 1;
- return 0;
- }
- void ClearDisplay()
- {
- for (int x=0; x<9; x++)
- displaybuffer[x] = 0;
- }
- // call when mode switches, it will indicate the new mode for 1 second
- void ModeSwitchPattern()
- {
- for (int x=0; x<9; x++)
- {
- if ((current_mode & (1<<x)) > 0)
- displaybuffer[x] = 255;
- else
- displaybuffer[x] = 0;
- }
- loops = 0;
- while (loops < 132)
- {
- if (ExitCheck())
- return;
- }
- }
- /**********************************************************************/
- void KnightRider()
- {
- int foo = 0;
- int dotpos = 0;
- int direction = 1;
- int x;
- loops = 0;
- ClearDisplay();
- for (;;)
- {
- _delay_ms(5);
- for (x=0; x<9; x++)
- if (displaybuffer[x]>0 && displaybuffer[x] < 250)
- displaybuffer[x] -=10;
- if (foo++ > 20)
- {
- displaybuffer[dotpos] = 200;
- if (direction == 1)
- if (dotpos == 8)
- {
- dotpos = 7;
- direction = -1;
- }
- else
- dotpos++;
- else
- if (dotpos == 0)
- {
- dotpos = 1;
- direction = 1;
- }
- else
- dotpos--;
- foo = 0;
- displaybuffer[dotpos] = 250;
- }
- if (ExitCheck())
- return;
- }
- }
- /**********************************************************************/
- // simple chase
- void chase()
- {
- ClearDisplay();
- while(1)
- {
- for (int x=0; x<9; x++)
- {
- if (x == 0)
- displaybuffer[8] = 0;
- else
- displaybuffer[x-1] = 0;
- displaybuffer[x] = 255;
- _delay_ms(50);
- if (ExitCheck())
- return;
- }
- }
- }
- // center weighted throb
- void centerThrob()
- {
- loops = 0;
- int ctrVal = 0; // goes to 1024, overflow goes to next LED over, next LED over also gets 1/2 of brigher LED
- int carryVal, curval, offset;
- int direction = 1;
- while (1)
- {
- _delay_ms(100);
- if (direction == 1)
- {
- if (ctrVal > 1023)
- {
- direction = -1;
- }
- else
- ctrVal += 64;
- } else {
- if (ctrVal < 1)
- {
- direction = 1;
- }
- else
- ctrVal -= 64;
- }
- // now do the display
- carryVal = ctrVal;
- offset = 0;
- while (offset < 5)
- {
- if (offset > 0)
- curval = displaybuffer[4+offset-1] / 4;
- if (carryVal > 255)
- curval += 255;
- else
- curval += carryVal;
- if (curval > 255)
- curval == 255;
- carryVal -= curval;
- displaybuffer[4+offset] = curval;
- displaybuffer[4-offset] = curval;
- offset++;
- }
- // if (ExitCheck())
- // return;
- }
- }
- void LEDTEST()
- {
- #define LEDTESTDELAY 100
- int x,y;
- #if 0
- for (x=0; x<9; x++)
- {
- displaybuffer[x] = 128;
- _delay_ms(LEDTESTDELAY);
- displaybuffer[x] = 0;
- }
- #endif
- for (y=0; y<8; y++)
- for (x=0; x<9; x++)
- {
- switch(y)
- {
- case 0: displaybuffer[x] = (unsigned char)1; break;
- case 1: displaybuffer[x] = (unsigned char)2; break;
- case 2: displaybuffer[x] = (unsigned char)4; break;
- case 3: displaybuffer[x] = (unsigned char)8; break;
- case 4: displaybuffer[x] = (unsigned char)16; break;
- case 5: displaybuffer[x] = (unsigned char)32; break;
- case 6: displaybuffer[x] = (unsigned char)64; break;
- case 7: displaybuffer[x] = (unsigned char)128; break;
- }
- // displaybuffer[x] = 1<<y;
- _delay_ms(LEDTESTDELAY);
- // displaybuffer[x] = 0;
- }
- for (y=0;y<3; y++)
- {
- for (x=0; x<9; x++)
- displaybuffer[x] = 128;
- _delay_ms(LEDTESTDELAY);
- for (x=0; x<9; x++)
- displaybuffer[x] = 0;
- _delay_ms(LEDTESTDELAY);
- }
- }
- // VU Meter utility function: set level
- void vulevel(short level)
- {
- for (short x=0; x<9; x++)
- {
- if (x <= level)
- displaybuffer[x] = 255;
- else
- displaybuffer[x] = 0;
- }
- }
- void vumeter()
- {
- loops = 0;
- short currentlevel = 0;
- while (1)
- {
- }
- }
- /**********************************************************************/
- void SetupDisplayInterrupt()
- {
- // TCCR0A = 0b00000000; // Normal Mode
- // TCCR0B = 0b00000010; // Div 32 Prescaler
- /*
- Control Register A for Timer/Counter-0 (Timer/Counter-0 is configured using two registers: A and B)
- TCCR0A is 8 bits: [COM0A1:COM0A0:COM0B1:COM0B0:unused:unused:WGM01:WGM00]
- 2<<COM0A0: sets bits COM0A0 and COM0A1, which (in Fast PWM mode) clears OC0A on compare-match, and sets OC0A at BOTTOM
- 2<<COM0B0: sets bits COM0B0 and COM0B1, which (in Fast PWM mode) clears OC0B on compare-match, and sets OC0B at BOTTOM
- 3<<WGM00: sets bits WGM00 and WGM01, which (when combined with WGM02 from TCCR0B below) enables Fast PWM mode
- */
- TCCR0A = 2<<COM0A0 | 2<<COM0B0 | 3<<WGM00;
- /*
- Control Register B for Timer/Counter-0 (Timer/Counter-0 is configured using two registers: A and B)
- TCCR0B is 8 bits: [FOC0A:FOC0B:unused:unused:WGM02:CS02:CS01:CS00]
- 0<<WGM02: bit WGM02 remains clear, which (when combined with WGM00 and WGM01 from TCCR0A above) enables Fast PWM mode
- 1<<CS00: sets bits CS01 (leaving CS01 and CS02 clear), which tells Timer/Counter-0 to not use a prescalar
- */
- TCCR0B = 0<<WGM02 | 1<<CS00;
- /*
- Control Register for Timer/Counter-1 (Timer/Counter-1 is configured with just one register: this one)
- TCCR1 is 8 bits: [CTC1:PWM1A:COM1A1:COM1A0:CS13:CS12:CS11:CS10]
- 0<<PWM1A: bit PWM1A remains clear, which prevents Timer/Counter-1 from using pin OC1A (which is shared with OC0B)
- 0<<COM1A0: bits COM1A0 and COM1A1 remain clear, which also prevents Timer/Counter-1 from using pin OC1A (see PWM1A above)
- 1<<CS10: sets bit CS11 which tells Timer/Counter-1 to not use a prescalar
- */
- TCCR1 = 0<<PWM1A | 0<<COM1A0 | 1<<CS10;
- /*
- General Control Register for Timer/Counter-1 (this is for Timer/Counter-1 and is a poorly named register)
- GTCCR is 8 bits: [TSM:PWM1B:COM1B1:COM1B0:FOC1B:FOC1A:PSR1:PSR0]
- 1<<PWM1B: sets bit PWM1B which enables the use of OC1B (since we disabled using OC1A in TCCR1)
- 2<<COM1B0: sets bit COM1B1 and leaves COM1B0 clear, which (when in PWM mode) clears OC1B on compare-match, and sets at BOTTOM
- */
- GTCCR = 1<<PWM1B | 2<<COM1B0;
- // enable interrupts
- TIMSK = (1<<TOIE0); // Timer Mask: Enable interrupt on Timer 0 overflow
- /*
- Port B Data Direction Register (controls the mode of all pins within port B)
- DDRB is 8 bits: [unused:unused:DDB5:DDB4:DDB3:DDB2:DDB1:DDB0]
- 1<<DDB4: sets bit DDB4 (data-direction, port B, pin 4), which puts PB4 (port B, pin 4) in output mode
- 1<<DDB1: sets bit DDB1 (data-direction, port B, pin 1), which puts PB1 (port B, pin 1) in output mode
- 1<<DDB0: sets bit DDB0 (data-direction, port B, pin 0), which puts PB0 (port B, pin 0) in output mode
- JRR also want PB2 set as output for bank select
- */
- DDRB = 1<<DDB4 | 1<<DDB1 | 1<<DDB0 | 1<<DDB2;
- }
- /*
- program entry-point
- */
- int main()
- {
- SetupDisplayInterrupt();
- sei(); // Global enable Interrupts
- displaybuffer[0] = 0;
- displaybuffer[1] = 0;
- displaybuffer[2] = 0;
- displaybuffer[3] = 0;
- displaybuffer[4] = 0;
- displaybuffer[5] = 0;
- displaybuffer[6] = 0;
- displaybuffer[7] = 0;
- displaybuffer[8] = 0;
- displaybuffer[0] = 1;
- while (1)
- {
- displaybuffer[3] = 1;
- _delay_ms(1000);
- displaybuffer[3] = 0;
- _delay_ms(1000);
- }
- while (1)
- LEDTEST();
- centerThrob();
- while (1)
- {
- if (button_mode != current_mode)
- {
- current_mode = button_mode;
- ModeSwitchPattern(); // this displays the current mode in binary for 1 second
- }
- else
- {
- switch (current_mode)
- {
- case 1:
- KnightRider();
- break;
- case 2:
- chase();
- break;
- }
- }
- }
- return 0;
- }
- // **********************************************************
- // *** DISPLAY HANDLER
- // **********************************************************
- // This gets called on timer interrupt
- // this also detects button presses
- ISR(TIM0_OVF_vect)
- {
- static short loopcount=0;
- unsigned char DDRval = 0;
- if (loopcount == 0)
- {
- // **** Display bank 0 ****
- // we are coming off of bank 2 where
- // all banks off when switching to avoid flicker
- DDRB = 0; // tristate everything
- DDRval = 1<<DDB2; // value to tristate PB3, turn PB2 on as output
- // set bank 0 values
- if (displaybuffer[0] > 0)
- {
- OCR0A = displaybuffer[0];
- DDRval |= 1<<DDB0;
- }
- if (displaybuffer[1] > 0)
- {
- OCR0B = displaybuffer[1];
- DDRval |= 1<< DDB1;
- }
- if (displaybuffer[2] > 0)
- {
- OCR1B = displaybuffer[2];
- DDRval |= 1<<DDB4;
- }
- TCNT0 = 0; // Restart PWM timer
- TCNT1 = 0; // Restart PWM timer
- // switch back to bank 0
- // tristate PB3, turn PB2 on and set it low
- PORTB = 0; // bank 0
- DDRB = DDRval;
- }
- if (loopcount == 10)
- {
- // **** Display bank 1 ****
- // all banks off when switching to avoid flicker
- // DDRB = 0;
- DDRval = 1<<DDB2; // value to tristate PB3, turn PB2 on as output
- // set bank 1 values
- if (displaybuffer[3] > 0)
- {
- OCR0A = (unsigned char)(256-displaybuffer[3]);
- // DDRval |= 1<<DDB0;
- }
- if (displaybuffer[4] > 0)
- {
- OCR0B = 256-displaybuffer[4];
- DDRval |= 1<< DDB1;
- }
- if (displaybuffer[5] > 0)
- {
- OCR1B = 256-displaybuffer[5];
- DDRval |= 1<<DDB4;
- }
- TCNT0 = 0; // Restart PWM timer
- TCNT1 = 0; // Restart PWM timer
- // switch back to bank 0/1
- // tristate PB3, turn PB2 on and set it low
- PORTB = 1<<2; // bank 1
- DDRB = DDRval;
- }
- if (loopcount == 20)
- {
- // **** Display bank 2 ****
- // all banks off when switching to avoid flicker
- // DDRB = 0;
- DDRval = 1<<DDB3; // value to tristate PB2 and turn PB3 on as output
- // set bank 2 values
- if (displaybuffer[6] > 0)
- {
- OCR0A = displaybuffer[6];
- DDRval |= 1<<DDB0;
- }
- if (displaybuffer[7] > 0)
- {
- OCR0B = displaybuffer[7];
- DDRval |= 1<< DDB1;
- }
- if (displaybuffer[8] > 0)
- {
- OCR1B = displaybuffer[8];
- DDRval |= 1<<DDB4;
- }
- TCNT0 = 0; // Restart PWM timer
- TCNT1 = 0; // Restart PWM timer
- // switch to bank 2
- // tristate PB2, turn PB3 on and set it low
- PORTB = 0;
- DDRB = DDRval;
- }
- loopcount++;
- if (loopcount == 30)
- {
- // this is a good place to read the switch
- DDRB = 0x00; // all pins to input
- PORTB = 0x01; // PB0 pull-up on
- _delay_ms(1);
- if ((PINB & 0x01) == 0) // button on = 0 on PB0
- {
- // if button has been down for a long time, power off
- if (button_down_count++ == 100)
- {
- button_down_count = 0;
- power_off(); // then shut off until the button is pressed
- }
- } else {
- // if button down count is > 100 then we're returning from power down, ignore button activity on first release.
- // if button is now up and was down long enough for it to not be a bounce but not long enough to be power off, switch modes
- if ((button_down_count > 5) && (button_down_count < 101))
- {
- if (button_mode == max_mode)
- button_mode = 1;
- else
- button_mode++;
- }
- button_down_count = 0;
- }
- PORTB = 0; // don't leave the pull-up on
- // finally, recycle back to beginning of display cycle
- loopcount = 0;
- loops++;
- }
- }
- // **********************************************************
- // *** BUTTON PRESS HANDLING
- // **********************************************************
- //Setup pin change interrupt used to wake from sleep
- void init_pcint(void)
- {
- GIMSK |= 1<<PCIE; //Enable Pin Change Interrupt
- PCMSK |= 1<<PCINT0; //Watch for Pin Change on Pin5 (PB0)
- }
- //Pin Change Interrupt
- ISR(PCINT0_vect)
- {
- sleep_disable();
- GIMSK &= ~(1<<PCIE); //Disable the interrupt so it doesn't keep flagging
- PCMSK &= ~(1<<PCINT0);
- }
- // turn off and wait for button press
- void power_off(void)
- {
- cli();
- TIMSK &= ~(1<<TOIE0); //Turn off the timer (overflow) interrupt
- // wait for the button to be released
- DDRB = 0x0; // PB0 to input mode
- PORTB = 0x01; // pull-up active
- while ((PINB & 0x01) == 0);
- _delay_ms(50); // debouncing
- // go into power down mode until the button gets pressed again
- gotosleep:
- init_pcint(); // set up pin change interrupt
- set_sleep_mode(SLEEP_MODE_PWR_DOWN);
- sei(); // enable interrupts (so we can come back alive again when the button is pressed)
- sleep_mode();
- // interrupts will be disabled when we get back from here but RETI at the end of this function will enable interrupts
- // detect double click
- _delay_ms(50); // debounce button press
- while ((PINB & 0x01) == 0); // wait for button release
- _delay_ms(50); // debounce button release
- for (int x=0; x<500; x++) // 1/2 second to press button again
- {
- if ((PINB & 0x01) == 0) // button was pressed again
- goto wakeup;
- _delay_ms(1);
- }
- // no subsequent button press, false alarm
- goto gotosleep;
- wakeup:
- button_down_count = 101; // don't switch modes upon release.
- SetupDisplayInterrupt(); // put interrupts back to run mode
- // _delay_ms(200);
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement