// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// + Copyright (c) 04.2007 Holger Buss
// + only for non-profit use
// + www.MikroKopter.com
// + see the File "License.txt" for further Informations
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#include <stdarg.h>
#include <string.h>
#include "main.h"
#include "uart.h"
#define FC_ADDRESS 1
#define NC_ADDRESS 2
#define MK3MAG_ADDRESS 3
unsigned char GetExternalControl
= 0,DebugDisplayAnforderung1
= 0, DebugDisplayAnforderung
= 0,DebugDataAnforderung
= 0,GetVersionAnforderung
= 0, GetPPMChannelAnforderung
= 0;
unsigned char DisplayLine
= 0;
unsigned volatile char SioTmp
= 0;
unsigned volatile char SendeBuffer
[MAX_SENDE_BUFF
];
unsigned volatile char RxdBuffer
[MAX_EMPFANGS_BUFF
];
unsigned volatile char NMEABuffer
[MAX_EMPFANGS_BUFF
];
unsigned volatile char NeuerDatensatzEmpfangen
= 0;
unsigned volatile char NeueKoordinateEmpfangen
= 0;
unsigned volatile char UebertragungAbgeschlossen
= 1;
unsigned volatile char CntCrcError
= 0;
unsigned volatile char AnzahlEmpfangsBytes
= 0;
unsigned char *pRxData
= 0;
unsigned char RxDataLen
= 0;
unsigned volatile char PC_DebugTimeout
= 0;
unsigned volatile char PC_MotortestActive
= 0;
unsigned char DebugTextAnforderung
= 255;
unsigned char PcZugriff
= 100;
unsigned char MotorTest
[16];
unsigned char MeineSlaveAdresse
= 1; // Flight-Ctrl
unsigned char ConfirmFrame
;
struct str_DebugOut DebugOut
;
struct str_ExternControl ExternControl
;
struct str_VersionInfo VersionInfo
;
struct str_WinkelOut WinkelOut
;
struct str_Data3D Data3D
;
int Debug_Timer
,Kompass_Timer
,Timer3D
;
unsigned int DebugDataIntervall
= 200, Intervall3D
= 0;
const unsigned char ANALOG_TEXT
[32][16] =
{
//1234567890123456
"AngleNick ", //0
"AngleRoll ",
"AccNick ",
"AccRoll ",
"GyroGier ",
"Hight Value ", //5
"AccZ ",
"Gas ",
"Compass Value ",
"Voltage ",
"Empfang ", //10
"Gyro Kompass ",
"Motor Front ",
"Motor Rear ",
"Motor Left ",
"Motor Right ", //15
" ",
" ",
"VarioMeter ",
"MK3Mag CalState ",
"Servo ", //20
" ",
" ",
" ",
" ",
" ", //25
" ",
"Kalman_MaxDrift ",
" ",
"Navi Serial Data",
"GPS_Nick ", //30
"GPS_Roll "
};
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//++ Sende-Part der Datenübertragung
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SIGNAL
(INT_VEC_TX
)
{
static unsigned int ptr
= 0;
unsigned char tmp_tx
;
if(!UebertragungAbgeschlossen
)
{
ptr
++; // die [0] wurde schon gesendet
tmp_tx
= SendeBuffer
[ptr
];
if((tmp_tx
== '\r') || (ptr
== MAX_SENDE_BUFF
))
{
ptr
= 0;
UebertragungAbgeschlossen
= 1;
}
UDR
= tmp_tx
;
}
else ptr
= 0;
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//++ Empfangs-Part der Datenübertragung, incl. CRC-Auswertung
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SIGNAL
(INT_VEC_RX
)
{
static unsigned int crc
;
static unsigned char crc1
,crc2
,buf_ptr
;
static unsigned char UartState
= 0;
unsigned char CrcOkay
= 0;
SioTmp
= UDR
;
if(buf_ptr
>= MAX_EMPFANGS_BUFF
) UartState
= 0;
if(SioTmp
== '\r' && UartState
== 2)
{
UartState
= 0;
crc
-= RxdBuffer
[buf_ptr
-2];
crc
-= RxdBuffer
[buf_ptr
-1];
crc
%= 4096;
crc1
= '=' + crc
/ 64;
crc2
= '=' + crc
% 64;
CrcOkay
= 0;
if((crc1
== RxdBuffer
[buf_ptr
-2]) && (crc2
== RxdBuffer
[buf_ptr
-1])) CrcOkay
= 1; else { CrcOkay
= 0; CntCrcError
++;};
if(!NeuerDatensatzEmpfangen
&& CrcOkay
) // Datensatz schon verarbeitet
{
NeuerDatensatzEmpfangen
= 1;
AnzahlEmpfangsBytes
= buf_ptr
+ 1;
RxdBuffer
[buf_ptr
] = '\r';
if(RxdBuffer
[2] == 'R')
{
wdt_enable
(WDTO_250MS
); // Reset-Commando
ServoActive
= 0;
}
}
}
else
switch(UartState
)
{
case 0:
if(SioTmp
== '#' && !NeuerDatensatzEmpfangen
) UartState
= 1; // Startzeichen und Daten schon verarbeitet
buf_ptr
= 0;
RxdBuffer
[buf_ptr
++] = SioTmp
;
crc
= SioTmp
;
break;
case 1: // Adresse auswerten
UartState
++;
RxdBuffer
[buf_ptr
++] = SioTmp
;
crc
+= SioTmp
;
break;
case 2: // Eingangsdaten sammeln
RxdBuffer
[buf_ptr
] = SioTmp
;
if(buf_ptr
< MAX_EMPFANGS_BUFF
) buf_ptr
++;
else UartState
= 0;
crc
+= SioTmp
;
break;
default:
UartState
= 0;
break;
}
}
// --------------------------------------------------------------------------
void AddCRC
(unsigned int wieviele
)
{
unsigned int tmpCRC
= 0,i
;
for(i
= 0; i
< wieviele
;i
++)
{
tmpCRC
+= SendeBuffer
[i
];
}
tmpCRC
%= 4096;
SendeBuffer
[i
++] = '=' + tmpCRC
/ 64;
SendeBuffer
[i
++] = '=' + tmpCRC
% 64;
SendeBuffer
[i
++] = '\r';
UebertragungAbgeschlossen
= 0;
UDR
= SendeBuffer
[0];
}
// --------------------------------------------------------------------------
void SendOutData
(unsigned char cmd
,unsigned char address
, unsigned char BufferAnzahl
, ...
) //unsigned char *snd, unsigned char len)
{
va_list ap
;
unsigned int pt
= 0;
unsigned char a
,b
,c
;
unsigned char ptr
= 0;
unsigned char *snd
= 0;
int len
= 0;
SendeBuffer
[pt
++] = '#'; // Startzeichen
SendeBuffer
[pt
++] = 'a' + address
; // Adresse (a=0; b=1,...)
SendeBuffer
[pt
++] = cmd
; // Commando
va_start(ap
, BufferAnzahl
);
if(BufferAnzahl
)
{
snd
= va_arg(ap
, unsigned char*);
len
= va_arg(ap
, int);
ptr
= 0;
BufferAnzahl
--;
}
while(len
)
{
if(len
)
{
a
= snd
[ptr
++];
len
--;
if((!len
) && BufferAnzahl
)
{
snd
= va_arg(ap
, unsigned char*);
len
= va_arg(ap
, int);
ptr
= 0;
BufferAnzahl
--;
}
}
else a
= 0;
if(len
)
{
b
= snd
[ptr
++];
len
--;
if((!len
) && BufferAnzahl
)
{
snd
= va_arg(ap
, unsigned char*);
len
= va_arg(ap
, int);
ptr
= 0;
BufferAnzahl
--;
}
}
else b
= 0;
if(len
)
{
c
= snd
[ptr
++];
len
--;
if((!len
) && BufferAnzahl
)
{
snd
= va_arg(ap
, unsigned char*);
len
= va_arg(ap
, int);
ptr
= 0;
BufferAnzahl
--;
}
}
else c
= 0;
SendeBuffer
[pt
++] = '=' + (a
>> 2);
SendeBuffer
[pt
++] = '=' + (((a
& 0x03) << 4) | ((b
& 0xf0) >> 4));
SendeBuffer
[pt
++] = '=' + (((b
& 0x0f) << 2) | ((c
& 0xc0) >> 6));
SendeBuffer
[pt
++] = '=' + ( c
& 0x3f);
}
va_end(ap
);
AddCRC
(pt
);
}
// --------------------------------------------------------------------------
void Decode64
(void) // die daten werden im rx buffer dekodiert, das geht nur, weil aus 4 byte immer 3 gemacht werden.
{
unsigned char a
,b
,c
,d
;
unsigned char x
,y
,z
;
unsigned char ptrIn
= 3; // start at begin of data block
unsigned char ptrOut
= 3;
unsigned char len
= AnzahlEmpfangsBytes
- 6; // von der Gesamtbytezahl eines Frames gehen 3 Bytes des Headers ('#',Addr, Cmd) und 3 Bytes des Footers (CRC1, CRC2, '\r') ab.
while(len
)
{
a
= RxdBuffer
[ptrIn
++] - '=';
b
= RxdBuffer
[ptrIn
++] - '=';
c
= RxdBuffer
[ptrIn
++] - '=';
d
= RxdBuffer
[ptrIn
++] - '=';
x
= (a
<< 2) | (b
>> 4);
y
= ((b
& 0x0f) << 4) | (c
>> 2);
z
= ((c
& 0x03) << 6) | d
;
if(len
--) RxdBuffer
[ptrOut
++] = x
; else break;
if(len
--) RxdBuffer
[ptrOut
++] = y
; else break;
if(len
--) RxdBuffer
[ptrOut
++] = z
; else break;
}
pRxData
= (unsigned char*)&RxdBuffer
[3]; // decodierte Daten beginnen beim 4. Byte
RxDataLen
= ptrOut
- 3; // wie viele Bytes wurden dekodiert?
}
// --------------------------------------------------------------------------
void BearbeiteRxDaten
(void)
{
if(!NeuerDatensatzEmpfangen
) return;
unsigned char tempchar1
, tempchar2
;
Decode64
(); // dekodiere datenblock im Empfangsbuffer
switch(RxdBuffer
[1]-'a') // check for Slave Address
{
case FC_ADDRESS
: // FC special commands
switch(RxdBuffer
[2])
{
case 'K':// Kompasswert
memcpy((unsigned char *)&KompassValue
, (unsigned char *)pRxData
, sizeof(KompassValue
));
KompassRichtung
= ((540 + KompassValue
- KompassStartwert
) % 360) - 180;
break;
case 't':// Motortest
if(AnzahlEmpfangsBytes
> 20) memcpy(&MotorTest
[0], (unsigned char *)pRxData
, sizeof(MotorTest
));
else memcpy(&MotorTest
[0], (unsigned char *)pRxData
, 4);
PC_MotortestActive
= 240;
//while(!UebertragungAbgeschlossen);
//SendOutData('T', MeineSlaveAdresse, 0);
PcZugriff
= 255;
break;
case 'n':// "Get Mixer
while(!UebertragungAbgeschlossen
);
SendOutData
('N', FC_ADDRESS
, 1, (unsigned char *) &Mixer
,sizeof(Mixer
));
break;
case 'm':// "Write Mixer
while(!UebertragungAbgeschlossen
);
if(pRxData
[0] == MIXER_REVISION
)
{
memcpy(&Mixer
, (unsigned char *)pRxData
, sizeof(Mixer
));
eeprom_write_block
(&Mixer
, &EEPromArray
[EEPROM_ADR_MIXER_TABLE
], sizeof(Mixer
));
tempchar1
= 1;
}
else tempchar1
= 0;
SendOutData
('M', FC_ADDRESS
, 1, &tempchar1
, sizeof(tempchar1
));
break;
case 'p': // get PPM Channels
GetPPMChannelAnforderung
= 1;
break;
case 'q':// "Get"-Anforderung für Settings
// Bei Get werden die vom PC einstellbaren Werte vom PC zurückgelesen
if(pRxData
[0] == 0xFF)
{
pRxData
[0] = GetActiveParamSetNumber
();
}
// limit settings range
if(pRxData
[0] < 1) pRxData
[0] = 1; // limit to 5
else if(pRxData
[0] > 5) pRxData
[0] = 5; // limit to 5
// load requested parameter set
ReadParameterSet
(pRxData
[0], (unsigned char *) &EE_Parameter.
Kanalbelegung[0], STRUCT_PARAM_LAENGE
);
while(!UebertragungAbgeschlossen
);
tempchar1
= pRxData
[0];
tempchar2
= EE_DATENREVISION
;
SendOutData
('Q', FC_ADDRESS
, 3, &tempchar1
, sizeof(tempchar1
), &tempchar2
, sizeof(tempchar2
), (unsigned char *) &EE_Parameter.
Kanalbelegung[0], STRUCT_PARAM_LAENGE
);
break;
case 's': // Parametersatz speichern
if((1 <= pRxData
[0]) && (pRxData
[0] <= 5) && (pRxData
[1] == EE_DATENREVISION
)) // check for setting to be in range
{
memcpy((unsigned char *) &EE_Parameter.
Kanalbelegung[0], (unsigned char *)&pRxData
[2], STRUCT_PARAM_LAENGE
);
WriteParameterSet
(pRxData
[0], (unsigned char *) &EE_Parameter.
Kanalbelegung[0], STRUCT_PARAM_LAENGE
);
Umschlag180Nick
= (long) EE_Parameter.
WinkelUmschlagNick * 2500L;
Umschlag180Roll
= (long) EE_Parameter.
WinkelUmschlagRoll * 2500L;
SetActiveParamSetNumber
(pRxData
[0]);
tempchar1
= GetActiveParamSetNumber
();
Piep
(tempchar1
,110);
}
else
{
tempchar1
= 0; // mark in response an invlid setting
}
while(!UebertragungAbgeschlossen
);
SendOutData
('S', FC_ADDRESS
, 1, &tempchar1
, sizeof(tempchar1
));
break;
} // case FC_ADDRESS:
default: // any Slave Address
switch(RxdBuffer
[2])
{
// 't' comand placed here only for compatibility to BL
case 't':// Motortest
if(AnzahlEmpfangsBytes
> 20) memcpy(&MotorTest
[0], (unsigned char *)pRxData
, sizeof(MotorTest
));
else memcpy(&MotorTest
[0], (unsigned char *)pRxData
, 4);
while(!UebertragungAbgeschlossen
);
SendOutData
('T', MeineSlaveAdresse
, 0);
PC_MotortestActive
= 250;
PcZugriff
= 255;
break;
// 'K' comand placed here only for compatibility to old MK3MAG software, that does not send the right Slave Address
case 'K':// Kompasswert
memcpy((unsigned char *)&KompassValue
, (unsigned char *)pRxData
, sizeof(KompassValue
));
KompassRichtung
= ((540 + KompassValue
- KompassStartwert
) % 360) - 180;
break;
case 'a':// Texte der Analogwerte
DebugTextAnforderung
= pRxData
[0];
if (DebugTextAnforderung
> 31) DebugTextAnforderung
= 31;
PcZugriff
= 255;
break;
case 'b':
memcpy((unsigned char *)&ExternControl
, (unsigned char *)pRxData
, sizeof(ExternControl
));
ConfirmFrame
= ExternControl.
Frame;
PcZugriff
= 255;
break;
case 'c': // Poll the 3D-Data
if(!Intervall3D
) { if(pRxData
[0]) Timer3D
= SetDelay
(pRxData
[0] * 10);}
Intervall3D
= pRxData
[0] * 10;
break;
case 'd': // Poll the debug data
DebugDataIntervall
= pRxData
[0] * 10;
if(DebugDataIntervall
> 0) DebugDataAnforderung
= 1;
break;
case 'h':// x-1 Displayzeilen
PcZugriff
= 255;
RemoteKeys
|= pRxData
[0];
if(RemoteKeys
) DisplayLine
= 0;
DebugDisplayAnforderung
= 1;
break;
case 'l':// x-1 Displayzeilen
PcZugriff
= 255;
MenuePunkt
= pRxData
[0];
DebugDisplayAnforderung1
= 1;
break;
case 'v': // Version-Anforderung und Ausbaustufe
GetVersionAnforderung
= 1;
break;
case 'g'://
GetExternalControl
= 1;
break;
}
break; // default:
}
NeuerDatensatzEmpfangen
= 0;
pRxData
= 0;
RxDataLen
= 0;
}
//############################################################################
//Routine für die Serielle Ausgabe
int uart_putchar
(char c
)
//############################################################################
{
if (c
== '\n')
uart_putchar
('\r');
//Warten solange bis Zeichen gesendet wurde
loop_until_bit_is_set
(USR
, UDRE
);
//Ausgabe des Zeichens
UDR
= c
;
return (0);
}
// --------------------------------------------------------------------------
void WriteProgramData
(unsigned int pos
, unsigned char wert
)
{
//if (ProgramLocation == IN_RAM) Buffer[pos] = wert;
// else eeprom_write_byte(&EE_Buffer[pos], wert);
// Buffer[pos] = wert;
}
//############################################################################
//INstallation der Seriellen Schnittstelle
void UART_Init
(void)
//############################################################################
{
//Enable TXEN im Register UCR TX-Data Enable & RX Enable
UCR
=(1 << TXEN
) | (1 << RXEN
);
// UART Double Speed (U2X)
USR
|= (1<<U2X
);
// RX-Interrupt Freigabe
UCSRB
|= (1<<RXCIE
);
// TX-Interrupt Freigabe
UCSRB
|= (1<<TXCIE
);
//Teiler wird gesetzt
UBRR
=(SYSCLK
/ (BAUD_RATE
* 8L) - 1);
//UBRR = 33;
//öffnet einen Kanal für printf (STDOUT)
//fdevopen (uart_putchar, 0);
//sbi(PORTD,4);
Debug_Timer
= SetDelay
(DebugDataIntervall
);
Kompass_Timer
= SetDelay
(220);
VersionInfo.
SWMajor = VERSION_MAJOR
;
VersionInfo.
SWMinor = VERSION_MINOR
;
VersionInfo.
SWPatch = VERSION_PATCH
;
VersionInfo.
ProtoMajor = VERSION_SERIAL_MAJOR
;
VersionInfo.
ProtoMinor = VERSION_SERIAL_MINOR
;
pRxData
= 0;
RxDataLen
= 0;
}
//---------------------------------------------------------------------------------------------
void DatenUebertragung
(void)
{
if(!UebertragungAbgeschlossen
) return;
if(DebugDisplayAnforderung
&& UebertragungAbgeschlossen
)
{
Menu
();
SendOutData
('H', FC_ADDRESS
, 2, &DisplayLine
, sizeof(DisplayLine
), &DisplayBuff
[DisplayLine
* 20], 20);
DisplayLine
++;
if(DisplayLine
>= 4) DisplayLine
= 0;
DebugDisplayAnforderung
= 0;
}
if(DebugDisplayAnforderung1
&& UebertragungAbgeschlossen
)
{
Menu
();
SendOutData
('L', FC_ADDRESS
, 3, &MenuePunkt
, sizeof(MenuePunkt
), &MaxMenue
, sizeof(MaxMenue
), DisplayBuff
, sizeof(DisplayBuff
));
DebugDisplayAnforderung1
= 0;
}
if(GetVersionAnforderung
&& UebertragungAbgeschlossen
)
{
SendOutData
('V', FC_ADDRESS
, 1, (unsigned char *) &VersionInfo
, sizeof(VersionInfo
));
GetVersionAnforderung
= 0;
}
if(GetExternalControl
&& UebertragungAbgeschlossen
) // Bei Get werden die vom PC einstellbaren Werte vom PC zurückgelesen
{
SendOutData
('G',MeineSlaveAdresse
, 1, (unsigned char *) &ExternControl
, sizeof(ExternControl
));
GetExternalControl
= 0;
}
if((CheckDelay
(Kompass_Timer
)) && UebertragungAbgeschlossen
)
{
WinkelOut.
Winkel[0] = (int) (IntegralNick
/ (EE_Parameter.
GyroAccFaktor * 4)); // etwa in 0.1 Grad
WinkelOut.
Winkel[1] = (int) (IntegralRoll
/ (EE_Parameter.
GyroAccFaktor * 4)); // etwa in 0.1 Grad
WinkelOut.
UserParameter[0] = Parameter_UserParam1
;
WinkelOut.
UserParameter[1] = Parameter_UserParam2
;
SendOutData
('w', MK3MAG_ADDRESS
, 1, (unsigned char *) &WinkelOut
,sizeof(WinkelOut
));
if(WinkelOut.
CalcState > 4) WinkelOut.
CalcState = 6; // wird dann in SPI auf Null gesetzt
Kompass_Timer
= SetDelay
(99);
}
if(((DebugDataIntervall
>0 && CheckDelay
(Debug_Timer
)) || DebugDataAnforderung
) && UebertragungAbgeschlossen
)
{
SendOutData
('D', FC_ADDRESS
, 1, (unsigned char *) &DebugOut
,sizeof(DebugOut
));
DebugDataAnforderung
= 0;
if(DebugDataIntervall
>0) Debug_Timer
= SetDelay
(DebugDataIntervall
);
}
if(Intervall3D
> 0 && CheckDelay
(Timer3D
) && UebertragungAbgeschlossen
)
{
Data3D.
Winkel[0] = (int) (IntegralNick
/ (EE_Parameter.
GyroAccFaktor * 4)); // etwa in 0.1 Grad
Data3D.
Winkel[1] = (int) (IntegralRoll
/ (EE_Parameter.
GyroAccFaktor * 4)); // etwa in 0.1 Grad
Data3D.
Winkel[2] = (int) ((10 * ErsatzKompass
) / GIER_GRAD_FAKTOR
);
SendOutData
('C', FC_ADDRESS
, 1, (unsigned char *) &Data3D
,sizeof(Data3D
));
Timer3D
= SetDelay
(Intervall3D
);
}
if(DebugTextAnforderung
!= 255) // Texte für die Analogdaten
{
SendOutData
('A', FC_ADDRESS
, 2, (unsigned char *)&DebugTextAnforderung
, sizeof(DebugTextAnforderung
),(unsigned char *) ANALOG_TEXT
[DebugTextAnforderung
], 16);
DebugTextAnforderung
= 255;
}
if(ConfirmFrame
&& UebertragungAbgeschlossen
) // Datensatz bestätigen
{
SendOutData
('B', FC_ADDRESS
, 1, (uint8_t*)&ConfirmFrame
, sizeof(ConfirmFrame
));
ConfirmFrame
= 0;
}
if(GetPPMChannelAnforderung
&& UebertragungAbgeschlossen
)
{
SendOutData
('P', FC_ADDRESS
, 1, (unsigned char *) &PPM_in
, sizeof(PPM_in
));
GetPPMChannelAnforderung
= 0;
}
}