Subversion Repositories Projects

Rev

Blame | Last modification | View Log | RSS feed

 
/****************************************************************/
/*                                                                                                                                                                                                                                                      */
/*                                                                       NG-Video 5,8GHz                                                                                                                */
/*                                                                                                                                                                                                                                                      */
/*                                              Copyright (C) 2011 - gebad                                                                                              */
/*                                                                                                                                                                                                                                                      */
/*      This code is distributed under the GNU Public License                           */
/*      which can be found at http://www.gnu.org/licenses/gpl.txt               */
/*                                                                                                                                                                                                                                                      */
/****************************************************************/

#include <avr/io.h>
#include <stdlib.h>
#include <string.h>
#include <avr/interrupt.h>
#include <avr/eeprom.h>
#include <util/delay.h>

#include "config.h"
#include "dogm.h"
#include "messages.h"
#include "ngvideo.h"
#include "menue.h"
#include "servo.h"
#include "tools.h"
#include "keys.h"
#include "usart.h"
#include "tracking.h"

// LCD selbst definierte Sonderzeichen, RSSI-Balken und wi232 Empfang Daten im Flash
// deshalb in dogm.c lcdPutc(pgm_read_byte(&lcdChr[i]));
SpecialChr_t lcdSpecialChr[7] PROGMEM = {{32,32,16,16,16,16,32,32},
                                                                                                                                                                        {32,32,24,24,24,24,32,32},
                                                                                                                                                                        {32,32,28,28,28,28,32,32},
                                                                                                                                                                        {32,32,30,30,30,30,32,32},
                                                                                                                                                                        {32,32,31,31,31,31,32,32},
                                                                                                                                                                        {6,8,20,19,20,8,6,32},
                                                                                                                                                                        {4,10,32,14,4,4,14,32}};                // Antenne und Imax

SpecialChr_t lcdSpecialChrLs[8] PROGMEM =       {{32,1,1,1,1,1,1,32},
                                                                                                                                                                                {32,31,1,1,1,1,31,32},
                                                                                                                                                                                {32,31,3,3,3,3,31,32},
                                                                                                                                                                                {32,31,7,7,7,7,31,32},
                                                                                                                                                                                {32,31,15,15,15,15,31,32},
                                                                                                                                                                                {32,31,31,31,31,31,31,32},
                                                                                                                                                                                {32,16,16,16,16,16,16,32},
                                                                                                                                                                                {32,31,32,32,32,32,31,32}};

SpecialChr_t lcdSpecialChrRs[5] PROGMEM =       {{32,1,1,1,1,1,1,32},
                                                                                                                                                                                {32,31,16,16,16,16,31,32},
                                                                                                                                                                                {32,31,24,24,24,24,31,32},
                                                                                                                                                                                {32,31,28,28,28,28,31,32},
                                                                                                                                                                                {32,31,30,30,30,30,31,32}};

/**************************************************************/
/*                                                                                                                                                                                                                                              */
/*                                                                                                      EEPROM                                                                                                          */
/*                                                                                                                                                                                                                                              */
/**************************************************************/
uint8_t                         ep_eep_init                                                                     EEPROM;
char                                    ep_version[sizeof(VERSION)]             EEPROM;
uint8_t                         ep_dogm_vers                                                                    EEPROM;
uint8_t                         ep_contrast                                                                     EEPROM;
uint8_t                         ep_light_time                                                                   EEPROM;
uint8_t                         ep_u_offset                                                                     EEPROM;
uint16_t                        ep_u_min                                                                                        EEPROM;
uint8_t                         ep_channel                                                                              EEPROM;
uint8_t                         ep_av_source                                                                    EEPROM;
uint8_t                         ep_language                                                                             EEPROM;
udbm_t                          ep_udbm[CHANNEL_MAX]                                    EEPROM;
uint8_t                         ep_sIdxSteps                                                                    EEPROM;
servo_t                         ep_servo[SERVO_NUM_CHANNELS]    EEPROM;
uint8_t                         ep_servo_frame                                                          EEPROM;
uint8_t                         ep_servo_nr                                                                             EEPROM;
uint8_t                         ep_single_step                                                          EEPROM;
uint8_t                         ep_repeat                                                                                       EEPROM;
uint8_t                         ep_pause                                                                                        EEPROM;
uint8_t                         ep_pause_step                                                                   EEPROM;
uint8_t                         ep_tracking                                                                             EEPROM;
uint8_t                         ep_track_hyst                                                                   EEPROM;
uint8_t                         ep_track_tx                                                                             EEPROM;
uint8_t                         ep_baudrate                                                                             EEPROM;
mk_current_t    ep_mk_current                                                                   EEPROM;
uint8_t                         ep_mk_akku_nr                                                                   EEPROM;
mk_lipo_t               ep_mk_lipo[AKKU_NR_MAX+1]                       EEPROM;
uint8_t                         ep_mk_i_offset                                                          EEPROM;
uint8_t                         ep_mk_i_faktor                                                          EEPROM;
uint8_t                         ep_mk_w_faktor                                                          EEPROM;

