Created
January 24, 2021 22:59
-
-
Save ngs/6f9f3e6cf3a35f8fa1bb011d4b92a238 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// tinyCylon_2_1.c | |
// 22 february 2010 - dale wheat - added mode memory | |
// based on tinyCylon2.c | |
// revised firmware for tinyCylon LED scanner | |
// written by dale wheat - 18 november 2008 | |
// based on behavior of original tinyCylon firmware | |
// notes: | |
// device = ATtiny13A | |
// clock = 128 KHz internal RC oscillator | |
// max ISP frequency ~20 KHz | |
// brown-out detect = 1.8 V | |
#include <avr/io.h> | |
#include <avr/interrupt.h> | |
#include <avr/eeprom.h> // 2.1 | |
// These registers are available on the ATtiny13A but not the original ATtiny13 | |
// Brown out detector control register | |
#define BODCR _SFR_IO8(0x30) | |
#define BODSE 0 | |
#define BODS 1 | |
// Power reduction register | |
#define PRR _SFR_IO8(0x3C) | |
#define PRADC 0 | |
#define PRTIM0 1 | |
/////////////////////////////////////////////////////////////////////////////// | |
// macros | |
/////////////////////////////////////////////////////////////////////////////// | |
#define nop() asm("nop") | |
#define sbi(port, bit) (port) |= (1 << (bit)) | |
#define cbi(port, bit) (port) &= ~(1 << (bit)) | |
typedef enum { | |
MODE_0, // cylon scanner | |
MODE_0a, // single direction cylon scan | |
MODE_0b, // other direction cylon scan | |
MODE_1, // glowing pig eyes (2) | |
MODE_1a, // single glowing pig eye | |
MODE_1b, // single (random) glowing pig eye | |
MODE_2, // random blinking - multi | |
MODE_2a, // random blinking - single | |
MODE_2b, // random blinking - intermittant | |
MODE_2c, // random bblinking - really sparse | |
MODE_MAX // off | |
} MODE; | |
volatile MODE mode __attribute__ ((section (".noinit"))); | |
// EEPROM non-volatile storage section | |
unsigned char eeprom_do_not_use EEMEM; // bad luck - do not use location 0 | |
unsigned char eeprom_mode EEMEM; // mode storage location | |
/////////////////////////////////////////////////////////////////////////////// | |
// init() - initialize everything | |
// note: this "function" is in section .init3 so it is executed before main() | |
/////////////////////////////////////////////////////////////////////////////// | |
void init(void) __attribute__ ((naked, section(".init3"))); | |
void init(void) { | |
// turn off unused peripherals to save power | |
PRR = 0<<PRTIM0 | 1<<PRADC; // power down ADC - 2.1 | |
ACSR = 1<<ACD; // disable analog comparator | |
DIDR0 = 1<<ADC3D | 1<<ADC2D | 1<<ADC1D | 1<<ADC0D | 1<<AIN1D | 1<<AIN0D; // disable all digital inputs | |
// determine cause of device reset; act accordingly | |
// if(bit_is_set(MCUSR, PORF)) { | |
// mode = MODE_0; // power on! | |
// } else if(bit_is_set(MCUSR, EXTRF)) { | |
// mode++; // advance mode | |
// if(mode > MODE_MAX) { | |
// mode = MODE_0; // reset mode | |
// } | |
// } | |
// EEPROM address register is always pointed to the "mode memory" address, 1 | |
asm("ldi r24, 1"); // EEPROM address regsiter always points to mode memory location: 1 | |
asm("out 0x1e, r24"); | |
if(bit_is_set(MCUSR, PORF)) { | |
//mode = MODE_POWER_ON; // power on! | |
//mode = eeprom_read_byte(&eeprom_mode); // recall | |
sbi(EECR, EERE); // strobe EEPROM read enable signal | |
mode = EEDR; // read mode memory location from EEPROM | |
if(mode > MODE_MAX) { | |
mode = MODE_0; // classic cylon scanner | |
} | |
} else if(bit_is_set(MCUSR, EXTRF)) { | |
mode++; // advance mode | |
if(mode > MODE_MAX) { | |
mode = MODE_0; // reset mode | |
} | |
//eeprom_write_byte(&eeprom_mode, mode); // memorize | |
EEDR = mode; // move mode to EEPROM data register, address register is already set up | |
asm("out 0x1c, r1"); // write a zero to the EEPROM control register | |
sbi(EECR, EEMPE); // enable the "master program" bit | |
sbi(EECR, EEPE); // write the data to the EEPROM | |
} | |
MCUSR = 0; // reset bits | |
// initialize ATtiny13 input & output port | |
// PORTB | |
// PB0 5 MOSI/AIN0/OC0A/PCINT0 D1 output, active low | |
// PB1 6 MISO/AIN1/OC0B/INT0/PCINT1 D2 output, active low | |
// PB2 7 SCK/ADC1/T0/PCINT2 D3 output, active low | |
// PB3 2 PCINT3/CLKI/ADC3 D4 output, active low | |
// PB4 3 PCINT4/ADC2 D5 output, active low | |
// PB5 1 PCINT5/-RESET/ADC0/dW MODE advance pushbutton | |
PORTB = 0<<PORTB5 | 1<<PORTB4 | 1<<PORTB3 | 1<<PORTB2 | 1<<PORTB1 | 1<<PORTB0; | |
DDRB = 0<<DDB5 | 1<<DDB4 | 1<<DDB3 | 1<<DDB2 | 1<<DDB1 | 1<<DDB0; | |
// initialize ATtiny13 timer/counter | |
TCCR0B = 0<<FOC0A | 0<<FOC0B | 0<<WGM02 | 0<<CS02 | 0<<CS01 | 1<<CS00; | |
TIMSK0 = 0<<OCIE0B | 0<<OCIE0A | 1<<TOIE0; // interrupts | |
sei(); // enable global interrupts | |
} | |
/////////////////////////////////////////////////////////////////////////////// | |
// timing & delay functions | |
/////////////////////////////////////////////////////////////////////////////// | |
volatile unsigned char downcounter; | |
void delay(unsigned char n) { | |
downcounter = n; | |
while(downcounter) { | |
MCUCR = 1<<PUD | 1<<SE | 0<<SM1 | 0<<SM0 | 0<<ISC01 | 0<<ISC00; // idle mode | |
asm("sleep"); // go to sleep to save power | |
} | |
} | |
/////////////////////////////////////////////////////////////////////////////// | |
// pseudorandom number generator | |
/////////////////////////////////////////////////////////////////////////////// | |
unsigned int prand(void) { | |
static unsigned int prand_value = 0xDA1E; // randomly seeded ;) | |
prand_value = (prand_value >> 1) ^ (-(prand_value & 1) & 0xd001); | |
return prand_value; | |
} | |
/////////////////////////////////////////////////////////////////////////////// | |
// cylon() - simulate cylon scanner | |
/////////////////////////////////////////////////////////////////////////////// | |
#define CYLON_SCAN_DELAY 30 | |
void cylon(unsigned char cylon_style) { | |
const unsigned char cylon_bits[] = { | |
0b00001110, // 1 & 5 | |
0b00011110, // l | |
0b00011100, // 1 & 2 | |
0b00011101, // 2 | |
0b00011001, // 2 & 3 | |
0b00011011, // 3 | |
0b00010011, // 3 & 4 | |
0b00010111, // 4 | |
0b00000111, // 4 & 5 | |
0b00001111 // 5 | |
}; | |
unsigned char i; // array iterator | |
while(1) { | |
if(cylon_style == 0) { | |
// traditional (back & forth) cylon scanner | |
for(i = 1; i < sizeof(cylon_bits); i++) { | |
PORTB = cylon_bits[i]; | |
delay(CYLON_SCAN_DELAY); | |
} | |
for(i = sizeof(cylon_bits) - 2; i > 1; i--) { | |
PORTB = cylon_bits[i]; | |
delay(CYLON_SCAN_DELAY); | |
} | |
} else if(cylon_style == 1) { | |
// single direction scan | |
for(i = 0; i < sizeof(cylon_bits); i++) { | |
PORTB = cylon_bits[i]; | |
delay(CYLON_SCAN_DELAY); | |
} | |
} else if(cylon_style == 2) { | |
// other direction scan | |
for(i = sizeof(cylon_bits); i > 0; i--) { | |
PORTB = cylon_bits[i - 1]; | |
delay(CYLON_SCAN_DELAY); | |
} | |
} | |
} | |
} | |
/////////////////////////////////////////////////////////////////////////////// | |
// pig_eyes() - glowing pig eyes are scary | |
/////////////////////////////////////////////////////////////////////////////// | |
void pig_eyes(unsigned char n) { | |
unsigned char leds_on, leds_off = 0b00011111; | |
unsigned char pwm_counter, pwm_value; | |
while(1) { | |
if(n == 1) { | |
leds_on = 0b00011011; // one eye | |
} else if(n == 2) { | |
leds_on = 0b00001110; // 2 eyes | |
} else { | |
leds_on = 0b00011111 ^ (1 << (prand() % 5)); // single (random) eye | |
} | |
// ramp up... | |
for(pwm_value = 1; pwm_value < 128; pwm_value++) { | |
for(pwm_counter = 0; pwm_counter < 128; pwm_counter++) { | |
if(pwm_value > pwm_counter) { | |
PORTB = leds_on; | |
} else { | |
PORTB = leds_off; | |
} | |
} | |
} | |
// ... & ramp back down again | |
for(pwm_value = 127; pwm_value > 0; pwm_value--) { | |
for(pwm_counter = 0; pwm_counter < 128; pwm_counter++) { | |
if(pwm_value > pwm_counter) { | |
PORTB = leds_on; | |
} else { | |
PORTB = leds_off; | |
} | |
} | |
} | |
delay(250); // scary dark time | |
} | |
} | |
/////////////////////////////////////////////////////////////////////////////// | |
// random() - random blinking | |
/////////////////////////////////////////////////////////////////////////////// | |
void random(unsigned char n) { | |
while(1) { | |
if(n == 0) { | |
// light up a random number of LEDs | |
PORTB = prand() & 0x1F; | |
} else if(n == 1) { | |
// light up a single LED every time | |
PORTB = 0b00011111 ^ (1 << (prand() % 5)); | |
} else { | |
if((prand() & n) == n) { | |
// light up an LED (maybe) | |
PORTB = 0b00011111 ^ (1 << (prand() % 5)); | |
} else { | |
// no LEDs on (maybe not) | |
PORTB = 0b00011111; | |
} | |
} | |
delay(50); | |
} | |
} | |
/////////////////////////////////////////////////////////////////////////////// | |
// main() - main program function | |
/////////////////////////////////////////////////////////////////////////////// | |
void main(void) { | |
switch(mode) { | |
case MODE_0: // traditional (back & forth) cylon scanner | |
cylon(0); | |
break; | |
case MODE_0a: // single direction cylon scanner | |
cylon(1); | |
break; | |
case MODE_0b: // other direction cylon scanner | |
cylon(2); | |
break; | |
case MODE_1: // 2 glowing pig eyes | |
pig_eyes(2); | |
break; | |
case MODE_1a: // single glowing pig eye | |
pig_eyes(1); | |
break; | |
case MODE_1b: // single (random) glowing pig eye | |
pig_eyes(0); | |
break; | |
case MODE_2: // random blinking - multi | |
random(0); | |
break; | |
case MODE_2a: // random blinking - single | |
random(1); | |
break; | |
case MODE_2b: // random blinking - intermittant | |
random(3); | |
break; | |
case MODE_2c: // random blinking - really sparse | |
random(15); | |
break; | |
case MODE_MAX: // off | |
PORTB = 0b00011111; // all LEDs off | |
while(1) { | |
// deepest sleep mode | |
cli(); // disable interrupts | |
PRR = 1<<PRTIM0 | 1<<PRADC; // power down timer/counter0 & ADC | |
BODCR = 1<<BODS | 1<<BODSE; // enable BOD disable during sleep, step 1 | |
BODCR = 1<<BODS | 0<<BODSE; // step 2 | |
MCUCR = 1<<PUD | 1<<SE | 1<<SM1 | 0<<SM0 | 0<<ISC01 | 0<<ISC00; // select "power down" mode | |
asm("sleep"); // go to sleep to save power | |
} | |
} | |
} | |
/////////////////////////////////////////////////////////////////////////////// | |
// timer/counter0 overflow interrupt handler | |
/////////////////////////////////////////////////////////////////////////////// | |
ISR(TIM0_OVF_vect) { | |
downcounter--; // decrement downcounter for delay functions | |
} | |
/////////////////////////////////////////////////////////////////////////////// | |
// [end-of-file] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment