Program #7 - Send & Receive Serially at 1200 baud

;-----------------------------------------------------------------------;
; ALPHABET.ASM        Send and Receive serially at 1200 baud to PC      ;
;-----------------------------------------------------------------------;

;-----------------------------------------------------------------------;
;        the next 5 lines are directions to the assembler               ;
;-----------------------------------------------------------------------;

        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'0C'
           txreg              ; holds byte to be transmitted
           rxreg              ; holds byte received
           char               ; holds character to be transmitted
           bitcount           ; holds count of bits when transmitting
           timecnt            ; holds counter for timer
         ENDC

           ORG 0              ; start a program memory location zero
           goto main          ; skip over subroutines

;-----------------------------------------------------------------------;
;          initialization - set up ports and timer options              ;
;-----------------------------------------------------------------------;
init:
         movlw B'00000001'    ; RA0 to input
         tris PORTA           ; contents of W copied to PORT A ...
         movlw 0
         tris PORTB           ; and PORT B
         movlw B'00000001'    ; pull-ups active
                              ; prescalar assigned to TMR0 and set 1:4
         option               ; rolls over each second
         return

;-----------------------------------------------------------------------;
;                        time delay routines                            ;
;-----------------------------------------------------------------------;
eighth:
          movlw 1              ; wait 1/8 th second
          goto $ +2            ; jump over
onesecond:
          movlw D'8'           ; wait one second
          movwf timecnt
          clrf TMR0
          bcf INTCON, T0IF     ; clear the interrupt flag
          btfss INTCON, T0IF   ; wait on T0IF to be set
          goto $ -1            ; loop till it is
          decfsz timecnt, f    ; finished timing?
          goto $ -3            ; not yet
          return               ; yes


;-----------------------------------------------------------------------;
;                     the main program                                  ;
;-----------------------------------------------------------------------;
main:                         ; this is the main program
         call init            ; set up ports
         call onesecond       ; let things settle for 1 second
loop:
         call rx              ; get a character from keyboard
         movwf char           ; save so we can increment it
         incf char, f         ; send back the next 3 characters
         movf char, W
         call tx
         incf char, f
         movf char, W
         call tx
         incf char, f
         movf char, W
         call tx
         goto loop            ; do it forever

;-----------------------------------------------------------------------;
;                 transmit byte in W at 1200 baud out RB7               ;
;-----------------------------------------------------------------------;
; normal rs232 is -V for a high, +V for a low
; this would translate to 0V for high +5 for low
tx:
         movwf txreg          ; set up transmit register
         comf txreg, f        ; flip all bits
         movlw D'8'           ; eight bits to transmit
         movwf bitcount
         bsf PORTB, 7         ; start bit (+5V is 'low')
         nop                  ; a total of 7 instruction cycles
         nop                  ; (one is the rrf at txloop)
         nop
         nop
         nop
txloop:
         rrf txreg, f         ; lsb out of txreg into carry
         rrf PORTB, f         ; bit 7 of port B high or low with carry
         nop                  ; make total loop delay 7 instruction counts
         nop                  ; 7 * 122 usec = 854, (should be 833)
         decfsz bitcount, f   ; 4 instruction cycles to here
         goto txloop          ; total 4 + 3 = 7 instructions cycles in loop
         bcf PORTB, 7         ; 0 Volts is the 'high' state this is
         nop                  ; stop bit
         nop                  ; delay 7 instruction cycles
         nop
         nop
         return

;-----------------------------------------------------------------------;
;             receive byte  at 1200 baud in RA0, put in W               ;
;-----------------------------------------------------------------------;
rx:      movlw 8              ; eight bits to input
         movwf bitcount
waitstart:
         btfss PORTA, 0       ; wait for input to go high, (start bit)
         goto waitstart
         nop                  ; wait 1/2 start bit time 3 including branch
         nop                  ; next four instruction are to make
         nop                  ; 1 bit time delay before first reading of
         nop                  ; input port
         nop