/**************************************************************/
// Working variables of EEPROM contents
uint8_t                         dogm_vers;
uint8_t                         contrast;
uint8_t                         light_time = BACKGR_LIGHT_MAX;
uint8_t                         backgr_light = OFF;
uint8_t                         u_offset = U_OFFSET;
uint16_t                        u_min = U_MIN;
uint8_t                         channel = CHANNEL;
uint8_t                         av_source = AV_SOURCE;
uint8_t                         language = NO_LANGUAGE;
udbm_t                          udbm = {UDBM_MIN, UDBM_MAX, UDBM_KORR_FA, UDBM_KORR_FA};
uint16_t                        hyst_u_min;
servo_t                         servo[SERVO_NUM_CHANNELS] =     {{SERVO_REV, SERVO_I0_RIGHT, SERVO_I0_LEFT, SERVO_I0_MIDDLE},\
                                                                                                                                                                         {SERVO_REV, SERVO_I0_RIGHT, SERVO_I0_LEFT, SERVO_I0_MIDDLE}};
uint8_t                         servo_frame = SERVO_PERIODE;
uint8_t                         single_step = SINGLE_STEP;
uint8_t                         repeat = REPEAT;
uint8_t                         pause = PAUSE;
uint8_t                         pause_step = PAUSE_STEP;
uint8_t                         baudrate = BAUDRATE;

/**************************************************************/
uint8_t                         coldstart;                                      // Flag erstmaliger MK-Start(Motore) nur nach GPS-Fix
uint8_t                         ch_stored;                                      // Flag zur Speicherung, wenn Wert im Hauptdisplay geändert

uint8_t                         gps_display = 0;
uint8_t                         gps_disp_clear;

uint16_t                        bar_udbm[12];                           // RSSI-Bargraph
uint8_t                         wudbm;                                                  // Differenz/Abstand benachbarter udbm[i] für differenzierteren Bargraphen
uint8_t                         bat_low = 0;
uint8_t                         sw_avx;                                                 // steuert über Set_AV_Source() oder rssi_diversity(av_source) Marker-Position und RX-Umschaltung
uint8_t                         mux_X;                                                  // berücksichtigt Schaltung auf Grund Synchronisation und RSSI (sw_avx meist RSSI) RX-Zählung
uint8_t                         MK_Motor_run = 0;               // Flag für RX Time-Counter Start/Stop
uint8_t                         mk_timer = 0;                           // Zeitzähler lipo.time_on auf aus

const uint16_t PROGMEM          baud[8] = {4800, 7200, 9600, 14400, 19200, 38400, 57600};

volatile uint8_t        vsync0;
volatile uint8_t        vsync1;
volatile uint8_t        vscount0;
volatile uint8_t        vscount1;
volatile uint32_t rxcount0 = 0;
volatile uint32_t rxcount1 = 0;


