/*
TV-B-Gone
Firmware
for use with ATtiny2313
Chaos Communications Camp 2007
Mitch Altman
31-Jul-07

Distributed under Creative Commons 2.5 -- Attib & Share Alike
*/


/*
Ladyada has a great website about the MiniPOV kit, from which this project was hacked:
http://ladyada.net/make/minipov3/index.html
Her website also has a user forum, where people can ask and answer questions by people building
various projects, including this one:
http://www.ladyada.net/forums/
(Click on the link for MiniPOV).
*/


#include <avr/io.h>             // this contains all the IO port definitions
#include <avr/interrupt.h>      // definitions for interrupts
#include <avr/sleep.h>          // definitions for power-down modes
#include <avr/pgmspace.h>       // definitions or keeping constants in program memory


/*
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 Red  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 POEWR code will 
turn a TV off, and the duplicate will turn it on again (which we certainly 
do not want).  I have compiled the top-40 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).

Using the MiniPOV for this project 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.  If you 
would like to make a TV-B-Gone with all 40 codes, then they should all fit into 
another Atmel chip with 8KB of flash, such as ATtiny85.

This version of the firmware has the most popular 7 POWER codes for North America
(which is how many codes fit into the ATtiny2313 2KB flash memory).
*/


/*
This project is a good example of how to use the AVR chip timers.
*/


/*
The hardware for this project is very simple:
     ATtiny2313 has 20 pins:
       pin 1   connects to serial port programming circuitry
       pin 4   one pin of ceramic resonator
       pin 5   one pin of ceramic resonator
       pin 10  ground
       pin 12  PB0 - visible LED
       pin 14  OC0A - IR emitter, through a 2N3904 driver (with 120 ohm base resistor)
       pin 17  connects to serial port programming circuitry
       pin 18  connects to serial port programming circuitry
       pin 19  connects to serial port programming circuitry
       pin 20  +3v
    All other pins are unused

    This firmware requires that the clock frequency of the ATtiny 
      is the default that it is shipped with:  8.0MHz

    IMPORTANT:  to use the ceramic resonator, you must perform the following:
                    make burn-fuse_cr
*/


/*
The C compiler creates code that will transfer all constants into RAM when the microcontroller
resets.  Since this firmware has a table (powerTab) that is too large to transfer into RAM,
the C compiler needs to be told to keep it in program memory space.  This is accomplished by
the macro PROGMEM (this is used, below, in the definition for the powerTab).  Since the
C compiler assumes that constants are in RAM, rather than in program memory, when accessing
the powerTab, we need to use the pgm_read_word() macro, and we need
to use the powerTab as an address, i.e., precede it with "&".  For example, to access 
powerTab[3].onTime, which is a word, this is how to do it:
     pgm_read_word( &powerTab[3].onTime );
And to access powerTab[3].offTime, which is also a word, this is how to do it:
     pgm_read_word( &powerTab[3].offTime );
*/



/*
The following POWER code table consists of any number of POWER codes that will fit into the 2k flash ROM.
Each POWER code consists of any number of codeElements, each of which consists of an onTime
and an offTime (both onTime and offTime are expressed as the number of 10 microseconds -- for 
example, 4110 microseconds would be entered as 411).  The onTime is a length of time that the 
IR emitter is being pulsed with the carrier frequency (usually about 40KHz).  The offTime is 
a length of time that the IR emitter is not emitting any IR.  The first codeElement of each 
POWER code is actually a fake one:  the onTime contains the value to store in the compare 
register for Timer 0 (called OCR0A), and the offTime is ignored -- see more info on this in 
the next paragraph.  The last codeElement of each POWER code in the table is signified by 
making its offTime = 0.  To signify the last POWER code in the table, the last POWER code in 
the table must be followed by a codeElement with onTime = 0 (the offTime is ignored).

As stated above, the carrier frequency for each POWER code is determined by the value for 
onTime in the POWER code's first codeElement.  This value is derived from the following 
equation, given in the datasheet for the ATtiny2313:
     Output frequency = Fclk / (2 * Prescale * (1 + OCR1A) ) = 8,000,000 / (2 * (1 + OCR0A) )
So, given a desired carrier frequency, with the ATtiny2313 Fclk at 8.0Mhz, we determine OCR0A 
by the following equation:
     OCR0A = ( ( (8,000,000.0 / carrierFrequency) / 2.0 ) - 1.0)
Where the carrierFrequency is stated in Hz (with at least one decimal place at the end).
Since all of these values are constants, we will let the C compiler calculate this value in 
powerTab (using floating point by using a decimal point with all of the numbers).


There is a delay of 205 milliseconds after each POWER code is transmitted (whenever offTime = 0).
This delay is necessary so that a TV receiving the wrong POWER code will have time to reset itself 
before receiving the next POWER code (which might be the correct one for it).
*/


// table of POWER codes
struct codeElement {
  unsigned int onTime;   // duration of "On" time
  unsigned int offTime;  // duration of "Off" time
} const powerTab[] PROGMEM = {
  { ( ( (8000000.0 / 37520.0) / 2.0 ) - 1.0),   0 },   // Code 000 -- Sony, Baur and a few others
  { 245,    60 },
  { 123,    60 },
  {  61,    60 },
  { 123,    60 },
  {  61,    60 },
  { 123,    60 },
  {  61,    60 },
  {  61,    60 },
  { 123,    60 },
  {  61,    60 },
  {  61,    60 },
  {  61,    60 },
  {  61,  2758 },
  { 246,    60 },
  { 123,    60 },
  {  61,    60 },
  { 123,    60 },
  {  61,    60 },
  { 123,    60 },
  {  61,    60 },
  {  61,    60 },
  { 123,    60 },
  {  61,    60 },
  {  61,    60 },
  {  61,    60 },
  {  61,     0 },
  { ( ( (8000000.0 / 55600.0) / 2.0 ) - 1.0),   0 },   // Code 001 -- Proscan, RCA and a few others
  { 411,   410 },
  {  52,   205 },
  {  52,   205 },
  {  52,   205 },
  {  52,   205 },
  {  52,   102 },
  {  52,   102 },
  {  52,   205 },
  {  52,   102 },
  {  52,   205 },
  {  52,   102 },
  {  52,   205 },
  {  52,   102 },
  {  52,   102 },
  {  52,   102 },
  {  52,   102 },
  {  52,   102 },
  {  52,   205 },
  {  52,   205 },
  {  52,   102 },
  {  52,   205 },
  {  52,   102 },
  {  52,   205 },
  {  52,   102 },
  {  52,   205 },
  {  52,   821 },
  { 411,   410 },
  {  52,   205 },
  {  52,   205 },
  {  52,   205 },
  {  52,   205 },
  {  52,   102 },
  {  52,   102 },
  {  52,   205 },
  {  52,   102 },
  {  52,   205 },
  {  52,   102 },
  {  52,   205 },
  {  52,   102 },
  {  52,   102 },
  {  52,   102 },
  {  52,   102 },
  {  52,   102 },
  {  52,   205 },
  {  52,   205 },
  {  52,   102 },
  {  52,   205 },
  {  52,   102 },
  {  52,   205 },
  {  52,   102 },
  {  52,   205 },
  {  52,     0 },
  { ( ( (8000000.0 / 36130.0) / 2.0 ) - 1.0),   0 },   // Code 002 -- Panasonic
  { 358,   179 },
  {  44,    45 },
  {  44,   135 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,   135 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,   135 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,   135 },
  {  44,    45 },
  {  44,   135 },
  {  44,   135 },
  {  44,   135 },
  {  44,   135 },
  {  44,    45 },
  {  44,    45 },
  {  44,   135 },
  {  44,    45 },
  {  44,   135 },
  {  44,   135 },
  {  44,   135 },
  {  44,   135 },
  {  44,    45 },
  {  44,   135 },
  {  44,  7720 },
  { 358,   180 },
  {  44,    45 },
  {  44,   135 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,   135 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,   135 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,    45 },
  {  44,   135 },
  {  44,    45 },
  {  44,   135 },
  {  44,   135 },
  {  44,   135 },
  {  44,   135 },
  {  44,    45 },
  {  44,    45 },
  {  44,   135 },
  {  44,    45 },
  {  44,   135 },
  {  44,   135 },
  {  44,   135 },
  {  44,   135 },
  {  44,    45 },
  {  44,   135 },
  {  44,     0 },
  { ( ( (8000000.0 / 37470.0) / 2.0 ) - 1.0),   0 },   // Code 003 -- Sharp
  {  28,   189 },
  {  29,    81 },
  {  29,    81 },
  {  29,    81 },
  {  29,    81 },
  {  29,    81 },
  {  29,   189 },
  {  29,   189 },
  {  29,    81 },
  {  29,   189 },
  {  29,    81 },
  {  29,    81 },
  {  29,    81 },
  {  29,   189 },
  {  29,    81 },
  {  29,  4670 },
  {  29,   189 },
  {  29,    81 },
  {  29,    81 },
  {  29,    81 },
  {  29,    81 },
  {  29,   189 },
  {  29,    81 },
  {  29,    81 },
  {  29,   189 },
  {  29,    81 },
  {  29,   189 },
  {  29,   189 },
  {  29,   189 },
  {  29,    81 },
  {  29,   189 },
  {  29,  4670 },
  {  29,   189 },
  {  29,    81 },
  {  29,    81 },
  {  29,    81 },
  {  29,    81 },
  {  29,    81 },
  {  29,   189 },
  {  29,   189 },
  {  29,    81 },
  {  29,   189 },
  {  29,    81 },
  {  29,    81 },
  {  29,    81 },
  {  29,   189 },
  {  29,    81 },
  {  29,  4670 },
  {  29,   189 },
  {  29,    81 },
  {  29,    81 },
  {  29,    81 },
  {  29,    81 },
  {  29,   189 },
  {  29,    81 },
  {  29,    81 },
  {  29,   189 },
  {  29,    81 },
  {  29,   189 },
  {  29,   189 },
  {  29,   189 },
  {  29,    81 },
  {  29,   189 },
  {  29,     0 }, 
  { ( ( (8000000.0 / 37470.0) / 2.0 ) - 1.0),   0 },   // Code 004 -- Toshiba, Apex
  { 924,   464 },
  {  58,    57 },
  {  58,    57 },
  {  58,    57 },
  {  58,    57 },
  {  58,    57 },
  {  58,    57 },
  {  58,   173 },
  {  58,    57 },
  {  58,   173 },
  {  58,   173 },
  {  58,   173 },
  {  58,   173 },
  {  58,   173 },
  {  58,   173 },
  {  58,    57 },
  {  58,   173 },
  {  58,    57 },
  {  58,   173 },
  {  58,    57 },
  {  58,    57 },
  {  58,   173 },
  {  58,    57 },
  {  58,    57 },
  {  58,    57 },
  {  58,   173 },
  {  58,    57 },
  {  58,   173 },
  {  58,   173 },
  {  58,    57 },
  {  58,   173 },
  {  58,   173 },
  {  58,   173 },
  {  58,  4054 },
  { 926,   230 },
  {  58,  9880 },
  { 926,   230 },
  {  58,     0 },
  { ( ( (8000000.0 / 34800.0) / 2.0 ) - 1.0),   0 },   // Code 005 -- Philips, Grundig, Pye
  {  92,    92 },
  { 184,    92 },
  {  92,    92 },
  {  92,    92 },
  {  92,    92 },
  {  92,    92 },
  {  92,    92 },
  {  92,    92 },
  {  92,   184 },
  {  92,    92 },
  { 184,    92 },
  {  92,  9216 },
  {  92,    92 },
  { 184,    92 },
  {  92,    92 },
  {  92,    92 },
  {  92,    92 },
  {  92,    92 },
  {  92,    92 },
  {  92,    92 },
  {  92,   184 },
  {  92,    92 },
  { 184,    92 },
  {  92,     0 },
  { ( ( (8000000.0 / 37470.0) / 2.0 ) - 1.0),   0 },   // Code 006 -- Samsung
  { 462,   476 },
  {  53,   175 },
  {  53,   175 },
  {  53,   175 },
  {  53,    62 },
  {  53,    62 },
  {  53,    62 },
  {  53,    62 },
  {  53,    62 },
  {  53,   175 },
  {  53,   175 },
  {  53,   175 },
  {  53,    62 },
  {  53,    62 },
  {  53,    62 },
  {  53,    62 },
  {  53,    62 },
  {  53,    62 },
  {  53,   175 },
  {  53,    62 },
  {  53,    62 },
  {  53,    62 },
  {  53,    62 },
  {  53,    62 },
  {  53,    62 },
  {  53,   175 },
  {  53,    62 },
  {  53,   175 },
  {  53,   175 },
  {  53,   175 },
  {  53,   175 },
  {  53,   175 },
  {  53,   175 },
  {  53,  4661 },
  { 464,   476 },
  {  53,   175 },
  {  53,   175 },
  {  53,   175 },
  {  53,    62 },
  {  53,    62 },
  {  53,    62 },
  {  53,    62 },
  {  53,    62 },
  {  53,   175 },
  {  53,   175 },
  {  53,   175 },
  {  53,    62 },
  {  53,    62 },
  {  53,    62 },
  {  53,    62 },
  {  53,    62 },
  {  53,    62 },
  {  53,   175 },
  {  53,    62 },
  {  53,    62 },
  {  53,    62 },
  {  53,    62 },
  {  53,    62 },
  {  53,    62 },
  {  53,   175 },
  {  53,    62 },
  {  53,   175 },
  {  53,   175 },
  {  53,   175 },
  {  53,   175 },
  {  53,   175 },
  {  53,   175 },
  {  53,     0 },
//  { ( ( (8000000.0 / 38200.0) / 2.0 ) - 1.0),   0 },   // Code 007 -- Zenith
//  {  52,    49 },
//  {  52,   419 },
//  {  52,   522 },
//  {  52,    49 },
//  {  52,   419 },
//  {  52,    49 },
//  {  52,   419 },
//  {  52,   522 },
//  {  52,    49 },
//  {  52,   419 },
//  {  52,   522 },
//  {  52,    49 },
//  {  52,   419 },
//  {  52,   522 },
//  {  52,   522 },
//  {  52,    49 },
//  {  52, 12433 },
//  {  52,    49 },
//  {  52,   419 },
//  {  52,   522 },
//  {  52,    49 },
//  {  52,   419 },
//  {  52,    49 },
//  {  52,   419 },
//  {  52,   522 },
//  {  52,    49 },
//  {  52,   419 },
//  {  52,   522 },
//  {  52,    49 },
//  {  52,   419 },
//  {  52,   522 },
//  {  52,   522 },
//  {  52,    49 },
//  {  52,     0 },
  {   0,     0 }
};

