/*!
@file yack.c
@brief CW Keyer library
@author Jan Lategahn DK3LJ jan@lategahn.com (C) 2011; modified by Jack Welch AI4SV; modified by Don Froula WD9DMP
@version 0.87
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
@date 15.10.2010 - Created
@date 03.10.2013 - Last update
@date 21.12.2016 - Added additional prosigns and punctuation. Added 2 additional memories for ATTINY85. Fixed save of speed change to EEPROM. (WD9DMP)
@date 03.01.2017 - If memory recording is interrupted by command button, keyer now returns txok ("R") and stays in command mode. Memory is unchanged.
Memory playback halts immediately on command key instead of looping through message length without playing anything.
Removed playback of recorded message before saving.
Changed yackstring command to return to command mode instead of normal mode if interrupted with command key
@todo Make the delay dependent on T/C 1
*/
#include
#include
#include
#include
#include
#include
#include
#include "yack.h"
// Forward declaration of private functions
static void key( byte mode);
static char morsechar(byte buffer);
static void keylatch(void);
// Enumerations
enum FSMSTATE {
IDLE, //!< Not keyed, waiting for paddle
KEYED, //!< Keyed, waiting for duration of current element
IEG //!< In Inter-Element-Gap
};
// Module local definitions
static byte yackflags; // Permanent (stored) status of module flags
static byte volflags=0; // Temporary working flags (volatile)
static word ctcvalue; // Pitch
static word wpmcnt; // Speed
static byte wpm; // Real wpm
static byte farnsworth; // Additional Farnsworth pause
// EEPROM Data
byte magic EEMEM = MAGPAT; // Needs to contain 'A5' if mem is valid
byte flagstor EEMEM = ( IAMBICB | TXKEY | SIDETONE); // Defaults
word ctcstor EEMEM = DEFCTC; // Pitch = 800Hz
byte wpmstor EEMEM = DEFWPM; // 15 WPM
byte fwstor EEMEM = 0; // No farnsworth pause
word user1 EEMEM = 0; // User storage
word user2 EEMEM = 0; // User storage
//char eebuffer1[100] EEMEM = "message 1";
//char eebuffer2[100] EEMEM = "message 2";
char eebuffer1[100] EEMEM = "message 1";
char eebuffer2[100] EEMEM = "message 2";
char eebuffer3[100] EEMEM = "message 3";
char eebuffer4[100] EEMEM = "message 4";
// Flash data
//! Morse code table in Flash
//! Encoding: Each byte is read from the left. 0 stands for a dot, 1
//! stands for a dash. After each played element the content is shifted
//! left. Playback stops when the leftmost bit contains a "1" and the rest
//! of the bits are all zero.
//!
//! Example: A = .-
//! Encoding: 01100000
//! .-
//! | This is the stop marker (1 with all trailing zeros)
const byte morse[] PROGMEM =
{
0b11111100, // 0
0b01111100, // 1
0b00111100, // 2
0b00011100, // 3
0b00001100, // 4
0b00000100, // 5
0b10000100, // 6
0b11000100, // 7
0b11100100, // 8
0b11110100, // 9
0b01100000, // A
0b10001000, // B
0b10101000, // C
0b10010000, // D
0b01000000, // E
0b00101000, // F
0b11010000, // G
0b00001000, // H
0b00100000, // I
0b01111000, // J
0b10110000, // K
0b01001000, // L
0b11100000, // M
0b10100000, // N
0b11110000, // O
0b01101000, // P
0b11011000, // Q
0b01010000, // R
0b00010000, // S
0b11000000, // T
0b00110000, // U
0b00011000, // V
0b01110000, // W
0b10011000, // X
0b10111000, // Y
0b11001000, // Z
0b00110010, // ?
0b01010110, // .
0b10010100, // /
0b11101000, // ! (American Morse version, commonly used in ham circles)
0b11001110, // ,
0b11100010, // :
0b10101010, // ;
0b01001010, // "
0b00010011, // $
0b01111010, // ' (Apostrophe)
0b10110100, // ( or [ (also prosign KN)
0b10110110, // ) or ]
0b10000110, // - (Hyphen or single dash)
0b01101010, // @
0b00110110, // _ (Underline)
0b01010010, // Paragaraph break symbol
0b10001100, // = and BT
0b00010110, // SK
0b01010100, // + and AR
0b10001011, // BK
0b01000100, // AS
0b10101100, // KA (also ! in alternate Continental Morse)
0b00010100, // VE
0b01011000 // AA
};
// The special characters at the end of the above table can not be decoded
// without a small table to define their content. # stands for SK, $ for AR
// To add new characters, add them in the code table above at the end and below
// Do not forget to increase the legth of the array..
const char spechar[24] PROGMEM = "?./!,:;~$^()-@_|=#+*%&<>";
// Functions
// ***************************************************************************
// Control functions
// ***************************************************************************
void yackreset (void)
/*!
@brief Sets all yack parameters to standard values
This function resets all YACK EEPROM settings to their default values as
stored in the .h file. It sets the dirty flag and calls the save routine
to write the data into EEPROM immediately.
*/
{
ctcvalue=DEFCTC; // Initialize to 800 Hz
wpm=DEFWPM; // Init to default speed
wpmcnt=(1200/YACKBEAT)/DEFWPM; // default speed
farnsworth=0; // No Farnsworth gap
yackflags = FLAGDEFAULT;
volflags |= DIRTYFLAG;
yacksave(); // Store them in EEPROM
}
void yackinit (void)
/*!
@brief Initializes the YACK library
This function initializes the keyer hardware according to configurations in the .h file.
Then it attempts to read saved configuration settings from EEPROM. If not possible, it
will reset all values to their defaults.
This function must be called once before the remaining fuctions can be used.
*/
{
byte magval;
// Configure DDR. Make OUT and ST output ports
SETBIT (OUTDDR,OUTPIN);
SETBIT (STDDR,STPIN);
// Raise internal pullups for all inputs
SETBIT (KEYPORT,DITPIN);
SETBIT (KEYPORT,DAHPIN);
SETBIT (BTNPORT,BTNPIN);
magval = eeprom_read_byte(&magic); // Retrieve magic value
if (magval == MAGPAT) // Is memory valid
{
ctcvalue = eeprom_read_word(&ctcstor); // Retrieve last ctc setting
wpm = eeprom_read_byte(&wpmstor); // Retrieve last wpm setting
wpmcnt=(1200/YACKBEAT)/wpm; // Calculate speed
farnsworth = eeprom_read_byte(&fwstor); // Retrieve last wpm setting
yackflags = eeprom_read_byte(&flagstor); // Retrieve last flags
}
else
{
yackreset();
}
yackinhibit(OFF);
#ifdef POWERSAVE
PCMSK |= PWRWAKE; // Define which keys wake us up
GIMSK |= (1< 0))
farnsworth--;
if ((dir == DOWN) && (farnsworth < MAXFARN))
farnsworth++;
}
else // WPMSPEED
{
if ((dir == UP) && (wpm < MAXWPM))
wpm++;
if ((dir == DOWN) && (wpm > MINWPM))
wpm--;
wpmcnt=(1200/YACKBEAT)/wpm; // Calculate beats
}
volflags |= DIRTYFLAG; // Set the dirty flag
yackplay(DIT);
yackdelay(IEGLEN); // Inter Element gap
yackplay(DAH);
yackdelay(ICGLEN); // Inter Character gap
yackfarns(); // Additional Farnsworth delay
}
void yackbeat (void)
/*!
@brief Heartbeat delay
Several functions in the keyer are timing dependent. The most prominent example is the
yackiambic function that implements the IAMBIC keyer finite state machine.
The same expects to be called in intervals of YACKBEAT milliseconds. How this is
implemented is left to the user. In a more complex application this would be done
using an interrupt or a timer. For simpler cases this is a busy wait routine
that delays exactly YACKBEAT ms.
*/
{
while((TIFR & (1< MINCTC)
ctcvalue = MINCTC;
volflags |= DIRTYFLAG; // Set the dirty flag
}
void yacktune (void)
/*!
@brief Activates Tuning mode
This produces a solid keydown for TUNEDURATION seconds. After this the TX is unkeyed.
The same can be achieved by presing either the DIT or the DAH contact or the control key.
*/
{
word timer = YACKSECS(TUNEDURATION);
key(DOWN);
while(timer && (KEYINP & (1<0 if flag(s) were set
*/
{
return yackflags & flag;
}
void yacktoggle(byte flag)
/*!
@brief Toggle feature flags
When passed one (or more) flags, this routine flips the according bit in yackflags and
thereby enables or disables the corresponding feature.
@param flag A byte where any bit to toggle is set e.g. SIDETONE
@return TRUE if all was OK, FALSE if configuration lock prevented changes
*/
{
yackflags ^= flag; // Toggle the feature bit
volflags |= DIRTYFLAG; // Set the dirty flag
}
void yackerror (void)
/*!
@brief Creates a series of 8 dits
The error prosign (8 dits) can not be encoded in our coding table. A call to this
function produces it..
*/
{
byte i;
for (i=0;i<8;i++)
{
yackplay(DIT);
yackdelay(DITLEN);
}
yackdelay(DAHLEN);
}
// ***************************************************************************
// CW Playback related functions
// ***************************************************************************
static void key(byte mode)
/*!
@brief Keys the transmitter and produces a sidetone
.. but only if the corresponding functions (TXKEY and SIDETONE) have been set in
the feature register. This function also handles a request to invert the keyer line
if necessary (TXINV bit).
This is a private function.
@param mode UP or DOWN
*/
{
if (mode == DOWN)
{
if (volflags & SIDETONE) // Are we generating a Sidetone?
{
OCR0A = ctcvalue; // Then switch on the Sidetone generator
OCR0B = ctcvalue;
// Activate CTC mode
TCCR0A |= (1<='0' && c<='9') // Is it a numerical digit?
code = pgm_read_byte(&morse[c-'0']); // Find it in the beginning of array
if(c>='a' && c<='z') // Is it a character?
code = pgm_read_byte(&morse[c-'a'+10]); // Find it from position 10
if(c>='A' && c<='Z') // Is it a character in upper case?
code = pgm_read_byte(&morse[c-'A'+10]); // Same as above
// Last we need to handle special characters. There is a small char
// array "spechar" which contains the characters for the morse elements
// at the end of the "morse" array (see there!)
for(i=0;i=RBSIZE) // End of buffer reached?
{
yackerror();
i = 0;
}
yackbeat(); // 10 ms heartbeat
}
// Extimer has expired. Message has ended
if(i) // Was anything received at all?
{
rambuffer[--i] = 0; // Add a \0 end marker over last space
// Replay the message
//for (n=0;n