;MORSE Keyer ;Button 1 = dot, 1 = dash, 3 = speed ;assembled using Atmel's avrasm assembler. ;Define Registers Names .def EEDAT =r18 ;register to hold EEPROM data .def EEAD =r21 ;register to hold EEPROM data address .def TEMP =r16 ;temporary register for transferring .def TEMP1 =r17 ;used by timer0 .def TEMPCNT =r19 ;keeps track of number of interrupts .def COUNT =r20 ;speed .def CNTLOW =r22 ;speed also .def HIDEL =r23 ;delay register in debounce .def STATFLG =r24 ;status flag, whether to play tone .def XLO =r26 ;X reg pair Lo byte .def XHI =r27 ;X reg Hi .def YLO =r28 ;Y reg Lo .def YHI =r29 ;Y reg Hi .def ZLO =r30 ;Z reg Lo .def ZHI =r31 ;Z reg Hi ;Define Label addresses .equ SREG =0x3F .equ DDRB =0x17 ;Data Direction Register, Port B .equ DDRD =0x11 ;Data Direction Register, Port D .equ PINB =0x16 ;Input Pins, Port B .equ PORTB =0x18 ;Data Register, Port B .equ PORTD =0x12 ;Data Register, Port D .equ TCCR0 =0x33 ;Timer0 prescale setup register .equ TIMSK =0x39 ;Timer0 control register .equ TCNT0 =0x32 ;Timer0 reload register .equ EEAR =0x1E ;EEPROM Address register .equ EEDR =0x1D ;EEPROM Data register .equ EECR =0x1C ;EEPROM Control register .equ RAM =0x3D ;Stack Pointer .equ TONE_ON =255 ;tone duration along with COUNT .equ TONE_OFF=0 .equ LOW =20 ;low limit of speed count .equ HIGH =240 ;high limit of speed count .equ T1 = -70 ;Timer0 reload value for timer0 (BA) ;------------------------------------------------------------------- .cseg .org 0x000 ;Start at 000 rjmp RESET ;Jump to reset reti ;Interupt 0 vector reti ;Interupt 1 reti ;Timer/Counter1 Capture Event reti ;Timer/Counter1 Compare Match reti ;Timer/Counter1 Overflow rjmp TMR0 ;Timer/Counter0 Overflow reti ;UART, Rx Complete reti ;UART Data Register Empty reti ;UART, TX Complete reti ;Analog Comparator ;-------------------------------------------------------------------- .org 0x0d ;start of main program RESET: ldi TEMP,0xDF out RAM,TEMP ;load Stack Pointer to top SRAM ldi TEMP, 0b00000000 ;load register r16 with all 0's out DDRB,TEMP ;configure PORTB for all inputs ldi TEMP, 0b11111111;enable the pull up resistors out PORTB,TEMP ldi TEMP, 0b01111111;load register r16 with all 1's out DDRD, TEMP ;configure PORT D for all outputs out PORTD,TEMP ldi TEMP, 3 ;select DIV64 prescale for Timer0 out TCCR0,TEMP ; ldi TEMP, 2 ;Set the TOIE0 bit to enable the out TIMSK, TEMP ;Timer interupt to occur ldi TEMP, T1 ;Load Timer reload count out TCNT0,TEMP ;This is the tone frequency ldi EEAD,01 ;EEPROM address rcall RDEEP ;call EEPROM read mov COUNT, EEDAT ;default for 5WPM ldi EEAD,02 rcall RDEEP mov CNTLOW,EEDAT ldi TEMPCNT, 0 ;current duration sei ldi STATFLG, TONE_OFF ;enable global interrupts ;-------------------------------------------------------------------- ;This is the main part of the program ;The program just loops to get a key stroke and ;to act accordingly. It allows 4 actions: play a dot, play a ;dash, increment speed or decrement speed MAIN: rcall BUTTON nop rjmp MAIN ;-------------------------------------------------------------------- ; load settings stored in EEPROM into selected address. ;Note avoid using address 00, refer to the manual. ;read EEPROM as pointed to by address in EEAD, data goes into EEDAT reg. RDEEP: sbic EECR,1 ;skip if EEWE clear rjmp RDEEP ;loop until EEPROM ready READ: out EEAR, EEAD ;output address sbi EECR, 0 ;set EERE (read-strobe) low nop nop ;mandatory 2 cycle delay in EEDAT, EEDR ;input data ret ;reverse of above ;write EEPROM as pointed to by address in EEAD and data comes from EEDAT RWEEP: sbic EECR,1 ;skip if EEWE clear rjmp RWEEP ;loop until EEPROM ready WRITE: out EEAR, EEAD ;output address out EEDR, EEDAT ; cli ;disable interrupts sbi EECR,2 ;set EEMWE (Master-enable) sbi EECR,1 ;set EEWE (write-enable) sei ;enable interrupts ret ;-------------------------------------------------------------------- BUTTON: sbis PINB,2 ;check bit 2 lo if SW3 is pressed rcall SPEED ;change speed nop sbis PINB,0 ;check bit 0 lo if SW1 is pressed rcall DOTONE ;play a dot tone nop sbis PINB,1 ;check bit 1 lo if SW2 is pressed rcall DASHTNE ;play a dash tone nop rjmp BUTOUT BUTOUT: ret ;get out if no buttons pressed ;------------------------------------------------------------ ;PB0 PLAYDOT ;PB1 PLAYDASH ;PB2 Hold down for speed change ;PB2 and PB0 DECSPEED ;PB2 and PB1 INCSPEED ;--------------------------------------------------------------- ;This segment just plays the tone for a dot duration ;followed by no tone for another dot duration DOTONE: ldi TEMPCNT, 0 ;then is inc by timer ldi STATFLG, TONE_ON ;255 DOT1: cpi STATFLG, TONE_ON ;send dot breq DOT1 ldi TEMPCNT, 0 ;send a dot space DOT2: cp TEMPCNT, COUNT brne DOT2 ret ;----------------------------------------------------- ;This segment plays tone for a dash duration (which is ;three times the dot duration) followed by no tone ;for a dot duration. DASHTNE: ldi STATFLG, TONE_ON ldi TEMPCNT, 0 DASH1: cp TEMPCNT, CNTLOW ;send a dot time brne DASH1 ldi TEMPCNT, 0 DASH2: cp TEMPCNT, CNTLOW ;send a dot time brne DASH2 ldi TEMPCNT, 0 DASH3: cpi STATFLG, TONE_ON ;send a dot time breq DASH3 ldi TEMPCNT, 0 DASH4: cp TEMPCNT, COUNT ;send a dot space brne DASH4 ret ;---------------------------------------------------- ;This increases the dot/dash duration ;thus reducing the speed SPEED: ldi TEMP,0x00 out TIMSK,TEMP ;turn timer0 off cbi PORTD,0 ;turn on PD0 LED in TEMP,PINB cpi TEMP, 0b11111010 ;if SW1 and SW3 then DECSPEED brne SP1 rcall DECSPEED rjmp SP2 SP1: cpi TEMP, 0b11111001 ;if SW2 and SW3 then INCSPEED brne INCOUT ;out if not rcall INCSPEED SP2: in TEMP, PINB ;wait for PD1 or PD0 to be ori TEMP, 0b11111100 ;released cpi TEMP, 0b11111111 brne SP2 ldi HIDEL, 50 ;then debounce it with a small SPDEL: ldi TEMP, 255 ;delay SPDEL1: dec TEMP brne SPDEL1 dec HIDEL brne SPDEL INCOUT: sbis PINB,2 rjmp SPEED ;loop while speed button pressed sbi PORTD,0 ;turn off LED ldi EEAD,01 ;store new settings in EEPROM mov EEDAT,COUNT rcall RWEEP ldi EEAD,02 mov EEDAT,CNTLOW rcall RWEEP ldi TEMP,2 out TIMSK,TEMP ;start timer0 again rjmp BUTTON ;now return to main DECSPEED:cpi COUNT, 240 ;compare if upper limit is reached breq NOMORE ;then dont increase anymore,240 ldi TEMP, 10 ;else increase count by 10 add COUNT, TEMP add CNTLOW, TEMP NOMORE: ret ;This decreases the dot/dash duration thus ;increasing the speed. INCSPEED:cpi COUNT, 20 ;check is lower limit is reached breq NOLESS ;dont reduce any more,20 subi COUNT, 10 ;else reduce count by 10 subi CNTLOW, 10 NOLESS: ret ;-------------------------------------------------------------- ;Timer0 Interrupt Subroutine.1.12mS delay, reload BA ;subtract BA from FF = 45H = 69 dec. +1 for overflow to 00. ;0.25uS x 64(prescale) x 69+1 = 1,120uS. TMR0: push TEMP in TEMP,SREG push TEMP ;save machine state ldi TEMP1, T1 ;reload TCNT0 register out TCNT0, TEMP1 ;so that Timer0 int can occur inc TEMPCNT ;increment the count which keeps ;track of how many ISRs have occurred cpi STATFLG, TONE_OFF ;chk if tone is to be played brne TMR1 ;else put PD5 to logic 0 (Pin9 T1) in TEMP, PORTD andi TEMP1, 0b11011111 ;bit 6 out PORTD, TEMP rjmp NOTONE TMR1: cp TEMPCNT, COUNT ;check if play brne TONEOUT ;duration is over ldi STATFLG, TONE_OFF ;reset the flag then rjmp NOTONE TONEOUT:in STATFLG, PORTD ;else prepare to toggle the ldi TEMP, 0x20 ;PD5 bit which generates eor STATFLG, TEMP ;the tone out PORTD, STATFLG ldi STATFLG, TONE_ON NOTONE: pop TEMP out SREG,TEMP ;restore machine state and pop TEMP reti ;return ;--------------------------------------------------------------