Subversion Repositories Projects

Rev

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;
  }

}