receive:
         nop                  ; make total loop delay 7 instruction counts
         nop                  ; 7 * 122 usec = 854, (should be 833)
         rrf PORTA, f         ; PORT A bit 0 into carry
         rrf rxreg, f         ; rotate carry into receive register
         decfsz bitcount, f   ;
         goto receive         ; total 4 + 3 = 7 instructions cycles in loop
         comf rxreg, W        ; return compliment of value collected
         nop                  ; wait at least one stop bit
         nop
         nop
         nop
         return

         end

A cheap display?

What is the cheapest display you can use with a PIC? One you already have of course. You already have a PC because you are using it to program PICs. Let's see if we can use the PC as a display and input device. We will hook up to a COM port on the PC and use any simple terminal program to communicate with the PIC. Use 1200 baud 8ND, (8 bit, no parity, handshake disabled). We will send a ASCII letter from the keyboard and return the next 3 letters in the alphabet.

Communication problems

We are limited in rate at which we communicate. Ideally the length of each bit we send should be an exact multiple of the instruction length of the PIC which is operating at 32.768 kHz. Each instruction is about 122 microseconds long. About the best we can do is 7 instructions which is about 854 usec. The bit length at 1200 baud is 1/1200 = 833 usec. Over 8 bits this 2.5% error will add up to 20% of a bit length. Hopefully we can still manage.

The limit of 7 instructions also causes another problem. We have to keep track of what bit of a byte we are sending and also set the port bit high or low depending on the value of that bit. The only way I could manage in seven instructions was to use carry, shifting successive bits out of the byte to send and shifting the bit into an output port. Shifting in can only be done to bit 7 or bit 0. This led to the use of RB7 to transmit the data and RA0 to receive it, (from the PIC's point of view) .

RS232 communication on the COM port is usually done at negative voltage to send a '1' and positive voltage to send a '0'. It is possible for short lengths to get away with 0 volts for '1' and +5 volt for '0'. That is what we will use. The normal waiting condition is high, ('1' or 0 volts), and the start bit is a '0', (+5V). A 22K resistor is used in the 'serial in' line to the PIC to limit current and clipping diodes in the PIC are called upon to limit the voltage at the PIC pin. Notice that since we want 0 volts to send a '1', the bits of the byte sent must the flipped, (comf).

A higher crystal frequency would allow more accurate bit generation but would cause the PIC to draw more current. You could use a 4 mHz crystal, (see alpha4m.asm) but for the programs we've done needing accuracy over long time periods, a 4.096 mHz crystal would be better. The frequency divided by four is 1024 mHz with a period of 0.97656 microseconds. 256 of these gives 250 microseconds, (rollover of TMR0 at 1:1). A prescalar ratio of 1:4 then gives exactly 1 millisecond.

Sending and Receiving Bytes

The subroutine 'tx' handles transmitting bytes. It is entered with the byte to transmit in W which stored in txreg where it can be shifted. The instruction rrf (reg) moves every bit of the register down one bit. The carry bit of STATUS is shifted into the 7th bit of the register and the zero bit is shifted out into carry. This is done 8 times for the eight bits of the byte. Each time it is done the resulting bit in carry is shifted into the Port B register bit 7, (RB7). Remember that this screws up all of the other bits of Port B also. You wouldn't normally do it this way but I could think of no other way to do what is required in just the 7 instruction cycles allowed. A 7 instruction length '1' is sent out first which is the start bit.

A similar scheme is used to receive a byte. We first wait on the start bit, wait half a bit length and then examine the input port for the 1st bit 7 instruction cycles after that. In the loop, every additional bit is examined 7 instruction cycles later. The half bit position was taken to be 3 instruction cycles because one bit length is actually a little under 7 cycles so the examination comes a little more than half way with each bit.

Your Turn

Sending back the character sent modified slightly is instructive but not very practical. You might consider how you would send a series of characters making up a string. How would you send out a binary number as a decimal number? Also consider how you would receive ASCII digits and translate those into a binary number in a register. You can find routines in PIC assembly language to do these things. A good place to look is http://www.piclist.com/faq under routines.

Questions:

Johan Hedlund Says:

I accidently held some keys on my computer for a couple of seconds. That blew the RA0. No response from that pin anymore... Except from that, great tutorial!

Interested:

Comments:

See also: