; W8DIZ Keyer used with the PigRig. ; April 19, 2013 ; using ATmel ATtiny13A or ATtiny25/45/85 ; ; cd ~/Desktop/Dropbox/w8diz.com/pigrig/ ; ; ./avra13 keyer_i.asm ; avrdude -P usb -p t13 -c avrispv2 -U keyer_i.hex ; avrdude -P usb -p t13 -c avrispv2 -U lfuse:w:0x7A:m ; 0x6A is default ; avrdude -P usb -p t13 -c avrispv2 -U hfuse:w:0xF9:m Brownout; 0xFF is default ; avrdude -P usb -p t45 -c avrispv2 -U keyer_i.hex ; avrdude -P usb -p t45 -c avrispv2 -U lfuse:w:0xE2:m ; Clock divided by 8 turned off; 0x62 is default ; avrdude -P usb -p t45 -c avrispv2 -U hfuse:w:0xDC:m ; Brownout; 0xDF is default ; avrdude -P usb -p t85 -c avrispv2 -U keyer_i.hex ; avrdude -P usb -p t85 -c avrispv2 -U lfuse:w:0xE2:m ; Clock divided by 8 turned off; 0x62 is default ; avrdude -P usb -p t85 -c avrispv2 -U hfuse:w:0xDC:m ; Brownout; 0xDF is default .nolist ;.include "tn45def.inc" ;.include "tn85def.inc" .include "tn13Adef.inc" .list .equ DIT_MASK = 0 .equ DAH_MASK = 1 .equ CMD = 2 .equ XMIT = 4 ; .equ XMIT = 4 on production rig .equ TONE = 3 ; .equ TONE = 3 on production rig .equ DIT = 2 ; 1 on bit and 1 off bit .equ DAH = 4 ; 3 on bits and 1 off bit ;--Rst--------Vcc-- ;--PB3--------PB2-- ;--PB4--------PB1-- ;--Gnd--------PB0-- .equ NUM_0 = 0b00111111 .equ NUM_1 = 0b00101111 .equ NUM_2 = 0b00100111 .equ NUM_3 = 0b00100011 .equ NUM_4 = 0b00100001 .equ NUM_5 = 0b00100000 .equ NUM_6 = 0b00110000 .equ NUM_7 = 0b00111000 .equ NUM_8 = 0b00111100 .equ NUM_9 = 0b00111110 .equ LET_A = 0b00000101 .equ LET_B = 0b00011000 .equ LET_C = 0b00011010 .equ LET_D = 0b00001100 .equ LET_E = 0b00000010 .equ LET_F = 0b00010010 .equ LET_G = 0b00001110 .equ LET_H = 0b00010000 .equ LET_I = 0b00000100 .equ LET_J = 0b00010111 .equ LET_K = 0b00001101 .equ LET_L = 0b00010100 .equ LET_M = 0b00000111 .equ LET_N = 0b00000110 .equ LET_O = 0b00001111 .equ LET_P = 0b00010110 .equ LET_Q = 0b00011101 .equ LET_R = 0b00001010 .equ LET_S = 0b00001000 .equ LET_T = 0b00000011 .equ LET_U = 0b00001001 .equ LET_V = 0b00010001 .equ LET_W = 0b00001011 .equ LET_X = 0b00011001 .equ LET_Y = 0b00011011 .equ LET_Z = 0b00011100 .equ CHR_ERROR = 0b01001100 .equ CHR_COMMA = 0b01110011 .equ CHR_PERIOD = 0b01010101 .equ CHR_SLASH = 0b00110010 ;R0 and R1 reserved for hardware multiply .DEF rd1l = R3 ; LSB 16-bit-number to be divided .DEF rd1h = R4 ; MSB 16-bit-number to be divided .DEF rd1u = R5 ; interim register .DEF rd2 = R6 ; 8-bit-number to divide with .def sidetone = r7 .def tone_delay = r8 ; load and count down to zero for action .def straight_key = r12 ; straight key flag .def paddles = r13 ; reverse paddles flag .def wpm_lo = r14 ; load and count down to zero for action .def wpm_hi = r15 ; load and count down to zero for action .def temp1 = r16 .def temp2 = r17 .def temp3 = r18 .def key_down = r19 .def transmit_enabled = r20 .def character = r21 .def dit_period = r22 ; 4 for dah & 2 for dit .def delay = r23 ; 100 mS per bit general delay .def delay_100_lo = r24 ; load and count down to zero for action .def delay_100_hi = r25 ; load and count down to zero for action .def delay_wpm_lo = r26 ; load and count down to zero for action .def delay_wpm_hi = r27 ; load and count down to zero for action .def capture_dit = r28 .def capture_dah = r29 ;R30/ZL and R31/ZH used for data pointer .dseg .org SRAM_START .cseg .org 0 rjmp RESET #ifdef _TN13ADEF_INC_ .org INT0addr reti ; External Interrupt 0 .org PCI0addr reti ; External Interrupt Request 0 .org OVF0addr rjmp TIM0_OVF_ISR ; Timer/Counter0 Overflow .org ERDYaddr reti ; EEPROM Ready .org ACIaddr reti ; Analog Comparator .org OC0Aaddr reti ; Timer/Counter Compare Match A .org OC0Baddr reti ; Timer/Counter Compare Match B .org WDTaddr reti ; Watchdog Time-out .org ADCCaddr reti ; ADC Conversion Complete #else .org INT0addr reti ; External Interrupt 0 .org PCI0addr reti ; ; Pin change Interrupt Request 0 .org OC1Aaddr reti ; Timer/Counter1 Compare Match 1A .org OVF1addr reti ; Timer/Counter1 Overflow .org OVF0addr rjmp TIM0_OVF_ISR ; Timer/Counter0 Overflow .org ERDYaddr reti ; EEPROM Ready .org ACIaddr reti ; Analog comparator .org ADCCaddr reti ; ADC Conversion ready .org OC1Baddr reti ; Timer/Counter1 Compare Match B .org OC0Aaddr reti ; Timer/Counter0 Compare Match A .org OC0Baddr reti ; Timer/Counter0 Compare Match B .org WDTaddr reti ; Watchdog Time-out .org USI_STARTaddr reti ; USI START .org USI_OVFaddr reti ; USI Overflow #endif RESET: #ifdef _TN13ADEF_INC_ #else ldi temp1,low(RAMEND) out SPL,temp1 ; Set stack pointer to last internal RAM location ldi temp1,high(RAMEND) out SPH,temp1 #endif ldi temp1,$07 out PORTB,temp1 ; pull-ups enabled for DIT, DAH, CMD ldi temp1,$18 out DDRB,temp1 ; set outputs for XMIT and TONE cbi PORTB,XMIT ;turn XMIT high ;ATtiny13A clock set at 9.6 MHz with the CKDIV8 fuse turned off = 0.104 uS clock ;ATtiny45 clock set at 8 MHz with the CKDIV8 fuse turned off = 0.125 uS clock ldi temp1,0b00000010 ; set timer0 prescale divisor to 8 out TCCR0B,temp1 ; using 8 MHz clock = 1 uS timer pulse ;ATtiny13A using 9.6 MHz clock = 0.832 uS timer pulse ;ATtiny45 using 8 MHz clock = 1 uS timer pulse ldi temp1,0b00000010 #ifdef _TN13ADEF_INC_ out TIMSK0,temp1 ;enable TIMER0 overflow interrupts #else out TIMSK,temp1 ;enable TIMER0 overflow interrupts #endif ldi delay_100_lo,232 ; default 100mS ldi delay_100_hi,3 ; default 100mS sbic PINB,CMD ; Skip if CMD button pressed rjmp RESET2 ldi temp1,0 ; set address for WPM value ldi temp2,0 ldi temp3,15 ; set default WPM value rcall EEPROM_WRITE ldi temp2,1 ; set address for sidetone value ldi temp3,8 ; set default sidetone value rcall EEPROM_WRITE ldi temp2,2 ; set address for paddles value ldi temp3,0 ; set default paddles value rcall EEPROM_WRITE RESET2: clr temp1 ; set address for WPM value clr temp2 rcall EEPROM_READ cpi temp3,$FF brne SET_WPM ldi temp3,15 ; default WPM SET_WPM: rcall WPM_SET clr temp1 ; set address for sidetone value ldi temp2,1 rcall EEPROM_READ cpi temp3,$FF brne SET_SIDETONE ldi temp3,8 ; default sidetone 625 Hz SET_SIDETONE: mov sidetone,temp3 mov tone_delay,sidetone ; toggle every 800 uS = 625 Hz clr temp1 ; set address for paddles value ldi temp2,2 rcall EEPROM_READ cpi temp3,$FF brne SET_PADDLES ldi temp3,0 ; default paddles value SET_PADDLES: mov paddles,temp3 clr key_down clr capture_dit clr capture_dah clr transmit_enabled clr straight_key sei ; global all interrupt enable ldi temp1,LET_O rcall SEND_CHAR ; send an "O" rcall CHAR_DELAY ldi temp1,LET_O rcall SEND_CHAR ; send an "O" sbic PINB,CMD ; Skip if CMD button pressed rjmp LOOP ldi delay,15 rcall WAIT rjmp LOOP WAIT: tst delay brne WAIT ret CHAR_DELAY: ldi delay,2 rcall WAIT ret MENU: cpi character,LET_S ; SET_SPEED ****************************** brne MENU20 SET_SPEED: ldi temp1,LET_E rcall SEND_CHAR ; send an ACK rcall GET_CHAR cpi character,1 brne SET_SPEED10 rjmp ERROR SET_SPEED10: rcall MTON ; convert morse code from character to ascii in temp1 cpi temp1,$FF brne SET_SPEED20 rjmp ERROR SET_SPEED20: cbr temp1,$30 mov temp2,temp1 ; multiply by 10 add temp2,temp1 lsl temp1 lsl temp1 lsl temp1 add temp2,temp1 ; temp2 holds upper digit for wpm ldi temp1,LET_E rcall SEND_CHAR ; send an ACK rcall GET_CHAR cpi character,1 brne SET_SPEED30 rjmp ERROR SET_SPEED30: rcall MTON ; convert morse code from character to ascii in temp1 cpi temp1,$FF brne SET_SPEED40 rjmp ERROR SET_SPEED40: cbr temp1,$30 add temp1,temp2 tst temp1 ; error if zero brne SET_SPEED50 rjmp ERROR SET_SPEED50: cpi temp1,46 ; error if same or higher brlo SET_SPEED60 rjmp ERROR SET_SPEED60: mov temp3,temp1 clr temp1 ; set up address for WPM clr temp2 rcall EEPROM_WRITE ; save WPM to EEPROM rcall WPM_SET rjmp OK MENU20: cpi character,LET_F ; SET_TONE Frequency ****************************** brne MENU30 SET_TONE: ldi temp1,LET_E rcall SEND_CHAR ; send an ACK rcall GET_CHAR cpi character,1 brne SET_TONE10 rjmp ERROR SET_TONE10: rcall MTON ; convert morse code from character to binary number in temp1 cpi temp1,0x3A brlo SET_TONE20 rjmp ERROR SET_TONE20: ldi temp2,43 sub temp1,temp2 mov sidetone,temp1 mov temp3,temp1 clr temp1 ; set up address for sidetone ldi temp2,1 rcall EEPROM_WRITE ; save sidetone to EEPROM rjmp OK ;9/14 = 357 hz ;8/13 = 385 Hz ;7/12 = 417 Hz ;6/11 = 455 Hz ;5/10 = 500 Hz ;4/9 = 555 Hz ;3/8 = 625 Hz ;2/7 = 714 Hz ;1/6 = 833 Hz ;0/5 = 1000 Hz MENU30: cpi character,LET_R ; REVERSE_KEY ****************************** brne MENU40 REVERSE_KEY: tst paddles breq REVERSE2 clr paddles ldi temp1,LET_P rcall SEND_CHAR rjmp REVERSE3 REVERSE2: inc paddles ldi temp1,LET_X rcall SEND_CHAR REVERSE3: clr temp1 ; set up address for REVERSE_PADDLES ldi temp2,2 mov temp3,paddles rcall EEPROM_WRITE ; save PADDLES value to EEPROM rjmp LOOP MENU40: cpi character,LET_E ; STRAIGHT_KEY ****************************** breq KEY5 cpi character,LET_T brne MENU50 KEY5: inc straight_key ldi temp1,LET_S ; send "S" for straight key active rcall SEND_CHAR KEY10: sbic PINB,CMD ; Skip if CMD button pressed rjmp KEY15 clr straight_key rjmp OK KEY15: sbis PINB,PB0 ; Skip if DIT paddle open rjmp KEY20 sbis PINB,PB1 ; Skip if DAH paddle open rjmp KEY20 clr key_down clr transmit_enabled rjmp KEY10 KEY20: ; key down ser transmit_enabled ser key_down rjmp KEY10 MENU50: #ifdef _MESSAGE_ cpi character,LET_M ; MESSAGE ****************************** brne MENU60 ldi ZH,high(2*msg1) ldi ZL,low(2*msg1) MESSAGE1: lpm temp1,Z+ push ZH push ZL tst temp1 breq MSG_COMPLETE ldi ZH,high(2*xref1) ldi ZL,low(2*xref1) MESSAGE3: lpm temp2,Z+ cp temp1,temp2 breq MESSAGE7 lpm temp2,Z+ ; dummy to increment pointer only rjmp MESSAGE3 MESSAGE7: lpm temp1,Z+ ser transmit_enabled rcall SEND_CHAR rcall CHAR_DELAY pop ZL pop ZH rjmp MESSAGE1 MSG_COMPLETE: pop ZL pop ZH rjmp LOOP MENU60: #endif ERROR: ldi temp1,CHR_ERROR rcall SEND_CHAR rjmp LOOP OK: ldi temp1,LET_R ; ROGER rcall SEND_CHAR clr capture_dit clr capture_dah LOOP: ser transmit_enabled rcall READ_PADDLES sbic PINB,CMD ; Skip if CMD button pressed rjmp LOOP clr key_down clr transmit_enabled ldi temp1,LET_R ; READY rcall SEND_CHAR rcall GET_CHAR cpi character,1 breq LOOP20 rjmp MENU ; LOOP20: sbic PINB,CMD ; Skip if CMD button pressed rjmp ERROR rjmp RESET READ_PADDLES: tst capture_dit breq DAH10 lsl character tst paddles brne DIT15 ldi dit_period,DIT rjmp DIT20 DIT15: inc character ldi dit_period,DAH DIT20: tst dit_period brne DIT20 clr capture_dit DAH10: tst capture_dah breq NEXT lsl character tst paddles brne DAH15 ldi dit_period,DAH inc character rjmp DAH20 DAH15: ldi dit_period,DIT DAH20: tst dit_period brne DAH20 clr capture_dah NEXT: ret WPM_SET: rcall DIVIDE16BY8 mov delay_wpm_lo,temp1 mov delay_wpm_hi,temp2 mov wpm_lo,delay_wpm_lo mov wpm_hi,delay_wpm_hi ret GET_CHAR: push temp1 push temp2 ldi delay,30 ; 3 sec get char period ldi character,1 GET_CHAR10: rcall READ_PADDLES mov temp2,character cpi character,1 brne GET_CHAR20 tst delay brne GET_CHAR10 rjmp GET_CHAR_EXIT GET_CHAR20: mov temp1,wpm_hi lsl temp1 ;add temp1,wpm_hi mov delay,temp1 ; set delay to 5 x wpm_hi GET_CHAR30: tst delay brne GET_CHAR40 GET_CHAR_EXIT: pop temp2 pop temp1 ret GET_CHAR40: rcall READ_PADDLES cp temp2,character breq GET_CHAR30 mov temp2,character rjmp GET_CHAR20 SEND_CHAR: ; character is in temp1 push temp2 ldi temp2,9 SEND10: dec temp2 lsl temp1 brcc SEND10 SEND20: dec temp2 breq SEND_CHAR_EXIT lsl temp1 brcc SEND_DIT SEND_DAH: ldi dit_period,DAH SEND_DAH1: tst dit_period brne SEND_DAH1 rjmp SEND20 SEND_DIT: ldi dit_period,DIT SEND_DIT1: tst dit_period brne SEND_DIT1 rjmp SEND20 SEND_CHAR_EXIT: pop temp2 ret TIM0_OVF_ISR: push temp1 in temp1,SREG push temp1 #ifdef _TN13ADEF_INC_ ;ldi temp1,256-120 ; counts up to 120 ldi temp1,256-110 ; counts up to 110 #else ldi temp1,256-100 ; counts up to 100 #endif out TCNT0,temp1 ; each overflow = 100 uS sbis PINB,PB0 ; Skip if Bit in I/O Register is set (dit not active) ldi capture_dit,1 sbis PINB,PB1 ; Skip if Bit in I/O Register is set (dah not active) ldi capture_dah,1 tst delay ; used for command timing breq TIM0_OVF_10 sbiw delay_100_lo,1 ; decrement delay_100 timer brne TIM0_OVF_10 ldi delay_100_lo,232 ; default 100mS ldi delay_100_hi,3 ; default 100mS dec delay TIM0_OVF_10: tst key_down brne TIM0_OVF_30 tst straight_key breq TIM0_OVF_12 cbi PORTB,XMIT ;turn XMIT low TIM0_OVF_12: tst dit_period breq TIM0_OVF_EXIT sbiw delay_wpm_lo,1 ; decrement wpm timer brne TIM0_OVF_20 mov delay_wpm_lo,wpm_lo mov delay_wpm_hi,wpm_hi dec dit_period breq TIM0_OVF_EXIT TIM0_OVF_20: cpi dit_period,1 breq TIM0_OVF_50 TIM0_OVF_30: dec tone_delay brne TIM0_OVF_EXIT ;TONE producing routine tst transmit_enabled breq TONE2 sbi PORTB,XMIT ;turn XMIT high TONE2: mov tone_delay,sidetone sbi PINB,TONE ; toggle every 800 uS = 625 Hz rjmp TIM0_OVF_EXIT TIM0_OVF_50: sbi PORTB,TONE ;turn output high cbi PORTB,XMIT ;turn XMIT low TIM0_OVF_EXIT: pop temp1 out SREG,temp1 pop temp1 reti DIVIDE16BY8: ; divide 12000 by temp1, result temp1=LSB temp2=MSB mov rd2,temp3 ldi temp1,$E0 mov rd1l,temp1 ldi temp2,$2E mov rd1h,temp2 div8: clr rd1u ; clear interim register clr temp2 ; clear result (the result registers clr temp1 ; are also used to count to 16 for the inc temp1 ; division steps, is set to 1 at start) div8a: clc ; clear carry-bit rol rd1l ; rotate the next-upper bit of the number rol rd1h ; to the interim register (multiply by 2) rol rd1u brcs div8b ; a one has rolled left, so subtract cp rd1u,rd2 ; Division result 1 or 0? brcs div8c ; jump over subtraction, if smaller div8b: sub rd1u,rd2; subtract number to divide with sec ; set carry-bit, result is a 1 rjmp div8d ; jump to shift of the result bit div8c: clc ; clear carry-bit, resulting bit is a 0 div8d: rol temp1 ; rotate carry-bit into result registers rol temp2 brcc div8a ; as long as zero rotate out of the result stop: ret MTON: ; convert morse code from character to binary number in temp1 push temp2 ldi ZH,high(2*xref2) ldi ZL,low(2*xref2) MTON1: lpm temp1,Z+ lpm temp2,Z+ tst temp1 breq MTON8 cp character,temp2 brne MTON1 rjmp MTON9 MTON8: ldi temp1,$FF MTON9: pop temp2 ret EEPROM_READ: sbic EECR,EEPE rjmp EEPROM_READ ; Wait for completion of previous write out EEARH,temp1 ; Set up address register out EEARL,temp2 sbi EECR,EERE ; Start eeprom read by writing EERE in temp3,EEDR ; Read data from data register ret EEPROM_WRITE: sbic EECR,EEPE rjmp EEPROM_write ; Wait for completion of previous write push temp1 ldi temp1,0 ; Set Programming mode out EECR,temp1 pop temp1 out EEARH,temp1 ; Set up address register out EEARL,temp2 out EEDR,temp3 ; Write data temp3 to data register sbi EECR,EEMPE ; Write logical one to EEMPE sbi EECR,EEPE ; Start eeprom write by setting EEPE ret msg1: ; 1234567890123456789012345678901234567890 .db "CQ CQ CQ DE W8DIZ W8DIZ @",0 xref1: .db " ",0b00000001 xref2: .db "0",0b00111111 .db "1",0b00101111 .db "2",0b00100111 .db "3",0b00100011 .db "4",0b00100001 .db "5",0b00100000 .db "6",0b00110000 .db "7",0b00111000 .db "8",0b00111100 .db "9",0b00111110 .db "A",0b00000101 .db "B",0b00011000 .db "C",0b00011010 .db "D",0b00001100 .db "E",0b00000010 .db "F",0b00010010 .db "G",0b00001110 .db "H",0b00010000 .db "I",0b00000100 .db "J",0b00010111 .db "K",0b00001101 .db "L",0b00010100 .db "M",0b00000111 .db "N",0b00000110 .db "O",0b00001111 .db "P",0b00010110 .db "Q",0b00011101 .db "R",0b00001010 .db "S",0b00001000 .db "T",0b00000011 .db "U",0b00001001 .db "V",0b00010001 .db "W",0b00001011 .db "X",0b00011001 .db "Y",0b00011011 .db "Z",0b00011100 .db "?",0b01001100 .db ",",0b01110011 .db ".",0b01010101 .db "/",0b00110010 .db "@",0b00101010 ; "AR" .db "$",0b01000101 ; "SK" .db 0,0