Subversion Repositories Projects

Rev

Blame | Last modification | View Log | RSS feed

/*------------------------------------------------------------------------------
**                                                                            **
** Ident        : main.c                                                      **
** Project      : MX12 2 (3) ppm channel expander                             **
** Author       : Jacques Wanders                                             **
** Description  : main module                                                 **
** Copyright (c): 05.2008 Jacques Wanders                                     **
** modified     : 05.2008 Heinrich Fischer for use with WinAVR                **
**                                                                            **
**----------------------------------------------------------------------------**
** Release      : v1.0 initial release                                        **
** Date         : 13-05-2008                                                  **
**----------------------------------------------------------------------------**
** Release      : v1.1                                                        **
** Date         : 13-05-2008                                                  **
** Notes        : Added Channel 9                                             **
**----------------------------------------------------------------------------**
** Release      : v1.2                                                        **
** Date         : 22-05-2008                                                  **
** Notes        : Modified time definitions for FORCE_LOW_END_FRAME           **
**                and MIN_SYNC_TIME to avoid lost pulse groups                **
**----------------------------------------------------------------------------**
** Release      : v1.3                                                        **
** Date         : 14-08-2008                                                  **
** Notes        : Pass thrue for PCM mode                                     **
**                Detects a NON-PPM signal, enables endless passthrue mode    **
**----------------------------------------------------------------------------**
** The use of this project (hardware, software, binary files, sources and     **
** documentation) is only permittet for non-commercial use                    **
** (directly or indirectly)                                                   **
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"**
** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE  **
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE **
** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE   **
** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR        **
** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF       **
** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS   **
** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN    **
** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)    **
** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE **
** POSSIBILITY OF SUCH DAMAGE.                                                **
**                                                                            **
------------------------------------------------------------------------------*/


#define ENABLE_BIT_DEFINITIONS
//#include <ioavr.h>
//#include <inavr.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include "main.h"


enum{
  stPPM_SYNC,
  stPPM_SYNC_WAIT,
  stPPM_CHANNEL_START,
  stPPM_CHANNEL_DATA,
  stPPM_CHANNEL_7_DATA_TRIGGER,
  stPPM_CHANNEL_7_DATA,
  stPPM_CHANNEL_8_START,
  stPPM_CHANNEL_8_DATA,
  stPPM_CHANNEL_9_START,
  stPPM_CHANNEL_9_DATA,
  stPPM_FINISH_PULSE,
  stPPM_FINISH_FRAME,
  stPPM_FRAME_END,
  stPCM_MODE_PULSE_LOW,
  stPCM_MODE_PULSE_HIGH,
};

unsigned char          channel_number   = 0;
unsigned char          ppm_state        = stPPM_SYNC;
unsigned char          adc_channel      = 0;
unsigned char          sync_retry_count = 0;
volatile unsigned int  channel_7        = 0xffff;                               // set to max. for testing if conversion is valid
volatile unsigned int  channel_8        = 0xffff;                               // set to max. for testing if conversion is valid
volatile unsigned int  channel_9        = 0xffff;                               // set to max. for testing if conversion is valid


/*------------------------------------------------------------------------------
**                                                                            **
**  function :  init_pin(void)                                                **
**  purpose  :  Initialise I/O pins                                           **
**                                                                            **
**----------------------------------------------------------------------------*/

void init_pin(void)
{

  DDRA &= ~(1<<PPM_IN);                                                         // set Input Capture Pin as input
  PORTA |= (1<<PPM_IN);                                                         // enable pullup

  DDRB  |=  (1<<PPM_OUT_PIN);                                                   // configure PPM_OUT pin as output
  SET_PPM_OUT_LOW;                                                              // set low

  DDRA  &= ~(1 << CHANNEL_7_ADC);                                               // Channel 7 (pin12) input
  PORTA &= ~(1 << CHANNEL_7_ADC);                                               // disable pullup

  DDRA  &= ~(1 << CHANNEL_8_ADC);                                               // Channel 8 (pin11) input
  PORTA &= ~(1 << CHANNEL_8_ADC);                                               // disable pullup

  DDRA  &= ~(1 << CHANNEL_9_ADC);                                               // Channel 8 (pin11) input
  PORTA &= ~(1 << CHANNEL_9_ADC);                                               // disable pullup

}
/*-init_pin-------------------------------------------------------------------*/


