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