Program #4 - Output Decimal Numbers in Morse Code

;-----------------------------------------------------------------------;
; MORSENBR.ASM      Send numbers 0-9 in morse code to speaker and LEDs  ;
;-----------------------------------------------------------------------;

        LIST P=16F84           ;  tells which processor is used
        INCLUDE "p16f84.inc"   ;  defines various registers etc. Look it over.
        ERRORLEVEL -224        ;  supress annoying message because of tris
        __CONFIG _PWRTE_ON & _LP_OSC & _WDT_OFF   ;  configuration switches


            CBLOCK     H'C'
                temp            ; a temporary variable
                bitcount        ; counter for bits
                nbr             ; holds current number to send
            ENDC
          
            ORG 0               ; start at location 0

            goto main           ; jump over to main routine       

;-----------------------------------------------------------------------;
;     Use W to convert number in W to pattern needed to send #          ;
;-----------------------------------------------------------------------;
pattern:
            addwf PCL, f    ; PCL points to the first entry in the table

       dt H'1F',H'0F',H'07',H'03',H'01',H'00',H'10',H'18',H'1C',H'1E'
       ;     0    1     2     3     4     5     6     7     8     9

;-----------------------------------------------------------------------;
;                           Delay routine                               ;
;-----------------------------------------------------------------------;
nmsec:                         ; delay for # msec in W on entry
         nop                   ; each nop is 0.122 milliseconds
         nop
         nop                   ; each total loop is 8 X 0.122 = 0.976 msec
         nop
         addlw H'FF'           ; same as subtracting 1 from W
         btfss STATUS, Z       ; skip if result is zero
         goto nmsec            ; this is  2 X 0.122 msec  
         return                ; back to calling point

;-----------------------------------------------------------------------;
;           Sound routine lasting about 100 milliseconds                ;
;-----------------------------------------------------------------------;
snd1: 
             movlw D'153'         ; 103 additional would mean roll-over 
sndloop:
             bsf PORTA,3          ;  RA3 high
             nop
             nop
             bcf PORTA,3          ;  RA3 low
             addlw 1              ;  bump W and check for roll-over to 0
             btfss STATUS, Z      ;  8 instructions in loop
             goto sndloop         ;  0.122 msec/instruction
             return               ;  103 loops 


;-----------------------------------------------------------------------;
;    Output number in W to LEDs on Port B and to spkr on  RA3           ;
;-----------------------------------------------------------------------;
sendnbr:
             movwf PORTB         ; display number on LEDs 
             call pattern        ; get pattern of dits and dahs
             movwf temp          ; save it 
             movlw 5             ; total of 5 dits or dahs ...
             movwf bitcount      ; into a counter
bitloop:     btfss temp, 4       ; check 5th bit, if it is 1, send dah
             call dit            ; else it is 0, send dit
             btfsc temp, 4       ; and skip next instruction
             call dah            ; get here only if bit 5 is 1
             rlf temp, f         ; rotate left once giving new bit 5 
             decfsz bitcount, f  ; are we finished?
             goto bitloop        ; no, send new bit 5
             clrf PORTB          ; yes, turn display off
             return

;-----------------------------------------------------------------------;
;                               Dit routine                             ;
;-----------------------------------------------------------------------;
dit          
             call snd1               ; 100 msec of tone
             movlw D'100'            ; followed by 100 msec of silence
             call nmsec
             return

;-----------------------------------------------------------------------;
;                               Dah routine                             ;
;-----------------------------------------------------------------------;
dah:         
             call snd1               ; 100 msec of tone
             call snd1               ; stretch it to 300 msec
             call snd1
             movlw D'100'            ; and 100 msec of silence
             call nmsec
             return
             
;-----------------------------------------------------------------------;
;            Wait until button on RB4 is released                       ;
;-----------------------------------------------------------------------;
waitup:    
            btfss PORTB, 4           ; test RB4
            goto $ -1                ; ck again if pressed
            movlw D'10'
            call nmsec               ; wait 10 msec for debounce
            btfss PORTB, 4           ; check again, still up?
            goto waitup              ; no start over
            return                   ; yes, finished

