Serin (receive data)

receives a byte or string of bytes serially at 300 to 2400 bps (4 MHz clock). Optionally filters incoming data to a particular range of ASCII characters, such as numbers.

Serin lets a PIC application receive data from another PIC or any computer equipped with an RS-232 serial port.

Before we go into the details of using Serin, let's define a term: Polarity is the sense of the serial input. Normally, a computer that sends data via RS-232 will output it to an inverting line driver. The computer will output +5 volts for 1 and 0 volts for 0. The line driver will invert this and increase the voltage swing so that +10 volts is 0 and -10 volts is 1.

 When we say "true" polarity, we mean the computer output (+5V = 1). "Inverted" polarity is like the output of the line driver, but without the wide voltage swing (+5V = 0). When you want to connect the PC's serial output directly to a PIC without a line receiver, use inverted polarity and a series resistor of approximately 22k. Static-protection diodes inside the PIC will clip the input voltage to +5V and ground. The resistor will limit the current from the RS-232 line into the PIC to a safe level.

To use Serin, make the desired pin an input and set up the filter flag. With filtering on, Serin will ignore bytes that fall outside the range of ASCII values defined by filt_lo and filt_hi. In the demo program, these are set to accept only the characters representing numbers 0 through 9. You could set them to A and Z or any other contiguous range of ASCII character values. If you want numeric text converted to binary values--as shown in the next entry, Serin (convert # data)--turn filtering on, and leave t he range set for numbers. 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 number of characters you wish to receive into bytes.

Put the pin number into pin and the port number into port. Call Serin. The routine will automatically receive the specified number of bytes into a buffer at the end of memory. When Serin returns, the data will be stored starting at buffer-1 and the count (number of bytes received) will be in the location buffer.

 The reason Serin records the number of bytes actually received is that it can differ from the value you requested with bytes. When the filter is on, Serin will reject bytes data outside the filter range. When it receives the first byte that meets the filter requirements, it sets a flag called filt_1st. Once this flag is set, Serin will continue to accept bytes until it either receives the number specified by bytes, or a character outside the filter range arrives. This allows Serin to receive variable-length numbers and reject text.

To receive a 16-bit number, you would turn on the filter and put 5 in bytes. Serin would receive from 1 to 5 characters representing numbers from 0 to 65535. Input values must not contain commas; 65,535 would be received as "65".

When the filter is off, the buffer will contain exactly the number of bytes you specified. Serin can receive 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 input and leave it at that. The disk that accompanies this book contains stripped versions of Serin for such applications. The advantage is that these use much less program memory than the routine listed here. Check the files SERIN_2.SRC and SERIN_3.SRC on the accompanying disk.

Serin, 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 Serin.

To see Serin in operation, connect the circuit below to an erasable PIC or PIC emulator, such as the Parallax downloader. Assemble and run SERIN.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). Type a few characters and watch the LEDs display the binary equivalents of the received data. To check out the filtering capability, modify the program to set the filter bit, then reassemble and rerun it. Now the LEDs will respond only when you press the number keys 0 through 9.


; SERIN port, pin, baud, polarity, bytes, filter
; Receives data serially at the specified rate and polarity via the specified 
; port pin. Stores the data as a counted string starting at buffer-1 (buffer is 
; the number of bytes in the string). An optional filter causes the routine to
; ignore data outside a range of ASCII character values set by filt_lo and 
; filt_hi. 

buffer  =       31      ; String storage. 
B2400   =       30      ; Bit delay for 2400 baud. 
B1200   =       62      ; Bit delay for 1200 baud. 
B600    =       126     ; Bit delay for 600 baud. 
B300    =       255     ; Bit delay for 300 baud. 
filt_lo =       '0'             ; Set filter for ASCII 
filt_hi =       '9'             ; chars representing 0-9. 

        org     8
baud    ds      1       ; Baud rate. 
pin     ds      1       ; Pin number (0-7).
port    ds      1       ; Port number (0-2).
bytes   ds      1       ; Number of bytes to receive. 
temp1   ds      1       ; Temporary counter for Serin.
temp2   ds      1       ; Temporary counter for Serin. 
temp3   ds      1       ; Temporary counter for delay.
ser_fl  ds      1       ; Flags for serin switches. 