/*------------------------------------------------------------------------------
**                                                                            **
**  function :  init_adc(void)                                                **
**  purpose  :  Initialise ADC registers                                      **
**                                                                            **
**----------------------------------------------------------------------------*/

void init_adc(void)
{

  cli();                                                                        // disable interrupts

  DIDR0 |= ((1<<ADC2D)|(1<<ADC1D));                                             // digital input disable for pin 11 and 12
  ADMUX  = 0x00;                                                                // VCC as reference voltage, select channel 0
  ADCSRA = (1 << ADPS2) | (1 << ADPS1)|| (0 << ADPS0);                          // 8.0 [Mhz] / 128 = 62,5 [kHz]
  ADCSRB = 0x00;                                                                // free running mode

  ADC_ENABLE;

  sei();                                                                        // enable interrupts
}
/*-init_adc-------------------------------------------------------------------*/


/*------------------------------------------------------------------------------
**                                                                            **
**  function :  init_timer1(void)                                             **
**  purpose  :  Initialise timer0                                             **
**  Note(s)  :  Frequency : 8.0 [Mhz]                                         **
**              8 / 8.0 Mhz : 1.00 [us]                                       **
**                                                                            **
**----------------------------------------------------------------------------*/

void init_timer1(void)
{
  cli();                                                                        // disable interrupts

  TCCR1A = ((0<<WGM11)|(0<<WGM10));                                             // CTC mode
  TCCR1B = ((0<<WGM13)|(1<<WGM12)|(0<<CS12)|(1<<CS11)|(0<<CS10));               // CTC mode for COMPA, prescaler 8 / 8MHz => [1.0us]

  CLEAR_INPUT_CAPTURE_INTERRUPT_FLAG;                                           // reset ICF flag

  SET_COUNTER_TO_ZERO;
  SET_COMPARE_COUNTER_TO_ZERO;
  ENABLE_INPUT_CAPTURE;
  DISABLE_OUTPUT_COMPARE;
  RISING_EDGE_TRIGGER;

  sei();                                                                        // enable interrupts
}
/*-init_timer1----------------------------------------------------------------*/


/*------------------------------------------------------------------------------
**                                                                            **
**  function :  timer1_capture_interrupt(void)                                **
**  purpose  :  Synchronise PPM frame and copy input events to PPM_OUT,       **
**              start mixing Ch7, 8 and 9 data when detect start pulse of Ch7 **
**                                                                            **
**----------------------------------------------------------------------------*/

