0,0 → 1,408 |
/*------------------------------------------------------------------------------ |
** ** |
** 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.2sp ** |
** Date : 06-10-2008 ** |
** Notes : Special version, with a total of 8 channels. ** |
** original 6th MX12 channel is ignored and replaced ** |
**----------------------------------------------------------------------------** |
** ** |
** 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_6_DATA_TRIGGER, |
stPPM_CHANNEL_6_DATA, |
stPPM_CHANNEL_7_START, |
stPPM_CHANNEL_7_DATA, |
stPPM_CHANNEL_8_START, |
stPPM_CHANNEL_8_DATA, |
stPPM_FINISH_PULSE, |
stPPM_FINISH_FRAME, |
stPPM_FRAME_END, |
}; |
|
unsigned char channel_number = 0; |
unsigned char ppm_state = stPPM_SYNC; |
unsigned char adc_channel = 0; |
volatile unsigned int channel_6 = 0xffff; // set to max. for testing if conversion is valid |
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 |
|
|
/*------------------------------------------------------------------------------ |
** ** |
** 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_6_ADC); // Channel 6 (pin12) input |
PORTA &= ~(1 << CHANNEL_6_ADC); // disable pullup |
|
DDRA &= ~(1 << CHANNEL_7_ADC); // Channel 7 (pin11) input |
PORTA &= ~(1 << CHANNEL_7_ADC); // disable pullup |
|
DDRA &= ~(1 << CHANNEL_6_ADC); // Channel 8 (pin10) input |
PORTA &= ~(1 << CHANNEL_6_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 Ch6, 7 and 7 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; |
} |
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: |
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; |
|
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>4) // all 5 channels read |
{ |
ppm_state = stPPM_CHANNEL_6_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_6_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_6_DATA; |
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_6_DATA: // create 6th channel data |
SET_PPM_OUT_LOW; |
SET_COUNTER_TO_ZERO; |
OCR1A = channel_6; // COMPA: 0,7ms + channel 6 ADC value |
ppm_state = stPPM_CHANNEL_7_START; // next State |
break; |
|
case stPPM_CHANNEL_7_START: // create 7th channel start pulse |
SET_PPM_OUT_HIGH; |
SET_COUNTER_TO_ZERO; |
OCR1A = START_PULSE_HIGH; // startpulse length 0.3ms |
ppm_state = stPPM_CHANNEL_7_DATA; // next State |
break; |
|
case stPPM_CHANNEL_7_DATA: // create 7th channel data |
SET_PPM_OUT_LOW; |
SET_COUNTER_TO_ZERO; |
OCR1A = START_PULSE_LOW + channel_7; // COMPA: 0,7ms + channel 6 ADC value |
ppm_state = stPPM_CHANNEL_8_START; // next State |
break; |
|
case stPPM_CHANNEL_8_START: // create 7th 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 7th 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_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 6, 7 and 8 ** |
** ** |
**----------------------------------------------------------------------------*/ |
//#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_6) |
{ |
channel_6 = AdcResult; // set channel 6 value; |
adc_channel = ADC_CHANNEL_7; // set next event for channel 7 conversion |
} |
else 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 |
{ |
channel_8 = AdcResult; // set channel 8 value; |
adc_channel = ADC_CHANNEL_6; // set next event for channel 6 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_6 < 0xffff && channel_7 < 0xffff && channel_8 < 0xffff) // both 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-----------------------------------------------------------------------*/ |
|