// This function delays the specified number of 10 microseconds
void delay_ten_us(unsigned long int us) {
  unsigned long int timer;
  const unsigned long int DelayCount=6;  // this value was determined by trial and error

  while (us != 0) {
    // Toggling PD0 is done here to force the compiler to do this loop, rather than optimize it away
    for (timer=0; timer <= DelayCount; timer++) {PIND |= 0b0000001;};
    us--;
  }
}


// This function quickly pulses the visible LED (connected to PB0, pin 12)
void quickflashLED( void ) {
  // pulse LED at PB0 on for 30ms
  PORTB |= 0b00000001;          // turn on LED at PB0
  delay_ten_us(3000);           // 30 millisec delay
  PORTB &= 0b11111110;          // turn off LED at PB0
}


// This function quickly pulses the visible LED (connected to PB0, pin 12) 4 times
void quickflashLED4x( void ) {
  quickflashLED();
  delay_ten_us(15000);        // 150 millisec delay
  quickflashLED();
  delay_ten_us(15000);        // 150 millisec delay
  quickflashLED();
  delay_ten_us(15000);        // 150 millisec delay
  quickflashLED();
}


// This function transmits one Code Element of a POWER code to the IR emitter, 
//   given index to the codeElement in powerTab
//     If offTime = 0 that signifies the last Code Element of the POWER code
//     and the delay_ten_us function will have no delay for offTime 
//     (but we'll delay for 205 milliseconds in the main function)
void xmitCodeElement( int index ) {
  // start Timer0 outputting the carrier frequency to IR emitter on OC0A (PB2, pin 14)
  TCCR0A = 0b01000010;   // COM0A1:0=01 to toggle OC0A on Compare Match
                         // COM0B1:0=00 to disconnect OC0B from PB2 (pin 14)
                         // bits 3:2 are unused
                         // WGM01:00=10 for CTC Mode (WGM02=0 in TCCR0B)
  TCCR0B = 0b00000001;   // FOC0A=0 (no force compare)
                         // F0C0B=0 (no force compare)
                         // bits 5:4 are unused
                         // WGM2=0 for CTC Mode (WGM01:00=10 in TCCR0A)
                         // CS02:00=100 for divide by 1 prescaler

  // keep transmitting carrier for onTime
  delay_ten_us(pgm_read_word(&powerTab[index].onTime));

  // turn off output to IR emitter on 0C0A (PB2, pin 14) for offTime
  TCCR0B &= 0b11111000;  // CS02:CS00=000 to stop Timer0
  TCCR0A &= 0b00111111;  // COM0A1:0=00 to disconnect 0OC0A from PB2 (pin 14)
  PORTB &= 0b11111011;   // turn off IR emitter on 0C0A (PB2, pin 14) in case it was High
  delay_ten_us(pgm_read_word(&powerTab[index].offTime));
}


