Wednesday, February 3, 2016

Straight Key Keyer

There was talk recently on the QRP-L mailing list about making an automatic keyer that could record and playback a straight key.  Oddly enough, this is something that I've always considered doing. I'm a fan of the Farnsworth CW method, and I typically need a longer space between letters to accurately copy what I'm receiving. One issue I have always had with other keyers is that the time between characters isn't usually adjustable.

The solution seemed like an easy enough job for any PIC with an EEPROM. After a little fiddling I came up with a simple but effective code in MPLAB XC8. My PIC of choice was a 16LF1938. This chip really is overkill for this application, but it at least gives me tons of room to expand to other functions as I decide.

PORTB.3 = output
PORTB.0 = yellow LED recording indicator
PORTB.1 = key
PORTB.2 = record/play/cancel button


This video is a quick demonstration of how it will record anything, including a sloppy fist.

  1. Is key pressed?
    1. Key radio.
  2. Is record button held down for 3 seconds?
    1. Erase eeprom.
    2. Watch for first key press.
      1. Loop until key is released. Checking every 10ms. (This gives a total recordable time of 2.55 seconds per mark or space.)
      2. Record the duration in EEPROM and increment the EEPROM address.
      3. Record duration until key pressed again or until 2.54s reached. Increment EEPROM address.
      4. Continue loop until record button is pressed again.
  3. When record button is clicked briefly, EEPROM is 'played back' in a key down/key up rotating format until an EEPROM value of 255 is read, or EEPROM address of 255 is reached which equates to ~126 marks (more than enough for even the windiest CQ call).
That's all there is to it. I've posted my code below incase anyone is interested. I realize using interrupts would be much more efficient, but this was just a quick hack job to get it working. Also, I'm hardly a programmer, just a tinkerer...


 /*  
  * File:  SKeyer.c  
  * Author: AB9LM  
  *  
  * Created on January 26, 2016, 6:06 PM  
  */  
 #define _XTAL_FREQ 8000000  
 #include <xc.h>  
 // #pragma config statements should precede project file includes.  
 // Use project enums instead of #define for ON and OFF.  
 // CONFIG1  
 #pragma config FOSC = INTOSC  // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)  
 #pragma config WDTE = OFF    // Watchdog Timer Enable (WDT disabled)  
 #pragma config PWRTE = OFF   // Power-up Timer Enable (PWRT disabled)  
 #pragma config MCLRE = OFF   // MCLR Pin Function Select (MCLR/VPP pin function is digital input)  
 #pragma config CP = OFF     // Flash Program Memory Code Protection (Program memory code protection is disabled)  
 #pragma config CPD = OFF    // Data Memory Code Protection (Data memory code protection is disabled)  
 #pragma config BOREN = ON    // Brown-out Reset Enable (Brown-out Reset enabled)  
 #pragma config CLKOUTEN = OFF  // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)  
 #pragma config IESO = ON    // Internal/External Switchover (Internal/External Switchover mode is enabled)  
 #pragma config FCMEN = ON    // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled)  
 // CONFIG2  
 #pragma config WRT = OFF    // Flash Memory Self-Write Protection (Write protection off)  
 #pragma config PLLEN = OFF    // PLL Enable (4x PLL enabled)  
 #pragma config STVREN = ON   // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)  
 #pragma config BORV = LO    // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)  
 #pragma config LVP = ON     // Low-Voltage Programming Enable (Low-voltage programming enabled)  
 //End config  
 char address;  
 void blink_yellow(void);  
 void system_setup(){  
  OSCCON = 0b01110000;  
  OPTION_REG = 0b00000000;  
  TRISB = 0b00000110;  
  PORTB = 0b00000000;  
  TRISC = 0b00010000;  
  ANSELB = 0b00000000;  
  WPUB1 = 1;  
  WPUB2 = 1;  
 }  
 void delay_ms(unsigned int milliseconds)  
 {  
   while(milliseconds > 0)  
   {  
    milliseconds--;  
     __delay_us(990);  
   }  
 }  
 void erase_eeprom(){  
   char i = 0;  
   for(i=0; i != 255; i++){  
     eeprom_write(i, 0xff);  
   }  
 }  
 void record_mark(){  
   char count = 0;  
   while(!RB1){  
     LATBbits.LATB3 = 1;  
     count += 1;  
     delay_ms(10);  
   }  
   LATBbits.LATB3 = 0;  
   eeprom_write(address, count);  
 }  
 void record_space(){  
   char count = 0;  
   while(RB1){  
     count += 1;  
     delay_ms(10);  
     if(!RB2 | count == 255){  
       eeprom_write(address, count - 1);  
       break;  
     }  
   }  
   eeprom_write(address, count);  
 }  
 void blink_yellow(void){  
   LATBbits.LATB0 = 0;    
   delay_ms(50);  
   LATBbits.LATB0 = 1;  
   delay_ms(50);  
   LATBbits.LATB0 = 0;  
 }  
 void send_eeprom(){  
   char delay = 0x00;  
   char address = 0x00;  
   while(delay != 0xFF){  
     if(!RB1){  
       LATBbits.LATB3 = 0;  
       blink_yellow();  
       delay_ms(100);  
       while(!RB1){}  
       break;  
     }  
     delay = eeprom_read(address);  
     if(delay == 0xFF){  
       break;  
     }  
     LATBbits.LATB3 = 1;  
     delay_ms(delay * 10);  
     LATBbits.LATB3 = 0;  
     address += 1;  
     delay = eeprom_read(address);  
     delay_ms(delay * 10);  
     address += 1;  
   }  
 }  
 int main(){  
   address = 0x00;  
   int ms_count = 0;  
   system_setup();  
   while(1){  
     if(RB1 == 0){  
       LATBbits.LATB3 = 1;  
     }  
     else{  
       LATBbits.LATB3 = 0;  
     }  
     ms_count = 0;  
     while(!RB2){  
       ms_count += 1;  
       delay_ms(10);  
       if(RB2){  
         delay_ms(100);  
         send_eeprom();  
       }  
       if(ms_count > 200){  
         erase_eeprom();  
         address = 0x00;  
         while(1){  
           LATBbits.LATB0 = 1;  
           while(!RB2){}  
           if(!RB1){  
             record_mark();  
             address += 1;  
             record_space();  
             address += 1;  
             eeprom_write(0xFF,address);  
           }          
           if(!RB2 | address >= 0xF0){  
             while(!RB2){}  
             LATBbits.LATB0 = 0;  
             delay_ms(100);  
             break;  
           }  
         }  
       }  
     }  
   }  
   return 0;  
 }