www.mikrocontroller.net

switch4ch.c

switch4ch.c
/*
 * switch4ch.c
 * ATtiny13 mit 1,25 MHz internal RC resonator
 * 
 * This small program parses the signal of an RC receiver output and switches
 * 4 functions according to the input signal. The pcb will be connected 
 * exactly like a standard servo. 
 * The servo signal usually repeats every 20ms and has a high value of the 
 * signal ranges from 0.5ms to 2.5ms maximum and 1ms to 2ms at a standard 
 * RC control.
 * 
 * Usage:
 * A 'click' your RC-sender pult channel to 100% a short time 
 * (<0.5s) will toggle channel 1
 * A click to -100% will toggle channel 2
 * If you stay at 100% for more than a half second, channel 3 will be set
 * until you release the channel again.
 * The same happens to channel 4 if you stay at -100% for more than a half
 * second - currently this generates a funny 'morse code' signal for a 
 * small tweeter.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND
 * This file is licensed under the GNU GENERAL PUBLIC LICENSE Version 2
 */

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
#include <avr/wdt.h>

/**
 * The pin where we get the servo signal from the receiver 
 */ 
#define PIN_PULSE_IN PB3


#define MAX_CLICK_TIME 25 

/**
 * the ouputs
 */
#define OUT1 PB0
#define OUT2 PB1
#define OUT3 PB2
#define OUT4 PB4


/** 
 * Higher byte of the timer. this will be incremented by the int SIG_OVERFLOW0 
 * This is needed since the TIMER0 is running without any prescaler.
 * This variable is defined as volatile to avoid that C is optimising it to a register
 * which cannot be shared between the interrupt service routine and the mail loop.
 */
volatile uint8_t timerHighByte;

/**
 * counts the number of cycles the high value has been reached. 
 * If this value is 0 then no high value has been detected previously.
 * If the value is > 50 (~1 second) then stop counting and switch on SW4.
 */
uint8_t highCounter;

/**
 * counts the number of cycles the low value has been reached. 
 * If this value is 0 then no low value has been detected previously.
 */
uint8_t lowCounter;

/**
 * If this variable is > 0 then play a sound. If 0 then stop playing.
 * This is used as a ship horn which is output by SW2.
 */
uint8_t soundRunning;


/**
 * This interrupt gets triggered if the timer0 is running and an overflow (from 0xff to 0x00) happens
 */
SIGNAL (SIG_OVERFLOW0) {
  timerHighByte++;                          // incrementing timerHighByte
}


/**
 * reset the timer value to 0x0000 and start the timer
 */ 
void restartTimer() {
  timerHighByte = 0;                        // higher Byte of the timer
  TCNT0 = 0;                                // lower Byte of the timer
  TCCR0B  |= (1 << CS00);                   // set Prescaler to 1:1 (count with clkspeed of ~1MHz)
}

/**
 * stop the timer0
 */
void stopTimer() {
  TCCR0B  &= ~(1 << CS00);                  // set Prescaler to 0 will stop the timer
}

/**
 * This function is called if a valid servo pulse has been detected.
 * The timerValue is the pulse length in microseconds  
 */    
void performAction(uint16_t timerValue) {
  if (timerValue < 1500) {
    if (lowCounter >= MAX_CLICK_TIME) {
      // the button has been pressed for aprox 1 second -> switch on SW2
      //X disabled. SW2 is now used for playing sound: PORTB |= (1<<OUT2);
      soundRunning = 1;
    } else {
      // simply continue counting
      lowCounter++;
    }
    
  } else if (timerValue > 2100) {
    if (highCounter >= MAX_CLICK_TIME) {
      // the button has been pressed for aprox 1 second -> switch on SW4
      PORTB |= (1<<OUT4);
    } else {
      // simply continue counting
      highCounter++;
    }
  } else {
    // a mid value (zero position)
    
    if (highCounter > 3 && highCounter < MAX_CLICK_TIME) {
      // single 'click' detected -> toggle switch SW3
      PORTB ^= (1<<OUT3);
    } else if (highCounter == MAX_CLICK_TIME) {
      // so its now time to switch off SW4 again
      PORTB &= ~(1<<OUT4);
    }

    //X TODO toggle SW2 also
    if (lowCounter > 3 && lowCounter < MAX_CLICK_TIME) {
      // single 'click' detected -> toggle switch SW1
      PORTB ^= (1<<OUT1);
    } else if (lowCounter == MAX_CLICK_TIME) {
      // so its now time to switch off SW2 again
      //X disabled. SW2 is now used for playing sound: PORTB &= ~(1<<OUT2);
      soundRunning = 0;      
    }

      // reset the counters
      lowCounter = 0;
      highCounter = 0;
    
  }
  return;
  
 
}

/**
 * This function is called if an invalid valid servo pulse has been detected.
 */
void performActionOnInvalidPulse() {
  // switch out all ports
  PORTB = 0;
}


/**
 * The main loop does the following:
 * .) initialise all ports
 * .) restart the timer
 * 
 * in an endless loop the following things will be done:
 * .) check if the PIN_PULSE_IN is high and if so start the timer
 * .) if the timer went low again, stop the timer and read the current timer value
 * .) depending on the timervalue (should be from 1ms (timer=1000) to 2ms (timer=2000) perform actions
 */                                            
int main() {
  uint16_t timerValue=0;
  
  uint8_t soundCounter = 0;
  
  // enable the watchdogtimer and set it to 120 ms timeout
  //X seems to have a bug with tiny13 wdt_enable(WDTO_500MS);
  
  // on startup there is always an invalid pulse
  performActionOnInvalidPulse();
  
  PORTB = 0x00;                             // clear the PORTB to avoid glitches
  DDRB  = 0x17;                             // set bits 0,1,2 and 4 of PORTB as output

  TIMSK0  |= (1 << TOIE0);                  // Timer 0 Overflow Interrupt enable
  sei();                                    // enable Interrupts
  
  for(;;) {                                 // main loop
    
    // stay in loop as long as PIN_PULSE_IN is low
    while( !(PINB & (1 << PIN_PULSE_IN) ) ){
      soundCounter++;
      if (soundRunning > 0 && soundCounter & (1<<5)) {
        PORTB ^= (1<<OUT2);
      }              
    }
    
    // switch off the tweeter
    PORTB &= ~(1<<OUT2);
     
    restartTimer();                         // if PIN_PULSE_IN went high, we start the timer from 0
    
    // stay in loop as long as PIN_PULSE_IN is high
    while( (PINB & (1 << PIN_PULSE_IN) ) ) {
    } 
    
    // if all signals are available, this should have lasted only ~25 ms,
    // so tell the watchdog timer that all is ok!
    //X seems to have a bug with tiny13 wdt_reset();
    
    stopTimer();                            // if PIN_PULSE_IN went low again, we stop the timer
    
    timerValue = (timerHighByte << 8) + TCNT0;// fill the 16bit timerValue 
    
    if (timerValue > 800 && timerValue < 3000) {
      performAction(timerValue);
    } else {
      performActionOnInvalidPulse();
    }
    
  }                                         // end of main loop
}

webmaster@mikrocontroller.netImpressumWerbung auf Mikrocontroller.net