;-----------------------------------------------------------------------;
;                       Initialization Subroutine                       ;
;-----------------------------------------------------------------------;
init:
            movlw 0                    ; RA0 - RA4 outputs
            tris PORTA                 
            movlw H'10'                ; all outputs except RB4 
            tris PORTB                 ; on port B
            movlw H'0'                 ; all outputs low (off)
            movwf PORTB
            movwf PORTA
            movlw 0                    ; pull-ups enabled                                    
            option 
            return               
 
;-----------------------------------------------------------------------;
;       Main routine, sends a new number each time button pressed       ;
;-----------------------------------------------------------------------;
main:    
            call init                 ; set up ports etc
start:      clrf nbr                  ; start with nbr = 0
mainloop:   call waitup
            btfsc PORTB, 4            ; wait on button press
            goto $ -1
            movf nbr, W               ; get number into W
            call sendnbr              ; and output it
            incf nbr, f               ; set up for next number
            movlw H'0A'               ; reached 10?
            subwf nbr, W
            btfss STATUS, Z           ; yes start again at 0
            goto mainloop             ; no, wait for next button press
            goto start

            end

Getting data from tables

A major addition in this program is using a table to get patterns describing the dits and dahs to be sent. It appears right at the first of the assembly listing. The register PCL represents the low 8 bits of the program counter, ( tells where the next instruction comes from ). When a call to pattern is made the instruction 'addwf PCL, f' is executed. Just before the instruction is executed, PCL points at the instruction. After the instruction is executed PCL would normally point to the next memory location, but the instruction itself modifies PCL. If W = 0, then indeed no modification is made and the first entry in the data table, (dt), is taken as the next instruction. If W = 1, it is the 2nd entry in the table and so on.

The data table assembles as a bunch of 'retlw' instructions which in words says: ' return from the subroutine but first put this literal, (number), in W'. That is, in this case; retlw H'1F', retlw H'0F', retlw H'07' etc. So you see that the result of calling 'pattern' is to place a number in W given by the table value specified by W on entry. You of course have to stay within the table. If W is too large on entry you will overun the table.

Sound routine

The routine 'snd1' is like a delay routine with toggling of pin RA3 built in. The loops are 1 msec long and flipping is about half way through, meaning a almost square wave of frequency 1 kHz. The speaker is a piezoelectric speaker which is looks like a capacitor as a load. The 100 ohm resistor prevents a large inrush current. If you have a voice coil speaker you might also try it with a 100 resistor. Unless it is fairly high impedance, you may not get a very loud sound. Piezo speakers with a paper cone attached seem to give louder sound. I have what I think is a Radio Shack 40-1383 which works well.

RLF

This instructions rotates the bits of register f left through carry. Each bit 0 - 6 moves one bit position higher and the carry bit of STATUS moves into bit 0. Bit 7 is pushed into the carry bit of STATUS. There is also and instruction 'rrf', ( roll right through carry ), which moves the bits the other way. If you are concerned about the bits coming in from carry you should set, ( bsf STATUS, C), or clear, ( bcf STATUS, C ), carry before you do the shift. In this program we are only interested in getting the original bits 4-0 into the bit 4 position in order.

Now it's your turn

random numbers

The program is supposed to be used to learn morse code numbers but it's too easy if you know what the next number is to be. How about changing it so you get a random number between 0 and 9. Where can we get a random number? How about TMR0? It is continually running and the press of the button should be fairly random. Can you write the code? Hint: how about adding a random number 0-2 to a random number 0-8 and not allowing 10?

read a BCD switch

How about sending the value of an external BCD switch out the speaker whenever a button is pressed? The switch could be hooked to RB4-RB7, with the pushbutton moved to RA0. Remember that Port A has no internal pull-up resistors, you will need to supply and external one.

output a count

The output from the Counter in program #2 could also be output to the speaker with the routines contained here. What happens when you get to double digits though, (10-15)? One way to handle this is to have a units and a tens register as in program #3. What if another button press comes in while the current number is being output on the speaker? Perhaps the counting can be done using an interrupt routine.

sending ASCII

The code for this program was simplified considerably because it sends only numbers. Morse digits are all 5 dot-dash combinations long. Not so with other Morse characters. Can you think of a scheme to handle various length combinations?

One way to do this is to make all higher order bits not used zeros and then use a one to signify that all lower bits make up the valid pattern. Could you write the code to decode such a pattern?

Interested:

Comments:

See: