Serout (send data)

transmits a byte or string of bytes serially at 300 to 2400 bps (4 MHz clock).

Serout lets a PIC application transmit data to another PIC or any computer equipped with an RS-232 serial port.

 If you have not already done so, please read the discussion in paragraph two of the first Serin entry (page 74) on the meaning of "polarity" as it applies to these serial routines.

When you want to connect the PIC's serial output directly to a computer without a line driver, use inverted polarity. Even though the voltage swing isn't legal RS-232, this will generally work, provided cables are kept short.

To use Serout, put the desired pin into the stop-bit state (0 for inverted output, 1 for true) and make the pin an output. This will prevent sending a false start bit to the receiver.

Next, create a string of the bytes you want to transmit at the location labeled buffer. Store the number of bytes at buffer, the first byte at buffer-1, the second byte at buffer-2, and so on.

Specify the baud rate by moving one of the constants B300 through B2400 into the variable baud. Specify the sense by setting or clearing the polarity bit (0 = true, 1 = inverted). Put the pin number into pin and the port number into port. Call Serout. The routine will automatically send each byte of the string.

When Serout returns, the data starting at buffer-1 will be intact, but the count (at buffer) will have been cleared to 0.

Note that Serout permits your program to make a serious mistake; If the count at buffer is 0 when Serout is called, the routine will attempt to send 256 bytes of data. It will alter ports and control registers in the lower seven or eight bytes of RAM. Make sure that buffer contains the correct count before calling Serout.