/************************************************************************************/
/*      initialisiert den EEPROM mit default Werten, bzw. liest EEPROM gespeicherte                     */
/*      Werte in gloabale Variablen.                                                                                                                                                                                                            */
/*      Parameter:                                                                                                                                                                                                                                                                                      */
/*      uint8_t ep_reset         :0 = zwangsweises Rückstetzen auf default-Werte                                                        */
/*                                                                                                                                                                                                                                                                                                                                      */
/************************************************************************************/
void Init_EEPROM(uint8_t ep_reset)
{ char ver[sizeof(VERSION)];
        uint8_t eep_init;
       
        eep_init = eeprom_read_byte(&ep_eep_init);
        eeprom_read_block(&ver, &ep_version, sizeof(VERSION));
        _delay_ms(1);

        if ((eep_init != EEP_INITB) || (ep_reset == 0) || strcmp(VERSION, ver))
        {
                 // nur bei Erstinitialisierung DOGM auf default 3,3V setzen
                if ((eep_init != EEP_INITB) || strcmp(VERSION, ver)){
                        eeprom_write_byte(&ep_eep_init, EEP_INITB);
                        eeprom_write_byte(&ep_dogm_vers, DOGM3V);
                        eeprom_write_byte(&ep_contrast, CONTRAST3V);
                        eeprom_write_block(&VERSION, &ep_version, sizeof(VERSION));
                }
                eeprom_write_byte(&ep_light_time, BACKGR_LIGHT_MAX);
                eeprom_write_byte(&ep_u_offset, U_OFFSET);
                eeprom_write_word(&ep_u_min, U_MIN);
                eeprom_write_byte(&ep_channel, CHANNEL);
                eeprom_write_byte(&ep_av_source, AV_SOURCE);
                eeprom_write_byte(&ep_language, NO_LANGUAGE);
                for (uint8_t i = 0; i < CHANNEL_MAX; i++)
                        eeprom_write_block(&udbm,&ep_udbm[i],sizeof(udbm_t));
                eeprom_write_byte(&ep_sIdxSteps, STEPS_255);
                eeprom_write_block(&servo[0],&ep_servo[0],sizeof(servo_t));
                eeprom_write_block(&servo[1],&ep_servo[1],sizeof(servo_t));
                eeprom_write_byte(&ep_servo_frame, SERVO_PERIODE);
                eeprom_write_byte(&ep_servo_nr, 0);                                                                     // nur bei Test-Servo
                eeprom_write_byte(&ep_single_step, SINGLE_STEP);                // nur bei Test-Servo
                eeprom_write_byte(&ep_repeat, REPEAT);                                                  // nur bei Test-Servo
                eeprom_write_byte(&ep_pause, PAUSE);                                                            // nur bei Test-Servo
                eeprom_write_byte(&ep_pause_step, PAUSE_STEP);                  // nur bei Test-Servo
                eeprom_write_byte(&ep_tracking, TRACKING_MIN);
                eeprom_write_byte(&ep_track_hyst, TRACKING_HYSTERESE);
                eeprom_write_byte(&ep_track_tx, 0);
                eeprom_write_byte(&ep_baudrate, BAUDRATE);
                eeprom_write_block(&mk_current,&ep_mk_current,sizeof(mk_current_t));
                eeprom_write_byte(&ep_mk_akku_nr, AKKU_NR_MIN);
                for (uint8_t i = 0; i <= AKKU_NR_MAX; i++)
                        eeprom_write_block(&mk_lipo,&ep_mk_lipo[i],sizeof(mk_lipo_t));
                eeprom_write_byte(&ep_mk_i_offset, MK_I_OFFSET);
                eeprom_write_byte(&ep_mk_i_faktor, MK_I_FAKTOR);
                eeprom_write_byte(&ep_mk_w_faktor, MK_W_FAKTOR);
                sIdxSteps = STEPS_255;
        }
        else
        {
                light_time = eeprom_read_byte(&ep_light_time);
                u_offset = eeprom_read_byte(&ep_u_offset);
                u_min = eeprom_read_word(&ep_u_min);
                channel = eeprom_read_byte(&ep_channel);
                av_source = eeprom_read_byte(&ep_av_source);
                language = eeprom_read_byte(&ep_language);
                sIdxSteps = eeprom_read_byte(&ep_sIdxSteps);
                eeprom_read_block(&servo[0],&ep_servo[0],sizeof(servo_t));
                eeprom_read_block(&servo[1],&ep_servo[1],sizeof(servo_t));
                servo_frame = eeprom_read_byte(&ep_servo_frame);                // nur bei Test-Servo
                single_step = eeprom_read_byte(&ep_single_step);                // nur bei Test-Servo
                repeat = eeprom_read_byte(&ep_repeat);                                                  // nur bei Test-Servo
                pause = eeprom_read_byte(&ep_pause);                                                            // nur bei Test-Servo
                pause_step = eeprom_read_byte(&ep_pause_step);                  // nur bei Test-Servo
                tracking = eeprom_read_byte(&ep_tracking);
                track_hyst = eeprom_read_byte(&ep_track_hyst);
                track_tx = eeprom_read_byte(&ep_track_tx);
                baudrate = eeprom_read_byte(&ep_baudrate);
                eeprom_read_block(&mk_current,&ep_mk_current,sizeof(mk_current_t));
                mk_akku_nr = eeprom_read_byte(&ep_mk_akku_nr);
                eeprom_read_block(&mk_lipo,&ep_mk_lipo[mk_akku_nr],sizeof(mk_lipo_t));
                mk_i_offset = eeprom_read_byte(&ep_mk_i_offset);
                mk_i_faktor = eeprom_read_byte(&ep_mk_i_faktor);
                mk_w_faktor = eeprom_read_byte(&ep_mk_w_faktor);
        }
        dogm_vers = eeprom_read_byte(&ep_dogm_vers);
        contrast = eeprom_read_byte(&ep_contrast);
        hyst_u_min = u_min;
        sw_avx = av_source;
        for (uint8_t i = 0; i < SERVO_NUM_CHANNELS; i++) {
                servoSet_rev(i, servo[i].rev);
                servoSet_min(i, servo[i].min);
                servoSet_max(i, servo[i].max);
                servoSet_mid(i, servo[i].mid);
        }
        // Vorberechnung von ServoChannels[channel].duty
        servoSetDefaultPos();           // Ausgangsstellung beider Servos
        coldstart = 1;
        USART_Init_Baudrate();
        USART_RX_Mode(tracking);
        mk_dUsedCapacity = mk_lipo.UsedCapacity;
}

void servoSetDefaultPos(void)
{
        servoSetPosition(SERVO_PAN, ServoSteps()/2);            // Ausgangsstellung SERVO_PAN
        servoSetPosition(SERVO_TILT, 0);                                                                // Ausgangsstellung SERVO_TILT
}

