A few notes about this code:
- I like modular code with small amounts of code in each file, so I typically end up with lots of files in a project. This one is no exception. There are lots of files, but they’re small.
- I haven’t gotten around to actually hooking up a keyboard to this project and trying it out. The core keyboard routines all work and have been tested elsewhere, so they’re in relatively good shape, but I have no idea if the keyboard features of this project work or not. Let me know if you try it.
- This was all written specifically for an Atmel ATTiny84 running at 1 MHz. It’s easy to port to other Atmel MCUs just by making changes to interface.h and timing.h. There might be a couple of other gotchas, but I’m not aware of them off hand.
- I’m posting the main morse processing code below so casual readers can readily take a peak at it. It works for paddle, memory, and, in theory, for keyboard as well.
- You can download the complete source here.
volatile word m_millis;
volatile word m_delay;
volatile word m_dit_ms;
volatile word m_dah_ms;
volatile word m_elt_ms;
volatile word m_char_ms;
volatile word m_word_ms;
volatile char m_last_char;
volatile byte m_byte;
volatile byte m_wpm;
volatile byte m_weight;
volatile byte m_volume;
volatile byte m_pre_paddle;
volatile byte m_tx_record;
volatile byte m_element_count;
volatile byte m_delay_type;
ABF_Declare( completeFifo, 16 );
ABF_Declare( sendFifo, 128 );
#define PADDLE_BUFFER_SIZE 2
volatile byte m_paddleBuffer[PADDLE_BUFFER_SIZE];
volatile byte m_pb_index = 0;
volatile byte m_pb_count = 0;
volatile byte m_pb_prev = 0;
void Morse_SetSpeed( byte wpm, byte weight )
{
if( wpm < 5 )
{
wpm = 5;
}
else if( wpm > 50 )
{
wpm = 50;
}
m_wpm = wpm;
m_weight = weight;
m_dit_ms = 1100 / wpm;
m_dah_ms = (( 3 + weight ) * m_dit_ms) / 2;
m_elt_ms = m_dit_ms - 1;
m_char_ms = 3 * m_dit_ms - 1;
m_word_ms = 5 * m_dit_ms - 1;
}
static inline void KeyUp()
{
TX_PORT &= ~TX_BIT;
PWM_SetLevel( 0 );
}
static inline void KeyDown()
{
TX_PORT |= TX_BIT;
PWM_SetLevel( m_volume );
}
static inline void EnqueuePaddleInput()
{
// unoptimized paddle buffering implementation to support
// going back and forth between paddles prematurely
if( m_pb_count < PADDLE_BUFFER_SIZE )
{
if( ( PADDLE_DAH_ON ) &&
( m_pb_prev != MORSE_BYTE_DAH ) &&
( m_byte != MORSE_BYTE_DAH ) )
{
m_paddleBuffer[(m_pb_index+m_pb_count)%PADDLE_BUFFER_SIZE] = MORSE_BYTE_DAH;
m_pb_prev = MORSE_BYTE_DAH;
m_pb_count++;
}
if( ( PADDLE_DIT_ON ) &&
( m_pb_prev != MORSE_BYTE_DIT ) &&
( m_byte != MORSE_BYTE_DIT ) )
{
m_paddleBuffer[(m_pb_index+m_pb_count)%PADDLE_BUFFER_SIZE] = MORSE_BYTE_DIT;
m_pb_prev = MORSE_BYTE_DIT;
m_pb_count++;
}
}
}
static inline byte DequeuePaddleInput()
{
byte rc = 0;
if( m_pb_count > 0 )
{
rc = m_paddleBuffer[m_pb_index];
m_pb_index = (m_pb_index+1)%PADDLE_BUFFER_SIZE;
m_pb_count -= 1;
if( m_pb_count == 0 )
m_pb_prev = 0;
}
return rc;
}
void Morse_Init()
{
ABF_Init( completeFifo );
ABF_Init( sendFifo );
// set up our "tx" pin
TX_DDR |= TX_BIT;
// set pull-ups on our input pins
PADDLE_PORT |= (PADDLE_DAH|PADDLE_DIT);
// set up PWM sound output
PWM_Init();
// now set up our timer overflow interrupt
SetupTimerInterrupt();
// todo: pull these params out of eeprom
Morse_SetSpeed( 20, 3 );
Morse_SetVolume( 10 );
}
// service routine for the timer interrupt
TIMER_INTERRUPT()
{
// if the paddle is being touched while we're sending letters
// then we need to stop immediately and clear the send queue
if( !MORSE_BYTE_IS_ELEMENT(m_byte) && PADDLE_TOUCHED )
{
KeyUp();
m_delay = 0;
m_byte = 0;
ABF_Reset( sendFifo );
Memkey_SetPlaybackState( false );
}
// if m_delay is set (non-zero) then we're in the middle of
// sending a dit, a dah or some space
if( m_delay != 0 )
{
word elapsed = GetMillis() - m_millis;
// check for paddle input
EnqueuePaddleInput( elapsed );
if( elapsed < m_delay )
{
return;
}
// we've waited the appropriate time now stop keying the TX
KeyUp();
// delay between dits and dahs within a character
if( m_delay_type == MORSE_DT_DIT || m_delay_type == MORSE_DT_DAH )
{
// add some intra-element delay here
m_delay = MORSE_ELEMENT_DELAY;
m_delay_type = MORSE_DT_ELEMENT;
m_millis = GetMillis();
return;
}
if( !MORSE_BYTE_IS_ELEMENT(m_byte) )
{
m_byte <<= 1;
if( m_byte == 0x80 )
{
m_byte = 0;
// we're at the end of the morse character
// add some intra-character delay
m_delay = MORSE_CHAR_DELAY;
m_delay_type = MORSE_DT_CHAR;
m_millis = GetMillis();
m_tx_record |= 0x80 >> m_element_count;
ABF_Push( completeFifo, m_tx_record );
m_tx_record = 0;
m_element_count = 0;
return;
}
}
m_delay = 0;
}
if( PADDLE_TOUCHED || MORSE_BYTE_IS_ELEMENT(m_byte) )
{
m_byte = DequeuePaddleInput();
// last input came from the paddle rather than keyboard
// if we have some paddle input queued up then use that
// otherwise, check the physical paddle for input
if( m_byte == 0 )
{
if( PADDLE_DAH_ON )
m_byte = MORSE_BYTE_DAH;
if( PADDLE_DIT_ON )
m_byte = MORSE_BYTE_DIT;
}
if( m_byte == 0 )
{
m_delay = 0;
m_byte = 0;
return;
}
if( m_element_count == 0 )
{
if( ( GetMillis() - m_millis ) > MORSE_CHAR_DELAY )
{
// long delay between paddle inputs
// must be a space
ABF_Push( completeFifo, 0 );
}
}
if( m_byte == MORSE_BYTE_DAH )
{
m_delay = m_dah_ms;
m_delay_type = MORSE_DT_DAH;
m_tx_record |= 0x80 >> m_element_count++;
}
else if( m_byte == MORSE_BYTE_DIT )
{
m_delay = m_dit_ms;
m_delay_type = MORSE_DT_DIT;
m_element_count++;
}
}
else if( m_byte == 0 )
{
// completed sending a character (or user stopped touching the paddle)
if( m_element_count > 0 && ( GetMillis() - m_millis ) > MORSE_CHAR_DELAY - MORSE_ELEMENT_DELAY )
{
// user completed a character using the paddles
// enqueue the completed character
m_tx_record |= 0x80 >> m_element_count;
ABF_Push( completeFifo, m_tx_record );
m_millis = GetMillis();
m_tx_record = 0;
m_element_count = 0;
return;
}
// try to get another character from the queue
if( ABF_Empty( sendFifo ) )
{
// nothing in the queue
Memkey_SetPlaybackState( false );
return;
}
ABF_Pop( sendFifo, (byte*)&m_byte );
if( m_byte == 0 )
{
// send some intra-word space
m_delay = MORSE_WORD_DELAY;
m_delay_type = MORSE_DT_WORD;
m_millis = GetMillis();
ABF_Push( completeFifo, 0 );
return;
}
}
else
{
// still in the process of sending a character from the queue
if( m_byte & 0x80 )
{
m_delay = m_dah_ms;
m_delay_type = MORSE_DT_DAH;
m_tx_record |= 0x80 >> m_element_count++;
}
else
{
m_delay = m_dit_ms;
m_delay_type = MORSE_DT_DIT;
m_element_count++;
}
}
KeyDown();
m_millis = GetMillis();
return;
}
[…] source is here. Posted by gorsat Filed in Uncategorized Leave a Comment […]