Introduction
This discussion deals with interfacing a PIC16C84 device with a serial device. It is limited to the PIC transferring data to a serial receiver. The receiver may be a PC COM port, a "dumb" terminal, a serial LCD Backpack or a serial printer.
Such interfacing is useful in displaying screen messages or prompts to the user, in displaying quantities or transferring the content of data logger's EEPROM to a PC.
Typically, the home experimenter doesn't have the resources to purchase an emulator. The ability to interface with a serial device gives you the ability to transfer a register of interest to the w register and displaying it on a terminal or LCD.
A Few Words about the RS232 EIA Interface.
In sending a byte to a terminal the sequence is to send the start bit, fllowed by each of the eight bits in turn, beginning with the least significant bit, followed by a stop bit.
In the following example, the letter "A" (0100 0000) is sent to the terminal.
START 0123 4567 STOP 0 0000 0010 1
Note that a start bit is a logic zero and a stop bit is a logic one.
Thus, on receipt of the start bit, the terminal sees a logic one (from the stop bit of the previous character to zero transition.
Each bit occupies an equal time slot. For example, at 9600 Buad, each time slot is 1/9600 = 104.167 usecs. At 2400 baud, each is four times as long or 416.67 usecs. The stop bit may be any length longer than one bit time.
An ASCII character occupies the lower seven bits. The highest bit, bit 7 may be used for parity checking. In this discussion, parity is not used. Rather the bit is always 0.
The RS232 EIA logic levels are different than TTL. A logic one is less than -3.0 V and a logic zero is greater than +3.0.
Thus, in interfacing with a PIC, one might use an EIA driver IC which converts a TTL logic one (greater than 2.8 V) to an EIA logic one (less than -3.0 V) and a TTL logic zero (less than 0.4) to an EIA logic zero (greater than +3.0). Typical devices are the 1488 and Maxim's 230 series.
However, over they years designers considered the negative supply to be an added expense and thus many transmitting devices were designed to send a logic one as near ground and a logic zero as greater than +3.0. Thus, most serial receivers will respond to near ground and greater than +3.0. Note that this is pretty close to TTL levels except the states are inverted.
Therefore, in this application, no EIA line driver is used. Rather the TTL is sent inverted. Not using an EIA driver may limit the distance between the PIC and the terminal. However, I have used the direct connection with no problems at distances of four feet.
Program SERIAL_1.ASM.
All this routines does is to continually send the character "7" to the terminal every 250 msecs.
Note that in SEROUT a counter is set to nine (start bit plus eight data bits). The start bit (logic zero) is implemented by clearing the carry bit.
In SEROUT1, the carry bit is checked and either a logic one or zero is sent. Note that is is here that I implemented the inversion discussed above; when a logic one should be sent (if an EIA driver was used), I really send a zero.
Continuing, the data is shifted to the right, causing the least significant bit to shift into the carry which then determines the state of the TX bit on PORTB. This continues until the counter is decremented to zero, and the stop bit is then sent. Note that it is inverted as I am not using an EIA driver.
Note that the time from beginning SEROUT1 to again beginning it is 416 instructions;
1 + 1 + 1 + 1 + 1 + SEROUT_TIME * (1 + 2) + 1 + 1 + 2 or 9 + 136 * 3 = 417
You can easily calculate the loop constant for other baud rates. For example; for 9600 Baud;
t = 1/9600 = 104 instructions SEROUT_TIME = (104 - 9) / 3 = 31.67 or 32
By adding NOP instruction outside the loop, the timing may be made precise. However, one or two microseconds out of 104 usecs over ten bits certainly isn't going to cause a problem.
; SERIAL_1.ASM
;
;
; Continually sends the character '7' to the terminal at 2400 Baud.
; 250 msec delay.
;
; PORTB2 (terminal 8) ------------------ RX on terminal
;
; Peter H. Anderson, MSU, May 29, '97
;
LIST p=16c84
#include <p16c84.inc> ;
__CONFIG 11h
CONSTANT TX=2 ; PORTB bit used for TX
SEROUT_DATA EQU 0CH
SEROUT_LOOP EQU 0DH
SEROUT_TIME EQU 0EH
DELAY_MS_LOOP EQU 10H
DELAY_100MS_LOOP EQU 11H
ORG 000H ;program code to start at 000H
MAIN:
BCF STATUS, RP1
BSF STATUS, RP0 ; switch to bank 1 and
; make all bits on Portb outputs
BCF TRISB, TX ; make TX an output
BCF STATUS, RP0 ; back to data bank 0
TOP:
MOVLW .7
ADDLW '0' ; character 7
CALL OUT_SER_CHAR ; output to terminal
CALL DELAY_250_MS
GOTO TOP
;;;;;;
OUT_SER_CHAR: ; transmits content of W at 2400 Baud
MOVWF SEROUT_DATA
MOVLW .9
MOVWF SEROUT_LOOP
BCF STATUS, C ; set C to 0, start bit
SEROUT1:
BTFSC STATUS, C ;1 cycles
BCF PORTB, TX ; send a one
BTFSS STATUS, C ;1
BSF PORTB, TX ; or a zero ;1
MOVLW .136 ;1
MOVWF SEROUT_TIME ; bit time delay, 408uS at 2400 baud ;1
SEROUT2:
DECFSZ SEROUT_TIME, F ;1 cycles in loop
GOTO SEROUT2 ;2 cycles in loop
NOP ;1
RRF SEROUT_DATA, F ; least sign bit now in C ;1
DECFSZ SEROUT_LOOP, F ; does not affect status ;1
GOTO SEROUT1 ; next character ;2
BCF PORTB, TX ; send stop bit
MOVLW .136
MOVWF SEROUT_TIME ; bit time delay, 408uS at 2400 baud
SEROUT3:
DECFSZ SEROUT_TIME, F
GOTO SEROUT3
NOP
RETURN
DELAY_250_MS: ; provides 250 ms loop
MOVLW .250
MOVWF DELAY_100MS_LOOP
DELAY_MS_1
MOVLW .110 ; close to 1.0 msec delay when set to .110
MOVWF DELAY_MS_LOOP
DELAY_MS_2
NOP
NOP
NOP
NOP
NOP
NOP
DECFSZ DELAY_MS_LOOP, F
GOTO DELAY_MS_2
DECFSZ DELAY_100MS_LOOP, F
GOTO DELAY_MS_1
RETURN
END
Program SERIAL_2.ASM.
This program includes the OUT_SER_CHAR routine discussed above. Any
character may be displayed on the terminal by calling OUT_SER_CHAR with
the character in the W register.
In addition, OUT_1_DIG converts a single digit to a character and displays
it . OUT_2_DIG first converts the high nibble to a character and displays
it, followed by the low nibble. Note that both of these routines are
capable of printing the digits as hexadecimal characters.
That is, if 1EH is passed to OUT_2_DIG, it will be displayed as "1E". If
the user desires the quantity to be displayed in decimal, the quantity 1E
must be first converted to decimal and the two nibble decimal quantity, in
this case; 30, is then passed to OUT_2_DIG.
CONVERT_TO_BCD is provided to convert a byte in natural binary to BCD.
This is limited to quanties in the range of 0 to 255.
In SERIAL_2.ASM, the quantity "1E" is converted to BCD and displayed as
030 on the terminal followed by a "new line".
; SERIAL_2.ASM
;
;
;
; PORTB2 (terminal 8) ------------------ RX on terminal
;
; Peter H. Anderson, MSU, May 29, '97
;
LIST p=16c84
#include <c:\mplab\p16c84.inc>
__CONFIG 11h
CONSTANT TX = 2 ; bit on PORTB
CONSTANT VARS=0CH ; location of first variable
BCD_HI EQU VARS+0
BCD_LO EQU VARS+1
SEROUT_LOOP EQU VARS+2
SEROUT_TIME EQU VARS+3
TEMP EQU VARS+4
TEMP1 EQU VARS+5
;;;;;;;
ORG 000H
MAIN:
BCF STATUS, RP1
BSF STATUS, RP0 ; switch to bank 1 and
; make all bits on Portb outputs
CLRF TRISB ; make all PortB bits outputs
BCF STATUS, RP0 ; back to data bank 0
TOP:
; in this routine, i simply used a data value of 1E
; however, this data might be the result of a temperature
; measurement, data in eeprom or any other variable you
; wish to display
MOVLW 1EH ; this is raw data byte to be displayed
CALL CONVERT_TO_BCD
MOVF BCD_HI, W
CALL OUT_1_DIG ; output low byte only
MOVF BCD_LO, W
CALL OUT_2_DIG ; output high and low nibbles
CALL OUT_CR_LF ; followed by a CR and line feed
DONE:
GOTO DONE:
;;;;; Subroutines
CONVERT_TO_BCD:
; takes natural 8-bit natural binary quantity in W and converts to
; to BCD. result is returned in BCD_HI and BCD_LO. W is destroyed
; requires variables TEMP and TEMP1
MOVWF TEMP ; save value
CLRF BCD_HI
CLRF BCD_LO
HUNDS: ; determine the number of 100s
MOVLW .100
SUBWF TEMP, W ; W = TEMP - 100
BTFSS STATUS, C ; then W >= 0, if TEMP >= 100
GOTO TENS ; TEMP < 100
MOVWF TEMP ; save the result
INCF BCD_HI, F
MOVF TEMP, F
BTFSC STATUS, Z ; if zero
GOTO TENS
GOTO HUNDS
TENS: ; determine the number of tens
MOVLW .10
SUBWF TEMP, W ; W = TEMP - 10
BTFSS STATUS, C ; then W >= 0, if TEMP >= 10
GOTO TENS_1 ; TEMP < 10
MOVWF TEMP ; save the result
INCF BCD_LO, F
MOVF TEMP, F
BTFSC STATUS, Z ; test for zero
GOTO TENS_1
GOTO TENS
TENS_1:
SWAPF BCD_LO, F ; tens now in high nibble
UNITS: ; determine number of untis
MOVLW .1
SUBWF TEMP, W ; W = TEMP - 1
BTFSS STATUS, C ; then W >= 0, or TEMP >= 1
GOTO UNITS_1 ; TEMP < 1
MOVWF TEMP ; save the result
INCF BCD_LO, F
GOTO UNITS
UNITS_1:
RETURN
;;;;;
OUT_2_DIG:
; outputs byte in W. high nibble and then low nibble to serial
; terminal uses variable TEMP1. W is destroyed.
MOVWF TEMP1 ; save to TEMP
SWAPF TEMP1, W ; high nibble now in low nibble of W
CALL OUT_1_DIG ; convert high nibble to a charcter and output
MOVF TEMP1, W ; low nibble
CALL OUT_1_DIG ; convert low nibble to character and output
RETURN
;;;;;
OUT_1_DIG:
; converts numeric quantity in low byte of W to a hex character
; outputs to serial port. W is destroyed. Uses variable TEMP
ANDLW 0F ; low nibble
MOVWF TEMP ; copy to TEMP
SUBLW .9 ; W = 9 - TEMP
BTFSS STATUS, C ; TEMP > 9
GOTO ALPHA
NUMERIC:
MOVF TEMP, W ; get the original
ADDLW '0' ; add character 0
GOTO OUT_1_DIG_1
ALPHA:
MOVF TEMP, W
ADDLW '0'+7 ; for 'A', 'B', 'C', 'D', 'E' and 'F'
OUT_1_DIG_1:
CALL OUT_SER_CHAR
RETURN
;;;;;;;
OUT_CR_LF ; outputs CR and LF, destroys W
MOVLW 0AH
CALL OUT_SER_CHAR
MOVLW 0DH
CALL OUT_SER_CHAR
RETURN
;;;;;;
OUT_SER_CHAR: ; outputs character in W on serial port. Uses variables
;TEMP, SEROUT_LOOP and SEROUT_TIME, destroys W.
MOVWF TEMP
MOVLW .9
MOVWF SEROUT_LOOP
BCF STATUS, C ; set C to 0, start bit
SEROUT1:
BTFSC STATUS, C
BCF PORTB, TX ; send a one. note inversion.
BTFSS STATUS, C
BSF PORTB, TX ; or a zero. note inversion
MOVLW .135
MOVWF SEROUT_TIME ; bit time delay, 416 uS at 2400 baud
SEROUT2:
DECFSZ SEROUT_TIME, F
GOTO SEROUT2
NOP
RRF TEMP, F ; least sign bit now in C
DECFSZ SEROUT_LOOP, F ; does not affect status
GOTO SEROUT1 ; next character
BCF PORTB, TX ; send stop bit
MOVLW .135
MOVWF SEROUT_TIME ; bit time delay, 416 uS at 2400 baud
SEROUT3:
DECFSZ SEROUT_TIME, F
GOTO SEROUT3
NOP
RETURN
END