void USART_Init_Baudrate(void)
{
        if (tracking == TRACKING_MKCOCKPIT || tracking == TRACKING_NMEA)
                USART_Init(getBaud(baudrate));
        else
                USART_Init(57600);
}

/************************************************************************************/
/*      setzt Flag für 3,3V oder 5V DOGM                                                                                                                                                                                                */
/*      Parameter:                                                                                                                                                                                                                                                                                      */
/*      uint8_t dogm            :Version                                                                                                                                                                                                                                */
/*                                                                                                                                                                                                                                                                                                                                      */
/************************************************************************************/
void Set_DOGM_Version(void)
{
        if(dogm_vers == DOGM5V) {
                dogm_vers = DOGM3V;
                contrast = CONTRAST3V;
        }
        else {
                dogm_vers = DOGM5V;
                contrast = CONTRAST5V;
        }
        eeprom_write_byte(&ep_dogm_vers, dogm_vers);
        eeprom_write_byte(&ep_contrast, contrast);
}

/************************************************************************************/
/*      setzt den RX-Kanal von 1 bis 7                                                                                                                                                                                                  */
/*      Parameter:                                                                                                                                                                                                                                                                                      */
/*      uint8_t channel         :Kanal                                                                                                                                                                                                                          */
/*                                                                                                                                                                                                                                                                                                                                      */
/************************************************************************************/
void Set_Channel(uint8_t channel)
{ uint8_t tmp;

        channel--;
        tmp = channel & 0b00000111;                             // Kanal 1 bis 7 Werte 0 bis 6 setzen
        PORTA |= tmp;
        PORTB |= tmp;
        tmp = channel | 0b11111000;
        PORTA &= tmp;
        PORTB &= tmp;
        wudbm = RSSI_Calc_UdBm(bar_udbm); // Vergleichstabelle für dBm-Balken berechnen
}

/************************************************************************************/
/*      schaltet den MUX auf AV1 oder AV2 ohne Darstellung und en-/disabled Interrupt           */
/*      wird nur in main.c (Initialisierung) und Menü Sourceumschaltung eingesetzt                      */
/*      deswegen cli() und sei() nur in Menu_AV_Source(void)                                                                                                            */
/*      Parameter:                                                                                                                                                                                                                                                                                      */
/*      uint8_t src             :0-AV1, 1-AV2                                                                                                                                                                                                                   */
/*                                                                                                                                                                                                                                                                                                                                      */
/************************************************************************************/
void SetMux0(void) {
        SET_MUX_0;
        mux_X = 0;              // für Erkennung RX Zeitzähler
}

void SetMux1(void) {
        SET_MUX_1;
        mux_X = 1;              // für Erkennung RX Zeitzähler
}

uint8_t Set_AV_Source(uint8_t src)
{
        switch(src) {
                case AV1:                       CLEAR_INT10;            // Interrupt für Sync ausschalten
                                                                                SetMux0();
                                                                                break;
                case AV2:                       CLEAR_INT10;            // Interrupt für Sync ausschalten
                                                                                SetMux1();
                                                                                break;
                case DIVERSITY: SET_INT10;                      // External Interrupt Mask Register ein
                                                                                SetMux0();
                                                                                break;
        }
        return(src);
}


/**************************************************************/
/*                                                                                                                                                                                                                                              */
/*                                                                               LCD-Backlight                                                                                                  */
/*                                                                                                                                                                                                                                              */
/**************************************************************/

void lcdSet_BackgrLight_Off(void)
{
        backgr_light = OFF;
        lcdBacklightOff();
}

void lcd_BackgrLight_On(void)
{ // ...&& (light_count < light_time)) ==> sonst wird Beleuchtung laufend wieder eingeschaltet
        if ((backgr_light == OFF)  && (light_count < light_time)) {
                backgr_light = ON;
                lcdBacklightOn();
        }
}

void lcd_BackgrLight(void)
{
        if (backgr_light == ON) {                                                                               // nur wenn Beleuchtung an
                if (light_time == BACKGR_LIGHT_MIN)                             // Hintergrundbeleuchtung immer aus?
                        lcdSet_BackgrLight_Off();
                else
                        if (light_time < BACKGR_LIGHT_MAX) {            // Hintergrundbeleuchtung immer an?
                                cli();
                                light_count++;
                                sei();
                                if (light_count >= light_time) lcdSet_BackgrLight_Off();
                        }
        }
}

/**************************************************************/
/*                                                                                                                                                                                                                                              */
/*                                                                                                       ADC                                                                                                                    */
/*                                                                                                                                                                                                                                              */
/*      http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial        */
/*                                                                                                                                                                                                                                              */
/**************************************************************/