If your application requires sending a number out the serial port as a text string, see the next entry, Serout (format # data). It converts a 16-bit value into a Serout string with an optional fixed decimal point.

Note that this version of Serout does not support the open-drain/open-source capabilities of the PBASIC version. Those require manipulation of the TRIS register, which we've avoided in this series of subroutines.

Serout can transmit serial data through any pin of any I/O port, and can change port and pin assignments while the program is running. In many applications, it's enough to assign one pin for serial output and leave it at that. The disk that accompanies this book contains stripped versions of Serout for such applications. The advantage is that they use less program memory than the routine listed here. Check the files SEROUT_2.SRC and SEROUT_3.SRC on the accompanying disk.

Serout, like the other routines that accept pin and port arguments, requires the short table Pinz. Remember that tables and subroutines must be located in the first 256 words of a 512-word program memory page.

Demonstrating Serout.

To see Serout in operation, connect the circuit below to an erasable PIC or PIC emulator, such as the Parallax downloader. Assemble and run SEROUT.SRC. Make sure to use a 4-MHz clock on the PIC or downloader. Load a terminal program on the PC connected to the PIC and set it for 2400 baud, no parity, 8 data bits, 1 stop bit (N81). When you apply power to the PIC or reset the downloader, the letters "OK" will appear on the PC screen.


; SEROUT port (in w), pin, baud, polarity, data
; Transmits data serially at the specified rate and polarity out the specified port 
; pin. Data bytes to be transmitted must be arranged in memory starting at the 
; address "buffer-1" and working downward (e.g., 30,29,28...). The number of 
; bytes to transmit must be stored at the address labeled "buffer." A companion 
; routine, BIN_ASC, converts 16-bit numbers to this form, optionally adding a 
; decimal point. See the listing for Serout ...(format # data). 
; Serout uses the common data format of no parity, 8 data
; bits, 1 stop bit (N81). 

buffer  =       31      ; String storage at end of memory. 
B2400   =       30      ; Bit delay for 2400 baud at 4MHz. 
B1200   =       62      ; Bit delay for 1200 baud at 4MHz. 
B600    =       126     ; Bit delay for 600 baud at 4MHz. 
B300    =       254     ; Bit delay for 300 baud at 4MHz. 

        org     8
baud    ds      1       ; Baud rate. 
pin     ds      1       ; Pin number (0-7).
port    ds      1       ; Port number (0-2).
temp1   ds      1       ; Temporary counter for Serout.
temp2   ds      1       ; Temporary counter for Serout. 
temp3   ds      1       ; Temporary counter for delay.
polarity        =       PA2     ; Polarity: 0=true, 1=inverted. 
; Note that the polarity flag is the unused page-address bit of the PIC 16C5x 
; series. If you port this program to another type of PIC, or another routine uses 
; this bit, you may have to reassign it. 

; Device data and reset vector
        device  pic16c55,xt_osc,wdt_off,protect_off
        reset   start
        org     0

; Table to convert pin number (0-7) into bit mask (00000001b to 10000000b). 
Pinz    jmp     pc+w
        retw    1,2,4,8,16,32,64,128

; Subroutine used by Serout to send a bit with the specified polarity (true or 
; inverted) and timing (300, 600, 1200, or 2400 bps). 
emitBit mov     fsr,port        ; Point to the output port. 
        mov     temp3,baud      ; Load baud rate into temp counter. 
        mov     w,status        ; Copy carry bit (data bit) into w. 
        snb     polarity        ; If polarity = 1 then invert w.0. 
        XOR     w,#1
        AND     w,#1    ; Copy w.0 back to carry bit. 
        clc             ; (This roundabout method of inverting 
        snz             ; carry is needed since "XOR status,w"
        stc             ; doesn't work properly.)

; This loop deserves some explanation. The first time through, it sets or clears 
; the serial output bit. Thereafter, it's just a delay loop. 
:loop   mov     w,pin   ; Get the pin mask. 
        sc              ; Data in carry bit. 
        OR      indirect,w      ; If carry = 0 then set bit.
        mov     w,/pin
        snc     
        AND     indirect,w      ; Else if carry = 1 then clear bit. 
        jmp     $+1     ; Two-cycle nops for time delay. 
        jmp     $+1
        djnz    temp3,:loop     ; Number of trips through loop 
        ret             ; set by baud rate in temp3. 

; Start of demo program. Upon power up or reset, the PIC transmits the string
; "OK" via pin 2 of port ra at 2400 baud, inverted polarity

start   clr     ra      ; High is a start bit, so hold ra low. 
        mov     !ra, #0 ; All outputs. 
        mov     buffer,#2       ; Put test string "OK"
        mov     buffer-1,#'O'   ; into the buffer, starting with the 
        mov     buffer-2,#'K'   ; number of characters. 
        setb    polarity        ; Inverted for direct connection. 
        mov     baud,#B2400     ; 2400 baud. 
        mov     pin,#2  ; Pin 2. 
        mov     port,#0 ; of port ra. 
        call    Serout  ; Send the data. 
        jmp     $       ; Endless loop

; Upon entry, the desired pin must already be set up as an output. 
; The w register contains a number representing the output port (0-2) for 
; RA through RC. Variable "pin" contains the pin number (0-7). The 
; variable baud must contain a number representing the baud rate. 
; The polarity flag must be set (1) for true, or cleared (0) for inverted
; output. A data string consisting of the number of bytes, followed by the 
; bytes arranged downward in memory, must be at the location "buffer." 
; Upon return, the data in the buffer will be unchanged, but the 0th entry
; (representing the string length) will have been decremented to 0. 

Serout  add     port,#RA        ; Add offset for port RA. 
        mov     w,pin
        call    Pinz    ; Get bit mask from the table. 
        mov     pin,w   ; Put the mask into pin. 
        mov     temp1,#buffer-1 ; Point to first data address. 
:xmit   clc             ; Start bit into carry
        call    emitBit ; Send start bit. 
        mov     temp2,#8        ; Send 8 data bits. 
:bits   mov     fsr,temp1       ; Point to data byte.
        rr      indirect        ; Rotate bit 0 into carry.
        call    emitBit ; Send the data bit. 
        djnz    temp2,:bits     ; All 8 bits sent? No: send more bits.
        mov     fsr,temp1       ; Point to the data byte.
        rr      indirect        ; Rotate it back into original position. 
        sb      polarity        ; If it was inverted, invert it again to 
        NOT     indirect        ; restore original. 
        stc             ; Move stop bit into carry
        call    emitBit ; Send the stop bit. 
        dec     temp1   ; Point to next data byte. 
        djnz    buffer,:xmit    ; All bytes sent? No: send more bytes. 
        ret             ; Yes: return.