You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1287 lines
47 KiB
1287 lines
47 KiB
|
|
/********************************************************************************************************* |
|
Title : C file for the rc ppm encoder (servo2ppm_v4_2.c) |
|
Author: Chris Efstathiou |
|
E-mail: hendrix at vivodinet dot gr |
|
Homepage: ........................ |
|
Date: 3/Aug/2009 |
|
Compiler: AVR-GCC with AVR-AS |
|
MCU type: ATmega168 |
|
Comments: This software is FREE. Use it at your own risk. |
|
*********************************************************************************************************/ |
|
|
|
|
|
/********************************************************************************************************/ |
|
/* PREPROCESSOR DIRECTIVES */ |
|
/********************************************************************************************************/ |
|
|
|
|
|
#include <avr/io.h> |
|
#include <avr/interrupt.h> |
|
#include <avr/wdt.h> |
|
#include <avr/eeprom.h> |
|
#include <inttypes.h> |
|
#include "servo2ppm_settings.h" |
|
|
|
|
|
|
|
/********************************************************************************************************/ |
|
/* Normaly you shouldn't need to change anything below this line but who knows? */ |
|
/********************************************************************************************************/ |
|
|
|
|
|
|
|
#define RC_SERVO_PORT D /* The port for the servo pulse input. */ |
|
|
|
#define RC_LED_PORT B /* The port for the PPM waveform and the led. */ |
|
#define RC_LED_PIN 0 /* The led indicator pin. */ |
|
|
|
#define RC_PPM_PORT B |
|
#define RC_PPM_PIN 2 /* The PPM waveform output pin */ |
|
|
|
#define RC_MUX_PORT B /* The port for the MUX controller. */ |
|
#define RC_MUX_PIN 1 /* The pin to control the MUX. */ |
|
|
|
#define RC_SETUP_PORT B |
|
#define RC_SETUP_PIN 4 |
|
|
|
#define RC_TIMER0_TIMSK TIMSK0 /* Timer0 registers */ |
|
#define RC_TIMER0_TIFR TIFR0 |
|
#define RC_TIMER0_PRESCALER_REG TCCR0B |
|
|
|
#define RC_TIMER1_TIMSK TIMSK1 /* Timer1 registers */ |
|
#define RC_TIMER1_TIFR TIFR1 |
|
#define RC_TIMER1_PRESCALER_REG TCCR1B |
|
#define RC_TIMER1_MODE_REG TCCR1A |
|
#define RC_TIMER1_COMP1_REG OCR1A |
|
#define RC_TIMER1_COMP2_REG OCR1B |
|
|
|
#define RC_PIN_INT_EN_REG PCICR |
|
#define RC_PIN_INT_EN_BIT PCIE2 |
|
#define RC_PIN_INT_FLAG_REG PCIFR |
|
#define RC_PIN_INT_FLAG_BIT PCIF2 |
|
#define RC_PIN_INT_MASK_REG PCMSK2 |
|
|
|
#define RC_SERVO_INPUT_CHANNELS 8 |
|
|
|
|
|
/* |
|
You can copy the above avr type data and edit them as needed if you plan to use |
|
a different avr type cpu having at least 2k bytes of flash memory. |
|
*/ |
|
|
|
|
|
#define _CONCAT2_(a, b) a ## b |
|
#define CONCAT2(a, b) _CONCAT2_(a, b) |
|
|
|
#define RC_SERVO_PORT_OUT_REG CONCAT2(PORT, RC_SERVO_PORT) |
|
#define RC_SERVO_PORT_DDR_REG CONCAT2(DDR, RC_SERVO_PORT) |
|
#define RC_SERVO_PORT_PIN_REG CONCAT2(PIN, RC_SERVO_PORT) |
|
|
|
#define RC_LED_PORT_OUT_REG CONCAT2(PORT, RC_LED_PORT) |
|
#define RC_LED_PORT_DDR_REG CONCAT2(DDR, RC_LED_PORT) |
|
#define RC_LED_PORT_PIN_REG CONCAT2(PIN, RC_LED_PORT) |
|
|
|
#define RC_MUX_PORT_OUT_REG CONCAT2(PORT, RC_MUX_PORT) |
|
#define RC_MUX_PORT_DDR_REG CONCAT2(DDR, RC_MUX_PORT) |
|
#define RC_MUX_PORT_PIN_REG CONCAT2(PIN, RC_MUX_PORT) |
|
|
|
#define RC_PPM_PORT_OUT_REG CONCAT2(PORT, RC_PPM_PORT) |
|
#define RC_PPM_PORT_DDR_REG CONCAT2(DDR, RC_PPM_PORT) |
|
#define RC_PPM_PORT_PIN_REG CONCAT2(PIN, RC_PPM_PORT) |
|
|
|
#define RC_SETUP_PORT_OUT_REG CONCAT2(PORT, RC_SETUP_PORT) |
|
#define RC_SETUP_DDR_REG CONCAT2(DDR, RC_SETUP_PORT) |
|
#define RC_SETUP_PIN_REG CONCAT2(PIN, RC_SETUP_PORT) |
|
|
|
|
|
#if RC_PPM_GEN_CHANNELS > 8 |
|
#error PPM generator max number of channels is 8 |
|
#endif |
|
#if RC_PPM_GEN_CHANNELS == 0 |
|
#error PPM generator channels cannot be zero! |
|
#endif |
|
|
|
#if RC_PPM_GEN_CHANNELS < RC_SERVO_INPUT_CHANNELS |
|
#undef RC_SERVO_INPUT_CHANNELS |
|
#define RC_SERVO_INPUT_CHANNELS RC_PPM_GEN_CHANNELS |
|
#warning servo channels = PPM generator channels |
|
#endif |
|
|
|
#if RC_PPM_RESET_PW == 0 && RC_PPM_FRAME_LENGTH_MS == 0 |
|
|
|
#undef RC_PPM_FRAME_LENGTH_MS |
|
#undef RC_PPM_RESET_PW |
|
#define RC_PPM_RESET_PW (23500UL - (8 * RC_SERVO_CENTER_PW)) |
|
#define RC_PPM_FRAME_LENGTH_MS (RC_PPM_RESET_PW + (RC_PPM_GEN_CHANNELS * RC_SERVO_CENTER_PW)) |
|
|
|
#elif RC_PPM_FRAME_LENGTH_MS == 0 && RC_PPM_RESET_PW > 0 |
|
|
|
#undef RC_PPM_FRAME_LENGTH_MS |
|
#define RC_PPM_FRAME_LENGTH_MS ((RC_PPM_GEN_CHANNELS * RC_SERVO_CENTER_PW)+RC_PPM_RESET_PW) |
|
|
|
#elif RC_PPM_FRAME_LENGTH_MS > 0 && RC_PPM_RESET_PW == 0 |
|
|
|
#undef RC_PPM_RESET_PW |
|
#define RC_PPM_RESET_PW (RC_PPM_FRAME_LENGTH_MS -(RC_PPM_GEN_CHANNELS * RC_SERVO_CENTER_PW)) |
|
|
|
#endif |
|
|
|
#if RC_PPM_FRAME_LENGTH_MS > 25000UL |
|
#warning PPM frame period exceeds 25ms |
|
#endif |
|
|
|
#if RC_PPM_RESET_PW <= (5000UL + RC_PPM_CHANNEL_SYNC_PW) |
|
#warning PPM reset period lower than 5ms |
|
#endif |
|
|
|
|
|
/* Search for a suitable timer prescaler. Both timers must use the same prescaling factor. */ |
|
#if ((F_CPU * (RC_MAX_TIMEOUT/1000))/1000) < 0xFFF0 |
|
|
|
#define TIMER0_PRESCALER 1 |
|
#define TIMER0_PRESCALER_BITS ((0<<CS02)|(0<<CS01)|(1<<CS00)) |
|
#warning TIMER0 PRESCALER SET TO 1 |
|
|
|
#elif (((F_CPU * (RC_PPM_FRAME_TIMEOUT/1000))/1000)/8) < 0xFFF0 |
|
|
|
#define TIMER0_PRESCALER 8 |
|
#define TIMER0_PRESCALER_BITS ((0<<CS02)|(1<<CS01)|(0<<CS00)) |
|
#warning TIMER0 PRESCALER SET TO 8 |
|
|
|
#elif (((F_CPU * (RC_PPM_FRAME_TIMEOUT/1000))/1000)/64) < 0xFFF0 |
|
|
|
#define TIMER0_PRESCALER 64 |
|
#define TIMER0_PRESCALER_BITS ((0<<CS02)|(1<<CS01)|(1<<CS00)) |
|
#warning TIMER0 PRESCALER SET TO 64 |
|
|
|
#endif |
|
|
|
#define TIMER1_PRESCALER TIMER0_PRESCALER |
|
|
|
#if TIMER1_PRESCALER == 1 |
|
|
|
#define TIMER1_PRESCALER_BITS ((0<<CS12)|(0<<CS11)|(1<<CS10)) |
|
#warning TIMER1 PRESCALER SET TO 1 |
|
|
|
#elif TIMER1_PRESCALER == 8 |
|
|
|
#define TIMER1_PRESCALER_BITS ((0<<CS12)|(1<<CS11)|(0<<CS10)) |
|
#warning TIMER1 PRESCALER SET TO 8 |
|
|
|
#elif TIMER1_PRESCALER == 64 |
|
|
|
#define TIMER1_PRESCALER_BITS ((0<<CS12)|(1<<CS11)|(1<<CS10)) |
|
#warning TIMER1 PRESCALER SET TO 64 |
|
|
|
#else |
|
|
|
#error NO SUITABLE PRESCALER FOR TIMER1 FOUND! |
|
|
|
#endif |
|
|
|
/* Now that the timer prescalers are known the conversion of microseconds to timer values follows */ |
|
#define RC_SERVO_CENTER_PW_VAL ((((F_CPU/1000) * RC_SERVO_CENTER_PW )/1000)/TIMER0_PRESCALER) |
|
#define RC_SERVO_MIN_PW_VAL ((((F_CPU/1000) * RC_SERVO_MIN_PW)/1000)/TIMER0_PRESCALER) |
|
#define RC_SERVO_MAX_PW_VAL ((((F_CPU/1000) * RC_SERVO_MAX_PW)/1000)/TIMER0_PRESCALER) |
|
#define RC_PPM_FRAME_TIMER_VAL ((((F_CPU/1000) * RC_PPM_FRAME_LENGTH_MS)/1000)/TIMER1_PRESCALER) |
|
#define RC_PPM_SYNC_PW_VAL ((((F_CPU/1000) * RC_PPM_CHANNEL_SYNC_PW)/1000)/TIMER1_PRESCALER) |
|
#define RC_PPM_OFF_THRESHOLD_VAL ((((F_CPU/1000) * RC_LOST_THRESHOLD)/1000)/TIMER1_PRESCALER) |
|
#define RC_PPM_OFF_UPPER_WINDOW_VAL ((((F_CPU/1000) * 1700)/1000)/TIMER1_PRESCALER) //1700 microseconds |
|
#define RC_PPM_OFF_LOWER_WINDOW_VAL ((((F_CPU/1000) * 1300)/1000)/TIMER1_PRESCALER) //1300 microseconds |
|
#define RC_PPM_OFF_OFFSET_VAL ((((F_CPU/1000) * RC_THROTTLE_CH_OFFSET_PW)/1000)/TIMER1_PRESCALER) |
|
|
|
#define RC_MAX_TIMEOUT_VAL ((((((F_CPU/1000) * RC_MAX_TIMEOUT)/1000)/TIMER0_PRESCALER)/256)+1) |
|
#define RC_PULSE_TIMEOUT_VAL ((((((F_CPU/1000) * RC_SERVO_MAX_PW)/1000)/TIMER0_PRESCALER)/256)+1) |
|
|
|
#define RC_FS_CH_1_TIMER_VAL ((((F_CPU/1000) * RC_FAILSAFE_CHANNEL_1)/1000)/TIMER0_PRESCALER) |
|
#define RC_FS_CH_2_TIMER_VAL ((((F_CPU/1000) * RC_FAILSAFE_CHANNEL_2)/1000)/TIMER0_PRESCALER) |
|
#define RC_FS_CH_3_TIMER_VAL ((((F_CPU/1000) * RC_FAILSAFE_CHANNEL_3)/1000)/TIMER0_PRESCALER) |
|
#define RC_FS_CH_4_TIMER_VAL ((((F_CPU/1000) * RC_FAILSAFE_CHANNEL_4)/1000)/TIMER0_PRESCALER) |
|
#define RC_FS_CH_5_TIMER_VAL ((((F_CPU/1000) * RC_FAILSAFE_CHANNEL_5)/1000)/TIMER0_PRESCALER) |
|
#define RC_FS_CH_6_TIMER_VAL ((((F_CPU/1000) * RC_FAILSAFE_CHANNEL_6)/1000)/TIMER0_PRESCALER) |
|
#define RC_FS_CH_7_TIMER_VAL ((((F_CPU/1000) * RC_FAILSAFE_CHANNEL_7)/1000)/TIMER0_PRESCALER) |
|
#define RC_FS_CH_8_TIMER_VAL ((((F_CPU/1000) * RC_FAILSAFE_CHANNEL_8)/1000)/TIMER0_PRESCALER) |
|
#define RC_RESET_PW_TIMER_VAL ((((F_CPU/1000) * RC_PPM_RESET_PW)/1000)/TIMER1_PRESCALER) |
|
|
|
#define RC_MUX_TIMER_VAL ((((F_CPU/1000) * 1500)/1000)/TIMER0_PRESCALER) |
|
|
|
//#define RC_TIMER0_VAL_CORRECTION ((RC_SERVO_CENTER_PW_VAL *100)/RC_SERVO_CENTER_PW ) |
|
|
|
/* Some error checking */ |
|
/* The timer can only count up to 65535 so the high byte can go up to 255 */ |
|
#if (RC_MAX_TIMEOUT_VAL > 255) |
|
#error RC_MAX_TIMEOUT is set too high! |
|
#endif |
|
|
|
#define RC_LED_FREQUENCY_VAL (((1000000/RC_LED_FREQUENCY)/23500)/2) //each frame is 23,5 ms |
|
#if RC_LED_FREQUENCY_VAL <= 0 |
|
#undef RC_LED_FREQUENCY_VAL |
|
#define RC_LED_FREQUENCY_VAL 1 |
|
#warning LED FREQUENCY set to maximum |
|
#endif |
|
|
|
//It is used when there is no servo signals received so the elapsed time is always RC_MAX_TIMEOUT ms. |
|
#define RC_LED_FREQUENCY_VAL_1HZ (((1000000/1)/RC_MAX_TIMEOUT)/2) |
|
|
|
/* Macro command definitions. */ |
|
#define LED_ON() { RC_LED_PORT_OUT_REG |= (1<<RC_LED_PIN); } |
|
#define LED_OFF() { RC_LED_PORT_OUT_REG &= (~(1<<RC_LED_PIN)); } |
|
#define TOGGLE_LED() { RC_LED_PORT_OUT_REG ^= (1<<RC_LED_PIN); } |
|
|
|
#define MUX_ON() { RC_MUX_PORT_OUT_REG |= (1<<RC_MUX_PIN); } |
|
#define MUX_OFF() { RC_MUX_PORT_OUT_REG &= (~(1<<RC_MUX_PIN)); } |
|
|
|
#define STOP_TIMER0() { RC_TIMER0_PRESCALER_REG = 0; } |
|
#define START_TIMER0() { RC_TIMER0_PRESCALER_REG = TIMER0_PRESCALER_BITS; } |
|
#define RESET_TIMER0() { RC_TIMER0_TIFR |= (1<<TOIE0); TCNT0=0; timer0.timer0[1]=0; } |
|
#define RESET_START_TIMER0() { STOP_TIMER0(); RESET_TIMER0(); START_TIMER0(); } |
|
|
|
#if RC_USE_FAILSAFE > 1 |
|
#error RC_USE_FAILSAFE can be 0 or 1 |
|
#endif |
|
#if RC_PPM_OUTPUT_TYPE > 1 |
|
#error RC_PPM_OUTPUT_TYPE can be 0 or 1 |
|
#endif |
|
#if RC_CONSTANT_PPM_FRAME_TIME > 1 |
|
#error RC_CONSTANT_PPM_FRAME_TIME can be 0 or 1 |
|
#endif |
|
#if RC_REDUCE_LATENCY > 1 |
|
#error RC_REDUCE_LATENCY can be 0 or 1 |
|
#endif |
|
|
|
|
|
/********************************************************************************************************/ |
|
/* TYPE DEFINITIONS */ |
|
/********************************************************************************************************/ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/********************************************************************************************************/ |
|
/* LOCAL FUNCTION PROTOTYPES */ |
|
/********************************************************************************************************/ |
|
void initialize_mcu(void); |
|
void wait_for_rx(void); |
|
unsigned char detect_connected_channels(void); |
|
unsigned int get_channel_pw(unsigned char pin); |
|
void check_for_setup_mode(void); |
|
void write_default_values_to_eeprom(void); |
|
void load_failsafe_values(void); |
|
void load_values_from_eeprom(void); |
|
static inline void ppm_off(void); |
|
static inline void ppm_on(void); |
|
|
|
|
|
/********************************************************************************************************/ |
|
/* GLOBAL VARIABLES */ |
|
/********************************************************************************************************/ |
|
|
|
volatile unsigned int isr_channel_pw[(RC_PPM_GEN_CHANNELS +1)]; // +1 is for the reset pulse. |
|
/* |
|
unsigned int fs_channel_pw[8]= { RC_FS_CH_1_TIMER_VAL, RC_FS_CH_2_TIMER_VAL, |
|
RC_FS_CH_3_TIMER_VAL, RC_FS_CH_4_TIMER_VAL, |
|
RC_FS_CH_5_TIMER_VAL, RC_FS_CH_6_TIMER_VAL, |
|
RC_FS_CH_7_TIMER_VAL, RC_FS_CH_8_TIMER_VAL, |
|
}; |
|
*/ |
|
const char version_info[]="Servo2ppm V4.20"; |
|
volatile unsigned char pin_interrupt_detected = 0; |
|
volatile unsigned char isr_channel_number = 0; |
|
volatile unsigned int isr_timer0_16 = 0; |
|
unsigned char channels_in_use = 0; |
|
unsigned char channel_mask = 0; |
|
|
|
union word2byte{ |
|
volatile unsigned int timer0_16; |
|
volatile unsigned char timer0[2]; |
|
} timer0; |
|
|
|
volatile unsigned int isr_timer0 = 0; |
|
|
|
#if RC_CONSTANT_PPM_FRAME_TIME == 1 |
|
volatile unsigned int reset_pw = 0; |
|
#endif |
|
|
|
//unsigned char failsafe_mode = 0; |
|
unsigned int ppm_off_threshold = RC_PPM_OFF_THRESHOLD_VAL; |
|
unsigned char rc_lost_channel = (RC_LOST_CHANNEL-1); |
|
//Dummy variable array in order not to use the beggining of the eeprom for usefull data. |
|
//The beggining of the eeprom and especially byte 0 is prone to data corruption. |
|
// "EEMEM" is equal to "__attribute__((section(".eeprom")))" |
|
unsigned int dummy_int[10] __attribute__((section(".eeprom")))= { 1, 1 ,1, 1, 1, 1, 1, 1, 1, 1 }; |
|
unsigned int EEMEM ppm_off_threshold_e[11] = { RC_PPM_OFF_THRESHOLD_VAL, RC_PPM_OFF_THRESHOLD_VAL, RC_PPM_OFF_THRESHOLD_VAL, |
|
RC_PPM_OFF_THRESHOLD_VAL, RC_PPM_OFF_THRESHOLD_VAL, RC_PPM_OFF_THRESHOLD_VAL, |
|
RC_PPM_OFF_THRESHOLD_VAL, RC_PPM_OFF_THRESHOLD_VAL, RC_PPM_OFF_THRESHOLD_VAL, |
|
RC_PPM_OFF_THRESHOLD_VAL, RC_PPM_OFF_THRESHOLD_VAL |
|
}; |
|
|
|
unsigned char EEMEM rc_lost_channel_e[11] = { RC_LOST_CHANNEL-1, RC_LOST_CHANNEL-1, RC_LOST_CHANNEL-1, |
|
RC_LOST_CHANNEL-1, RC_LOST_CHANNEL-1, RC_LOST_CHANNEL-1, |
|
RC_LOST_CHANNEL-1, RC_LOST_CHANNEL-1, RC_LOST_CHANNEL-1, |
|
RC_LOST_CHANNEL-1, RC_LOST_CHANNEL-1 |
|
}; |
|
//unsigned int EEMEM ch_failsafe_pw_e[RC_PPM_GEN_CHANNELS]; |
|
|
|
/********************************************************************************************************/ |
|
/* LOCAL FUNCTION DEFINITIONS */ |
|
/********************************************************************************************************/ |
|
|
|
/*11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111*/ |
|
|
|
void initialize_mcu(void) |
|
{ |
|
unsigned char x = 0; |
|
unsigned int eep_address = 0; |
|
|
|
asm("cli"); |
|
|
|
STOP_TIMER0(); |
|
RESET_TIMER0(); |
|
|
|
/* Enable pwm mode 15 (fast pwm with top=OCR1A) and stop the timer. */ |
|
/* The timer compare module must be configured before the DDR register. */ |
|
// THE PPM GENERATOR IS CONFIGURED HERE ! |
|
#if RC_PPM_OUTPUT_TYPE == 0 // NEGATIVE PULSES |
|
RC_TIMER1_MODE_REG = (1<<COM1B1) | (1<<COM1B0) | (1<<WGM11) | (1<<WGM10); |
|
#warning PPM output type set to NEGATIVE PULSE |
|
#endif |
|
#if RC_PPM_OUTPUT_TYPE == 1 // POSITIVE PULSES |
|
RC_TIMER1_MODE_REG = (1<<COM1B1) | (0<<COM1B0) | (1<<WGM11) | (1<<WGM10); |
|
#warning PPM output type set to POSITIVE PULSE |
|
#endif |
|
RC_TIMER1_PRESCALER_REG = (1<<WGM13)|(1<<WGM12); |
|
RC_TIMER1_COMP1_REG = RC_RESET_PW_TIMER_VAL; |
|
RC_TIMER1_COMP2_REG = RC_PPM_SYNC_PW_VAL; |
|
RC_TIMER1_TIMSK |= (1<<OCIE1B); |
|
RC_TIMER1_TIFR |= ( (1<<OCIE1B)|(1<<TOIE1) ); |
|
|
|
/*Enable timer0 overflow interupt and timer1 compare B interrupt */ |
|
RC_TIMER0_TIMSK |= (1<<TOIE0); |
|
RC_TIMER0_TIFR |= (1<<TOIE0); |
|
|
|
RC_LED_PORT_OUT_REG &= (~(1<<RC_LED_PIN)); |
|
RC_LED_PORT_DDR_REG |= (1<<RC_LED_PIN); |
|
LED_ON(); |
|
|
|
RC_MUX_PORT_OUT_REG &= (~(1<<RC_MUX_PIN)); |
|
RC_MUX_PORT_DDR_REG |= (1<<RC_MUX_PIN); |
|
MUX_ON(); |
|
|
|
// make the setup pin an input and activate the pull up resistor on the setup pin. |
|
RC_SETUP_DDR_REG &= (~(1<<RC_SETUP_PIN)); |
|
RC_SETUP_PORT_OUT_REG |= (1<<RC_SETUP_PIN); |
|
|
|
/* configure the servo pins as inputs and activate their pull up resistors for noise reduction. */ |
|
RC_SERVO_PORT_DDR_REG = 0; |
|
RC_SERVO_PORT_OUT_REG = 0xFF; |
|
|
|
isr_channel_number = RC_PPM_GEN_CHANNELS; |
|
rc_lost_channel = (RC_LOST_CHANNEL-1); |
|
ppm_off_threshold = RC_PPM_OFF_THRESHOLD_VAL; |
|
// VERSION CONTROL |
|
x=0; |
|
eep_address = (E2END - (sizeof(version_info)-1)); |
|
while(version_info[x]) |
|
{ |
|
if( (eep_address) < E2END) |
|
{ |
|
eeprom_write_byte( (unsigned char*)eep_address, version_info[x]); |
|
} |
|
eep_address++; |
|
x++; |
|
} |
|
eeprom_write_byte((unsigned char*)E2END, '\0'); //Terminate the version control string. |
|
|
|
asm("sei"); |
|
|
|
/* give some time for the pull up resistors to work. ~30 ms * 3 = 90 ms */ |
|
LED_OFF(); |
|
RESET_START_TIMER0(); |
|
for(x=0; x<3; x++) |
|
{ |
|
wdt_reset(); |
|
RESET_TIMER0(); |
|
while(timer0.timer0[1] < RC_MAX_TIMEOUT_VAL ); |
|
} |
|
x = 0; |
|
STOP_TIMER0(); |
|
RESET_TIMER0(); |
|
LED_ON(); |
|
/* |
|
The reason i configure the ppm port here is because i want to give some time |
|
to the timer1 compare module to initialize. |
|
The timer1 compare module as the Mega 168 manual states must be initialized before setting the DDR register |
|
of the OCR1X pins. |
|
*/ |
|
//RC_PPM_PORT_OUT_REG &= (~(1<<RC_PPM_PIN)); |
|
RC_PPM_PORT_DDR_REG |= (1<<RC_PPM_PIN); |
|
|
|
return; |
|
} |
|
|
|
/*11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111*/ |
|
/*22222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222*/ |
|
/* |
|
This function waits untill the receiver has been powered up and running so we can then detect |
|
the connected channels with certainty. |
|
*/ |
|
#if defined(RC_RX_READY_CHANNEL) && RC_RX_READY_CHANNEL > 0 |
|
|
|
void wait_for_rx(void) |
|
{ |
|
unsigned int pw=0; |
|
unsigned char x = 0; |
|
unsigned char servo_connected = 0; |
|
|
|
RESET_START_TIMER0(); |
|
servo_connected = 0; |
|
do{ |
|
|
|
wdt_reset(); |
|
RESET_TIMER0(); |
|
x=0; |
|
while(timer0.timer0[1] <= RC_MAX_TIMEOUT_VAL ) |
|
{ |
|
if( (RC_SERVO_PORT_PIN_REG & (1<<(RC_RX_READY_CHANNEL-1))) ) { x=timer0.timer0[1]; } |
|
if( (timer0.timer0[1] - x) >= RC_PULSE_TIMEOUT_VAL ){ servo_connected = 1; break; } |
|
} |
|
|
|
}while(servo_connected < 1); |
|
|
|
//Now test the found channel for a proper servo pulse. |
|
x = 0; |
|
while(1) |
|
{ |
|
pw=get_channel_pw( (RC_RX_READY_CHANNEL-1) ); |
|
if( (pw > RC_SERVO_MIN_PW_VAL) && (pw < RC_SERVO_MAX_PW_VAL) ) { x++; }else{ x=0; } |
|
if(x >= 3) { break; } |
|
} |
|
|
|
return; |
|
} |
|
|
|
#else |
|
|
|
void wait_for_rx(void) |
|
{ |
|
unsigned int pw=0; |
|
unsigned char x = 0; |
|
unsigned char servo_connected = 0; |
|
unsigned char channel = 0; |
|
|
|
RESET_START_TIMER0(); |
|
do{ |
|
for(channel=0; channel < RC_SERVO_INPUT_CHANNELS; channel++) |
|
{ |
|
wdt_reset(); |
|
RESET_TIMER0(); |
|
servo_connected = 0; |
|
x=0; |
|
while(timer0.timer0[1] <= RC_MAX_TIMEOUT_VAL ) |
|
{ |
|
if(RC_SERVO_PORT_PIN_REG & (1<<channel)) { x=timer0.timer0[1]; } |
|
if( (timer0.timer0[1] - x) >= RC_PULSE_TIMEOUT_VAL ){ servo_connected = 1; break; } |
|
} |
|
if(servo_connected >= 1){ break; } |
|
} |
|
|
|
}while(servo_connected < 1); |
|
|
|
//Now test the found channel for a proper servo pulse. |
|
x = 0; |
|
while(1) |
|
{ |
|
pw=get_channel_pw(channel); |
|
if( (pw > RC_SERVO_MIN_PW_VAL) && (pw < RC_SERVO_MAX_PW_VAL) ) { x++; }else{ x=0; } |
|
if(x >= 3) { break; } |
|
} |
|
|
|
return; |
|
} |
|
#endif |
|
|
|
/*22222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222*/ |
|
/*33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333*/ |
|
|
|
unsigned char detect_connected_channels(void) |
|
{ |
|
unsigned char channel=0; |
|
unsigned char servo_connected = 0; |
|
unsigned char connected_channels = 0; |
|
unsigned char x = 0; |
|
unsigned char y = 0; |
|
#if defined(RC_RX_READY_CHANNEL) && RC_RX_READY_CHANNEL > 0 |
|
unsigned int pw = 0; |
|
#endif |
|
|
|
/* |
|
There must be no error in which channels are connected to the encoder |
|
because this will have devastating effects later on. |
|
*/ |
|
wdt_reset(); |
|
RESET_START_TIMER0(); |
|
|
|
for(channel=0; channel < RC_SERVO_INPUT_CHANNELS; channel++) |
|
{ |
|
servo_connected = 0; |
|
for(y=0; y<5; y++) |
|
{ |
|
wdt_reset(); |
|
RESET_TIMER0(); |
|
x=0; |
|
while(timer0.timer0[1] <= RC_MAX_TIMEOUT_VAL ) |
|
{ |
|
if(RC_SERVO_PORT_PIN_REG & (1<<channel)) { x=timer0.timer0[1]; } |
|
if( (timer0.timer0[1] - x) >= RC_PULSE_TIMEOUT_VAL ){ servo_connected++; break; } |
|
} |
|
if(servo_connected >= 3){ channel_mask |= (1<<channel); connected_channels++; break; } |
|
} |
|
} |
|
#if defined(RC_RX_READY_CHANNEL) && RC_RX_READY_CHANNEL > 0 |
|
|
|
connected_channels = 0; |
|
for(channel=0; channel < RC_SERVO_INPUT_CHANNELS; channel++) |
|
{ |
|
if(channel_mask & (1<<channel) ) |
|
{ |
|
pw=get_channel_pw(channel); |
|
if(pw < RC_SERVO_MIN_PW_VAL || pw > RC_SERVO_MAX_PW_VAL) |
|
{ |
|
channel_mask &= (~(1<<channel)); |
|
}else{ connected_channels++; } |
|
} |
|
} |
|
#endif |
|
|
|
return(connected_channels); |
|
} |
|
|
|
/*33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333*/ |
|
/*44444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444*/ |
|
|
|
void write_default_values_to_eeprom(void) |
|
{ |
|
|
|
unsigned char x = 0; |
|
|
|
for(x=0; x<(sizeof(ppm_off_threshold_e)/sizeof(int)); x++) |
|
{ |
|
eeprom_write_word(&ppm_off_threshold_e[x], RC_PPM_OFF_THRESHOLD_VAL); |
|
} |
|
for(x=0; x < (sizeof(rc_lost_channel_e)/sizeof(char)); x++) |
|
{ |
|
eeprom_write_byte(&rc_lost_channel_e[x], (RC_LOST_CHANNEL - 1)); |
|
} |
|
rc_lost_channel = (RC_LOST_CHANNEL - 1); |
|
ppm_off_threshold = RC_PPM_OFF_THRESHOLD_VAL; |
|
|
|
return; |
|
} |
|
|
|
/*44444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444*/ |
|
/*55555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555*/ |
|
|
|
/* |
|
All this code because the Eeprom is prone to data corruption in noisy environments. |
|
11 copies of each variable are read and if more than 50% + 1 values are the same and within limits |
|
this value is taken to be valid. |
|
If not all 11 values are the same then the array is written again with this 50% +1 value. |
|
*/ |
|
void load_values_from_eeprom(void) |
|
{ |
|
unsigned int eeprom_buf_x = 0; |
|
unsigned int eeprom_buf_y = 0; |
|
unsigned char match = 0; |
|
unsigned char match_upper_limit = 0; |
|
unsigned char match_lower_limit = 0; |
|
unsigned char x = 0; |
|
unsigned char y = 0; |
|
|
|
wdt_reset(); |
|
|
|
/****************************************************************************************************/ |
|
/* READ WHICH CHANNEL WILL BE USED AS A RX LOST INDICATOR */ |
|
/****************************************************************************************************/ |
|
match_upper_limit = (sizeof(rc_lost_channel_e)/sizeof(char)); |
|
match_lower_limit = (((sizeof(rc_lost_channel_e)/sizeof(char))/2)+1); |
|
|
|
for(x=0; x<match_upper_limit; x++) |
|
{ |
|
match = 0; |
|
eeprom_buf_x = eeprom_read_byte(&rc_lost_channel_e[x]); |
|
for(y=0; y<match_upper_limit; y++) |
|
{ |
|
eeprom_buf_y = eeprom_read_byte(&rc_lost_channel_e[y]); |
|
if(eeprom_buf_y == eeprom_buf_x){ match++; } |
|
} |
|
// If 50% +1 or more char values in the array are the same a match has been found. |
|
// Now test them to see if they are within limits. |
|
if(match >= match_lower_limit ) |
|
{ |
|
if( eeprom_buf_x < RC_SERVO_INPUT_CHANNELS ) |
|
{ |
|
rc_lost_channel = eeprom_buf_x; //Load the stored value to throttle_thershold. |
|
if(match < match_upper_limit) |
|
{ |
|
for(x=0; x<match_upper_limit; x++) |
|
{ |
|
eeprom_write_byte(&rc_lost_channel_e[x], eeprom_buf_x); |
|
} |
|
} |
|
|
|
}else{ match = 0; } |
|
break; |
|
} |
|
} |
|
|
|
if( match < match_lower_limit ){ write_default_values_to_eeprom(); return; } |
|
|
|
/****************************************************************************************************/ |
|
/* NOW READ THE CHANNEL'S PULSE WIDTH SO IT CAN BE USED AS A TRIGGER */ |
|
/****************************************************************************************************/ |
|
match_upper_limit = (sizeof(ppm_off_threshold_e)/sizeof(int)); |
|
match_lower_limit = (((sizeof(ppm_off_threshold_e)/sizeof(int))/2)+1); |
|
|
|
for(x=0; x < match_upper_limit; x++) |
|
{ |
|
match = 0; |
|
eeprom_buf_x = eeprom_read_word(&ppm_off_threshold_e[x]); |
|
for(y=0; y < match_upper_limit; y++) |
|
{ |
|
eeprom_buf_y = eeprom_read_word(&ppm_off_threshold_e[y]); |
|
if(eeprom_buf_y == eeprom_buf_x){ match++; } |
|
} |
|
// If 50% +1 or more integer values in the array are the same a match has been found. |
|
// Now test them to see if they are within limits. |
|
if(match >= match_lower_limit ) |
|
{ |
|
if( (eeprom_buf_x >= RC_PPM_OFF_UPPER_WINDOW_VAL) && (eeprom_buf_x <= RC_SERVO_MAX_PW_VAL) ) |
|
{ |
|
ppm_off_threshold = eeprom_buf_x; //Load the stored value to throttle_thershold. |
|
if(match < match_upper_limit) |
|
{ |
|
for(x=0; x < match_upper_limit; x++){ eeprom_write_word(&ppm_off_threshold_e[x], eeprom_buf_x); } |
|
} |
|
|
|
}else if( (eeprom_buf_x <= RC_PPM_OFF_LOWER_WINDOW_VAL) && (eeprom_buf_x >= RC_SERVO_MIN_PW_VAL) ) |
|
{ |
|
ppm_off_threshold = eeprom_buf_x; //Load the stored value to throttle_thershold. |
|
if(match < match_upper_limit) |
|
{ |
|
for(x=0; x < match_upper_limit; x++){ eeprom_write_word(&ppm_off_threshold_e[x], eeprom_buf_x); } |
|
} |
|
|
|
}else{ match = 0; } |
|
break; |
|
} |
|
} |
|
|
|
if( match < match_lower_limit ){ write_default_values_to_eeprom(); return; } |
|
|
|
|
|
return; |
|
} |
|
|
|
/*55555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555*/ |
|
/*66666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666*/ |
|
|
|
unsigned int get_channel_pw(unsigned char pin) |
|
{ |
|
|
|
unsigned int pw = 0; |
|
unsigned char pw_measurement_started = 0; |
|
|
|
wdt_reset(); |
|
/* The servo input pins are already configured as inputs with pullup resistors. */ |
|
// First we must disable the pin interrupt. |
|
RC_PIN_INT_EN_REG &= (~(1<<RC_PIN_INT_EN_BIT)); |
|
//Now we must load the pin interrupt mask register. |
|
RC_PIN_INT_MASK_REG = (1<<pin); |
|
//Clear any pin interrupt flag set. |
|
RC_PIN_INT_FLAG_REG |= (1<<RC_PIN_INT_FLAG_BIT); |
|
// Clear the pin interrupt ISR detection variable |
|
pin_interrupt_detected = 0; |
|
// Finally we can enable the pin interrupt again. |
|
RC_PIN_INT_EN_REG |= (1<<RC_PIN_INT_EN_BIT); |
|
// Set and start timer1 |
|
RESET_START_TIMER0(); |
|
|
|
while(1) |
|
{ |
|
/* Wait until the pin change state. */ |
|
do{ |
|
if( timer0.timer0[1] >= RC_MAX_TIMEOUT_VAL ) |
|
{ |
|
return(0); |
|
} |
|
|
|
}while( pin_interrupt_detected == 0 ); |
|
pin_interrupt_detected = 0; |
|
if( RC_SERVO_PORT_PIN_REG & (1<<pin) ) /* if the pin is high then give it a time stamp */ |
|
{ |
|
pw = isr_timer0_16; |
|
pw_measurement_started = 1; /* signal that this channel got it's timer stamp.*/ |
|
|
|
}else{ |
|
// If the pin is low and it already has a time stamp then we are done. |
|
if( pw_measurement_started ) |
|
{ |
|
pw = isr_timer0_16 - pw; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
/*Stop the timer */ |
|
STOP_TIMER0(); |
|
RESET_TIMER0(); |
|
|
|
return((unsigned int)pw); |
|
} |
|
|
|
/*66666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666*/ |
|
/*77777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777*/ |
|
|
|
void check_for_setup_mode(void) |
|
{ |
|
unsigned int pw_buffer = 0; |
|
unsigned int pw = 0; |
|
unsigned char setup_mode = 0; |
|
unsigned char success = 0; |
|
unsigned char x = 0; |
|
unsigned char y = 0; |
|
|
|
|
|
//We have to make sure that the setup mode is requested. |
|
wdt_reset(); |
|
x=0; |
|
y=0; |
|
setup_mode = 1; |
|
RESET_START_TIMER0(); |
|
do{ |
|
if( (RC_SETUP_PIN_REG & (1<<RC_SETUP_PIN)) == 0){ x++; }else{ x=0; } |
|
{ |
|
if(timer0.timer0[1] > RC_MAX_TIMEOUT_VAL ){setup_mode = 0; break; } |
|
} |
|
|
|
}while(x < 100); |
|
|
|
if(setup_mode) |
|
{ |
|
/****************************************************************************************************/ |
|
/* FIRST WE MUST FIND WHICH CHANNEL WILL BE USED AS A TX SIGNAL LOST INDICATOR */ |
|
/****************************************************************************************************/ |
|
wdt_reset(); |
|
success = 0; |
|
if(channels_in_use > 1 ) |
|
{ |
|
if( channel_mask & (1<<(RC_LOST_CHANNEL - 1)) ) |
|
{ |
|
rc_lost_channel = (RC_LOST_CHANNEL - 1); |
|
for(x=0; x < (sizeof(rc_lost_channel_e)/sizeof(char)); x++) |
|
{ |
|
eeprom_write_byte(&rc_lost_channel_e[x], rc_lost_channel); |
|
} |
|
success += 1; |
|
} |
|
|
|
}else if(channels_in_use == 1) |
|
{ |
|
for(x=0; x < RC_SERVO_INPUT_CHANNELS; x++) |
|
{ |
|
if(channel_mask & (1<<x)) |
|
{ |
|
rc_lost_channel = x; |
|
for(x=0; x < (sizeof(rc_lost_channel_e)/sizeof(char)); x++) |
|
{ |
|
eeprom_write_byte(&rc_lost_channel_e[x], rc_lost_channel); |
|
} |
|
success += 1; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
|
|
/****************************************************************************************************/ |
|
/* NOW WE NEED TO FIND THE THRESHOLD PULSE WIDTH THAT WILL BE USED AN A TX SIGNAL LOST TRIGGER */ |
|
/****************************************************************************************************/ |
|
if(success == 1) |
|
{ |
|
wdt_reset(); |
|
y = 0; |
|
pw_buffer = 0; |
|
for(x=0; x < 10; x++) |
|
{ |
|
pw=get_channel_pw(rc_lost_channel); |
|
if(pw >= RC_SERVO_MIN_PW_VAL && pw <= RC_SERVO_MAX_PW_VAL) |
|
{ |
|
pw_buffer += pw; |
|
y++; |
|
} |
|
} |
|
pw_buffer /= y; |
|
wdt_reset(); |
|
if( (pw_buffer >= RC_PPM_OFF_UPPER_WINDOW_VAL) && (pw_buffer <= RC_SERVO_MAX_PW_VAL) ) |
|
{ |
|
for(x=0; x<(sizeof(ppm_off_threshold_e)/sizeof(int)); x++) |
|
{ |
|
eeprom_write_word(&ppm_off_threshold_e[x], (pw_buffer+RC_PPM_OFF_OFFSET_VAL)); |
|
} |
|
success += 1; |
|
|
|
}else if( (pw_buffer <= RC_PPM_OFF_LOWER_WINDOW_VAL) && (pw_buffer >= RC_SERVO_MIN_PW_VAL) ) |
|
{ |
|
for(x=0; x<(sizeof(ppm_off_threshold_e)/sizeof(int)); x++) |
|
{ |
|
eeprom_write_word(&ppm_off_threshold_e[x], (pw_buffer-RC_PPM_OFF_OFFSET_VAL)); |
|
} |
|
success += 1; |
|
} |
|
|
|
} |
|
|
|
/****************************************************************************************************/ |
|
/* LASTLY WE MUST INDICATE TO THE USER IF THE SETUP PROCEDURE WAS SUCCESSFUL */ |
|
/****************************************************************************************************/ |
|
if(success == 2) |
|
{ |
|
RC_SETUP_PORT_OUT_REG &= (~(1<<RC_SETUP_PIN)); |
|
while(1) |
|
{ |
|
LED_ON(); |
|
for(x=0; x<3; x++) |
|
{ |
|
wdt_reset(); |
|
RESET_START_TIMER0(); |
|
/* delay ~30 ms * 3 = 100 milliseconds */ |
|
while(timer0.timer0[1] < RC_MAX_TIMEOUT_VAL ); |
|
} |
|
LED_OFF(); |
|
for(x=0; x<30; x++) |
|
{ |
|
wdt_reset(); |
|
RESET_START_TIMER0(); |
|
/* delay ~30 ms * 30 = 900 milliseconds */ |
|
while(timer0.timer0[1] < RC_MAX_TIMEOUT_VAL ); |
|
} |
|
} |
|
|
|
}else{ |
|
write_default_values_to_eeprom(); |
|
while(1) |
|
{ |
|
LED_ON(); |
|
for(x=0; x<30; x++) |
|
{ |
|
wdt_reset(); |
|
RESET_START_TIMER0(); |
|
/* delay ~30 ms * 30 = 900 milliseconds */ |
|
while(timer0.timer0[1] < RC_MAX_TIMEOUT_VAL ); |
|
} |
|
LED_OFF(); |
|
for(x=0; x<3; x++) |
|
{ |
|
wdt_reset(); |
|
RESET_START_TIMER0(); |
|
/* delay ~30 ms * 3 = 100 milliseconds */ |
|
while(timer0.timer0[1] < RC_MAX_TIMEOUT_VAL ); |
|
} |
|
} |
|
} |
|
|
|
} // End of "if(setup_mode)" statement. |
|
|
|
return; |
|
} |
|
|
|
/*77777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777*/ |
|
/*88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888*/ |
|
|
|
void load_failsafe_values(void) |
|
{ |
|
wdt_reset(); |
|
isr_channel_pw[0] = RC_FS_CH_1_TIMER_VAL; |
|
#if RC_PPM_GEN_CHANNELS >= 2 |
|
isr_channel_pw[1] = RC_FS_CH_2_TIMER_VAL; |
|
#endif |
|
#if RC_PPM_GEN_CHANNELS >= 3 |
|
isr_channel_pw[2] = RC_FS_CH_3_TIMER_VAL; |
|
#endif |
|
#if RC_PPM_GEN_CHANNELS >= 4 |
|
isr_channel_pw[3] = RC_FS_CH_4_TIMER_VAL; |
|
#endif |
|
#if RC_PPM_GEN_CHANNELS >= 5 |
|
isr_channel_pw[4] = RC_FS_CH_5_TIMER_VAL; |
|
#endif |
|
#if RC_PPM_GEN_CHANNELS >= 6 |
|
isr_channel_pw[5] = RC_FS_CH_6_TIMER_VAL; |
|
#endif |
|
#if RC_PPM_GEN_CHANNELS >= 7 |
|
isr_channel_pw[6] = RC_FS_CH_7_TIMER_VAL; |
|
#endif |
|
#if RC_PPM_GEN_CHANNELS >= 8 |
|
isr_channel_pw[7] = RC_FS_CH_8_TIMER_VAL; |
|
#endif |
|
#if RC_PPM_GEN_CHANNELS >= 9 |
|
isr_channel_pw[8] = RC_FS_CH_8_TIMER_VAL; |
|
#endif |
|
isr_channel_pw[RC_PPM_GEN_CHANNELS] = RC_RESET_PW_TIMER_VAL; |
|
|
|
|
|
return; |
|
} |
|
/* |
|
void load_failsafe_values(void) |
|
{ |
|
unsigned char x = 0; |
|
|
|
for(x=0; x< RC_PPM_GEN_CHANNELS; x++) |
|
{ |
|
isr_channel_pw[x] = fs_channel_pw[x]; |
|
} |
|
|
|
// LOAD THE PPM FRAME RESET PULSE WIDTH. |
|
isr_channel_pw[RC_PPM_GEN_CHANNELS] = RC_RESET_PW_TIMER_VAL; |
|
|
|
|
|
return; |
|
|
|
} |
|
*/ |
|
|
|
/*88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888*/ |
|
/*99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999*/ |
|
|
|
|
|
static inline void ppm_on(void) |
|
{ |
|
|
|
RC_TIMER1_PRESCALER_REG &= (~(TIMER1_PRESCALER_BITS)); |
|
TCNT1 = 0; |
|
isr_channel_number = RC_PPM_GEN_CHANNELS; |
|
RC_TIMER1_COMP1_REG = RC_RESET_PW_TIMER_VAL; |
|
RC_TIMER1_COMP2_REG = RC_PPM_SYNC_PW_VAL; |
|
RC_TIMER1_TIFR |= ( (1<<OCIE1B)|(1<<TOIE1) ); |
|
RC_TIMER1_PRESCALER_REG |= TIMER1_PRESCALER_BITS; |
|
|
|
return; |
|
} |
|
|
|
/*99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999*/ |
|
/*10101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010*/ |
|
|
|
static inline void ppm_off(void) |
|
{ |
|
if(RC_TIMER1_PRESCALER_REG & TIMER1_PRESCALER_BITS) |
|
{ |
|
while( TCNT1 <= (RC_PPM_SYNC_PW_VAL+(RC_PPM_SYNC_PW_VAL / 10)) ); |
|
} |
|
RC_TIMER1_PRESCALER_REG &= (~(TIMER1_PRESCALER_BITS)); |
|
RC_TIMER1_TIFR |= ( (1<<OCIE1B)|(1<<TOIE1) ); |
|
|
|
return; |
|
} |
|
|
|
/*11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111*/ |
|
/*12121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212*/ |
|
|
|
|
|
void mux_control(void) |
|
{ |
|
long PULSE_WIDTH = isr_channel_pw[RC_MUX_CHANNEL-1]; |
|
|
|
#if RC_MUX_REVERSE == 0 |
|
|
|
if((PULSE_WIDTH>RC_MUX_MIN)&&(PULSE_WIDTH<RC_MUX_MAX)) |
|
{ |
|
MUX_ON(); |
|
} |
|
else |
|
{ |
|
MUX_OFF(); |
|
} |
|
|
|
#else |
|
|
|
if((PULSE_WIDTH>RC_MUX_MIN)&&(PULSE_WIDTH<RC_MUX_MAX)) |
|
{ |
|
MUX_OFF(); |
|
} |
|
else |
|
{ |
|
MUX_ON(); |
|
} |
|
#endif |
|
} |
|
|
|
|
|
/********************************************************************************************************/ |
|
/* MAIN FUNCTION */ |
|
/********************************************************************************************************/ |
|
__attribute__((noreturn)) void main(void) |
|
{ |
|
|
|
//Variables for the servo channel measuring code. |
|
unsigned short pw_of_channel[RC_SERVO_INPUT_CHANNELS]; |
|
unsigned char channel_mask_buffer = 0; |
|
unsigned char pin_reg_buffer0 = 0; |
|
unsigned char pin_reg_buffer1 = 0; |
|
unsigned char channels_to_check=0; |
|
unsigned char channel_status = 0; |
|
unsigned short timer0_buffer = 0; |
|
unsigned char x = 0; |
|
unsigned char y = 0; |
|
//Variables for the PPM control code. |
|
unsigned char tx_signal_lost = 0; |
|
unsigned char tx_signal_detected = 0; |
|
unsigned char servo_signals_lost = 0; |
|
unsigned char led_frequency = 0; |
|
unsigned char led_counter = 0; |
|
|
|
wdt_disable(); |
|
wdt_enable(WDTO_120MS); |
|
wdt_reset(); |
|
|
|
initialize_mcu(); |
|
/*Load the values stored in the eeprom like the throttle channel threshold etc. */ |
|
load_values_from_eeprom(); |
|
//Load the ISR array. This way if a channel is not connected it will have the failsafe value. |
|
load_failsafe_values(); |
|
/* |
|
The "wait_for_rx(): function waits untill the receiver has been powered up and running |
|
so we can then detect the connected channels with certainty. |
|
*/ |
|
wait_for_rx(); |
|
channels_in_use = detect_connected_channels(); |
|
check_for_setup_mode(); |
|
servo_signals_lost = 1; //Signal that the servo signals have gone missing(no rx signal). |
|
led_frequency = RC_LED_FREQUENCY_VAL_1HZ; //load the defined led frequency. |
|
|
|
/**************************** SETUP THE PIN INTERRUPT *************************************************/ |
|
|
|
// Now we must disable the pin interrupt. |
|
RC_PIN_INT_EN_REG &= (~(1<<RC_PIN_INT_EN_BIT)); |
|
//Now we must load the pin interrupt mask register. |
|
RC_PIN_INT_MASK_REG = channel_mask; |
|
//Clear any pin interrupt flag set. |
|
RC_PIN_INT_FLAG_REG |= (1<<RC_PIN_INT_FLAG_BIT); |
|
// Clear the pin interrupt ISR detection variable |
|
pin_interrupt_detected = 0; |
|
// Finally we can enable the pin interrupt again. |
|
RC_PIN_INT_EN_REG |= (1<<RC_PIN_INT_EN_BIT); |
|
// Set and start timer1 |
|
RESET_START_TIMER0(); |
|
// Take a snapshot of the servo pins in order to establish a starting point. |
|
pin_reg_buffer1 = (RC_SERVO_PORT_PIN_REG & channel_mask); |
|
RESET_START_TIMER0(); |
|
//Main endless loop. |
|
while(1) |
|
{ |
|
wdt_reset(); |
|
channel_mask_buffer = channel_mask; |
|
RESET_TIMER0(); |
|
while(channel_mask_buffer) |
|
{ |
|
/* Wait until a pin change state. */ |
|
do{ |
|
if( timer0.timer0[1] >= RC_MAX_TIMEOUT_VAL ) |
|
{ |
|
goto PPM_CONTROL; |
|
} |
|
|
|
}while( pin_interrupt_detected == 0 ); |
|
x=0; |
|
y=1; |
|
//Only pins that changed their state will be tested for a high or low level |
|
pin_reg_buffer0 = (RC_SERVO_PORT_PIN_REG & channel_mask_buffer); |
|
pin_interrupt_detected = 0; |
|
channels_to_check = pin_reg_buffer1 ^ pin_reg_buffer0; |
|
pin_reg_buffer1 = pin_reg_buffer0; |
|
while(x<RC_SERVO_INPUT_CHANNELS) |
|
{ |
|
if(channels_to_check & y) |
|
{ |
|
if( (pin_reg_buffer0 & y) ) /* if the pin is high then... */ |
|
{ |
|
pw_of_channel[x] = isr_timer0_16; |
|
channel_status |= y; /* signal that this channel got it's timer stamp. */ |
|
|
|
}else{ |
|
if( channel_status & y ) |
|
{ |
|
channel_mask_buffer &= (~y); |
|
timer0_buffer = isr_timer0_16 - pw_of_channel[x]; |
|
if( (timer0_buffer > RC_SERVO_MIN_PW_VAL) && (timer0_buffer < RC_SERVO_MAX_PW_VAL) ) |
|
{ |
|
#if defined(RC_LOST_CHANNEL) && RC_LOST_CHANNEL > 0 |
|
if(x == rc_lost_channel) |
|
{ |
|
if(ppm_off_threshold > RC_SERVO_CENTER_PW_VAL) |
|
{ |
|
if(timer0_buffer >= ppm_off_threshold) |
|
{ |
|
channel_mask_buffer = 0xFF; |
|
goto PPM_CONTROL; |
|
} |
|
|
|
}else{ |
|
if(timer0_buffer <= ppm_off_threshold) |
|
{ |
|
channel_mask_buffer = 0xFF; |
|
goto PPM_CONTROL; |
|
} |
|
} |
|
} |
|
#endif |
|
if(servo_signals_lost == 0) |
|
{ |
|
asm("cli"); //Atomic operation needed here. |
|
isr_channel_pw[x] = timer0_buffer; |
|
asm("sei"); |
|
} |
|
|
|
} // End of " if( (timer0_buffer > RC_SERVO_MIN_PW_VAL) && ..." statement. |
|
} // End of "if( channel_status & y )" statement. |
|
|
|
} // End of "if( (pin_reg_buffer0 & y) )...else..." statement |
|
} // End of "if(channels_to_check & y)" statement. |
|
x++; |
|
y=(y<<1); |
|
} |
|
|
|
} // End of "while(channel_mask_buffer)" loop. |
|
PPM_CONTROL: |
|
|
|
led_counter++; |
|
if( led_counter >= led_frequency ){ led_counter = 0; TOGGLE_LED(); } |
|
|
|
//We need 'RC_MAX_BAD_PPM_FRAMES" consecutive readings in order to change the PPM generator's status. |
|
if( channel_mask_buffer == 0 ) //IF ALL CHANNELS HAVE BEEN MEASURED... |
|
{ |
|
tx_signal_lost = 0; |
|
if(servo_signals_lost == 1) //IF PREVIOUSLY THE SERVO SIGNAL WAS LOST... |
|
{ |
|
tx_signal_detected++; |
|
if(tx_signal_detected > RC_MAX_BAD_PPM_FRAMES) |
|
{ |
|
ppm_on(); |
|
servo_signals_lost = 0; |
|
LED_ON(); |
|
led_counter = 0; |
|
led_frequency = RC_LED_FREQUENCY_VAL; |
|
} |
|
} |
|
} |
|
else{ //IF NOT ALL CHANNELS HAVE BEEN MEASURED... |
|
pin_reg_buffer1 = (RC_SERVO_PORT_PIN_REG & channel_mask); |
|
tx_signal_detected = 0; |
|
if(servo_signals_lost == 0) |
|
{ |
|
tx_signal_lost++; |
|
if(tx_signal_lost > RC_MAX_BAD_PPM_FRAMES) |
|
{ |
|
servo_signals_lost = 1; |
|
led_counter = 0; |
|
led_frequency = RC_LED_FREQUENCY_VAL_1HZ; |
|
#if defined(RC_USE_FAILSAFE) && RC_USE_FAILSAFE == 1 |
|
load_failsafe_values(); |
|
#else |
|
ppm_off(); |
|
#endif |
|
} |
|
} //end of if(servo_signals_lost == 0) statement. |
|
} //end of if( x > (channels_in_use/2) ){...}else{...} statement. |
|
|
|
|
|
mux_control(); |
|
|
|
} //end of while(1) loop. |
|
|
|
} |
|
|
|
/********************************************************************************************************/ |
|
/* INTERRUPT SERVICE ROUTINES */ |
|
/********************************************************************************************************/ |
|
|
|
//ISR(TIMER0_OVF_vect, ISR_NAKED) |
|
ISR(TIMER0_OVF_vect) |
|
{ |
|
timer0.timer0[1]++; |
|
|
|
return; |
|
} |
|
|
|
/********************************************************************************************************/ |
|
/* |
|
By using the fast pwm mode 15 which uses the OCR1A value as TOP value and changing the values within the |
|
OCR1B interrupt, timing is very accurate because the OCR1A and OCR1B registers are double buffered. |
|
This means that although it looks like we modify both registers within the OCR1B interrupt we are actually |
|
modifying their buffers and not the actual registers. |
|
Both actual registers are updated when the timer reaches the OCR1A (TOP) value automatically. |
|
This way the OCR1B interrupt can be delayed as needed without any loss of timing accuracy. |
|
*/ |
|
ISR(TIMER1_COMPB_vect) |
|
{ |
|
asm("sei"); |
|
#if RC_CONSTANT_PPM_FRAME_TIME == 1 |
|
isr_channel_number++; |
|
if( isr_channel_number >= (RC_PPM_GEN_CHANNELS + 1) ) {isr_channel_number = 0; reset_pw = RC_PPM_FRAME_TIMER_VAL; } |
|
|
|
if(isr_channel_number < RC_PPM_GEN_CHANNELS) |
|
{ |
|
RC_TIMER1_COMP1_REG = isr_channel_pw[isr_channel_number]; |
|
reset_pw -= RC_TIMER1_COMP1_REG; |
|
|
|
}else{ |
|
RC_TIMER1_COMP1_REG = reset_pw; |
|
} |
|
|
|
#endif |
|
#if RC_CONSTANT_PPM_FRAME_TIME == 0 |
|
|
|
isr_channel_number++; |
|
if( isr_channel_number >= (RC_PPM_GEN_CHANNELS + 1) ) {isr_channel_number = 0; } |
|
RC_TIMER1_COMP1_REG = isr_channel_pw[isr_channel_number]; |
|
|
|
#endif |
|
|
|
return; |
|
} |
|
|
|
/********************************************************************************************************/ |
|
|
|
ISR(PCINT2_vect) |
|
{ |
|
|
|
timer0.timer0[0]= TCNT0; |
|
if( RC_TIMER0_TIFR & (1<<TOIE0) ){RC_TIMER0_TIFR |= (1<<TOIE0); timer0.timer0[1]++; timer0.timer0[0]= 0; } |
|
isr_timer0_16 = timer0.timer0_16; |
|
pin_interrupt_detected = 1; |
|
|
|
|
|
return; |
|
} |
|
|
|
/********************************************************************************************************/ |
|
/* |
|
ISR(TIMER1_OVF_vect) |
|
{ |
|
|
|
|
|
|
|
|
|
return; |
|
} |
|
*/ |
|
|
|
|
|
|
|
/*######################################################################################################*/ |
|
/* T H E E N D */ |
|
/*######################################################################################################*/ |
|
|
|
|