Tuesday, August 2, 2016

A Simple ATTiny13 Keyer / Paddle Keyer / ElectronicKeyer

;************************************************************************************

;            A Simple ATTiny13 Keyer / Electronic Keyer                                *

; Program by : Ram Sankar.V | callsign : VU3XVR | ramvsankar @ g m a i l . c o m    *
; Version 1.0, | Date : 02-Aug-2016                                                    *
; www.vu3xvr.blogspot.com                                                            *
; Compiled using AVR Studio 4.19                                                    *
; Microcontroller : ATMEL ATTiny13, clock 9.6MHZ , internally divide by 8            *      


;                                                                                    *

; Features:                                                                            *

;    Selectable IAMBIC Mode A. B or Snglehandle key                                    *
;    Adjustable WPM using potentiometer (fixed to 5,10,15,20,25,30,35,40 wpm)        *
;    Twin-T Sine wave tone oscillator, a very pleasant tone for your ear                *
;    Very small code size, 216 bytes                                                    *
;    Easy to customize or automate for beacons                                        *  
;    On board LED indicatior of dit & dah                                            *
;                                                                                    *

; Operation:                                                                        *

;    By default its selected with IAMBIC B mode                                        *
;    To enable IAMBIC A mode, keep press DAH key while power ON                         *
;    To enable single handle key mode, Connect the key to DIT pin                    *
;        and keep press DIT key while power ON                                         *
;    To change WPM, adjust the VR1, and restart to changes take effect.                *
;    To connect it to rig plug in streo pin into headphone jack (written "To Rig")    *
;        Now the on board sine wave tone oscillator is disabled.                        *
;                                                                                    *
;************************************************************************************

;                       Iambic mode B     Paddle Keyer                                *

; while we're generating a dit or dah, we have to watch for the                     *
; opposite paddle to get closed and then remember it so we can immediately send its    *
; element after the current one finishes. Ideally, we would be checking the other    *
; paddle contact after every delaymillis subroutine.                                 *
; Credits : Rich Helneck, AC7MA, author of "A Simple PICAXE Keyer"                    *
;************************************************************************************                                                        *
; This program is free software. you can redistribute it and/or                        *
; modify it under the terms of the GNU General Public License.                        *
; This program is distributed in the hope that it will be useful,                    *
; but WITHOUT ANY WARRANTY                                                            *

Schematics :  

https://drive.google.com/open?id=0B-XdS52xCN8wNVc5SE54dWZITDg

Complete source code, schematics can be download from here

;************************************************************************************
;                 ____________                                                        *
;            1 /            |8                                                        *
;   RESET o--|            |--o VCC                                                    *
;                  2|     AT      |7                                                        *
;    -PB3   o--|             |--o PB2 --->> ADC1 / WPM potentiometer                    *
;                  3|    tiny     |6                                                        *
; OUT PB4 o--|             |--o PB1 --->> DAH                                        *
;                  4|     13      |5                                                        *
;     GND o--|             |--o PB0 --->> DIT                                        *
;                    |_____________|                                                        *
;                                                                                    *
;************************************************************************************
.nolist
.include "tn13def.inc"     ;(ATTiny13 definitions)
.list

.def    A             = R16 ; General purpose accumulator
.def    counter         = R17    ; Used for delayMillis counter
.def    wpm            = R18    ; Words per minute value read from ADC
.def    flagRegister    = R19    ; register reserved for latchflag compare

.equ    ditPin        = PB0
.equ    dahPin        = PB1
.equ    outPin        = PB4  
.equ    ledPin        = PB3

.equ    ditLatch        = 0    ; BIT 0 of register flagRegister used for dit key
.equ    dahLatch        = 1    ; BIT 1 of register flagRegister used for dah key

.equ    preloadTimer    = 4 ; prescale 4, @1.2MHz, = 1.05 milli Secs

.ORG 0000
    RJMP ON_RESET ;Reset vector
.ORG 0006
    RJMP TIM0_COMPA ; Timer compare match interrupt handler

;********************************************************************
;             TIMER OVER-FLOW INTERUPT ROUTINE                        *
;********************************************************************
; Clock Speed = 9.6MHz ; internaly divide by 8 ; Prescaler = 256    *
; 9.6MHz /8/ 256 = 4687.5Hz = 0.00021 Secs pulse                    *
; Preloader = 4 ; counting from 0-4 = 5 counts                        *
; 0.00021 * 5 = 0.00105secs  which is approx equal to 1 milli Sec    *
;********************************************************************
TIM0_COMPA:
    INC    counter    ; this counter is used in delaymillis
RETI

;************************************************************
;        Microcontroller Initiation Procedure                *
;************************************************************
ON_RESET:
    LDI    A, (1<<outPin) | (1<<ledPin)
    OUT    DDRB, A    ; set PB3, PB4 as output
    LDI    A, (1<<ditPin) | (1<<dahPin)
    OUT    PORTB, A    ; set internal pullup
    LDI    A, (1<<COM0A0) | (1<<WGM01)    ; set CTC Mode
    OUT    TCCR0A, A
    LDI    A, (1<<CS02)
    OUT    TCCR0B,A     ; Set prescaler to 1/256
    LDI    A, (1<<OCIE0A)
    OUT    TIMSK0, A    ; Enable timer compare match interrupt
    LDI    A,preloadTimer
    OUT    OCR0A, A    ; load the compare register = 4
    SEI     ; Enable interrupts globally
    LDI    A,(1<<ADEN) | (1<<ADPS1) | (1<<ADPS0)
    OUT    ADCSRA, A    ; ADC enable, ADC prescaler div factor 8 for better stability
    LDI    A, (0<<REFS0) | (1<<ADLAR) | (1<<MUX0)
    OUT    ADMUX, A     ; AVcc, left adjust( we need just ADCH only), ADC1
    SBI    ADCSRA, ADSC ; start ADC conversion
