;Copy and paste this into an assembler file.
;WARNING: A JMP IS USED IN THIS FILE, ALTHOUGH THE ATMEGA8 DOES NOT HAVE A JMP INSTRUCTION.
;THE LINE READS: "jmp Atodonoff". ONE POSSIBLE SOLUTION IS TO CHANGE THIS TO AN RJMP AND SEE IF THAT WORKS.
;ANOTHER IS TO IGNORE IT FOR NOW. IF YOU ASSEMBLER DOESN'T COMPLAIN, YOU CAN LEAVE FIXING THIS UNTIL LATER.
;AS FAR AS I KNOW THIS PART OF THE CODE WAS FULLY TESTED. WHY THE ASSEMBLER DID NOT COMPLAIN AND WHY THE CODE
;RAN CORRECTLY IS A MYSTERY AT THIS MOMENT. Noted March, 2007.
;-start of document-
;Program: Morse beacon with 4 A/D channels
;Version:morbecon050219A
;Copyright 2004, 2005 by the Dick Cappels and Jeff Heidbrier.
;projects(at)cappels.org www.projects.cappels.org
.include "m8def.inc" ;Include file is in the same directory as the project.
;Interrupt timer parameters
.equ a1wpmhigh
=$12 ;High byte of number of clocks between
interrupts.
.equ a1wpmlow
=$43 ;Low byte of number of clocks between
interrupts.
.equ a5wpmhigh
=$03 ;High byte of number of clocks between
interrupts.
.equ a5wmplow
=$A9 ;Low byte of number of clocks between
interrupts.
.equ a10wpmhigh
=$01 ;High byte of number of clocks between
interrupts.
.equ a10wpmlow
=$D4 ;Low byte of number of clocks between
interrupts.
.equ a25wpmhigh
=$00 ;High byte of number of clocks between
interrupts.
.equ a25wpmlow
=$BB ;Low byte of number of clocks between
interrupts.
.equ timer0rel1khz
=254 ;Reload value for tone timer. 254 = 1 kHz.
.equ timer0rel500hz
=252 ;Reload value for tone timer. 252 = 500 Hz.
;UART baud rate calculation
.equ clock = 4000000 ;clock
frequency
.equ baudrate = 9600 ;choose
a baudrate
.equ baudconstant =
(clock/(16*baudrate))-1
;ALLOCATE BUFFER(S)
.equ rambot =$60
.equ lbuffsize =
80 ;Bytes allocated to line buffer
.equ lbufftop = rambot+80
.equ lbuffbot = rambot ;Top
address (start of) line buffer
.def opsel
= r2 ;Option select bits
.def termsel = r3
;Options as selected by terminal
.def tim0rel = r4
;Timer 0 reload value
.def EEchecksum =
r5 ;Simple checksum of EEPROM from $60 to $FE
.def temp
= r16 ;General purpose scratch register.
.def a1 =
r17 ;Three byte number
.def a2 =
r18 ;Three byte number
.def a3 =
r19 ;Three byte number
.def h =
r20 ;Binary to decimal conversion.
.def t =
r21 ;Binary to decmial conversion.
.def u =
r22 ;Binary to decimal conversion.
.def flagreg =
r23 ;Flags.
.def temp2
= r24 ;Intermediate results
;EEPROM Map
;$60 to $B0
Line Buffer
;$FE
termsel byte
;$FF
simple checksum
;definiton
of flagreg bit assignments
;0
Status of code out bit last sent (memory used to toggle)
;1
True enables toggling of code output
;2
;3
;4
;5
;6
;7
;defintion
of opsel O(ption Select) bits
;0
lsb of code speed select (corresponds to port pin C4)
;1
msb of code speed select (correspoinds to port pin C5)
;2
lsb of A/D channels select (corrseponds to port p[in D2)
;3
msb of A/D channels select (corrseponds to port p[in D3)
;4
Tone Hi/low. 1 = 1 kHz; 0 = 500 Hz (corrsponds to port pin D4)
;5
Deault operation if 1 (corresponds to port pin D5)
;6
;7
;definition
of termsel (terminal selection) bits -parameters set by RS-232 interface
;and stored
in EEPROM.
;0
Terminal selections override user I/O selections when set. Can be
over-ridden by groudning pin 11.
;1
Analog channel A on when set.
;2
Analog channel B on when set.
;3
Analog channel C on when set.
;4
Analog channel D on when set.
;5
lsb of code speed (corresponds to obsel bit 0).
;6
msb of code speed (corresponds to obsel bit 1).
;7
Tone Hi/low (corresponds to obsel bit 4).
;definition
of I/O
;B0
+ comparitor input (Reserved)
;B1
- comparitor input (Reserved)
;B2
Tone (Morse code) output
;B3
Morse code output
;B4
A/D channels 0 - configure as INPUT with weak
pullup
;B5
A/D channels 1 - configure as INPUT with weak
pullu
;B6
(not assigned - configure as INPUT with weak pullup)
;B7
(not assigned - configure as INPUT with weak pullup)
.equ PORTBdata
=0b11110000 ;Initial data
.equ DDRBdata
=0b00001100 ;Initial data
.equ codeport = PORTB
.equ codeout = DDRB
.equ codebit = 2
;Tone output
.equ pulsebit = 3
;Morse code output
;C0
A/D A configure as INPUT, with weak pullup when
not used
;C1
A/D B configure as INPUT, with weak pullup when
not used
;C2
A/D C configure as INPUT, with weak pullup when
not used
;C3
A/D D configure as INPUT, with weak pullup when
not used
;C4
Code Speed 0 - configure as INPUT with weak
pullup
;C5
Code speed 1 - configure as INPUT with weak
pullup
;C6
Reset pin - configure as INPUT with weak pullup
;C7
(does not exist - configure as INPUT with weak pullup)
.equ PORTCdata
=0b11110000 ;Initial data
.equ DDRCdata
=0b00000000 ;Initial data
;D0
UART RECEIVE - configured with weak pullup
;D1
UART TRANSMIT
;D2
(not assigned - configure as INPUT with weak pullup)
;D3
(not assigned - configure as INPUT with weak pullup)
;D4
Tone Select - configure as INPUT with weak
pullup)
;D5
Default Op - configure as INPUT with weak
pullup)
;D6
(not assigned - configure as INPUT with weak pullup)
;D7
(not assigned - configure as INPUT with weak pullup)
.equ PORTDdata
=0b11111101 ;Initial data
.equ DDRDdata
=0b00000010 ;Initial data
.cseg
.ORG
$0000
rjmp
start
.org
$0009
rjmp
timer0service
;Initializaton code
start:
;Entry point after reset -initialize everything
warm:
;Warm is actually a reset.
ldi temp,DDRBdata
;Set PORTB.
out DDRB,temp
ldi temp,PORTBdata
out PORTB,temp
ldi temp,DDRCdata
;Set PORTC
out DDRC,temp
ldi temp,PORTcdata
out PORTc,temp
ldi temp,DDRDdata
;Set PORTD.
out DDRD,temp
ldi temp,PORTDdata
out PORTD,temp
ldi temp,high(ramend)
;Initialize 16 bit Stack Pointer
out sph,temp
ldi temp,low(ramend)
out spl,temp
clr flagreg
;Clear firmware flagreg (flag register).
;SET UP USART
clr temp
; Set baud rate
out UBRRH, temp
ldi temp,baudconstant
;load computed value for baud rate
out UBRRL, temp
;Enable Receiver and Transmitter
ldi temp, (1<<RXEN)|(1<<TXEN)
out UCSRB,temp
;Set frame format: 8data, 2stop bit
ldi temp,
(1<<URSEL)|(1<<USBS)|(3<<UCSZ0)
out UCSRC,temp
;8 BIT TIMER 0 SETUP
ldi temp,5
;Set prescaler for clk/8
out TCCR0,temp
ldi temp,timer0rel1khz ;Set
number of pulses to count up after division by prescaler
mov tim0rel,temp
;Default is for 1 kHz (4 MHz clock).
out TCNT0,temp
in temp,TIMSK
ori temp,0b00000001
;Enable timer 0 oveflow interrupt.
out TIMSK,temp
;ITINITALIZE A TO D
CONVERTER
ldi temp,0b00010101
;Set control and status register. 4 MHz
clock.Clear int flag.
out ADCSR,temp
sei
;Enable interrupts.
;Now that interrups are on
and there is a tone, send the greeting.
ldi temp,0b11111100
;25 words per minute
mov opsel,temp
rcall setdottime
;Set the dot period as a function of opsel bit
0,1
rcall crlf
rcall crlf
rcall crlf
rcall crlf
rcall Typeheader
;Type the header at 25 WPM.
rcall Loadcommand
;Load EEPROM contents into
RAM $60 to $FF
rcall typestatus
;Type terminal control setup.
rcall DumpLineBuffer
;Display line buffer contents
rcall GetOptionBits
;Get jumper selectable options to opsel
register
;Switch flow to Programmed
Main if bit o in termsel is set,
;EEPROM checksum is valid, and
opsel bit 5 is set.
ldi temp,0b00100000
;Check for jumper pin override.
and temp,opsel
breq DefaultMain
ldi temp,0b00000001
;Check for terminal program control bit set in
termsel
and temp,termsel
breq DefaultMain
clr YH
;Check EEProm checksum
ldi YL,$FF
ld temp,Y
cp temp,EEChecksum
brne DefaultMain
rjmp ProgrammedMain
DefaultMain:
;Main
Loop
rcall GetOptionBits
;Get jumper selectable options to opsel
register
rcall setdottime
;Set the dot period as a function of opsel bit
0,1
rcall ToggleTonepitch
;Set tone pitch based on opsel bit 4
rcall MeasureAndSendVolts
rcall interword
rcall OptionsEditor
;Check to see if Operator requested Options
Editor.
rjmp DefaultMain
ToggleTonepitch:
;Set the
interrupt period to 500 Hz or 1 Khz
push temp
;based on opsel bit 4.
mov temp,opsel
andi temp,0b00010000
breq x22
ldi temp,timer0rel1khz
rjmp x23
x22:
ldi temp,timer0rel500hz
x23:
mov tim0rel,temp
;Set interrupt time for chosen tone frequ.
pop temp
ret
GetOptionBits:
;Get
jumper selectable options to opsel register
push temp
in temp,PINC
;Get Port C bits 4,5.
andi temp,0b00110000
lsr temp
lsr temp
lsr temp
lsr temp
mov opsel,temp
in temp,PIND
;Get Port D bits 2,3,4, and 5.
andi temp,0b00111100
or opsel,temp
pop temp
ret
crlf:
;Send Carrige return and line feed ($0D, $0A)
push temp
ldi temp,$0D
rcall emitchar
ldi temp,$0A
rcall emitchar
pop temp
ret
emitchar0D:
;Send
outchar to terminal. Add line feed to carriage return.
push temp
rcall emitchar
cpi temp,$0D
;Its a carraiage return, send a linefeed also
brne notareturn
ldi temp,$0A
rcall emitchar
notareturn:
pop temp
ret
emitchar:
;Send character contained in temp.
sbis UCSRA,UDRE
;Test for TX register empty
rjmp emitchar
;Loop until TX empty
out UDR,temp
;Send the byte
ret
getchar:
sbis UCSRA,RXC
;Wait until a character has been received
rjmp getchar
in temp,UDR
;Read byte from the UART
ret
Typeheader:
;Type
header
push ZL
push ZH
ldi ZH,high(2*hellomessage)
;Load high part of byte address into ZH
ldi ZL,low(2*hellomessage)
;Load low part of byte address into ZL
rcall typeromstring
;Send it
pop ZH
pop ZL
ret
hellomessage:
.db $0A,$0D
.db $0A,$0D
.db "morbecon050219A "
.db $0A,$0D
.db $00,$00
sendromstring:
;call with location of
string in Z.
push ZL
;Save Z on stack.
push ZH
srs1:
lpm
;Load byte from program memory into r0.
tst r0
;Check if we've reached the
end of the message.
breq
finishsendstering ;If so, return.
mov temp,r0
rcall SendMorseAscii ;Send
upper case ASCII Char as Morse Code.
adiw ZL,1
;Increment Z registers
rjmp srs1
finishsendstering:
pop ZH
;Pop Z from stack.
pop ZL
rcall interword
ret
MeasureAndSendVolts:
;Measure 1 to 4 A/D channels and send the
values as Morse Code
;and via RS-232 accoding to obsel bits 2 and 3.
;Bit 3
Bit 2 Measure
;1
1 Channel A only (ADC 0)
;0
1 Channels A & B (ADC 0,1)
;1
0 Channels A,B & C (ADC 0,1,2)
;0
0 Channels A,B,C & D (ADC 0,1,2,3)
push temp
ldi temp,'A'
rcall SendMorseAscii
ldi temp,0
;Channel A always sent, so do that first.
rcall measure
;Measure it.
rcall SendVolts
;Send out as Morse Code
mov temp,opsel
andi temp,0b00001100
cpi temp,0b00001100
;If bit 2 and 3 are set, we're done.
breq donemeas
push temp
ldi temp,$20
rcall SendMorseAscii
ldi temp,'B'
rcall SendMorseAscii
ldi temp,1
;Do Channel B.
rcall measure
;Measure it.
rcall SendVolts
;Send out as Morse Code
pop temp
cpi temp,0b00000100
breq donemeas
;If only bit 2 is set, we're done.
push temp
ldi temp,$20
rcall SendMorseAscii
ldi temp,'C'
rcall SendMorseAscii
ldi temp,2
;Do Channel C..
rcall measure
;Measure it.
rcall SendVolts
;Send out as Morse Code
pop temp
cpi temp,0b00001000
breq donemeas
;If only bit 3 is set, we're done.
ldi temp,$20
rcall SendMorseAscii
ldi temp,'D'
rcall SendMorseAscii
ldi temp,3
;Do Channel D..
rcall measure
;Measure it.
rcall SendVolts
;Send out as Morse Code
donemeas:
;We are done.
ldi temp,$20
rcall SendMorseAscii
ldi temp,$0A
rcall SendMorseAscii
ldi temp,$0D
rcall SendMorseAscii
pop temp
ret
Measure:
;Measure A/D channel. Enter with channel number in temp. Exit
with data in YH:YL
;Allowed range is 0..5
push temp
sbi ADCSR,ADEN
;Enable A/D converter.
andi temp,0b00000011
;Mask off upper bits to restrict range of
channel selction.
ori temp,0b01000000
;Set reference voltage bit.
out ADMUX,temp
;Select channel.
sbi ADCSR,ADSC
;Start conversion.
wfc:
sbis ADCSR,ADIF
;Wait for bit to be set, indicating conversion
complete.
rjmp wfc
sbi ADCSR,ADIF
;Clear interrupt flag.
in YL,ADCL
;Get data into Y register.
in YH,ADCH
pop temp
ret
sendcodedcode:
;Send
Coded Morse Code
;Shift out morse code from
coded character.
;Enter with code for
character in temp.
;Description
of data format from David Robinson's web page:
;"At this
point I was reminded of the N1KDO NHRC-2 repeater controller published
;in
February 97 QST that had Morse ID. Investigation of the assembler
listing (1)
;revealed a
simple conversion scheme, where all morse characters are encoded in a
;single
Byte, bitwise, LSB to MSB.; 0 = dit, 1 = dah. The Byte is shifted out
;to the
right, until only a 1 remains. As an example 3 is encoded as binary
00111000,
;which
translates to 38 in hexadecimal. " The code that follows is based on
this technique.
morecode:
cpi temp,0b00000001
breq codedcodesent
clc
ror temp
brcs senddash
;Send a dash if lsb was a one
rcall dot
;Send a dot if lsb was not
a one
rjmp morecode
senddash:
rcall dash
rjmp morecode
codedcodesent:
;Finished sending the coded code.
ret
SendMorseAscii:
;Look up coded Morse code and send, followed by rcall to
interchar.
;Enter with ASCII character in temp.
Upper-case, don't process
;control characters.
rcall OptionsEditor
;Check to see if Operator requested Options
Editor.
push ZL
;Save registers
push ZH
push temp
;Save contents of temp.
rcall emitchar0D
;Emit the char via UART.
pop temp
;Restore value of temp.
cpi temp,$20
;If space character, do interword delay.
brne SMA1
rcall interword
rjmp lookupdone
SMA1:
cpi temp,$5B
brmi upperacse
andi temp,$5F
;Make upper-case
upperacse:
cpi temp,$2A
brmi lookupdone
;Set up pointer into
codechart.
ldi ZH,high(2*codechart)
;Load high part of byte address into ZH.
ldi ZL,low(2*codechart) ;Load
low part of byte address into ZL.
subi temp,$2A
;Removed offset from ASCII value in temp.
add ZL,temp
;Add the value to the index.
clr temp
adc ZH,temp
lpm
;Fetch the value from the
table.
mov temp,r0
rcall sendcodedcode
;Send as Morse Code
rcall interchar
;Dealy one interchar time
lookupdone:
pop ZH
;Restore registers
pop ZL
ret
;Return
dot:
;Send dot, wait one dot time.
sbr flagreg,0b00000010
;Set flag to send tone.
sbi codeport,pulsebit
;Set on-off key pulse high.
rcall dottime
cbr flagreg,0b00000010 ;Clear
flag to send tone.
cbi codeport,pulsebit
;Set on-off key pulse low.
rcall dottime
ret
dash:
;Send dash, wait one dot time.
sbr flagreg,0b00000010 ;Set
flag to send tone.
sbi codeport,pulsebit
;Set on-off key pulse high.
rcall dottime
rcall dottime
rcall dottime
cbr flagreg,0b00000010 ;Clear
flag to send tone.
cbi codeport,pulsebit
;Set on-off key pulse low.
rcall dottime
ret
interchar:
;Wait interchear period with output off -3 dot times
rcall dottime
rcall dottime
rcall dottime
ret
interword:
;Wait interword period with output off-6 dot times
rcall dottime
rcall dottime
rcall dottime
rcall dottime
rcall dottime
rcall dottime
rcall dottime
ret
setdottime:
push temp
;Load compare registers, OCR1AH,OCR1AL with
values for
;dot rate as a function of opsel bits 0 and 1.
;
; bit1
bit0 Code Speed
OCR1AH ORC1Al
; 1
1 10 WPM
; 1
0 1 WPM
; 0
1 5 WPM
; 0
0 25 WPM
;
;Note Code speed is calcualted using PARIS (50
spaces)
;send in 1 minute = 1 WPM.
mov temp,opsel
;Get option select bits into temp.
andi temp,0b00000011
;Mask off all but two lsb.
cpi temp,0
;Test if 0.
brne not0
;If not 0 skip
to next test.
ldi temp,a25wpmhigh
;If 0 load parameters for 25 WPM.
out OCR1AH,temp
ldi temp,a25wpmlow
out OCR1AL,temp
rjmp speedset
not0:
cpi temp,1
;Test for value = 1.
brne not1
ldi temp,a5wpmhigh
out OCR1AH,temp
ldi temp,a5wmplow
out OCR1AL,temp
rjmp speedset
not1:
cpi temp,2
;Test for value = 2
brne not2
ldi temp,a1wpmhigh
out OCR1AH,temp
ldi temp,a1wpmlow
out OCR1AL,temp
rjmp speedset
not2:
ldi temp,a10wpmhigh ;Its not
0,1, or 2, so it must be 3.
out OCR1AH,temp
ldi temp,a10wpmlow
out OCR1AL,temp
speedset:
;Come to here when speed has been set.
pop temp
ret
dottime:
;Delay one dot time.
push temp
ldi temp,0b00001101
;Set timer 1 to reset 0000 after compare
match. Prescaler = 1X. out
TCCR1B,temp
out TCCR1B,temp
wd1:
in temp,TIFR
sbrs temp,OCF1A
;Wait for flagreg bit two to go high (timer to
time out)
rjmp wd1
out TIFR,temp
;Clear the flag
ldi temp,0b00000000
;Set timer 1 to "normal" mode, prescale output
stopped.
out TCCR1B,temp
pop temp
ret
SendVolts:
;Enter with input value in
YH:YL. YH,YL,a1,a2,a3,temp,H,T,U modified.
;Scale input with range of
0 to 1023 to d digit expression of range
;from 0 to 5. This is for a
5 volt full scale input 10 bit ADC.
;Multiply input by 489 and
divide by 1,000 to get answer in tens of
;millivolts.
push temp
clr a1
;Clear the 3 byte number
clr a2
clr a3
mloop:
tst YL
brne notzero
tst YH
brne notzero
rjmp countdone
notzero:
sbiw YL,1
;Decrement the 10 bit value
and add 489 to each time
ldi temp,$E9
add a1,temp
;To multiply number in Y by 489
ldi temp,$01
adc a2,temp
clr temp
adc a3,temp
rjmp mloop
countdone:
;At this point, the product is in a1,2,3
;Test values come out of
489000 here.
clr H
clr T
clr U
MoreH:
inc H
subi a1,$A0
;Find out how many 100,000's
sbci a2,$86
sbci a3,1
brcc MoreH
ldi temp,$A0
;Subtracted one too many, add back on.
add a1,temp
ldi temp,$86
adc a2,temp
ldi temp,1
adc a3,temp
dec H
;VALUE EXITING THIS ROUTINE
IS 23112. SHOULD BE 89000.
MoreT:
inc T
subi a1,$10
;Find out how many 10.000's
sbci a2,$27
sbci a3,$00
brcc moreT
ldi temp,$10
;Subtracted one too many, add back on.
add a1,temp
ldi temp,$27
adc a2,temp
ldi temp,0
adc a3,temp
dec T
MoreU:
inc U
subi a1,$E8
;Find out how many 10.000's
sbci a2,$03
sbci a3,$00
brcc moreT
ldi temp,$E8
;Subtracted one too many, add back on.
add a1,temp
ldi temp,$03
adc a2,temp
ldi temp,0
adc a3,temp
dec U
subi H,-48
;Convert to ASCII and send.
Send H
mov temp,H
rcall SendMorseAscii
subi T,-48
mov temp,T
rcall SendMorseAscii
subi U,-48
;Send U
mov temp,U
rcall SendMorseAscii
rcall interword
rcall interword
rcall interword
pop temp
ret
;/////////////////////////////Morse
Code Chart\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
codechart:
;Coded Morse Code look up table. Use ASCII value -$30, so zero =
0, "A" = $11, etc.
;Note: Some
ASCII characters are silent, and are coded as 0b00000001
;Also note:
BT (pause) is coded for ASCII "<" and SK (end of contanct) is coded
for ASCII "*",
;and End of
Message is coded for ASCII "+".
;
* (SK) + (End of Message)
.db
0b01101000, 0b00101010
;
,(comma)
-
. /
.db
0b01110011,0b1011110,0b01111010,0b00101001
;
0
1
2 3
.db
0b00111111,0b00111110,0b00111100,0b00111000
;
4
5
6 7
.db
0b00110000,0b00100000,0b00100001,0b00100011
;
8
9
: ;
.db
0b00100111,0b00101111,0b01000111,0b01010101
;
< (BT)
=
> ?
.db
0b000110001,0b00000001,0b00000001,0b01001100
;
@
A
B C
.db
0b00000001,0b00000110,0b00010001,0b00010101
;
D
E
F G
.db
0b00001001,0b00000010,0b00010100,0b00001011
;
H
I
J K
.db
0b00010000,0b00000100,0b00011110,0b00001101
;
L
M
N 0
.db
0b00010010,0b00000111,0b00000101,0b00001111
;
P
Q
R S
.db
0b00010110,0b00011011,0b00001010,0b0001000
&nbs