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.

709 lines
20 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_TRX EEMEM = 0; // TRX 1 oder 2 oder beide
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_WinkeyerEnabled EEMEM = 0; // Winkeyer Emulation
uint8_t ee_Ratio EEMEM = 30; // Punkt/Strich Verhältnis 1:3
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; // 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
// Stringkonstanten für VT100 Terminals
const char CLRSCR[] = "\033[2J";
/** @brief Ein paar Millisekunden warten
*
* @param Zeit in Millisekunden, die gewartet werden soll
* @return void
*/
void DelayMilliSeconds(uint16_t ms)
{
volatile static uint16_t ctr;
ResetMilliSeconds();
do
{
ATOMIC_BLOCK(ATOMIC_FORCEON)
{
ctr = t_delayms;
}
} while (ctr < ms);
}
/** @brief Wert des Zählers für Millisekunden lesen
*
* @param none
* @return milliseconds
*/
uint16_t GetMilliSeconds(void)
{
volatile static uint16_t ctr;
ATOMIC_BLOCK(ATOMIC_FORCEON)
{
ctr = t_delayms;
}
return ctr;
}
/** @brief Setzt den Zähler für Millisekunden auf 0
*
* @param none
* @return none
*/
void ResetMilliSeconds(void)
{
ATOMIC_BLOCK(ATOMIC_FORCEON)
{
t_delayms = 0;
}
}
/** @brief SREG wieder herstellen und Interrupts erlauben
* @param none
* @return none
*/
void IntEnable(void)
{
SREG = sreg_tmp;
sei();
}
/** @brief SREG sichern und Interrupts verbieten
* @param none
* @return none
*/
void IntDisable(void)
{
sreg_tmp = SREG;
cli();
}
/** \brief EEPROM schreiben
*/
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_TRX, bConfig.TRX);
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_WinkeyerEnabled, bConfig.WinkeyerEnabled);
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();
}
void ReadEEpromWpM(void)
{
IntDisable();
bConfig.WpM = eeprom_read_byte(&ee_WpM);
IntEnable();
}
void WriteEEpromWpM(void)
{
IntDisable();
eeprom_write_byte(&ee_WpM, bConfig.WpM);
IntEnable();
}
/** @brief Standardeinstellungen (bei neuem Controller)
*
* @param none
* @return none
*/
void SetEEprom(void)
{
Dummy = 0x55;
bConfig.WpM = 15;
bConfig.KeyerMode = 2;
bConfig.SidetoneFreq = 600;
bConfig.TRX = 1;
bConfig.SidetoneEnabled = 1;
bConfig.WpMBpM = 1;
bConfig.Reverse = 0;
bConfig.WinkeyerEnabled = 0;
bConfig.Ratio = 30;
bConfig.Memory = 0;
bConfig.RiseTime = 5;
bConfig.RiseTimeCounter = 5;
bConfig.DebounceTime = 5;
WriteEEprom();
}
/** @brief 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.TRX = eeprom_read_byte(&ee_TRX);
bConfig.SidetoneEnabled = eeprom_read_byte(&ee_SidetoneEnabled);
bConfig.WpMBpM = eeprom_read_byte(&ee_WpMBpM);
bConfig.Reverse = eeprom_read_byte(&ee_Reverse);
bConfig.WinkeyerEnabled = eeprom_read_byte(&ee_WinkeyerEnabled);
bConfig.Ratio = eeprom_read_byte(&ee_Ratio);
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();
if(bConfig.WpM > 50) {
bConfig.WpM = 15;
WriteEEpromWpM();
}
if(bConfig.RiseTime > 10) {
bConfig.RiseTime = 10;
WriteEEprom();
}
if(bConfig.RiseTimeCounter > 6) {
bConfig.RiseTimeCounter = 6;
WriteEEprom();
}
if(bConfig.DebounceTime > 22) {
bConfig.DebounceTime = 25;
WriteEEprom();
}
}
/** @brief Aktuelle Einstellungen über serielle Schnittstelle ausgeben
* @param none
* @return none
*/
void SerialInfo(void)
{
sprintf(sdebug,"WpM : %i\r\n", bConfig.WpM);
SerialWriteString(sdebug);
sprintf(sdebug,"KeyerMode : %i\r\n", bConfig.KeyerMode);
SerialWriteString(sdebug);
sprintf(sdebug,"SidetoneFreq : %u\r\n", bConfig.SidetoneFreq);
SerialWriteString(sdebug);
sprintf(sdebug,"TRX : %i\r\n", bConfig.TRX);
SerialWriteString(sdebug);
sprintf(sdebug,"SidetoneEnabled : %i\r\n", bConfig.SidetoneEnabled);
SerialWriteString(sdebug);
sprintf(sdebug,"Ratio : %i\r\n", bConfig.Ratio);
SerialWriteString(sdebug);
sprintf(sdebug,"WpMBpM : %i\r\n", bConfig.WpMBpM);
SerialWriteString(sdebug);
sprintf(sdebug,"Reverse : %i\r\n", bConfig.Reverse);
SerialWriteString(sdebug);
sprintf(sdebug,"Memory : %i\r\n", bConfig.Memory);
SerialWriteString(sdebug);
sprintf(sdebug,"Winkeyer : %i\r\n", bConfig.WinkeyerEnabled);
SerialWriteString(sdebug);
sprintf(sdebug,"RiseTime : %i\r\n", bConfig.RiseTime);
SerialWriteString(sdebug);
sprintf(sdebug,"RiseTimeCounter : %i\r\n", bConfig.RiseTimeCounter);
SerialWriteString(sdebug);
sprintf(sdebug,"DebounceTime : %i\r\n", bConfig.DebounceTime);
SerialWriteString(sdebug);
}
/** @brief Versionsinformation über serielle Schnittstelle ausgeben
* @param none
* @return none
*/
void SerialAbout(void)
{
sprintf(sdebug,"%s", CLRSCR);
SerialWriteString(sdebug);
sprintf(sdebug,"%s %s %s\r\n\r\n", PRG,VER,CALL);
SerialWriteString(sdebug);
}
/** @brief Ein Zeichen über serielle Schnittstelle ausgeben
* @param Zeichen
* @return none
*/
void SerialWriteChar(unsigned char data)
{
while(!(UCSR0A & (1<<UDRE0)));
UDR0 = data;
}
/** @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++);
}
/** @brief Read port pin of morse keys
*
* This function reads the input of
* the Paddle or Straight key.
* @params: Pin to read
* @return: State of pin
*/
uint8_t ReadKeyPin(uint8_t pin)
{
return(PIND & (1<<pin));
}
/** @brief Tonausgabe einschalten
* @param none
* @return none
*/
void SidetoneOn(void)
{
bState.SidetoneOff = 0;
StateRiseTime = bConfig.RiseTime;
StateRiseTimeCounter = 0;
icnt = 0;
sbi(TIMSK1,OCIE1A);
}
/** @brief Tonausgabe abschalten
* @param none
* @return none
*/
void SidetoneOff(void)
{
bState.SidetoneOff = 1;
StateRiseTime = 0;
StateRiseTimeCounter = 0;
}
/** @brief Audioverstärker abschalten
* @param none
* @return none
*/
void SidetoneDisable(void)
{
bConfig.SidetoneEnabled = 0;
cbi(PORTB,AUDIO_EN);
}
/** @brief Audioverstärker einschalten
* @param none
* @return none
*/
void SidetoneEnable(void)
{
bConfig.SidetoneEnabled = 1;
sbi(PORTB,AUDIO_EN);
}
/*
** TXKey
**
** Tastung TRX1, TRX2 oder beide
*/
void TXKey(uint8_t State)
{
if((State) && (bState.KeyState == 0))
{
if(bState.KeyTX)
{
SidetoneOn();
sbi(PORTC,MORSE_LED);
bState.KeyState = 1;
}
}
else
{
if((State == 0) && (bState.KeyState))
{
if(bState.KeyTX)
{
SidetoneOff();
cbi(PORTC,MORSE_LED);
}
bState.KeyState = 0;
}
}
}
/** \brief StraigtKey
*
* Check Straightkey
*
*/
void CheckStraightKey(void)
{
if(PIND & (1<<STRAIGHT_KEY))
{
if(bState.SendStatus == 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 == NOTHING)
TXKey(1);
}
}
}
/** \brief CheckPaddles
**
*/
void CheckPaddles(void)
{
static uint8_t LastAction = NOTHING;
CheckDitPaddle();
CheckDahPaddle();
if(bConfig.KeyerMode == ULTIMATIC)
{
switch(LastAction)
{
case DAH_DIT_ON:
if(DahBuffer) {
if(DitBuffer)
DitBuffer = 0;
else
LastAction = DAH_DIT_OFF;
} else {
if(DitBuffer)
LastAction = DIT_DAH_OFF;
else
LastAction = NOTHING;
}
break;
case DIT_DAH_ON:
if(DitBuffer) {
if(DahBuffer)
DahBuffer = 0;
else
LastAction = DIT_DAH_OFF;
} else {
if(DahBuffer)
LastAction = DAH_DIT_OFF;
else
LastAction = NOTHING;
}
break;
case DAH_DIT_OFF:
if(DitBuffer) {
if(DahBuffer)
{
LastAction = DIT_DAH_ON;
DahBuffer = 0;
} else {
LastAction = DIT_DAH_OFF;
}
} else {
if(!DahBuffer)
LastAction = NOTHING;
}
break;
case DIT_DAH_OFF:
if(DahBuffer) {
if(DitBuffer)
{
LastAction = DAH_DIT_ON;
DitBuffer = 0;
} else {
LastAction = DAH_DIT_OFF;
}
} else {
if(!DitBuffer)
LastAction = NOTHING;
}
break;
case NOTHING:
if((DitBuffer) && (!DahBuffer)) {
LastAction = NOTHING;
} else {
if((DahBuffer) && (!DitBuffer)) {
LastAction = DAH_DIT_OFF;
} else {
if((DitBuffer) && (DahBuffer)) {
LastAction = DIT_DAH_ON;
DahBuffer = 0;
}
}
}
break;
}
}
}
/** \brief CheckDitPaddle
*
*
*/
void CheckDitPaddle(void)
{
uint8_t pinvalue = 0;
if(PaddleMode == PADDLE_NORMAL) // no reverse paddle
pinvalue = PIND & (1<<LEFT_PADDLE);
else
pinvalue = PIND & (1<<RIGHT_PADDLE); // reverse paddle
if(pinvalue == 0) // DitPaddle betätigt
{
DitBuffer = 1;
}
}
/*
** CheckDahPaddle
*/
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
if(pinvalue == 0)
{
DahBuffer = 1;
}
}
void SetRatio(uint8_t WPMCorrectFactor)
{
DitMillis = 1200 / WpM;
DahMillis = DitMillis * (float)(bConfig.Ratio/10);
}
/** @brief Schleife für die Dauer eines Zeichenelements Dit oder Dah
*
* @param Len Anzahl der Symbole (in Dits)
* @return none
*/
void SymbolLoop(uint8_t Len)
{
volatile static uint16_t ticks; // lengths of elements + AddMilliSeconds in ms
ticks = DitMillis * Len; //
t_elementlength = 0; // Reset Timer 0 variable
while(t_elementlength < ticks)
{
if(KeyerMode != ULTIMATIC)
{
if((KeyerMode == IAMBIC_A) && (!(PIND & (1<<LEFT_PADDLE))) && (!(PIND & (1<<RIGHT_PADDLE))))
{
IambicFlag = 1;
SerialWriteString("526:IambicFlag == 1\r\n");
}
if(bState.SendStatus == SENDING_DIT)
{
CheckDahPaddle();
} else {
if(bState.SendStatus == SENDING_DAH)
{
CheckDitPaddle();
} else {
CheckDahPaddle();
CheckDitPaddle();
}
}
} else {
if(bState.SendStatus == SENDING_DIT)
{
CheckDahPaddle();
} else {
if(bState.SendStatus == SENDING_DAH)
{
CheckDitPaddle();
} else {
CheckDahPaddle();
CheckDitPaddle();
}
}
}
}
// Wenn Taste losgelassen, dann im KeyerMode IAMBIC_A das IambicFlag löschen
if((KeyerMode == IAMBIC_A) && (IambicFlag) && (PIND & (1<<LEFT_PADDLE)) && (PIND & (1<<RIGHT_PADDLE)))
{
IambicFlag = 0;
DitBuffer = 0;
DahBuffer = 0;
SerialWriteString("562:IambicFlag == 0\r\n");
}
if((bState.SendStatus == SENDING_DIT) || (bState.SendStatus == SENDING_DAH))
{
if(bConfig.Memory == 0)
{
DitBuffer = 0;
DahBuffer = 0;
}
}
}
/*
** void SendDit(uint8_t SendingType)
*/
void SendDit(uint8_t SendingType)
{
bState.SendStatus = SENDING_DIT;
TXKey(1);
SymbolLoop(1);
TXKey(0);
SymbolLoop(1);
bState.SendStatus = NOTHING;
CheckPaddles();
}
/** \brief Send a dah
** A Dah with weight = 50 has the length of 3 Dits.
*/
void SendDah(uint8_t SendingType)
{
bState.SendStatus = SENDING_DAH;
TXKey(1);
SymbolLoop(3);
TXKey(0);
SymbolLoop(1);
bState.SendStatus = NOTHING;
CheckPaddles();
}
/** @brief Bearbeiten der Punkt- und Strichbuffer für Iambic Modes
*
* Bei Iambic A wird der Buffer gelöscht, bei Iambic B voher das
* gegensätzliche Zeichen ausgegeben.
* @param none
* @return none
*/
void DitDahBuffers(void)
{
if((bConfig.KeyerMode == IAMBIC_A) || (bConfig.KeyerMode == IAMBIC_B))
{
// IAMBIC_A
if((bConfig.KeyerMode == IAMBIC_A) && (IambicFlag) && (PIND & (1<<LEFT_PADDLE)) && (PIND & (1<<RIGHT_PADDLE)))
{
IambicFlag = 0;
DitBuffer = 0;
DahBuffer = 0;
SerialWriteString("616:IambicFlag == 0\r\n");
} else {
// IAMBIC_B
if(DitBuffer)
{
DitBuffer = 0;
SendDit(MAN);
}
if(DahBuffer)
{
DahBuffer = 0;
SendDah(MAN);
}
}
}
if(bConfig.KeyerMode == ULTIMATIC)
{
SerialWriteString("Ultimatic\r\n");
}
}
/*
** Set sidetone frequency
*/
void SetFrequency(uint16_t f)
{
IntDisable();
OCR1A = (F_CPUPRESIN / f) - 1;
IntEnable();
}
/*
** Output a tone width frequency f and duration duration
**
*/
void Tone(uint16_t f, uint8_t duration)
{
SetFrequency(f);
SidetoneOn();
DelayMilliSeconds(duration);
SidetoneOff();
SetFrequency(bConfig.SidetoneFreq);
}
void Boop(void)
{
Tone(600,100);
}
void Beep(void)
{
Tone(1200,100);
}
void BeepBoop(void)
{
DelayMilliSeconds(10);
Beep();
DelayMilliSeconds(100);
Boop();
}
void SendChar(unsigned char c)
{
switch(c)
{
// Buchstaben
case 'A': SendDit(AUTO); SendDah(AUTO); break;
case 'B': SendDah(AUTO); SendDit(AUTO); SendDit(AUTO); SendDit(AUTO); break;
case 'C': SendDit(AUTO); SendDah(AUTO); SendDit(AUTO); SendDah(AUTO); break;
case 'D': SendDah(AUTO); SendDit(AUTO); SendDit(AUTO); break;
case 'E': SendDit(AUTO); break;
case 'F': SendDit(AUTO); SendDah(AUTO); SendDit(AUTO); SendDit(AUTO); break;
case 'G': SendDah(AUTO); SendDah(AUTO); SendDit(AUTO); break;
case 'H': SendDit(AUTO); SendDit(AUTO); SendDit(AUTO); SendDit(AUTO); break;
case 'I': SendDit(AUTO); SendDit(AUTO); break;
case 'J': SendDit(AUTO); SendDah(AUTO); SendDah(AUTO); SendDah(AUTO); break;
case 'K': SendDah(AUTO); SendDit(AUTO); SendDah(AUTO); break;
case 'L': SendDit(AUTO); SendDah(AUTO); SendDit(AUTO); SendDit(AUTO); break;
case 'M': SendDah(AUTO); SendDah(AUTO); break;
case 'N': SendDah(AUTO); SendDit(AUTO); break;
case 'O': SendDah(AUTO); SendDah(AUTO); SendDah(AUTO); break;
case 'P': SendDit(AUTO); SendDah(AUTO); SendDah(AUTO); SendDit(AUTO); break;
case 'Q': SendDah(AUTO); SendDah(AUTO); SendDit(AUTO); SendDah(AUTO); break;
case 'R': SendDit(AUTO); SendDah(AUTO); SendDit(AUTO); break;
case 'S': SendDit(AUTO); SendDit(AUTO); SendDit(AUTO); break;
case 'T': SendDah(AUTO); break;
case 'U': SendDit(AUTO); SendDit(AUTO); SendDah(AUTO); break;
case 'V': SendDit(AUTO); SendDit(AUTO); SendDit(AUTO); SendDah(AUTO); break;
case 'W': SendDit(AUTO); SendDah(AUTO); SendDah(AUTO); break;
case 'X': SendDah(AUTO); SendDit(AUTO); SendDit(AUTO); SendDah(AUTO); break;
case 'Y': SendDah(AUTO); SendDit(AUTO); SendDah(AUTO); SendDah(AUTO); break;
case 'Z': SendDit(AUTO); SendDit(AUTO); SendDah(AUTO); SendDah(AUTO); break;
// Zahlen
case '0': SendDah(AUTO); SendDah(AUTO); SendDah(AUTO); SendDah(AUTO); SendDah(AUTO); break;
case '1': SendDit(AUTO); SendDah(AUTO); SendDah(AUTO); SendDah(AUTO); SendDah(AUTO); break;
case '2': SendDit(AUTO); SendDit(AUTO); SendDah(AUTO); SendDah(AUTO); break;
case '3': SendDit(AUTO); SendDit(AUTO); SendDit(AUTO); SendDah(AUTO); SendDah(AUTO); break;
case '4': SendDit(AUTO); SendDit(AUTO); SendDit(AUTO); SendDit(AUTO); SendDah(AUTO); break;
case '5': SendDit(AUTO); SendDit(AUTO); SendDit(AUTO); SendDit(AUTO); SendDit(AUTO); break;
case '6': SendDah(AUTO); SendDit(AUTO); SendDit(AUTO); SendDit(AUTO); SendDit(AUTO); break;
case '7': SendDah(AUTO); SendDah(AUTO); SendDit(AUTO); SendDit(AUTO); SendDit(AUTO); break;
case '8': SendDah(AUTO); SendDah(AUTO); SendDah(AUTO); SendDit(AUTO); SendDit(AUTO); break;
case '9': SendDah(AUTO); SendDah(AUTO); SendDah(AUTO); SendDah(AUTO); SendDit(AUTO); break;
// Sonderzeichen
case '=': SendDah(AUTO); SendDit(AUTO); SendDit(AUTO); SendDit(AUTO); SendDah(AUTO); break;
case '/': SendDah(AUTO); SendDit(AUTO); SendDit(AUTO); SendDah(AUTO); SendDit(AUTO); break;
case '.': SendDit(AUTO); SendDah(AUTO); SendDit(AUTO); SendDah(AUTO); SendDit(AUTO); SendDah(AUTO); break;
case ',': SendDah(AUTO); SendDah(AUTO); SendDit(AUTO); SendDit(AUTO); SendDah(AUTO); SendDah(AUTO); break;
case '?': SendDit(AUTO); SendDit(AUTO); SendDah(AUTO); SendDah(AUTO); SendDit(AUTO); SendDit(AUTO); break;
case '@': SendDit(AUTO); SendDah(AUTO); SendDah(AUTO); SendDit(AUTO); SendDah(AUTO); SendDit(AUTO); break;
}
}