keep_polling:
    SBIS    ADCSRA, ADIF ; look for ADC conversion finish flag ADIF
    RJMP    keep_polling  
    RCALL map        ; wpm to millis conversion loop. This determines the speed

    SBR    flagRegister, (1<<ditLatch) | (1<<dahLatch)
    RCALL delayMillis

    SBIS    PINB, dahPin    ; key not pressed
    RCALL iambicModeA
    SBIS    PINB, ditPin    ; key not pressed
    RCALL singleHandleMode
    RCALL    iambicModeB

;************************************************************
;*        Main loop starts here                                *
;************************************************************
iambicModeB:
    SBIS    PINB, ditPin    ; check if dit key is pressed
    RCALL dit        ; call dit routine
    SBRS    flagRegister, dahLatch    ; check if dit  key is pressed during dah key
    RJMP    dahLatchLoop

    SBIS    PINB, dahPin    ; check if dah key is pressed
    RCALL dah        ; call dah routine
    SBRS    flagRegister, ditLatch    ; check if dah key pressed during dit key
    RJMP    ditLatchLoop
    RJMP iambicModeB

ditLatchLoop:
    SBR    flagRegister, (0<<ditLatch)
    RCALL dit
    RJMP iambicModeB

dahLatchLoop:
    SBR    flagRegister, (0<<dahLatch)
    RCALL dah
    RJMP iambicModeB

iambicModeA:
    SBIS    PINB, ditPin    ; check if dit key is pressed
    RCALL dit        ; call dit routine

    SBIS    PINB, dahPin    ; check if dah key is pressed
    RCALL dah        ; call dah routine

    RJMP iambicModeA

singleHandleMode:
    SBIS    PINB, ditPin    ; check if dit key is pressed
    RJMP    outputON
    SBIC    PINB, ditPin    ; check if dit key is pressed
    RJMP    outputOFF
    RJMP singleHandleMode

outputON:
    SBI    PORTB, outPin         ;
    SBI    PORTB, ledPin
    RJMP singleHandleMode

outputOFF:  
    CBI    PORTB, outPin
    CBI    PORTB, ledPin
    RJMP singleHandleMode


;************************ Main Loop ENDS HERE **************

;************************************************************
;*        DIT Subroutine                                        *
;************************************************************
dit:
    SBI    PORTB, outPin
    SBI    PORTB, ledPin
    RCALL delayMillis
    SBIS    PINB, dahPin ; skip next If dah key is NOT pressed
    SBR    flagRegister, (1<<dahLatch) ; read this if dah key pressed
    CBI     PORTB, outPin
    CBI    PORTB, ledPin
    RCALL delayMillis
    RET

;************************************************************
;*        DIT Subroutine                                        *
;************************************************************
dah:
    SBI    PORTB, outPin
    SBI    PORTB, ledPin
    RCALL delayMillis
    SBIS    PINB, ditPin     ; skip next If dit key is NOT pressed
    SBR    flagRegister, (1<<ditLatch) ; read this if dit key pressed
    RCALL delayMillis
    SBIS    PINB, ditPin
    SBR    flagRegister, (1<<ditLatch)
    RCALL delayMillis
    SBIS    PINB, ditPin
    SBR    flagRegister, (1<<ditLatch)
    CBI     PORTB, outPin
    CBI    PORTB, ledPin
    RCALL delayMillis
    RET

;************************************************************
;*        Delay Millis Subroutine                                *
;************************************************************
delayMillis:
    LDI    counter, 0
millisStart:
    CP    counter, wpm    ; check counter value with wpm value
    BRNE    millisStart        ; do until counter = wpm
    RET

;************************************************************
;*    Subroutine for MAP value of ADC into WPM                  *
;************************************************************
map:
    IN    A, ADCH    ; load ADC value into temp register A

    LDI    wpm, 30    ; re-assign wpm 30, 1200ms/40wpm = 30ms
    CPI    A, 10     ; check with ADC value
    BRLO    mapEnd

    LDI    wpm, 34    ; delaymillis = 1200ms/35wpm = 34ms
    CPI    A, 35
    BRLO    mapEnd

    LDI    wpm, 40    ; delaymillis = 1200ms/30wpm = 40ms
    CPI    A, 75
    BRLO    mapEnd

    LDI    wpm, 48    ; delaymillis = 1200ms/25wpm = 48ms
    CPI    A, 105
    BRLO    mapEnd

    LDI    wpm, 60    ; delaymillis = 1200ms/20wpm = 60ms
    CPI    A, 145
    BRLO    mapEnd

    LDI    wpm, 80    ; delaymillis = 1200ms/15wpm = 80ms
    CPI    A, 175
    BRLO     mapEnd

    LDI    wpm, 120    ; delaymillis = 1200ms/10wpm = 120ms
    CPI    A, 185
    BRLO    mapEnd

    LDI    wpm, 240     ; delaymillis = 1200ms/5wpm = 240ms
mapEnd:  
    RET