void ADC_Init(void)
{
        uint16_t result;
 
        ADMUX = (0<<REFS1) | (1<<REFS0);                                // AVcc als Referenz benutzen, da an AREF 4,8 V
        ADCSRA = (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);          // Frequenzvorteiler Prescaler 128
        ADCSRA |= (1<<ADEN);                                                                            // ADC aktivieren
 
        /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
                 also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */

 
        ADCSRA |= (1<<ADSC);                                                                            // eine ADC-Wandlung
        while (ADCSRA & (1<<ADSC) ) {}                                  // auf Abschluss der Konvertierung warten
        /* ADCW muss einmal gelesen werden, sonst wird Ergebnis der nächsten
                 Wandlung nicht übernommen. */

        result = ADCW;
}

/* ADC Einzelmessung */
uint16_t ADC_Read( uint8_t channel )
{
        // Kanal waehlen, ohne andere Bits zu beeinflußen
        ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
        ADCSRA |= (1<<ADSC);                                                                            // eine Wandlung "single conversion"
        while (ADCSRA & (1<<ADSC) ) {}                                  // auf Abschluss der Konvertierung warten
        return ADCW;                                                                                                            // ADC auslesen und zurückgeben
}

/* ADC Mehrfachmessung mit Mittelwertbbildung */
adc_avg_t ADC_Read_Avg(uint8_t channel0, uint8_t channel1, uint16_t average )
{ adc_avg_t result;
        uint32_t u0 = 0;
        uint32_t u1 = 0;

        for (uint16_t i = 0; i < average; ++i){
                u0 += ADC_Read( channel0 );
                u1 += ADC_Read( channel1 );
                _delay_ms(1);
        }
        result.u0 = u0/average;
        result.u1 = u1/average;
        return(result);
}

/**************************************************************/
/*                                                                                                                                                                                                                                              */
/*                                                                                               Beeper                                                                                                                 */
/*                                                                                                                                                                                                                                              */
/*      Bei AVR-Studio 5 ist in delay.h                                                                                                         */
/*      #define __DELAY_BACKWARD_COMPATIBLE__                                                                                   */
/*      zu setzen                                                                                                                                                                                                       */
/*                                                                                                                                                                                                                                              */
/**************************************************************/

void Beep(uint8_t time)
{
        PORTB |= (1<<BEEPER);
        _delay_ms(time);
        PORTB &= ~(1<<BEEPER);
}

void Double_Beep(uint8_t time, uint8_t pause)
{
        Beep(time);
        _delay_ms(pause);
        Beep(time);
}

/**************************************************************/
/*                                                                                                                                                                                                                                              */
/*                                                                                              U-Batterie                                                                                                      */
/*                                                                                                                                                                                                                                              */
/**************************************************************/

// uint16_t u, da bei Displ_Fnct[fu_index](val) der größte Wert UBat gleich 16 Bit
void Displ_1Nk(uint16_t u)
{
        // 4 => 2 Ziffern vor Komma + 1 Komma + eine Ziffer Nachkomma darstellen, 2 Festkomma
        lcdPuts(my_itoa(u,4,2,1));
}

void Displ_U_2Nk(uint16_t u)
{
        lcdPuts(my_itoa(u,5,2,2));
        lcdPutc('V');
}

// uint8_t beep_timer :Akku-leer-Beeper nur mit Task_0_1()-Intervalle bei Menü-Rücksprung
uint16_t U_Messen_cmp(uint8_t beep_timer)
{ uint16_t ubat;
        static struct
        { uint8_t time;
                uint8_t count;
        } beep_low;

/* ubat = ((ADC_Read(VBAT) * Vref * (R104 + R103)) /(1024 * R103)) + UD10 (UD10 ist Offset)
         Verhältniswert * 100 *8192 ( Verhältniswert = realer Korrekturwert;
         mal 100 da alle Werte 2 Nachkommastellen berücksichtigt, aber ohne gerechnet wird
         mal 8192 => ohne Bruch gerechnet aber dabei mehr Kommastellen berücksichtigt) */

        ubat = (ADC_Read(VBAT) * (uint64_t)43504 + (uint64_t)u_offset * 8192)/ 8192;
        if ( ubat <= hyst_u_min )
        {
                if (!bat_low) {                                                 // nicht laufend Display neu schreiben
                        hyst_u_min = u_min + 20;        // 200mV Hysterese - beruhigt Anzeige
                        if (tracking == TRACKING_GPS)
                                store_LipoData();                               // wenigstens von GPS-Statisik UsedCapacity, time_on usw. speichern
                        lcdClear();
                        lcdPuts(Msg(MSG_ACCU_LOW));
                        bat_low = 1;
                        Beep(BEEPBAT);
                        // da derzeit Fkt. aller 500ms aufgerufen, mit 2 Min Abstand beginnen
                        beep_low.time = BEEP_LOW_TIME;
                        beep_low.count = 0;
                }
                // Akku leer, in immer kürzeren Intervallen Beep
                if ((beep_timer == 1) && (beep_low.count++ >= beep_low.time)){
                        Beep(BEEPBAT);
                        if (beep_low.time > 2)
                                beep_low.time /= 2;
                        beep_low.count = 0;
                }
        }
        else {
                if (hyst_u_min > u_min) {               // falls Anzeige von Batterie leer
                        bat_low = 0;                                                    // und zurück geschaltet wird,
                        hyst_u_min = u_min;                             // dann Main_Disp wieder darstellen
                        Displ_Main_Disp();
                }
        }
        return(ubat);
}

