0,0 → 1,335 |
/*--------------------------------------------------------------------------------------------------------------------------------------------------- |
* main.c - Servo controlled infrared transmitter |
* |
* Copyright (c) 2010-0xFFFF Stefan Pendsa |
* Based on the nice IR decoder/encoder routines (IRMP/IRSND) from Frank Meyer => http://www.mikrocontroller.net/articles/IRMP |
* |
* ATMEGA8 @ 16 MHz |
* |
* Fuses: lfuse:0xAE hfuse:0xD9 |
* |
*--------------------------------------------------------------------------------------------------------------------------------------------------- |
* This program is free software; you can redistribute it and/or modify |
* it under the terms of the GNU General Public License as published by |
* the Free Software Foundation; either version 2 of the License, or |
* (at your option) any later version. |
*--------------------------------------------------------------------------------------------------------------------------------------------------- |
*/ |
|
#include <inttypes.h> |
#include <avr/io.h> |
#include <util/delay.h> |
#include <avr/pgmspace.h> |
#include <avr/interrupt.h> |
#include <avr/eeprom.h> |
#include "irmp.h" |
#include "irsndconfig.h" |
#include "irsnd.h" |
|
#ifndef F_CPU |
#error F_CPU unkown |
#endif |
|
|
/*--------------------------------------------------------------------------------------------------------------------------------------------------- |
* Global variables |
*--------------------------------------------------------------------------------------------------------------------------------------------------- |
*/ |
unsigned char key=0; // goes 1 when key was pressed |
unsigned char ee_dummy EEMEM; // don't use the first byte in EEPROM, causes problems on some old AVRs... |
unsigned char ee_check EEMEM; // Check-Byte to see if EEPROM is initialized |
unsigned char active[5][5]; // Array to see which servo-combinations were populated with valid IR-codes |
unsigned char ee_active[5][5] EEMEM; // Array to see which servo-combinations were populated with valid IR-codes (in EEPROM) |
|
IRMP_DATA ir_data[5][5]; // Array with IR-codes for every servo-combination |
IRMP_DATA ee_ir_data[5][5] EEMEM; // Array with IR-codes for every servo-combination (in EEPROM) |
IRMP_DATA ir_temp; // Received IR-codes goes into here |
|
|
|
/*--------------------------------------------------------------------------------------------------------------------------------------------------- |
* timer 1 initialization |
*--------------------------------------------------------------------------------------------------------------------------------------------------- |
*/ |
void timer_init (void) |
{ |
OCR1A = (F_CPU / F_INTERRUPTS) - 1; // compare value: 1/10000 of CPU frequency |
TCCR1B = (1 << WGM12) | (1 << CS10); // switch CTC Mode on, set prescaler to 1 |
|
#if defined (__AVR_ATmega8__) || defined (__AVR_ATmega16__) || defined (__AVR_ATmega32__) || defined (__AVR_ATmega64__) || defined (__AVR_ATmega162__) |
TIMSK = 1 << OCIE1A; // OCIE1A: Interrupt by timer compare (use TIMSK for ATMEGA162) |
#else |
TIMSK1 = 1 << OCIE1A; // OCIE1A: Interrupt by timer compare (use TIMSK for ATMEGA162) |
#endif // __AVR... |
} |
|
|
|
/*--------------------------------------------------------------------------------------------------------------------------------------------------- |
* timer 1 compare handler, called every 1/20000 sec |
*--------------------------------------------------------------------------------------------------------------------------------------------------- |
*/ |
ISR(TIMER1_COMPA_vect) |
{ |
if (!irsnd_ISR()) // call irsnd ISR |
{ // if not busy... |
irmp_ISR(); // call irmp ISR |
} |
} |
|
|
|
/*--------------------------------------------------------------------------------------------------------------------------------------------------- |
* LED control: 0 = off, >0 = on |
*--------------------------------------------------------------------------------------------------------------------------------------------------- |
*/ |
void LED(unsigned char bit) |
{ |
if (bit == 0) PORTD |= 1<<PORTD7; |
else PORTD &= ~(1<<PORTD7); |
} |
|
|
|
|
/*--------------------------------------------------------------------------------------------------------------------------------------------------- |
* LED Flash (on for time1, off for time2, repeated). Keypressing is checked in background |
*--------------------------------------------------------------------------------------------------------------------------------------------------- |
*/ |
void Flash(unsigned char time1, unsigned char time2, unsigned char repeat) |
{ |
unsigned char i,j; |
key = 0; |
for (i=0;i<repeat;i++) |
{ |
PORTD &= ~(1<<PORTD7); |
for (j=0;j<time1;j++) |
{ |
_delay_ms(1); |
if (!(PINB & (1<<PINB2))) key = 1; |
} |
PORTD |= 1<<PORTD7; |
for (j=0;j<time2;j++) |
{ |
_delay_ms(1); |
if (!(PINB & (1<<PINB2))) key = 1; |
} |
} |
} |
|
|
|
/*--------------------------------------------------------------------------------------------------------------------------------------------------- |
* LED Flash2 (on/off for time1, repeated, followed by delay of time2). Keypressing is checked in background |
*--------------------------------------------------------------------------------------------------------------------------------------------------- |
*/ |
void Flash2(unsigned char time1, unsigned int time2, unsigned char repeat) |
{ |
unsigned int i,j; |
key = 0; |
|
for (i=0;i<repeat;i++) |
{ |
PORTD &= ~(1<<PORTD7); |
for (j=0;j<time1;j++) |
{ |
_delay_ms(1); |
if (!(PINB & (1<<PINB2))) key = 1; |
} |
PORTD |= 1<<PORTD7; |
for (j=0;j<time1;j++) |
{ |
_delay_ms(1); |
if (!(PINB & (1<<PINB2))) key = 1; |
} |
} |
|
for (j=0;j<time2;j++) |
{ |
_delay_ms(1); |
if (!(PINB & (1<<PINB2))) key = 1; |
} |
} |
|
|
|
/*--------------------------------------------------------------------------------------------------------------------------------------------------- |
* Waits for button release followed by a debounce delay |
*--------------------------------------------------------------------------------------------------------------------------------------------------- |
*/ |
void WaitForKeyRelease(void) |
{ |
while(!(PINB & (1<<PINB2))); |
_delay_ms(50); |
} |
|
|
|
/*--------------------------------------------------------------------------------------------------------------------------------------------------- |
* Get position from servo inputs 1 and 2. Returns 1/2/3 for down/middle/up and 0 for invalid signal |
*--------------------------------------------------------------------------------------------------------------------------------------------------- |
*/ |
unsigned char Servo(unsigned char chan) |
{ |
cli(); |
unsigned int i=0; |
|
for (i=0;i<7500;i++) // wait max. 75 ms for signal going down |
{ |
_delay_us(10); |
if (!(PINC & (1<<(chan+1)))) break; |
} |
|
for (i=0;i<7500;i++) // wait max. 75 ms for signal going up |
{ |
_delay_us(10); |
if (PINC & (1<<(chan+1))) break; |
} |
|
for (i=0;i<255;i++) // measure time until signal goes down |
{ |
if (!(PINC & (1<<(chan+1)))) break; |
_delay_us(10); |
} |
|
|
sei(); |
if (i < 75) return 0; // 0,00 - 0,74 ms => 0 (switch to GND) |
else if (i > 74 && i < 125) return 1; // 0,75 - 1,24 ms => 1 (stick down) |
else if (i > 124 && i < 175) return 2; // 1,25 - 1,74 ms => 2 (stick middle) |
else if (i > 174 && i < 225) return 3; // 1,75 - 2,24 ms => 3 (stick up) |
else if (i > 224) return 4; // 2,25 - ever ms => 4 (switch to VCC or open input) |
return 0; |
} |
|
|
|
/*--------------------------------------------------------------------------------------------------------------------------------------------------- |
* Learning-Mode: When an IR signal arrives, it is saved in an array position related to the actual servo positions |
*--------------------------------------------------------------------------------------------------------------------------------------------------- |
*/ |
void Learn(void) |
{ |
unsigned char i,j,Servo1,Servo2,mode=1; |
for (i=0;i<5;i++) // first, forget every assignment |
{ |
for (j=0;j<5;j++) |
{ |
active[i][j] = 0; |
} |
} |
|
WaitForKeyRelease(); |
|
while(1) |
{ |
if (irmp_get_data (&ir_temp)) // when IR code is decoded, |
{ |
Servo1 = Servo(1); // get Servo positions |
Servo2 = Servo(2); |
ir_temp.flags = 0; // ignore reapeat-flag |
ir_data[Servo1][Servo2] = ir_temp; // populate the array |
active[Servo1][Servo2] = mode; // and flag it with the actually mode |
Flash(25,25,10); // fast flash LED 10 times to indicate a learned code |
} |
|
Flash2(100,500,mode); // slow flash LED whole time to indicate we are in learning mode (flashing 1 or 2 times, depends on mode) |
|
if (key) // toggle mode: single -> multi -> single -> multi ... |
{ |
if (mode == 1) mode = 2; |
else mode = 1; |
|
_delay_ms(500); |
if (!(PINB & (1<<PINB2))) break; // Leave learning mode when button is pressed for 500 ms |
} |
|
} |
|
// Write to EEPROM |
eeprom_write_block(&ir_data,&ee_ir_data,sizeof(unsigned char)*25*6); |
eeprom_write_block(&active,&ee_active,sizeof(unsigned char)*25); |
|
Flash(25,25,10); // fast flash LED 10 times to indicate that we are leaving the learning mode |
WaitForKeyRelease(); |
} |
|
|
/*--------------------------------------------------------------------------------------------------------------------------------------------------- |
* MAIN: main routine |
*--------------------------------------------------------------------------------------------------------------------------------------------------- |
*/ |
int main (void) |
{ |
unsigned char i,j,Servo1,Servo2,Servo1old=99,Servo2old=99; |
|
DDRB = 0b00001000; // IRED output, Switch input |
PORTB = 0b00000100; // PullUp for Switch |
|
DDRC = 0b00000000; // Input for Servo-Signals |
PORTC = 0b00001100; // PullUp for (unused) Servos |
|
DDRD = 0b10000000; // Input for received IR, output for LED |
PORTD = 0b11000000; // PullUp for received IR, LED off |
|
|
if (eeprom_read_byte(&ee_check)!=0xE7) // When EEPROM is uninitialized, do it now |
{ |
for (i=0;i<5;i++) |
{ |
for (j=0;j<5;j++) |
{ |
active[i][j] = 0; |
} |
} |
|
eeprom_write_byte(&ee_check, 0xE7); |
eeprom_write_block(&active,&ee_active,sizeof(unsigned char)*25); |
} |
|
else // else, read in the contents |
{ |
eeprom_read_block(&ir_data,&ee_ir_data,sizeof(unsigned char)*25*6); |
eeprom_read_block(&active,&ee_active,sizeof(unsigned char)*25); |
} |
|
|
irmp_init(); // initialize irmp |
irsnd_init(); // initialize irsnd |
timer_init(); // initialize timer |
WaitForKeyRelease(); |
sei (); // enable interrupts |
|
|
while(1) // Main loop |
{ |
if (!(PINB & (1<<PINB2))) Learn(); // Enter learning mode when the button is pressed |
|
Servo1 = Servo(1); // get servo positions |
Servo2 = Servo(2); |
|
// when the actual posision was saved before, and the inputs are in a new position, send out its IR-code (single mode) |
if (active[Servo1][Servo2] == 1 && (Servo1old != Servo1 || Servo2old != Servo2)) |
{ |
LED(1); // light up LED while sending IR-code |
irsnd_send_data(&ir_data[Servo1][Servo2],FALSE); |
while (irsnd_is_busy()); // wait until the frame was sent |
_delay_ms(100); |
LED(0); |
} |
|
if (active[Servo1][Servo2] == 2) // when the actual position was saved before, send out its IR-code (multi-mode) |
{ |
LED(1); // light up LED while sending IR-code |
irsnd_send_data(&ir_data[Servo1][Servo2],FALSE); |
while (irsnd_is_busy()); // wait until the frame was sent |
LED(0); |
} |
|
Servo1old = Servo1; // save old positions for single-mode |
Servo2old = Servo2; |
} |
|
} |
|
|