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 […]