You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

720 lines
21 KiB
C

/** @file functions.c
* @brief Diverse Funktionen
* @author Tom, DL7BJ
*/
#include "functions.h"
// EEProm Speicher
uint8_t Dummy;
uint8_t ee_Dummy EEMEM = 0x55; // Dummy for Address 0
uint8_t ee_Trx1 EEMEM = 0; // TRX 1
uint8_t ee_Trx2 EEMEM = 0; // TRX 2
uint8_t ee_KeyerMode EEMEM = 1; // Iambic A, Iambic B oder Ultimatic
uint8_t ee_SidetoneEnabled EEMEM = 1; // Mithörton eingeschaltet
uint8_t ee_WpMBpM EEMEM = 0; // WpM oder BpM Anzeige
uint8_t ee_Reverse EEMEM = 0; // linkes/rechtes Paddle vertauschen
uint8_t ee_SpeedRatioEnabled EEMEM = 0; // Ratio von der Geschwindigkeit abhängig
uint8_t ee_Ratio EEMEM = 30; // Punkt/Strich Verhältnis 1:3
uint8_t ee_Weight EEMEM = 50; // Punkt/Strich Gewichtung
uint8_t ee_Memory EEMEM = 0; // Punkt/Strich Speicher
uint16_t ee_SidetoneFreq EEMEM = 600; // Frequenz des Mithörtons
uint8_t ee_WpM EEMEM = 12; // Geschwindigkeit WpM
uint8_t ee_RiseTime EEMEM = 5; // Anstiegszeit Sinuston
uint8_t ee_RiseTimeCounter EEMEM = 5; // Anzahl Sinusschwingungen für den Anstieg
uint8_t ee_DebounceTime EEMEM = 6; // Entprellzeit für Straight Key Eingang
// Textspeicher
uint8_t ee_Msg1[100] EEMEM;
uint8_t ee_Msg2[100] EEMEM;
uint8_t ee_Msg3[100] EEMEM;
uint8_t ee_Msg4[100] EEMEM;
uint8_t ee_Msg5[100] EEMEM;
//
//
/** @fn void DelayMilliSeconds(uint16_t ms)
* @brief Zeitschleife von 1-65535 Millisekunden
* @param Anzahl Millisekunden
* @return none
*/
void DelayMilliSeconds(uint16_t ms)
{
volatile static uint16_t ctr;
ResetMilliSeconds();
do
{
ATOMIC_BLOCK(ATOMIC_FORCEON)
{
ctr = t_delayms;
}
CheckPaddles();
} while (ctr < ms);
}
/** @fn uint16_t GetMilliSeconds(void)
* @brief Zäherwert für Zeitschleifen auslesen
* @param none
* @return Millisekunden
*/
uint16_t GetMilliSeconds(void)
{
volatile static uint16_t ctr;
ATOMIC_BLOCK(ATOMIC_FORCEON)
{
ctr = t_delayms;
}
return ctr;
}
/** @fn void ResetMilliSeconds(void)
* @brief Setzt den Millisekundenzähler für Zeitschleifen auf 0
* @param none
* @return none
*/
void ResetMilliSeconds(void)
{
ATOMIC_BLOCK(ATOMIC_FORCEON)
{
t_delayms = 0;
}
}
/** @fn void IntEnable(void)
* @brief Register wieder herstellen und Interrupts erlauben
* @param none
* @return none
*/
void IntEnable(void)
{
SREG = sreg_tmp;
sei();
}
/** @fn void IntDisable(void)
* @brief Register sichern und Interrupts verbieten
* @param none
* @return none
*/
void IntDisable(void)
{
sreg_tmp = SREG;
cli();
}
/** @fn void WriteEEprom(void)
* @brief Akt. Einstellungen in EEPROM schreiben
* @param none
* @return none
*/
void WriteEEprom(void)
{
IntDisable();
eeprom_write_byte(&ee_Dummy,0x55);
eeprom_write_byte(&ee_WpM, bConfig.WpM);
eeprom_write_byte(&ee_KeyerMode, bConfig.KeyerMode);
eeprom_write_word(&ee_SidetoneFreq, bConfig.SidetoneFreq);
eeprom_write_byte(&ee_Trx1, bConfig.Trx1);
eeprom_write_byte(&ee_Trx2, bConfig.Trx2);
eeprom_write_byte(&ee_SidetoneEnabled, bConfig.SidetoneEnabled);
eeprom_write_byte(&ee_WpMBpM, bConfig.WpMBpM);
eeprom_write_byte(&ee_Reverse, bConfig.Reverse);
eeprom_write_byte(&ee_SpeedRatioEnabled, bConfig.SpeedRatioEnabled);
eeprom_write_byte(&ee_Weight, bConfig.Weight);
eeprom_write_byte(&ee_Ratio, bConfig.Ratio);
eeprom_write_byte(&ee_Memory, bConfig.Memory);
eeprom_write_byte(&ee_RiseTime, bConfig.RiseTime);
eeprom_write_byte(&ee_RiseTimeCounter, bConfig.RiseTimeCounter);
eeprom_write_byte(&ee_DebounceTime, bConfig.DebounceTime);
IntEnable();
}
/** @fn void ReadEEpromWpM(void)
* @brief Gespeicherte Einstellung für die Geschwindigkeit aus EEprom lesen
* @param none
* @return none
*/
void ReadEEpromWpM(void)
{
IntDisable();
bConfig.WpM = eeprom_read_byte(&ee_WpM);
IntEnable();
}
/** @fn void WriteEEpromWpM(void)
* @brief Akt. Einstellung für die Geschwindigkeit in EEprom schreiben
* @param none
* @return none
*/
void WriteEEpromWpM(void)
{
IntDisable();
eeprom_write_byte(&ee_WpM, bConfig.WpM);
IntEnable();
}
/** @fn void SetEEprom(void)
/** @brief Standardeinstellungen (bei neuem Controller)
*
* Wird ein neuer Controller verwendet, gibt es keine
* Einstellungswerte im EEprom. Diese Funktion schreibt
* die initialen Werte in das EEprom. Wird sehr selten
* benötigt ;-)
*
* @param none
* @return none
*/
void SetEEprom(void)
{
Dummy = 0x55;
bConfig.WpM = 15;
bConfig.KeyerMode = 2;
bConfig.SidetoneFreq = 600;
bConfig.Trx1 = 1;
bConfig.Trx2 = 0;
bConfig.SidetoneEnabled = 1;
bConfig.WpMBpM = 1;
bConfig.Reverse = 0;
bConfig.SpeedRatioEnabled = 0;
bConfig.Weight = 50;
bConfig.Ratio = 30;
bConfig.Memory = 0;
bConfig.RiseTime = 5;
bConfig.RiseTimeCounter = 5;
bConfig.DebounceTime = 5;
WriteEEprom();
sprintf(bMessage.Msg1,"CQ CQ CQ DE DL7BJ DL7BJ DL7BJ PSE K\n");
}
/** @fn void ReadEEprom(void)
* @brief Einstellungen aus dem EEprom lesen
* @param none
* @return none
*/
void ReadEEprom(void)
{
IntDisable();
Dummy = eeprom_read_byte(&ee_Dummy);
IntEnable();
if(Dummy != 0x55)
{
SetEEprom();
}
IntDisable();
bConfig.WpM = eeprom_read_byte(&ee_WpM);
bConfig.KeyerMode = eeprom_read_byte(&ee_KeyerMode);
bConfig.SidetoneFreq = eeprom_read_word(&ee_SidetoneFreq);
bConfig.Trx1 = eeprom_read_byte(&ee_Trx1);
bConfig.Trx2 = eeprom_read_byte(&ee_Trx2);
bConfig.SidetoneEnabled = eeprom_read_byte(&ee_SidetoneEnabled);
bConfig.WpMBpM = eeprom_read_byte(&ee_WpMBpM);
bConfig.Reverse = eeprom_read_byte(&ee_Reverse);
bConfig.SpeedRatioEnabled = eeprom_read_byte(&ee_SpeedRatioEnabled);
bConfig.Ratio = eeprom_read_byte(&ee_Ratio);
bConfig.Weight = eeprom_read_byte(&ee_Weight);
bConfig.Memory = eeprom_read_byte(&ee_Memory);
bConfig.RiseTime = eeprom_read_byte(&ee_RiseTime);
bConfig.RiseTimeCounter = eeprom_read_byte(&ee_RiseTimeCounter);
bConfig.DebounceTime = eeprom_read_byte(&ee_DebounceTime);
IntEnable();
}
/** @fn void SerialWriteChar(unsigned char data)
* @brief Ein Zeichen über serielle Schnittstelle ausgeben
* @param Zeichen
* @return none
*/
void SerialWriteChar(unsigned char data)
{
while(!(UCSR0A & (1<<UDRE0)));
UDR0 = data;
}
/** @fn void SerialWriteString(unsigned char *s)
* @brief Eine Zeichenkette über serielle Schnittstelle ausgeben
* @param Zeiger auf Zeichenkette, die mit 0x00 abgeschlossen sein muss
* @return none
*/
void SerialWriteString(char *s)
{
while(*s != 0x00)
SerialWriteChar(*s++);
}
/** @fn void SidetoneOn(void)
* @brief Mithörton einschalten
* @param none
* @return none
*/
void SidetoneOn(void)
{
bState.SidetoneOff = 0;
StateRiseTime = bConfig.RiseTime;
StateRiseTimeCounter = 0;
icnt = 0;
sbi(TIMSK1,OCIE1A);
}
/** @fn void SidetoneOff(void)
* @brief Mithörtone ausschalten
* @param none
* @return none
*/
void SidetoneOff(void)
{
bState.SidetoneOff = 1;
StateRiseTime = 0;
StateRiseTimeCounter = 0;
}
/** @fn void SidetoneDisable(void)
/* @brief Audioverstärker abschalten
* @param none
* @return none
*/
void SidetoneDisable(void)
{
bConfig.SidetoneEnabled = 0;
cbi(PORTB,AUDIO_EN);
}
/** @fn void SidetoneEnable(void)
* @brief Audioverstärker einschalten
* @param none
* @return none
*/
void SidetoneEnable(void)
{
bConfig.SidetoneEnabled = 1;
sbi(PORTB,AUDIO_EN);
}
/** @fn void TXKey(uint8_t State)
* @brief Gibt ein Symbol aus
*
* Je nach Einstellung wird ein oder beide Transceiver
* getastet oder nur der Mithörton ausgegeben.
*
* @param State - Bitfeld für Transceiver und Tonausgabe
* @return none
*/
void TXKey(uint8_t State)
{
if((State) && (bState.KeyState == 0))
{
if(bState.KeyTX)
{
SidetoneOn();
sbi(PORTC,MORSE_LED);
if(bConfig.Trx1)
sbi(PORTC,TRX1);
if(bConfig.Trx2)
sbi(PORTC,TRX2);
bState.KeyState = 1;
}
}
else
{
if((State == 0) && (bState.KeyState))
{
if(bState.KeyTX)
{
SidetoneOff();
cbi(PORTC,MORSE_LED);
if(bConfig.Trx1)
cbi(PORTC,TRX1);
if(bConfig.Trx2)
cbi(PORTC,TRX2);
}
bState.KeyState = 0;
}
}
}
/** @fn void CheckButtons(void)
* @brief Fragt die Taster ab und löst entsprechende Funktion aus
* @param none
* @return none
*/
void CheckButtons(void)
{
if(!(PINB & (1<<MEM5)))
SendString(bMessage.Msg1);
}
/** @fn void CheckStraightKey(void)
* @brief Fragt die Handtasteneingänge ab und gibt das
* entsprechende Symbol aus. Bei der Handtaste
* findet über einen Timer noch eine Software
* Entprellung statt
* @param none
* @return none
*/
void CheckStraightKey(void)
{
if(PIND & (1<<STRAIGHT_KEY))
{
if(bState.SendStatus == SENDING_NOTHING)
TXKey(0);
StateStraightKeyPressed = NO_KEY_PRESSED;
} else {
if(StateStraightKeyPressed == NO_KEY_PRESSED)
{
StateStraightKeyPressed = KEY_PRESSED_DEBOUNCE;
TimerStraightKeyPressed = 0;
}
if(StateStraightKeyPressed == KEY_PRESSED)
{
if(bState.SendStatus == SENDING_NOTHING)
TXKey(1);
}
}
}
/** @fn void CheckPaddles(void)
** @brief Diese Funktion prüft, ob ein Paddle betätigt wurde.
**
** Wenn dies der Fall war, ist das jeweilige Statusbit gesetzt
** und die Funktion für die Ausgabe des entsprechendem Symbol
** wird aufgerufen.
**
** @param none
** @return none
*/
void CheckPaddles(void)
{
CheckDitPaddle();
CheckDahPaddle();
// Wenn Paddle betätigt wurde,
if(bState.SendStatus == SENDING_NOTHING)
{
if(bState.DitPressed || bState.DahPressed)
{
if(bState.DitPressed && !bState.DahPressed)
SendDit();
else if(bState.DahPressed && !bState.DitPressed)
SendDah();
else if(bState.DahPressed && bState.DitPressed)
SendIambic();
}
}
}
/** @fn void CheckDitPaddle(void)
* @brief Prüfen, ob das Punkt Paddle betätigt wurde.
*
* Ist dies der Fall, wird das Statusbit gesetzt.Wurde in den
* Einstellungen der Reverse Mode aktiviert, werden in der
* Abfrage die Paddle vertauscht.
*
* @param none
* @return none
*/
void CheckDitPaddle(void)
{
uint8_t pinvalue = 0;
if(PaddleMode == PADDLE_NORMAL)
pinvalue = PIND & (1<<LEFT_PADDLE);
else
pinvalue = PIND & (1<<RIGHT_PADDLE);
if((pinvalue == 0) && ((!(bState.SendStatus == SENDING_DIT)) || (bState.SendStatus == SENDING_NOTHING)))
{
bState.DitPressed = 1;
}
// Wenn Punkt/Strich Speicher aktiviert ist, das Symbol merken, wenn es nicht identisch ist
if((pinvalue == 0) && (!(bState.SendStatus == SENDING_DIT)) && (bConfig.Memory == 1))
bState.DitPressed = 1;
}
/** @fn void CheckDahPaddle(void)
* @brief Prüfen, ob das Strich Paddle betätigt wurde.
*
* Ist dies der Fall, wird das Statusbit gesetzt. Wurde in den
* Einstellungen der Reverse Mode aktiviert, werden in der
* Abfrage die Paddle vertauscht.
*
* @param none
* @return none
*/
void CheckDahPaddle(void)
{
uint8_t pinvalue = 0;
if(PaddleMode == PADDLE_NORMAL) // no reverse paddle
pinvalue = PIND & (1<<RIGHT_PADDLE);
else
pinvalue = PIND & (1<<LEFT_PADDLE); // reverse paddle
// Das Symbol nur merken, wenn nicht gerade eines ausgegeben wird oder gar nichts ausgegeben wird
if((pinvalue == 0) && ((!(bState.SendStatus == SENDING_DAH)) || (bState.SendStatus == SENDING_NOTHING)))
{
bState.DahPressed = 1;
}
// Wenn Punkt/Strich Speicher aktiviert ist, das Symbol merken, wenn es nicht identisch ist
if((pinvalue == 0) && (!(bState.SendStatus == SENDING_DAH)) && (bConfig.Memory == 1))
bState.DahPressed = 1;
}
/** @fn void SetRatio(void)
* @brief Berechnung der Zeitdauer für Punkt und Strich
*
* Die Länge eines Symbols ist abhängig von der
* eingestellten Geschwindigkeit. Intern wird immer
* mit Wörtern pro Minute gerechnet. Das normale
* Punkt/Strich Verhältnis ist 1:3. Dies kann durch
* den Parameter Ratio in den Einstellungen verändert
* werden. Wird ein Korrekturfaktor angegeben, kann
* das Ratio über eine Tabelle in Abhängigkeit der
* Geschwindigkeit bis zu 1:2.0 verändert werden.
*
* @param none
* @return none
*/
void SetRatio(void)
{
DitMillis = 1200 / WpM;
if(bConfig.SpeedRatioEnabled)
DahMillis = DitMillis * (float)(bState.WpMCorrectFactor / 10);
else
DahMillis = DitMillis * (float)(bConfig.Ratio/10);
}
/** @fn void GetWpMCorrectFactor(void)
* @brief Ermittelt den Korrekturfaktor für das Ratio
* param none
* return none
*/
void GetWpMCorrectFactor(void)
{
bState.WpMCorrectFactor = 30;
if(WpM > 20)
bState.WpMCorrectFactor = 28;
}
void SetWeight(void)
{
DitMillis = 1200/WpM;
DahMillis = 3 * DitMillis;
SpcMillis = 1200/WpM;
DitMillis = (bConfig.Weight/50.0) * DitMillis;
DahMillis = (bConfig.Weight/50.0) * DahMillis;
if(bConfig.Weight < 50)
SpcMillis = SpcMillis + (SpcMillis - DitMillis);
else
SpcMillis = SpcMillis - (DitMillis - SpcMillis);
}
/** @fn void SendSymbol(uint8_t Dit)
* @brief Symbol senden
* @param Dit = 1, wenn Symbol ein Punkt ist
* @return none
*/
void SendSymbol(uint8_t Dit)
{
static uint16_t ticks; // Länge des Symbols
if(Dit)
ticks = DitMillis;
else
ticks = DahMillis;
t_elementlength = 0;
TXKey(1);
DelayMilliSeconds(ticks);
bState.SendStatus = SENDING_SPC;
TXKey(0);
DelayMilliSeconds(SpcMillis);
}
/** @fn void SendDit(void)
* @brief Gibt einen Punkt aus
* @param none
* @return none
*/
void SendDit(void)
{
if((bState.SendStatus == NOTHING) || (bState.SendStatus == SENDING_DAH))
{
bState.DitPressed = 0;
bState.SendStatus = SENDING_DIT;
SendSymbol(DIT);
bState.SendStatus = SENDING_NOTHING;
bState.LastSymbolWasDit = 1;
}
if(bConfig.Memory == 0)
bState.DitPressed = 0;
}
/** @fn void SendDah(void)
* @brief Gibt einen Strich aus
* @param none
* @return none
*/
void SendDah(void)
{
if((bState.SendStatus == NOTHING) || (bState.SendStatus == SENDING_DIT))
{
bState.DahPressed = 0;
bState.SendStatus = SENDING_DAH;
SendSymbol(DAH);
bState.SendStatus = SENDING_NOTHING;
bState.LastSymbolWasDit = 0;
}
if(bConfig.Memory == 0)
bState.DahPressed = 0;
}
/** @fn void SendIambic(void)
* @brief Wenn beide Paddle betätigt sind, wird alternierend
* ein Punkt und Strich ausgegeben.
*
* Beim Iambic B Mode und Ultimatic Mode wird gegebenenfalls
* noch ein gegensätzliches Zeichen des zuletzt betätigten Paddle
* angefügt. Erläuterung und Timing siehe Dokumentation.
*
* @param none
* @return none
*/
void SendIambic(void)
{
bState.DahPressed = 0;
bState.DitPressed = 0;
if(bState.LastSymbolWasDit)
{
SendDah();
if(bConfig.KeyerMode == IAMBIC_B && !bState.DahPressed && !bState.DitPressed)
{
SendDit();
} else {
if(bConfig.KeyerMode == ULTIMATIC)
{
while(bState.DahPressed)
{
bState.DitPressed = 0;
bState.DahPressed = 0;
SendDah();
}
}
}
} else {
SendDit();
if(bConfig.KeyerMode == IAMBIC_B && !bState.DitPressed && !bState.DahPressed)
{
SendDah();
} else {
if(bConfig.KeyerMode == ULTIMATIC)
{
while(bState.DitPressed)
{
bState.DitPressed = 0;
bState.DahPressed = 0;
SendDit();
}
}
}
}
}
/** @fn void SetFrequency(uint16_t f)
* @brief Verändert die Frequenz des Mithörtons
*
* Durch die Anpassung des Timers 1 für das
* Laden der Werte der Sinuskurve wird die
* Tonhöhe des Mithörtons verändert.
* @param Frequenz des Mithörtons in Hz
* @return none
*/
void SetFrequency(uint16_t f)
{
IntDisable();
OCR1A = (F_CPUPRESIN / f) - 1;
IntEnable();
}
/** @fn void Tone(uint16_t f, uint8_t duration)
* @brief Ausgabe eines Tons mit Dauer und Frequenz
* @param f - Frequenz des Tons
* duration - Dauer des Tons
* @return none
*/
void Tone(uint16_t f, uint8_t duration)
{
SetFrequency(f);
SidetoneOn();
DelayMilliSeconds(duration);
SidetoneOff();
SetFrequency(bConfig.SidetoneFreq);
}
/** @fn void Boop(void)
* @brief Boop Ton ausgeben
* @param none
* @return none
*/
void Boop(void)
{
Tone(600,100);
}
/** @fn void Beep(void)
* @brief Beep Ton ausgeben
* @param none
* @return none
*/
void Beep(void)
{
Tone(1200,100);
}
/** @fn void BeepBoop(void)
* @brief BeepBoop Doppelton ausgeben
* @param none
* @return none
*/
void BeepBoop(void)
{
DelayMilliSeconds(10);
Beep();
DelayMilliSeconds(100);
Boop();
}
void SendString(char *s)
{
int c;
while(*s != 0x00)
{
if(*s != 0x20)
SendChar(*s);
else if (*s == 0x20)
{
DelayMilliSeconds(7*DahMillis);
}
*s++;
}
}
void SendChar(unsigned char c)
{
switch(c)
{
// Buchstaben
case 'A': SendDit(); SendDah(); break;
case 'B': SendDah(); SendDit(); SendDit(); SendDit(); break;
case 'C': SendDit(); SendDah(); SendDit(); SendDah(); break;
case 'D': SendDah(); SendDit(); SendDit(); break;
case 'E': SendDit(); break;
case 'F': SendDit(); SendDah(); SendDit(); SendDit(); break;
case 'G': SendDah(); SendDah(); SendDit(); break;
case 'H': SendDit(); SendDit(); SendDit(); SendDit(); break;
case 'I': SendDit(); SendDit(); break;
case 'J': SendDit(); SendDah(); SendDah(); SendDah(); break;
case 'K': SendDah(); SendDit(); SendDah(); break;
case 'L': SendDit(); SendDah(); SendDit(); SendDit(); break;
case 'M': SendDah(); SendDah(); break;
case 'N': SendDah(); SendDit(); break;
case 'O': SendDah(); SendDah(); SendDah(); break;
case 'P': SendDit(); SendDah(); SendDah(); SendDit(); break;
case 'Q': SendDah(); SendDah(); SendDit(); SendDah(); break;
case 'R': SendDit(); SendDah(); SendDit(); break;
case 'S': SendDit(); SendDit(); SendDit(); break;
case 'T': SendDah(); break;
case 'U': SendDit(); SendDit(); SendDah(); break;
case 'V': SendDit(); SendDit(); SendDit(); SendDah(); break;
case 'W': SendDit(); SendDah(); SendDah(); break;
case 'X': SendDah(); SendDit(); SendDit(); SendDah(); break;
case 'Y': SendDah(); SendDit(); SendDah(); SendDah(); break;
case 'Z': SendDit(); SendDit(); SendDah(); SendDah(); break;
// Zahlen
case '0': SendDah(); SendDah(); SendDah(); SendDah(); SendDah(); break;
case '1': SendDit(); SendDah(); SendDah(); SendDah(); SendDah(); break;
case '2': SendDit(); SendDit(); SendDah(); SendDah(); break;
case '3': SendDit(); SendDit(); SendDit(); SendDah(); SendDah(); break;
case '4': SendDit(); SendDit(); SendDit(); SendDit(); SendDah(); break;
case '5': SendDit(); SendDit(); SendDit(); SendDit(); SendDit(); break;
case '6': SendDah(); SendDit(); SendDit(); SendDit(); SendDit(); break;
case '7': SendDah(); SendDah(); SendDit(); SendDit(); SendDit(); break;
case '8': SendDah(); SendDah(); SendDah(); SendDit(); SendDit(); break;
case '9': SendDah(); SendDah(); SendDah(); SendDah(); SendDit(); break;
// Sonderzeichen
case '=': SendDah(); SendDit(); SendDit(); SendDit(); SendDah(); break;
case '/': SendDah(); SendDit(); SendDit(); SendDah(); SendDit(); break;
case '.': SendDit(); SendDah(); SendDit(); SendDah(); SendDit(); SendDah(); break;
case ',': SendDah(); SendDah(); SendDit(); SendDit(); SendDah(); SendDah(); break;
case '?': SendDit(); SendDit(); SendDah(); SendDah(); SendDit(); SendDit(); break;
case '@': SendDit(); SendDah(); SendDah(); SendDit(); SendDah(); SendDit(); break;
}
}