Go to most recent revision |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/*---------------------------------------------------------------------------------------------------------------------------------------------------
* 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;
}
}