void Displ_VBat(void)                                           // da u_offset globale Variable
{ uint16_t ubat;
 
        ubat = U_Messen_cmp(ENABLE_BTIMER);
        if (!bat_low) {                                                         // würde sonst Anzeige Akku leer überschreiben
                lcdGotoXY(11, 0);
                Displ_1Nk(ubat);
        }
}


/**************************************************************/
/*                                                                                                                                                                                                                                              */
/*                                                                                                      RSSI                                                                                                                    */
/*                                                                                                                                                                                                                                              */
/**************************************************************/

/* RSSI Werte Korrekturfaktor berechnen */
uint16_t        RSSI_Calc_Korr(uint8_t nchannel, uint16_t u0, uint16_t u1)
{ uint16_t u_max;

        // immer nur den kleineren Wert vergrößern
        if (u0 < u1) {
                udbm.korr_1 = (((uint32_t)u1 * UDBM_KORR_FA) / u0);             // nur mit Integer und 2 Nachkommastellen rechnen
                udbm.korr_2 = UDBM_KORR_FA;
                u_max = u1;
        }
        else {
                udbm.korr_2 = (((uint32_t)u0 * UDBM_KORR_FA) / u1);             // nur mit Integer und 2 Nachkommastellen rechnen
                udbm.korr_1 = UDBM_KORR_FA;
                u_max = u0;
        }
        eeprom_write_word(&ep_udbm[nchannel - 1].korr_1, udbm.korr_1);
        eeprom_write_word(&ep_udbm[nchannel - 1].korr_2, udbm.korr_2);
        return(u_max);
}

void Displ_Calibr_Aktiv(uint8_t nchannel)
{ char str[LCD_COLS + 1];
        uint8_t l;
        uint8_t zle = 1;

        // Anzeige für nur einen Kanal oder wenn in Schleife, Kanalnr. des z.Z. kalbrierenden Kanals
        lcdClear();
        lcdPutStrMid(Msg(MSG_CALIBRATION),0);
        if (nchannel > 0) {                                     // Anzeige aller RX-Kanäle min. kalibrieren?
                strcpy(str, Msg(MSG_RX_CHANNEL));
                strcat(str, ": ");
                l = strlen(str);
                str[l] = nchannel + 0x30;               // gerade zu kalibrierender Kanal, String zusammen stellen
                str[++l] = '\0';
                lcdPutStrMid(str,1);
                zle = 2;
        }
        lcdPutStrMid(Msg(MSG_RUNNING),zle);
}

void Displ_Error_TX(uint8_t message)
{
        lcdClear();
        lcdPutStrMid(Msg(MSG_ERROR), 0);
        lcdPutStrMid(Msg(MSG_TX_NOT), 1);
        lcdPutStrMid(Msg(message), 2);
        delay_ms100x(30);
}

uint8_t RSSI_Min_Calibrate(uint8_t nchannel, uint16_t *pbar_udbm)
{ adc_avg_t rssi_avg;
        uint16_t udbm_min;
        uint8_t one_channel = !nchannel;

        Displ_Calibr_Aktiv(nchannel);
        if (one_channel) nchannel = channel;
        rssi_avg = ADC_Read_Avg(RSSI0, RSSI1, 1000 ); //1000 Wiederholungen mit Mittelwertbildung
        // Plausibilitätsprüfung ob Sender ausgeschaltet
        if (rssi_avg.u0 + rssi_avg.u1 > 500) {
                udbm_min = RSSI_Calc_Korr(nchannel, rssi_avg.u0, rssi_avg.u1); // ist real die größere Spannung, aber der kleinere dbm Wert
                eeprom_write_word(&ep_udbm[nchannel - 1].min, udbm_min);
                if (one_channel) {
                        Double_Beep(DBEEPWR, DBEEPWRP);
                        wudbm = RSSI_Calc_UdBm(pbar_udbm);
                }
        }
        else
                if (one_channel)
                        Displ_Error_TX(MSG_TX_OFF);
                else
                        return(0);              // Fehleranzeige wird in menue.c gesammelt ausgewertet
        return(1);                              // kein Fehler, da bei einen Kanal bereits Fehler angezeigt wurde
}

