Subversion Repositories Projects

Compare Revisions

Ignore whitespace Rev 1197 → Rev 1198

/NGVideo5_8/tags/v1.21/ngvideo.c
0,0 → 1,825
/****************************************************************/
/* */
/* 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 <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 "tracking.c"
 
// LCD selbst definierte Sonderzeichen, RSSI-Balken und wi232 Empfang Daten im Flash
// deshalb in dogm.c lcdPutc(pgm_read_byte(&lcdChr[i]));
static SpecialChr7_t lcdSpecialChr 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
 
static SpecialChr8_t lcdSpecialChrLs 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}};
 
static SpecialChr5_t lcdSpecialChrRs 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}};
 
/************************************************************************************/
/* 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_dword(&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(&current,&ep_current,sizeof(current_t));
eeprom_write_byte(&ep_akku_nr, AKKU_NR_MIN);
for (uint8_t i = 0; i < AKKU_NR_MAX; i++)
eeprom_write_block(&lipo,&ep_lipo[i],sizeof(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_dword(&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(&current,&ep_current,sizeof(current_t));
akku_nr = eeprom_read_byte(&ep_akku_nr);
eeprom_read_block(&lipo,&ep_lipo[akku_nr],sizeof(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);
}
 
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)
USART_Init(baud[baudrate]);
else
USART_Init(baud[6]); //57600
}
 
/************************************************************************************/
/* Zeitanzeige */
/* */
/************************************************************************************/
 
uint32_t TimeBase60(char *str, uint32_t time, uint8_t idx_a)
{ uint32_t tmp;
tmp = time % 60;
time /= 60;
for (int8_t i = idx_a; i >= (idx_a - 1); i--) {
str[i] = (tmp % 10) + '0';
tmp /= 10;
}
return(time);
}
 
void Displ_TimeMS(int32_t time)
{ char str[7];
 
str[6] = '\0';
if (time < 0) {
time = abs(time);
str[0] = '-';
}
else
str[0] = ' ';
time = TimeBase60(str, time, 5);
TimeBase60(str, time, 2);
str[3] = ':';
lcdPuts(str);
}
 
/************************************************************************************/
/* 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(pbar_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_time > BACKGR_LIGHT_MIN) && (light_count < light_time)) {
cli();
light_count = 0;
sei();
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 */
/* */
/**************************************************************/
 
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 */
/* */
/**************************************************************/
 
/* Funktion zur Umwandlung einer vorzeichenbehafteten Integer
32-Bit "Festkomma-Zahl"(gedachtes Komma in Integer) in einen String
vereinfacht Variablenübergabe funktion change_value(uint32_t x),
kein printf, double oder float
siehe http://www.mikrocontroller.net/articles/Festkommaarithmetik
Vorz ==> 0 kein '-' trotz negativer Zahl, 1 wenn kein '-' bleibt Stelle leer
len max 11, Vorzeichen wird nicht mitgezählt aber Komma; Vorz 0 oder 1
Festkomma ==> Position der Kommastelle bei Integerwert
Nachkomma ==> angezeigte Nachkommastellen bei Festkomma
makefile derzeit somit auch ohne! Minimalistic printf version
Achtung: keine Fehlerprüfung! */
char* my_itoa(int32_t zahl, uint8_t Vorz, uint8_t len, uint8_t Festkomma, uint8_t Nachkomma)
{
int8_t i;
uint8_t neg = 0;
char Komma; // Sprachversion Deutsch ',' andere '.'
static char str[13];
 
Komma = Msg(MSG_KOMMA)[0]; // [0] nur als Char, sonst wäre es ein String
if( zahl < 0 ) { // ist die Zahl negativ?
zahl = -zahl;
neg = 1;
}
if (Vorz) str[0] = '0'; else len--;
str[len+1]='\0'; // String Terminator
if (Nachkomma > 0) Nachkomma++;
 
for(i=len; i>=Vorz; i--) {
if ((len - Festkomma == i) && (Festkomma > 0))
str[i]= Komma; // bei Bedarf Komma einfügen
else { // Integer-Dezimalumrechnung
str[i]=(zahl % 10) +'0'; // Modulo rechnen, dann den ASCII-Code von '0' addieren
zahl /= 10;
}
// festgelegt Festkomma aber verkleinern der Anzeige Mantisse
if ((Festkomma > 0) && (Festkomma >= Nachkomma) && (len - Festkomma + Nachkomma == i)) str[i]= '\0';
}
i=0;
while ((str[i+1] != Komma) && (i < len)) {
// Vorzeichen unmittelbar vor Zahl schreiben
if((Vorz) && (neg) && ((str[i+1] != '0') || (str[i+2] == Komma))) str[i] = '-';
// eine 0 vor Komma lassen
if(str[i] == '0') str[i++] = ' '; else break; // Vornullen entfernen
}
return(str);
}
 
// uint32_t u, da bei Displ_Fnct[fu_index](val) der größte Wert UBat gleich 32 Bit
void Displ_1Nk(uint32_t u)
{
// 0 kein Vorzeichen, 5 => 2 Ziffern vor Komma + 1 Komma + 2 Mantisse, Festkomma, eine Ziffer Nachkomma darstellen
lcdPuts(my_itoa(u,0,5,2,1));
}
 
void Displ_U_2Nk(uint32_t u)
{
lcdPuts(my_itoa(u,0,5,2,2));
lcdPutc('V');
}
 
// uint8_t beep_timer :Akku-leer-Beeper nur mit Task_0_1()-Intervalle bei Menü-Rücksprung
uint32_t U_Messen_cmp(uint8_t beep_timer)
{ uint32_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 != 0) { // 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 = 0;
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 = 1; // 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
{ uint32_t ubat;
ubat = U_Messen_cmp(ENABLE_BTIMER);
if (bat_low != 0) { // 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();
Displ_Str_mid(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';
Displ_Str_mid(str,1);
zle = 2;
}
Displ_Str_mid(Msg(MSG_RUNNING),zle);
}
 
void delay_ms100x(uint8_t delay)
{
for ( uint8_t i=0; i<delay; i++)
_delay_ms(100);
}
 
void Displ_Error_TX(uint8_t message)
{
lcdClear();
Displ_Str_mid(Msg(MSG_ERROR), 0);
Displ_Str_mid(Msg(MSG_TX_NOT), 1);
Displ_Str_mid(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
if (vsync0 == 0) {
div_flag = AV1;
}
else {
div_flag = 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) && (lipo.time_on < T2PROD_M59S59)) ++lipo.time_on; // T2PRODM59S59 = 3599 * 4000
if ((tracking == TRACKING_GPS) && (MK_Motor_run)) { // 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;
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;
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, pbar_udbm, bat_low);
}
}
 
void Task_0_3(void)
{
if (task_timer0_3) {
cli();
task_timer0_3 = 0;
sei();
sw_avx = RSSI_Diversity(av_source, pbar_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();
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_unvisible(void)
{
Task_0_3();
Task_0_4();
Task_0_5();
if (tracking == TRACKING_RSSI) Tracking_RSSI();
}