/*------------------------------------------------------------------------------
** **
** 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-----------------------------------------------------------------------*/