void RSSI_Max_Calibrate(uint16_t *pbar_udbm)
{ adc_avg_t rssi_avg;
        uint16_t udbm_max;

        Displ_Calibr_Aktiv(0);
        rssi_avg = ADC_Read_Avg(RSSI0, RSSI1, 1000 ); //1000 Wiederholungen mit Mittelwertbildung
        // Plausibilitätsprüfung ob Sender in der Nähe eingeschaltet
        if (rssi_avg.u0 + rssi_avg.u1 < 400) {
                udbm_max = RSSI_Calc_Korr(channel, rssi_avg.u0, rssi_avg.u1); // ist real die kleinere Spannung, aber der größere dbm Wert
                eeprom_write_word(&ep_udbm[channel - 1].max, udbm_max);
                Double_Beep(DBEEPWR, DBEEPWRP);
                wudbm = RSSI_Calc_UdBm(pbar_udbm);
        }
        else Displ_Error_TX(MSG_TX_ON);
}

// Vergleichstabelle für RSSI-Bargraph berechnen, vermeidet laufend gleiche Berechnung
uint8_t RSSI_Calc_UdBm(uint16_t *pbar_udbm)
{ uint8_t n;

        eeprom_read_block(&udbm,&ep_udbm[channel - 1],sizeof(udbm_t));
        // -15 um Ende dBm Skala sicher zu erreichen; ohne verfeinerten Bahrgraph war Wert -3
        n = (udbm.min - udbm.max -15)/11;
        for (uint8_t i = 0; i < 12; i++)
                pbar_udbm[i] = (udbm.min - i * n);
        return(n / 5); // da 5 Pixel Breite pro Display-Zeichen; Anzeigebalken pro Pixel differenzieren
}

void Displ_RSSI_Bargraph(uint16_t *pbar_udbm, uint8_t wudbm, uint16_t uadc)
{ char charBar[12];
        uint8_t i;
        int8_t lz = 11;
        char b = 4;
       
        // Balken zeichnen - udbm
        for (i = 0; i < 12; i++) {
                if ((b != ' ') && (uadc > pbar_udbm[i])) {
                        b       = ' ';
                        lz = i - 1;
                }
                charBar[i] = b;
        }
        if (lz >= 0) {
                charBar[lz] = (pbar_udbm[lz] - uadc) / wudbm ;// Anzeigebalken pro Pixel-"Breite" differenzieren
                // bei Teilung 4 wäre richtig und keine Korr. erforderlich, so aber gleichmäßigerer Balkenverlauf
                if (charBar[lz] > 4) charBar[lz] = 4;
        }
        for (i = 0; i < 12; i++)// lcdPuts (ist auch for) funktioniert hier nicht, da Char'\0' für Zeichen auch Stringende
                lcdPutc(charBar[i]);
}

uint8_t RSSI_Diversity(uint8_t src, uint16_t *pbar_udbm, uint8_t visible)
{ uint16_t u0, u1;
        static uint8_t div_flag, ret_div_flag;
        char marker;

        u0 = (ADC_Read(RSSI0) * (uint32_t)udbm.korr_1)/UDBM_KORR_FA;
        u1 = (ADC_Read(RSSI1) * (uint32_t)udbm.korr_2)/UDBM_KORR_FA;
       
        // falls beide RX gleich gut/schlecht synchronisieren
        // Achtung! Niedrigere Spannung - größere Feldstärke
        if (src == DIVERSITY) {
                if (u0 < u1) {
                        ret_div_flag = AV1;
                        if ((vscount0 == 255) && (vscount1 == 255)) SetMux0();          // egal wann RSSI schaltet ==> es ist kein sync vorhanden
                }
                else {
                        ret_div_flag = AV2;
                        if ((vscount0 == 255) && (vscount1 == 255)) SetMux1();          // egal wann RSSI schaltet ==> es ist kein sync vorhanden
                }
        }
        else ret_div_flag = src; // sonst leerer Returnwert
               
        if (visible) {
                if (src == DIVERSITY) {
                        // Synchronisation vorrangig zur Feldstärke
                        if ((vsync0 != vsync1) && ((vscount0 & vscount1) < 255)) {
                                // ist nur zur Anzeige - Sync-MUX wird über Interrupt gesteuert
                                div_flag = vsync0 == 0? AV1: AV2;
                                marker = MARKER_SYNC;
                        }
                        else {
                                div_flag = ret_div_flag;
                                marker = MARKER_RSSI;
                        }
                }
                else marker = MARKER_AV;
                // wäre unschön - keine RSSI-Anzeige, aber Marker springt
                if ((u0 > pbar_udbm[0]) && (u1 > pbar_udbm[0])) marker = ' ';
                lcdGotoXY(2, 1);
                Displ_RSSI_Bargraph(pbar_udbm, wudbm, u0);
                lcdGotoXY(2, 2);
                Displ_RSSI_Bargraph(pbar_udbm, wudbm, u1);
                if (src == DIVERSITY) Displ_AV_Mark(div_flag, marker);
        }
        return(ret_div_flag);
}

/**************************************************************/
/*                                                                                                                                                                                                                                              */
/*                      Diversity v-Synchronisation Interruptroutinen                                   */
/*                                                                                                                                                                                                                                              */
/**************************************************************/