polarity        =       ser_fl.0        ; Polarity: 0=true, 1=inverted. 
filter  =       ser_fl.1        ; Range filter: 0=off, 1=on. 
filt_1st        =       ser_fl.2        ; 1st filter byte passed?: 0=no, 1=yes. 
aux     =       ser_fl.3        ; Temporary flag for filter comparisons. 

; 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 Serin to get a bit and delay for a number of loops
; set by temp3. Jumping into get_bit:loop provides the delay, but ignores the 
; input bit. 
get_bit mov     fsr,port        ; Point to port. 
        mov     w,indirect      ; IF Polarity = 0 THEN w = Port 
        snb     Polarity        ; ELSE w = NOT Port
        mov     w,/indirect
        AND     w,pin   
        snz             ; IF w AND pin THEN carry = 1
        clc             ; ELSE carry = 0. 
        sz
        stc
        mov     fsr,temp1       ; Point to buffer location. 
        rr      indirect        ; Rotate carry into msb of data byte.
:loop   jmp     $+1     ; Two-cycle nops. 
        jmp     $+1
        jmp     $+1
        jmp     $+1
        jmp     $+1
        djnz    temp3,:loop
        ret

; Main program start. Receives a single byte of data, filtered for numeric 
; characters, and displays the data bits on LEDs connected to port RB. 
start   mov     !ra, #15        ; All inputs. 
        mov     !rb,#0  ; All outputs for LEDs.
        clr     rc      ; Clear the LEDs. 
        clr     ser_fl  ; Clear serial flags.
        mov     bytes,#1        ; Receive 1 byte 
        clrb    filter  ; not filtered 
        setb    polarity        ; inverted polarity
        mov     baud,#B2400     ; at 2400 baud 
        mov     pin,#2  ; via pin 2 
        mov     port,#0 ; of port ra. 
        call    Serin   ; Receive the data. 
        mov     rb,buffer-1     ; Move byte to LEDs on RB. 
        jmp     start   ; Endless loop

; Body of the Serin routine. Expects the baud constant in baud, number of
; bytes to receive in bytes, the I/O port (0-2 for RA through RC) in port, 
; the pin number (0-7) in pin, and the flag settings in ser_fl 
Serin   clr     buffer  ; Clear buffer counter. 
        clrb    filt_1st
        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 ; Pointer for first data address. 
:poll   mov     fsr,port
        mov     w,indirect      ; IF Polarity = 0 THEN w = Port 
        snb     Polarity        ; ELSE w = NOT Port
        mov     w,/indirect
        AND     w,pin   ; IF pin = 0 THEN receive data
        jnz     :poll   ; ELSE poll
        clc
        mov     w,>>baud        ; LET temp3 = baud/2
        mov     temp3,w ; Set up 1/2 bit time delay. 
        call    get_bit:loop    ; Jump into delay of get_bit.
        mov     temp3,baud      ; Set up full bit time delay. 
        call    get_bit:loop    ; Jump into delay of get_bit.
        mov     temp2,#8        ; Eight data bits.      
:rcv    mov     temp3,baud      ; Set up bit delay.
        call    get_bit ; Get the next data bit.
        djnz    temp2,:rcv      ; Eight bits received?
:done   mov     temp3,baud      ; Set up bit delay.
        call    get_bit:loop    ; Wait for stop bit.
        jnb     filter,:skip    ; IF filter=0 (off) THEN :skip
        clrb    aux     ; LET aux = 0. 
        csae    indirect,#filt_lo       ; IF byte < filt_lo THEN aux=1
        setb    aux
        csbe    indirect,#filt_hi       ; IF byte > filt_hi THEN aux=1
        setb    aux
        jnb     aux,:skip       ; If aux = 0 (byte is within 
        snb     filt_1st        ; filter range) :skip. If byte is
        ret             ; out of range, but valid bytes have 
        jmp     :poll   ; been received, return. Else poll. 
:skip   setb    filt_1st
        dec     temp1   ; Decrement buffer pointer. 
        inc     buffer  ; Increment buffer counter. 
        djnz    bytes,:poll     ; More bytes to receive: poll.
        ret