/* TV-B-Gone Firmware version 1.2 for use with ATtiny85v and v1.2 hardware (c) Mitch Altman + Limor Fried 2009 Last edits, August 16 2009 With some code from: Kevin Timmerman & Damien Good 7-Dec-07 Distributed under Creative Commons 2.5 -- Attib & Share Alike This is the 'universal' code designed for v1.2 - it will select EU or NA depending on a pulldown resistor on pin B1 ! */ #include // this contains all the IO port definitions #include #include // definitions for power-down modes #include // definitions or keeping constants in program memory #include #include "main.h" #include "util.h" /* This project transmits a bunch of TV POWER codes, one right after the other, with a pause in between each. (To have a visible indication that it is transmitting, it also pulses a visible LED once each time a POWER code is transmitted.) That is all TV-B-Gone does. The tricky part of TV-B-Gone was collecting all of the POWER codes, and getting rid of the duplicates and near-duplicates (because if there is a duplicate, then one POWER code will turn a TV off, and the duplicate will turn it on again (which we certainly do not want). I have compiled the most popular codes with the duplicates eliminated, both for North America (which is the same as Asia, as far as POWER codes are concerned -- even though much of Asia USES PAL video) and for Europe (which works for Australia, New Zealand, the Middle East, and other parts of the world that use PAL video). Before creating a TV-B-Gone Kit, I originally started this project by hacking the MiniPOV kit. This presents a limitation, based on the size of the Atmel ATtiny2313 internal flash memory, which is 2KB. With 2KB we can only fit about 7 POWER codes into the firmware's database of POWER codes. However, the more codes the better! Which is why we chose the ATtiny85 for the TV-B-Gone Kit. This version of the firmware has the most popular 100+ POWER codes for North America and 100+ POWER codes for Europe. You can select which region to use by soldering a 10K pulldown resistor. */ /* This project is a good example of how to use the AVR chip timers. */ /* The hardware for this project is very simple: ATtiny85 has 8 pins: pin 1 RST + Button pin 2 one pin of ceramic resonator MUST be 8.0 mhz pin 3 other pin of ceramic resonator pin 4 ground pin 5 OC1A - IR emitters, through a '2907 PNP driver that connects to 4 (or more!) PN2222A drivers, with 1000 ohm base resistor and also connects to programming circuitry pin 6 Region selector. Float for US, 10K pulldown for EU, also connects to programming circuitry pin 7 PB0 - visible LED, and also connects to programming circuitry pin 8 +3-5v DC (such as 2-4 AA batteries!) See the schematic for more details. This firmware requires using an 8.0MHz ceramic resonator (since the internal oscillator may not be accurate enough). IMPORTANT: to use the ceramic resonator, you must perform the following: make burn-fuse_cr */ /* This function is the 'workhorse' of transmitting IR codes. Given the on and off times, it turns on the PWM output on and off to generate one 'pair' from a long code. Each code has ~50 pairs! */ void xmitCodeElement(uint16_t ontime, uint16_t offtime, uint8_t PWM_code ) { // start Timer0 outputting the carrier frequency to IR emitters on and OC0A // (PB0, pin 5) TCNT0 = 0; // reset the timers so they are aligned TIFR = 0; // clean out the timer flags if(PWM_code) { // 99% of codes are PWM codes, they are pulses of a carrier frequecy // Usually the carrier is around 38KHz, and we generate that with PWM // timer 0 TCCR0A =_BV(COM0A0) | _BV(WGM01); // set up timer 0 TCCR0B = _BV(CS00); } else { // However some codes dont use PWM in which case we just turn the IR // LED on for the period of time. PORTB &= ~_BV(IRLED); } // Now we wait, allowing the PWM hardware to pulse out the carrier // frequency for the specified 'on' time delay_ten_us(ontime); // Now we have to turn it off so disable the PWM output TCCR0A = 0; TCCR0B = 0; // And make sure that the IR LED is off too (since the PWM may have // been stopped while the LED is on!) PORTB |= _BV(IRLED); // turn off IR LED // Now we wait for the specified 'off' time delay_ten_us(offtime); } /* transmit IR burst, using the miles tag II format * one IR burst is ticks*600uS on, and 600uS off * n=1 codes 0 * n=2 codes 1 * n=4 codes header */ void transmit_ir(uint8_t ticks) { xmitCodeElement(ticks*600/10*TIMING_CORRECTION,600/10*TIMING_CORRECTION,1); // added some correction to transmit code } void transmit_ir_byte(uint8_t data) { uint8_t tick; int8_t i; //DEBUGP(putstring("byte burst ")); for (i=7;i>=0;i--) { tick = ((data>>i)&0x01); //DEBUGP(putnum_ud(tick)); transmit_ir(tick+1); } //DEBUGP(putstring("\r\n")); } int main(void) { uint8_t i; TCCR1 = 0; // Turn off PWM/freq gen, should be off already TCCR0A = 0; TCCR0B = 0; // set OCR for Timer1 to output this POWER code's carrier frequency (lasertag milestag II uses 56kHz) OCR0A = freq_to_timerval(56000); i = MCUSR; // Save reset reason MCUSR = 0; // clear watchdog flag WDTCR = _BV(WDCE) | _BV(WDE); // enable WDT disable WDTCR = 0; // disable WDT while we setup DDRB = _BV(LED) | _BV(IRLED) | _BV(TX); // set the visible and IR LED pins to outputs PORTB = _BV(LED) | // visible LED is off when pin is high _BV(IRLED) | // IR LED is off when pin is high _BV(TX); // Turn on pullup on region switch pin DEBUGP(putstring_nl("\r\nHello cock suckers! Ready to swallow some infrared?\r\n")); // check the reset flags if (i & _BV(BORF)) { // Brownout // Flash out an error and go to sleep flashslowLEDx(2); tvbgone_sleep(); } delay_ten_us(5000); // Let everything settle for a bit // blink half the time for 4 seconds for (i=0;i<4;i++) { PORTB &= ~_BV(LED); // turn on visible LED at PB0 by pulling pin to ground delay_ten_us(50000);// wait 0.5s PORTB |= _BV(LED); // turn off visible LED at PB0 by pulling pin to +3V delay_ten_us(50000);// wait 0.5s } // blink fast for 1 seconds for (i=0;i<5;i++) { PORTB &= ~_BV(LED); // turn on visible LED at PB0 by pulling pin to ground delay_ten_us(10000); // wait 0.1s PORTB |= _BV(LED); // turn off visible LED at PB0 by pulling pin to +3V delay_ten_us(10000); // wait 0.1s } // turn on watchdog timer immediately, this protects against // a 'stuck' system by resetting it wdt_enable(WDTO_8S); // 1 second long timeout DEBUGP(putstring("fire in the hole\r\n")); transmit_ir(4); transmit_ir_byte(0x83); transmit_ir_byte(0x0b); transmit_ir_byte(0xe8); // end byte not needed delay_ten_us(50000); // wait 0.1s // the protprietary recorded version transmit_ir(4); transmit_ir_byte(0x84); transmit_ir_byte(0x00); transmit_ir_byte(0xa4); // We are done, no need for a watchdog timer anymore wdt_disable(); // flash the visible LED on PB0 4 times to indicate that we're done //delay_ten_us(65500); // wait maxtime quickflashLEDx(4); tvbgone_sleep(); } /****************************** SLEEP FUNCTIONS ********/ void tvbgone_sleep( void ) { // Shut down everything and put the CPU to sleep TCCR0A = 0; // turn off frequency generator (should be off already) TCCR0B = 0; // turn off frequency generator (should be off already) PORTB |= _BV(LED) | // turn off visible LED _BV(IRLED); // turn off IR LED wdt_disable(); // turn off the watchdog (since we want to sleep delay_ten_us(1000); // wait 10 millisec MCUCR = _BV(SM1) | _BV(SE); // power down mode, SE enables Sleep Modes sleep_cpu(); // put CPU into Power Down Sleep Mode } /****************************** LED AND DELAY FUNCTIONS ********/ // This function delays the specified number of 10 microseconds // it is 'hardcoded' and is calibrated by adjusting DELAY_CNT // in main.h Unless you are changing the crystal from 8mhz, dont // mess with this. void delay_ten_us(uint16_t us) { uint8_t timer; while (us != 0) { // for 8MHz we want to delay 80 cycles per 10 microseconds // this code is tweaked to give about that amount. for (timer=0; timer <= DELAY_CNT; timer++) { NOP; NOP; } NOP; us--; } } // This function quickly pulses the visible LED (connected to PB0, pin 5) // This will indicate to the user that a code is being transmitted void quickflashLED( void ) { PORTB &= ~_BV(LED); // turn on visible LED at PB0 by pulling pin to ground delay_ten_us(3000); // 30 millisec delay PORTB |= _BV(LED); // turn off visible LED at PB0 by pulling pin to +3V } // This function just flashes the visible LED a couple times, used to // tell the user what region is selected void quickflashLEDx( uint8_t x ) { quickflashLED(); while(--x) { wdt_reset(); delay_ten_us(15000); // 150 millisec delay between flahes quickflashLED(); } wdt_reset(); // kick the dog } // This is like the above but way slower, used for when the tvbgone // crashes and wants to warn the user void flashslowLEDx( uint8_t num_blinks ) { uint8_t i; for(i=0;i