/* Impulszähler für V-Synchronisation 0 und 1
         wird durch Interrupt des jewiligen vSync einzeln zurückgesetzt. 8-bit Timer*/

ISR(TIMER2_OVF_vect)
{
        TCNT2 = (int8_t)(int16_t)-(F_CPU / 64 * 500e-6);        // preload
        if (vscount0 < 255) ++vscount0; // Überlauf von vscount vermeiden
        if (vscount1 < 255) ++vscount1; // Überlauf von vscount vermeiden
        if (rx_timeout < RX_TIME_END) ++rx_timeout; // veranlasst bei GPS-Tracking MK Datensatz senden
        if ((mk_timer) && (mk_lipo.time_on < T2PROD_M59S59)) ++mk_lipo.time_on; // T2PRODM59S59 = 3599 * 4000
        if (((tracking == TRACKING_GPS) && (MK_Motor_run)) || ((tracking == TRACKING_NMEA) && !coldstart)) { // MK-Motoren müssen laufen
                if (mux_X)
                        rxcount1++;             // kein Test auf Überlauf ==> Counter groß genug - bis Stunden
                else
                        rxcount0++;
        }
}

/* Messung von Impulsabstand v-Synchronisation 0
         Zur Vermeidung von Bildstörunen erfolgt MUX-Umschaltung in Bildaustastung */

ISR(INT0_vect)
{
        if ((vscount0 >= 79) && (vscount0 <= 81)) {
                vsync0 = 0;
                if ((mux_X) && (vscount1 == 255)) {
                  SetMux0();
                        vsync1 = 1;
                }
        }
        else {
                vsync0 = 1;
                if (vsync1 == 0)
                        SetMux1();
        }
        if (vsync0 == vsync1) { //nur wenn vSynchronisation gleich gut/schlecht ist greift RSSI
                if (sw_avx == AV1) {
                        SetMux0();
                }
                else
                        SetMux1();
        }
        vscount0 = 0;
}

/* Messung von Impulsabstand v-Synchronisation 1
         Zur Vermeidung von Bildstörunen erfolgt MUX-Umschaltung in Bildaustastung */

ISR(INT1_vect)
{
        if ((vscount1 >= 79) && (vscount1 <= 81)) {
                vsync1 = 0;
                if (!(mux_X) && (vscount0 == 255)) {
                                SetMux1();
                                vsync0 = 1;
                }
        }
        else {
                vsync1 = 1;
                if (vsync0 == 0)
                        SetMux0();
        }
        if (vsync0 == vsync1) { //nur wenn vSynchronisation gleich gut/schlecht ist greift RSSI
                if (sw_avx == AV1) {
                        SetMux0();
                }
                else
                        SetMux1();
        }
        vscount1 = 0;
}

/**************************************************************/
/*                                                                                                                                                                                                                                              */
/*                                                                                                      Tasks                                                                                                                   */
/*              ermöglicht unterschiedliche Zeiten f. UBat, Sync...                     */
/*                                                                                                                                                                                                                                              */
/**************************************************************/

void Task_0_1(void)
{
        if (task_timer0_1) {
                cli();
                task_timer0_1 = 0;
                sei();
                Displ_VBat();
         }
 }

void Task_0_2(void)
{
        if (task_timer0_2) {
                cli();
                task_timer0_2 = 0;
                sei();
                sw_avx = RSSI_Diversity(av_source, bar_udbm, !bat_low);
        }
}

void Task_0_3(void)
{
        if (task_timer0_3) {
                cli();
                task_timer0_3 = 0;
                sei();
                sw_avx = RSSI_Diversity(av_source, bar_udbm, 0);
                if (tracking == TRACKING_MKCOCKPIT) Tracking_MKCockpit();
        }
}

void Task_0_4(void)
{
        if (task_timer0_4) {
                cli();
                task_timer0_4 = 0;
                sei();
                if (tracking == TRACKING_GPS)
                        Tracking_GPS();
                else if (tracking == TRACKING_NMEA)
                        Tracking_NMEA();
                if (gps_display == GPS_RX_COUNT) Displ_RX_Time(); // aktualisieren der Empfängerzeiten
        }
}

void Task_0_5(void)                                     // Nur für Tasten-Beschleunigung/-Wiederholrate! Hintergrund: Rücksetzung.
{                                                                                                               // Hintergrund: Rücksetzung. Beginnt nach jeden Tastendruck neu zu zählen.
        lcd_BackgrLight_On();                   // muss bei beliebiger Taste sofort eingeschaltet werden
        if (task_timer0_5) {
                cli();
                task_timer0_5 = 0;
                sei();
                lcd_BackgrLight();
        }
}

void Tasks_invisible(void)      // wird in main.c und menue.c aufgerufen
{
        Task_0_3();
        Task_0_4();
        Task_0_5();
        if (tracking == TRACKING_RSSI) Tracking_RSSI();
}