//#pragma vector=TIM1_CAPT_vect
//static __nested __interrupt void timer1_capture_interrupt (void)
ISR(TIMER1_CAPT_vect)
{
   cli();                                                                       // disable interrupts
   unsigned int data;

   switch (ppm_state)
   {
      case stPPM_SYNC:                                                          // detect rising edge pulse
         data = ICR1;                                                           // get timer value after input capture
         SET_COUNTER_TO_ZERO;
         FALLING_EDGE_TRIGGER;
         if(data >= MIN_SYNC_TIME && data <= MAX_SYNC_TIME)                     // valid sync trigger
         {
            SET_PPM_OUT_HIGH;
            ppm_state = stPPM_CHANNEL_DATA;                                     // next state: get data
            channel_number = 0;
            sync_retry_count = 0;                                               // when valid data, reset sync retry counter        (v1.3)
                 }
         else                                                                   // trigger but not a valid sync time
         {
            SET_PPM_OUT_LOW;
            ppm_state = stPPM_SYNC_WAIT;                                        // next state: wait for next sync event
         }
         break;

      case stPPM_SYNC_WAIT:
         sync_retry_count++;                                                    // count number of retry's                          (v1.3)
                 SET_PPM_OUT_LOW;                                                       // not nessecery, output should already be low
         SET_COUNTER_TO_ZERO;
         SET_CAPTURE_COUNTER_TO_ZERO;
         RISING_EDGE_TRIGGER;
                 if(sync_retry_count > 10)                                              // 10x retry is to much, no PPM                     (v1.3)
            ppm_state = stPCM_MODE_PULSE_HIGH;                                  // set to mode PCM pass thrue                       (v1.3)
                 else                                                                   //                                                  (v1.3)
            ppm_state = stPPM_SYNC;                                             // next state: try again for new sync
         break;

      case stPPM_CHANNEL_START:                                                 // detect rising edge pulse
         SET_COUNTER_TO_ZERO;
         SET_PPM_OUT_HIGH;
         FALLING_EDGE_TRIGGER;
         channel_number++;                                                      // prepare for next MX12 channel clock
         if(channel_number>5)                                                   // all six channels read
         {
            ppm_state = stPPM_CHANNEL_7_DATA_TRIGGER;                           // 7th. channel but now self created
            channel_number = 0;
         }
         else
            ppm_state = stPPM_CHANNEL_DATA;
         break;

      case stPPM_CHANNEL_DATA:                                                  // detect falling edge pulse
         SET_COUNTER_TO_ZERO;
         SET_PPM_OUT_LOW;
         RISING_EDGE_TRIGGER;
         ppm_state = stPPM_CHANNEL_START;                                       // wait for next channel rising edge pulse
         break;

      case stPPM_CHANNEL_7_DATA_TRIGGER:                                        // detect rising edge pulse
         SET_COUNTER_TO_ZERO;
         SET_PPM_OUT_LOW;
         SET_TIMER_TO_COMPA_CTC;
         DISABLE_INPUT_CAPTURE;
         ENABLE_OUTPUT_COMPARE;
         OCR1A = START_PULSE_LOW;                                               // startpulse length 0.3ms
         TRIGGER_INPUT_COMPARE_INTERRUPT;
         ppm_state = stPPM_CHANNEL_7_DATA;
         break;

      case stPCM_MODE_PULSE_LOW:                                                // detect falling edge pulse                        (v1.3)
         SET_COUNTER_TO_ZERO;                                                   // prevent overflow interrupt                        
         SET_PPM_OUT_LOW;                                                                                                        
         RISING_EDGE_TRIGGER;                                                                                                    
         ppm_state = stPCM_MODE_PULSE_HIGH;                                                                                            
         break;                                                                                                                  

      case stPCM_MODE_PULSE_HIGH:                                               // detect rising edge pulse                         (v1.3)
         SET_COUNTER_TO_ZERO;                                                   // prevent overflow interrupt                      
         SET_PPM_OUT_HIGH;                                                                                                        
         FALLING_EDGE_TRIGGER;
         ppm_state = stPCM_MODE_PULSE_LOW;                                                                                                                            
         break;

      default:
         SET_PPM_OUT_LOW;                                                       // not nessecery, output should already be low
         SET_COUNTER_TO_ZERO;
         SET_CAPTURE_COUNTER_TO_ZERO;
         RISING_EDGE_TRIGGER;
         ppm_state = stPPM_SYNC;                                                // next state: try again for new sync
         break;

   }
   sei();
}
/*-timer1_capture_interrupt---------------------------------------------------*/


/*------------------------------------------------------------------------------
**                                                                            **
**  function :  timer1_compare_interrupt(void)                                **
**  purpose  :  Mixing channel 7, 8 and 9 data into ppm out,                  **
**              start input capture for frame synchronisation                 **
**                                                                            **
**----------------------------------------------------------------------------*/