int main(void) {

  TIMSK = 0x00;  // no Timer interrupts enabled
  DDRB = 0xFF;   // set all PortB pins as outputs
  PORTB = 0x00;  // all PORTB output pins Off

  // loop through entire POWER Table of Code Elements
  //   each Code Element consists of an On Time and an Off Time
  //     but the first Code Element of each POWER code is a fake one, 
  //       with the onTime = value for OCR0A to create the POWER code's carrier frequency (offTime is ignored)
  //     and the last Code Element of each POWER code has an offTime = 0
  //       (to indicate it is the last Code Element)
  //     and the last Code Element in the POWER Table (after the last POWER Code in the table) 
  //       has onTime = 0 (offTime is ignored) (to indicate that there are no more POWER Codes in the table)
  int i = 0;

  while (pgm_read_word(&powerTab[i].onTime) != 0) {  // onTime = 0 signifies end of powerTab
    // indicate transmission of next POWER code with a blink of the visible LED on PB0 (pin 12)
    quickflashLED();

    // set OCR0A for Timer0 to output this POWER code's carrier frequency
    OCR0A = pgm_read_word(&powerTab[i].onTime);  // the 1st codeElement of a POWER code contains value for OCR0A in its onTime

    // transmit all codeElements for this POWER code
    do {
      i++;                 // increment to point to next codeElement in powerTab
      xmitCodeElement(i);  // transmit one codeElement
    } while ( (pgm_read_word(&powerTab[i].offTime)) != 0 );  // offTime = 0 signifies last codeElement for a POWER code

    // delay 250 milliseconds before transmitting next POWER code
    delay_ten_us(25000);

    // point to beginning of next POWER code in powerTab (or possibly the end of powerTab)
    i++;                 // increment to point to next codeElement in powerTab
  }

  // flash the visible LED on PB0 (pin 12) 4 times to indicate that we're done
  quickflashLED4x();

  // Shut down everything and put the CPU to sleep
  TCCR0B &= 0b11111000;  // CS02:CS00=000 to stop Timer0 (turn off carrier frequency generation) -- although it should already be off
  TCCR0A &= 0b00111111;  // COM0A1:0=00 to disconnect 0OC0A from PB2 (pin 14)
  MCUCR |= 0b00100000;   // SE=1 (bit 5) -- enables Sleep Modes
  MCUCR |= 0b00010000;   // SM1:0=01 -- selects Power Down Sleep Mode (bits 6, 4)
  delay_ten_us(1000);    // wait 10 millisec second
  PORTB = 0x00;          // turn off all PORTB outputs
  DDRB = 0x00;           // make PORTB all inputs
  sleep_cpu();           // put CPU into Power Down Sleep Mode
}


