1 | /*
|
2 | Project I.R.
|
3 |
|
4 | Pinout:
|
5 | 12 PB0 -I- IR Receiver
|
6 | 13 PB1 -O- Programming LED
|
7 | 14 PB2 -O- IR Emitter
|
8 | 15 PB3 -O- Status LED
|
9 | 16 PB4 -O- Transfer LED
|
10 |
|
11 | 6 PD2 -I- Record Button
|
12 | 7 PD3 -I- Playback Button
|
13 | 8 PD4 -I- Programming Switch
|
14 | */
|
15 |
|
16 | #define F_CPU 8000000L
|
17 |
|
18 | #include <avr/io.h>
|
19 | #include <avr/interrupt.h>
|
20 | #include <avr/sleep.h>
|
21 | #include <util/delay.h>
|
22 | #include <avr/eeprom.h>
|
23 | #include <avr/wdt.h>
|
24 | #include <stdint.h>
|
25 |
|
26 | //=== Variables ===
|
27 | #define MAX_CODE 110
|
28 | #define PLAYBACK 1
|
29 | #define RECORDING 2
|
30 | volatile uint8_t vMode = 0;
|
31 | volatile uint8_t vTick= 0;
|
32 | uint8_t ircode[MAX_CODE];
|
33 | uint8_t ircode_len = 0;
|
34 | uint8_t ircodeChanged = 0;
|
35 |
|
36 | uint8_t eeProgramming EEMEM;
|
37 | uint8_t eeIRCodeLength EEMEM;
|
38 | uint8_t eeIRCode[MAX_CODE] EEMEM;
|
39 |
|
40 | //=== Helper Macros ===
|
41 | #define bit(bits) _BV(bits)
|
42 |
|
43 | #define TOGGLE ^=
|
44 | #define DISABLE &= ~
|
45 | #define ENABLE |=
|
46 |
|
47 | #define LED_STATUS (bit(PB3))
|
48 | #define LED_XFER (bit(PB4))
|
49 | #define LED_IR (bit(PB2))
|
50 | #define LED_PROG (bit(PB1))
|
51 | #define INP_T1 (bit(PD2))
|
52 | #define INP_T2 (bit(PD3))
|
53 | #define INP_IR (bit(PB0))
|
54 | #define INP_PROG (bit(PD4))
|
55 |
|
56 | #define IR ((PINB & INP_IR) == 0) //Get IR State
|
57 |
|
58 | #define ENABLE_PWM (TCCR0A ENABLE bit(COM0A0)) //Toggle OC0A on Compare Match
|
59 | #define DISABLE_PWM (TCCR0A DISABLE bit(COM0A0)) //Normal port operation, OC0A disconnected.
|
60 | #define ENABLE_TIMER (TCCR1B ENABLE bit(CS10)) //1:1 ratio
|
61 | #define DISABLE_TIMER (TCCR1B DISABLE bit(CS10)) //Deactivate Timer 1 Clocksource
|
62 | #define ENABLE_BUTTONS (GIMSK ENABLE (bit(INT1) | bit(INT0)))
|
63 | #define DISABLE_BUTTONS (GIMSK DISABLE (bit(INT1) | bit(INT0)))
|
64 |
|
65 | //=== Events ===
|
66 | //Record button
|
67 | ISR(INT1_vect)
|
68 | {
|
69 | vMode = RECORDING;
|
70 | }
|
71 |
|
72 | //Playback button
|
73 | ISR(INT0_vect)
|
74 | {
|
75 | vMode = PLAYBACK;
|
76 | }
|
77 |
|
78 | //Timer
|
79 | ISR(TIMER1_COMPA_vect)
|
80 | {
|
81 | vTick++;
|
82 | }
|
83 |
|
84 | //=== Methods ===
|
85 | void IRPlayback()
|
86 | {
|
87 | //Playback time!
|
88 | vTick = TCNT1 = 0;
|
89 | ENABLE_TIMER;
|
90 | uint8_t activeByte;
|
91 | uint8_t previous = 255;
|
92 | for (activeByte = 0; activeByte < ircode_len; activeByte++)
|
93 | {
|
94 | uint8_t content = ircode[activeByte];
|
95 | uint8_t activeBit;
|
96 |
|
97 | for (activeBit = 0; activeBit < 8; activeBit++)
|
98 | {
|
99 | //Action
|
100 | uint8_t state = (content >> activeBit) & 0b1;
|
101 |
|
102 | if (state != previous)
|
103 | {
|
104 | if (state)
|
105 | {
|
106 | ENABLE_PWM;
|
107 | PORTB ENABLE LED_XFER;
|
108 | }
|
109 | else
|
110 | {
|
111 | DISABLE_PWM;
|
112 | PORTB DISABLE LED_XFER;
|
113 | }
|
114 | previous = state;
|
115 | }
|
116 |
|
117 | //Wait
|
118 | while (vTick < 1) asm volatile ("NOP");
|
119 | vTick--;
|
120 | }
|
121 | }
|
122 | DISABLE_TIMER;
|
123 | DISABLE_PWM;
|
124 | }
|
125 |
|
126 |
|
127 | void NarrowIR()
|
128 | {
|
129 | int8_t i;
|
130 | for (i = 0; i < ircode_len-1; i++)
|
131 | {
|
132 | if (ircode[i] && !ircode[i+1])
|
133 | ircode[i] = 0;
|
134 | }
|
135 | }
|
136 |
|
137 | void IRRecord()
|
138 | {
|
139 | while (IR == 0) asm volatile ("NOP"); //wait for signal
|
140 | ircode_len = 0;
|
141 | TCNT1 = vTick = 0;
|
142 | ENABLE_TIMER;
|
143 |
|
144 | uint8_t activeByte;
|
145 | for (activeByte = 0; activeByte < MAX_CODE; activeByte++)
|
146 | {
|
147 | uint8_t data = 0;
|
148 | uint8_t activeBit;
|
149 | for (activeBit = 0; activeBit < 8; activeBit++)
|
150 | {
|
151 | //Write bit
|
152 | if (IR) {
|
153 | PORTB ENABLE LED_XFER;
|
154 | data |= (1 << activeBit);
|
155 | ircode_len = activeByte+1;
|
156 | }
|
157 | else
|
158 | PORTB DISABLE LED_XFER;
|
159 |
|
160 | //Wait time ..
|
161 | while (vTick < 1) asm volatile ("NOP");
|
162 | vTick--;
|
163 | }
|
164 |
|
165 | ircode[activeByte] = data;
|
166 | }
|
167 |
|
168 | ircodeChanged = 1;
|
169 | DISABLE_TIMER;
|
170 |
|
171 | //NarrowIR(); //Receiver is not that exact, I guess
|
172 | }
|
173 |
|
174 | inline void Reset()
|
175 | {
|
176 | _delay_ms(10);
|
177 | cli();
|
178 |
|
179 | wdt_reset();
|
180 | wdt_enable(WDTO_500MS);
|
181 | while (1) asm volatile ("NOP");
|
182 | }
|
183 |
|
184 | void ProgrammingMode()
|
185 | {
|
186 | PORTB ENABLE (LED_PROG);
|
187 | _delay_ms(500);
|
188 |
|
189 | uint8_t prog = eeprom_read_byte(&eeProgramming);
|
190 | while ((PIND & INP_PROG) == 0)
|
191 | {
|
192 | //Enable programming LED
|
193 |
|
194 |
|
195 | //Program
|
196 | if ((PIND & INP_T2) == 0)
|
197 | {
|
198 | prog = (prog+1) % 4;
|
199 | _delay_ms(300);
|
200 | }
|
201 |
|
202 | //Show
|
203 | if (prog & 0b01) PORTB ENABLE LED_STATUS;
|
204 | else PORTB DISABLE LED_STATUS;
|
205 | if (prog & 0b10) PORTB ENABLE LED_XFER;
|
206 | else PORTB DISABLE LED_XFER;
|
207 |
|
208 | _delay_ms(20);
|
209 | }
|
210 |
|
211 | //Programmierwert
|
212 | eeprom_write_byte(&eeProgramming, prog);
|
213 |
|
214 | //Sendecode
|
215 | if (ircodeChanged)
|
216 | {
|
217 | eeprom_write_byte(&eeIRCodeLength, ircode_len);
|
218 | eeprom_write_block(&ircode, &eeIRCode, MAX_CODE);
|
219 | }
|
220 |
|
221 | //-> Reset!
|
222 | Reset();
|
223 | }
|
224 |
|
225 | //=== PSVM ===
|
226 | int main(void)
|
227 | {
|
228 | //### Basic Port Config ###
|
229 | MCUSR = 0;
|
230 | wdt_disable();
|
231 |
|
232 | //Port B
|
233 | DDRB = LED_STATUS | LED_XFER | LED_IR | LED_PROG; //outputs
|
234 | PORTB = ~DDRB; //pullups for all inputs
|
235 |
|
236 | //Port D
|
237 | DDRD = 0; //outputs
|
238 | PORTD = ~DDRD; //pullup
|
239 |
|
240 | //Deactivate 1/8 prescaler
|
241 | CLKPR = bit(CLKPCE); // enable clock prescale change
|
242 | CLKPR = 0; // div1 => 8 MHz
|
243 |
|
244 | //Pre-INIT Loop - prevent malprogramming
|
245 | PORTB ENABLE (LED_PROG | LED_STATUS | LED_XFER);
|
246 | _delay_ms(300);
|
247 | PORTB DISABLE (LED_STATUS | LED_XFER | LED_PROG);
|
248 |
|
249 | //Sleep Mode
|
250 | set_sleep_mode(SLEEP_MODE_PWR_DOWN);
|
251 | ACSR |= bit(ACD); //disable analog comperator
|
252 |
|
253 | //PWM settings | Phasing: high/low = 72kHz, one phase = 36k HZ
|
254 | TCCR0A |= bit(WGM01); // CTC Mode: Clear timer on compare match
|
255 | TCCR0B |= bit(CS00); // timer source is system clock (1:1)
|
256 | OCR0A = (F_CPU / 72000)-1; //OCR0A = Top => 4000 kHz / 72kHz = 55
|
257 |
|
258 | //Timer, dynamische Frequenz (200-1000us)
|
259 | switch (eeprom_read_byte(&eeProgramming))
|
260 | {
|
261 | default: OCR1A = (F_CPU / 1786)-1; break; //560us, 1786hz
|
262 | case 1: OCR1A = (F_CPU / 3571)-1; break; //280s, 3571hz
|
263 | case 2: OCR1A = (F_CPU / 7142)-1; break; //140us, 7142hz
|
264 | case 3: OCR1A = (F_CPU / 10000)-1; break; //100us, 10000hz
|
265 | }
|
266 |
|
267 | TCCR1A = 0;
|
268 | TCCR1B = bit(WGM12); // CTC Mode: Clear timer on compare match
|
269 | TIMSK |= bit(OCIE1A); // output compare interrupt enable 1a
|
270 |
|
271 | //Sendecode
|
272 | eeprom_read_block(&ircode, &eeIRCode, MAX_CODE);
|
273 | ircode_len = eeprom_read_byte(&eeIRCodeLength);
|
274 |
|
275 | //Interrupts
|
276 | sei(); //enable interrupts
|
277 |
|
278 | //Main Loop
|
279 | while (1) {
|
280 | //Go to sleep & Wait for interrupts
|
281 | ENABLE_BUTTONS;
|
282 | sleep_mode();
|
283 | DISABLE_BUTTONS;
|
284 |
|
285 | //Check for programming mode
|
286 | if ((PIND & INP_PROG) == 0)
|
287 | ProgrammingMode();
|
288 |
|
289 | //Something has happened!
|
290 | PORTB ENABLE LED_STATUS;
|
291 | switch (vMode)
|
292 | {
|
293 | case PLAYBACK: IRPlayback(); break;
|
294 | case RECORDING: IRRecord(); break;
|
295 | }
|
296 | vMode = 0;
|
297 | PORTB DISABLE (LED_STATUS | LED_XFER);
|
298 | _delay_ms(100);
|
299 | }
|
300 |
|
301 | return 0;
|
302 | }
|