5 Mitch Altman + Limor Fried
8 Distributed under Creative Commons 2.5 -- Attib & Share Alike
11 #include <avr/io.h> // this contains all the IO port definitions
12 #include <avr/interrupt.h> // definitions for interrupts
13 #include <avr/sleep.h> // definitions for power-down modes
14 #include <avr/pgmspace.h> // definitions or keeping constants in program memory
18 #define LED PB2 // visible LED
19 #define IRLED1 PB0 // IR LED
20 #define IRLED2 PB1 // IR LED
23 This project transmits a bunch of TV POWER codes, one right after the other,
24 with a pause in between each. (To have a visible indication that it is
25 transmitting, it also pulses a visible LED once each time a POWER code is
26 transmitted.) That is all TV-B-Gone does. The tricky part of TV-B-Gone
27 was collecting all of the POWER codes, and getting rid of the duplicates and
28 near-duplicates (because if there is a duplicate, then one POWER code will
29 turn a TV off, and the duplicate will turn it on again (which we certainly
30 do not want). I have compiled the top-40 most popular codes with the
31 duplicates eliminated, both for North America (which is the same as Asia, as
32 far as POWER codes are concerned -- even though much of Asia USES PAL video)
33 and for Europe (which works for Australia, New Zealand, the Middle East, and
34 other parts of the world that use PAL video).
36 Before creating a TV-B-Gone Kit, I originally started this project by hacking
37 the MiniPOV kit. This presents a limitation, based on the size of
38 the Atmel ATtiny2313 internal flash memory, which is 2KB. With 2KB we can only
39 fit about 7 POWER codes into the firmware's database of POWER codes. 40 codes
40 requires 8KB of flash memory, which is why we chose the ATtiny85 for the
43 This version of the firmware has the most popular 40 POWER codes for North America.
48 This project is a good example of how to use the AVR chip timers.
53 The hardware for this project is very simple:
55 pin 1 connects to programming circuitry
56 pin 2 one pin of ceramic resonator
57 pin 3 one pin of ceramic resonator
59 pin 5 PB0 - visible LED, and also connects to programming circuitry
60 pin 6 OC1A - IR emitter, through a PN2222A driver (with 47 ohm base resistor), and also connects to programming circuitry
61 pin 7 push-button switch, and also connects to serial port programming circuitry
63 See the schematic for more details.
65 This firmware requires using an 8.0MHz ceramic resonator
66 (since the internal oscillator may not be accurate enough).
68 IMPORTANT: to use the ceramic resonator, you must perform the following:
74 The C compiler creates code that will transfer all constants into RAM when the microcontroller
75 resets. Since this firmware has a table (powerCodes) that is too large to transfer into RAM,
76 the C compiler needs to be told to keep it in program memory space. This is accomplished by
77 the macro PROGMEM (this is used in the definition for powerCodes). Since the
78 C compiler assumes that constants are in RAM, rather than in program memory, when accessing
79 powerCodes, we need to use the pgm_read_word() and pgm_read_byte macros, and we need
80 to use powerCodes as an address. This is done with PGM_P, defined below.
81 For example, when we start a new powerCode, we first point to it with the following statement:
82 PGM_P thecode_p = pgm_read_word(powerCodes+i);
83 The next read from the powerCode is a byte that indicates the carrier frequency, read as follows:
84 uint8_t freq = pgm_read_byte(thecode_p);
85 Subsequent reads from the powerCode are onTime/offTime pairs, which are words, read as follows:
86 ontime = pgm_read_word(thecode_p+(offset_into_table);
87 offtime = pgm_read_word(thecode_p+(offset_into_table);
90 #define NOP __asm__ __volatile__ ("nop")
91 // This function delays the specified number of 10 microseconds
93 void delay_ten_us(uint16_t us
) {
96 for (timer
=0; timer
<= DELAY_CNT
; timer
++) {
106 // This function quickly pulses the visible LED (connected to PB0, pin 5)
107 void quickflashLED( void ) {
108 // pulse LED on for 30ms
110 PORTB
&= ~_BV(LED
); // turn on visible LED at PB0 by pulling pin to ground
111 delay_ten_us(3000); // 30 millisec delay
112 PORTB
|= _BV(LED
); // turn off visible LED at PB0 by pulling pin to +3V
116 // This function quickly pulses the visible LED (connected to PB0, pin 5) 4 times
117 void quickflashLED4x( void ) {
119 delay_ten_us(15000); // 150 millisec delay
121 delay_ten_us(15000); // 150 millisec delay
123 delay_ten_us(15000); // 150 millisec delay
128 // This function transmits one Code Element of a POWER code to the IR emitter,
129 // given offTime and onTime for the codeElement
130 // If offTime = 0 that signifies the last Code Element of the POWER code
131 // and the delay_ten_us function will have no delay for offTime
132 // (but we'll delay for 250 milliseconds in the main function)
133 void xmitCodeElement(uint16_t ontime
, uint16_t offtime
) {
134 // start Timer1 outputting the carrier frequency to IR emitters on OC1A (PB1, pin 6) and OC0A (PB0, pin 5)
135 TCNT0
= 0; // reset the timers so they are aligned
137 TIFR
= 0; // clean out the timer flags
139 TCCR0A
=_BV(COM0A0
) | _BV(WGM01
); // set up timer 0
141 TCCR1
=_BV(COM1A0
) | _BV(CS10
) | _BV(CTC1
); // set up and turn on timer 1
142 //TCCR1 = 0b10010001 // CTC1 = 1 to reset Timer1 to 0 when it reaches the value in OCR1C (i.e., on Compare Match)
143 // PWM1A = 0 to disable PWM mode
144 // COM1A1:0 = 01 to toggle OC1A on Compare Match
145 // CS13:10 = 0001 to start Timer1 with prescaler set to divide by 1 (i.e., no prescaler divide)
146 TCCR0B
= _BV(CS00
); // turn on timer 0 exactly 1 instruction later
148 // keep transmitting carrier for onTime
149 delay_ten_us(ontime
);
152 // turn off output to IR emitters on 0C1A (PB1, pin 6) for offTime
153 TCCR1
= 0; // stop Timer 1
154 TCCR0B
= 0; // stop Timer 0, exactly one instruction later
157 PORTB
&= ~_BV(IRLED1
) & ~_BV(IRLED2
); // turn off IR LED
159 delay_ten_us(offtime
);
163 void gotosleep(void) {
164 // Shut down everything and put the CPU to sleep
165 // put CPU into Power Down Sleep Mode
167 TCCR1
= 0; // turn off frequency generator (should be off already)
169 PORTB
|= _BV(LED
); // turn on the button pullup, turn off visible LED
170 PORTB
&= ~_BV(IRLED1
) & ~_BV(IRLED2
); // turn off IR LED
171 delay_ten_us(1000); // wait 10 millisec second
175 MCUCR
= _BV(SM1
) | _BV(SE
); // power down mode, SE=1 (bit 5) -- enables Sleep Modes
179 //extern const struct powercode powerCodes[] PROGMEM;
180 extern const PGM_P
*powerCodes
[] PROGMEM
;
182 extern uint8_t num_codes
;
186 uint16_t ontime
, offtime
;
188 TCCR1
= 0; // turn off frequency generator (should be off already)
191 i
= MCUSR
; // find out why we reset
192 MCUSR
= 0; // clear reset flags immediately
194 // turn on watchdog timer immediately, this protects against
195 // a 'stuck' system by resetting it
196 wdt_enable(WDTO_8S
); // 1 second long timeout
198 // Set the inputs and ouputs
199 PORTB
&= ~_BV(IRLED1
) & ~_BV(IRLED2
); // IR LED is off when pin is low
200 DDRB
= _BV(LED
) | _BV(IRLED1
) | _BV(IRLED2
); // set the visible and IR LED pins to outputs
201 PORTB
= _BV(LED
); // visible LED is off when pin is high
203 // check the reset flags
204 if ((i
& _BV(PORF
)) || // batteries were inserted
205 (i
& _BV(BORF
))) { // brownout reset
206 gotosleep(); // we only want to do something when the reset button is pressed
209 for (i
=0; i
<num_codes
; i
++) { // for every POWER code in our collection
210 wdt_reset(); // make sure we dont get 'stuck' in a code
212 quickflashLED(); // visible indication that a code is being output
213 PGM_P thecode_p
= pgm_read_word(powerCodes
+i
); // point to next POWER code
215 uint8_t freq
= pgm_read_byte(thecode_p
);
216 // set OCR for Timer1 and Timer0 to output this POWER code's carrier frequency
217 OCR0A
= OCR1C
= freq
;
219 // transmit all codeElements for this POWER code (a codeElement is an onTime and an offTime)
220 // transmitting onTime means pulsing the IR emitters at the carrier frequency for the length of time specified in onTime
221 // transmitting offTime means no output from the IR emitters for the length of time specified in offTime
222 j
= 0; // index into codeElements of this POWER code
224 // read the onTime and offTime from the program memory
225 ontime
= pgm_read_word(thecode_p
+(j
*4)+1);
226 offtime
= pgm_read_word(thecode_p
+(j
*4)+3);
228 xmitCodeElement(ontime
, offtime
); // transmit this codeElement (ontime and offtime)
230 } while ( offtime
!= 0 ); // offTime = 0 signifies last codeElement for a POWER code
232 PORTB
&= ~_BV(IRLED1
) & ~_BV(IRLED2
); // turn off IR LED
234 // delay 250 milliseconds before transmitting next POWER code
239 // flash the visible LED on PB0 4 times to indicate that we're done
240 delay_ten_us(65500); // wait maxtime
This page took 1.839236 seconds and 4 git commands to generate.