//#pragma vector=TIM1_COMPA_vect
//__interrupt void timer1_compare_interrupt (void)
ISR(TIM1_COMPA_vect)
{
   cli();
   switch (ppm_state)
   {
      case stPPM_CHANNEL_7_DATA:                                                // create 7th channel data
         SET_PPM_OUT_LOW;
         SET_COUNTER_TO_ZERO;
         OCR1A = channel_7;                                                     // COMPA: 0,7ms + channel 7 ADC value
         ppm_state = stPPM_CHANNEL_8_START;                                     // next State
         break;

      case stPPM_CHANNEL_8_START:                                               // create 8th channel start pulse
         SET_PPM_OUT_HIGH;
         SET_COUNTER_TO_ZERO;
         OCR1A = START_PULSE_HIGH;                                              // startpulse length 0.3ms
         ppm_state = stPPM_CHANNEL_8_DATA;                                      // next State
         break;

      case stPPM_CHANNEL_8_DATA:                                                // create 8th channel data
         SET_PPM_OUT_LOW;
         SET_COUNTER_TO_ZERO;
         OCR1A = START_PULSE_LOW + channel_8;                                   // COMPA: 0,7ms + channel 7 ADC value
         ppm_state = stPPM_CHANNEL_9_START;                                     // next State
         break;

      case stPPM_CHANNEL_9_START:                                               // create 8th channel start pulse
         SET_PPM_OUT_HIGH;
         SET_COUNTER_TO_ZERO;
         OCR1A = START_PULSE_HIGH;                                              // startpulse length 0.3ms
         ppm_state = stPPM_CHANNEL_9_DATA;                                      // next State
         break;

      case stPPM_CHANNEL_9_DATA:                                                // create 8th channel data
         SET_PPM_OUT_LOW;
         SET_COUNTER_TO_ZERO;
         OCR1A = START_PULSE_LOW + channel_9;                                   // COMPA: 0,7ms + channel 7 ADC value
         ppm_state = stPPM_FINISH_PULSE;                                        // next State
         break;

    case stPPM_FINISH_PULSE:                                                    // create last pulse
         SET_PPM_OUT_HIGH;
         SET_COUNTER_TO_ZERO;
         OCR1A = START_PULSE_HIGH;                                              // startpulse length 0.3ms
         ppm_state = stPPM_FINISH_FRAME;                                        // next State
         break;

      case stPPM_FINISH_FRAME:                                                  // create extra low pulse for masking PPM_IN data of channel 7 and 8
         SET_PPM_OUT_LOW;
         SET_COUNTER_TO_ZERO;
         OCR1A = FORCE_LOW_END_FRAME;                                           // keep last end low; 2 channels max - 2 channels min => 2x2ms - 2x1ms + extra length = 2 ms + 1 ms = 3ms => 3000 ticks
         ppm_state = stPPM_FRAME_END;                                           // next State
         break;

      case stPPM_FRAME_END:
      default:
         RISING_EDGE_TRIGGER;
         DISABLE_OUTPUT_COMPARE;
         ENABLE_INPUT_CAPTURE;
         SET_TIMER_TO_COMPA_CTC;
         SET_COUNTER_TO_ZERO;
         SET_COMPARE_COUNTER_TO_ZERO;
         SET_CAPTURE_COUNTER_TO_ZERO;
         ppm_state = stPPM_SYNC;                                                // next State
         TRIGGER_INPUT_CAPTURE_INTERRUPT;
         break;

   }
   sei();
}
/*-timer1_compare_interrupt---------------------------------------------------*/


/*------------------------------------------------------------------------------
**                                                                            **
**  function :  adc_server(void)                                              **
**  purpose  :  Handle Analog conversion of RC channel 7, 8 and 9             **
**                                                                            **
**----------------------------------------------------------------------------*/

//#pragma vector=ADC_vect
//__interrupt void adc_server(void)
ISR(ADC_vect)
{
  unsigned int AdcResult;

  ADC_DISABLE;

  AdcResult = ADC;
  if(AdcResult > 1000)                                                          // limit conversion value
     AdcResult = 1000;                                                          // 1000 => 1ms

  ADMUX &= ~(ADC_CHANNEL_MASK);                                                 // clear channel select bits

  if(adc_channel == ADC_CHANNEL_7)
  {
    channel_7 = AdcResult;                                                      // set channel 7 value;
    adc_channel = ADC_CHANNEL_8;                                                // set next event for channel 8 conversion
  }
  else if(adc_channel == ADC_CHANNEL_8)
  {
    channel_8 = AdcResult;                                                      // set channel 8 value;
    adc_channel = ADC_CHANNEL_9;                                                // set next event for channel 9 conversion
  }
  else
  {
    channel_9 = AdcResult;                                                      // set channel 9 value;
    adc_channel = ADC_CHANNEL_7;                                                // set next event for channel 7 conversion
  }

  ADMUX  |= adc_channel;                                                        // select new conversion channel

  ADC_ENABLE;
}
/*-adc_server-----------------------------------------------------------------*/


/*------------------------------------------------------------------------------
**                                                                            **
**  function :  check_valid_adc_value(void)                                   **
**  purpose  :  wait until 3 ADC channels are processed at least once         **
**              before init Input Capture/Timer1                              **
**                                                                            **
**----------------------------------------------------------------------------*/

void check_valid_adc_value (void)
{
  unsigned char exit = FALSE;

  do
  {
     if(channel_7 < 0xffff && channel_8 < 0xffff && channel_9 < 0xffff)         // All three channels must be processed
        exit = TRUE;

  }while (!exit);
}
/*-check_valid_adc_value------------------------------------------------------*/


/*------------------------------------------------------------------------------
**                                                                            **
**  function :  main(void)                                                    **
**                                                                            **
**----------------------------------------------------------------------------*/

int main(void)
{

  init_pin();
  init_adc();
  check_valid_adc_value();                                                      // wait until both ADC channels are processed at least once
  init_timer1();

  while(1)
  {}
}

/*-main-----------------------------------------------------------------------*/