Microchip PIC Simple EUSART Serial Example

Sometimes, it’s nice to see an example of an implementation that is, otherwise, strewn throughout pages of a datasheet. I decided to pull some serial code out of a debugging file and provide it as an example to anyone who is having trouble getting their Microchip EUSART to perform correctly. The example is thoroughly documented and I am providing a makefile that will build the example using GPUtils. Note that the example is using a Cygwin (read unix) installation of GPUtils, but simply changing the paths to the executable files defined in the makefile will allow building on Windows as well. Additionally, this example should be easily portable to MPLab and MPASM by changing the configuration bit section in main.asm.

This example was written for the PIC16F1828 device, but is pin-compatible with a number of 20-pin devices. With a few changes it will work with a PIC16F690, for example. It was written specifically to demonstrate how to produce serial output with simple macro insertions such as this

_SerialSendTableL TESTTEXT
_serialSendByteL ' '
_serialSendBitsL b'10101010'
_SerialSendTableL CRLF

The meat of the example is contained within the serialoutput.asm file. The UART is configured for 57.6k baud at 8MHz, 8 data bits and one stop bit with no parity. It contains a few macros that will allow the caller to output a byte, a binary representation of a byte, and string values defined in a program data table (DT).

;-----------------------------------------------------------------------------
; File:     serialoutput.inc
; Date:     02/24/2017
; Purpose:
;

   ERRORLEVEL     -202

;.............................................................................
;
#include "project.inc"

;.............................................................................
;Declare data for serial helpers
;
.serialoutput.data      udata
serialLoop              res      .2       ;We'll use these bytes for loop count
serialSendByte          res      .1       ;Temporary for shifting literal args
serialTableHi           res      .1       ;Table address high byte
serialTableLo           res      .1       ;Table address low byte

;=============================================================================
;Begin Code section
;
.serialoutput.code      code

;==============================================================================
; Function:    ConfigureSerial
; Purpose:     This function will configure a PIC16F1828 UART for transmission
;              at a data rate of 57.6k baud. All related pins must be set to
;              digital I/O before this function is called.
;
ConfigureSerial:
      banksel     TRISB                   ;Select tristate bank and
      movlw       b'01000000'             ; load work reg with mask
      iorwf       TRISB, f                ;Or work reg with tristate for input

      banksel     TXSTA                   ;Select transmitter status register
      bcf         TXSTA, SYNC             ;Enable Async communication
      bsf         TXSTA, BRGH             ;Enable high resolution BRG
      bcf         TXSTA, TX9D             ;Disable 9 bit transmission

      banksel     BAUDCON                 ;Select baud rate control register
      bsf         BAUDCON, BRG16          ;Enable 16-bit BRG value
      bcf         BAUDCON, WUE            ;

      banksel     RCSTA                   ;bank select status and control reg
      bsf         RCSTA, SPEN             ;Serial port enable bit

      banksel     SPBRGL                  ;Select low byte of 16-bit baud rate
      movlw       .34                     ;57.6k baud @8MHz is .34 (datasheet)
      movwf       SPBRGL                  ; move this value to LSB
      banksel     SPBRGH                  ;Select MSB bank of BRG value
      clrf        SPBRGH                  ; set to 0

      banksel     TXSTA                   ;Select status TX status again and
      bsf         TXSTA, TXEN             ; enable transmitter
   return
   global ConfigureSerial

;------------------------------------------------------------------------------
; Function:    _serialSendByteW
; Purpose:     Sends a single byte to the peripheral to be transmitted over
;              the TX pin. The byte to be sent should be in the working
;              register.
;
_serialSendByteW macro
   local ___txWait
___txWait
      banksel     PIR1                    ;Select bank
      btfss       PIR1, TXIF              ;Test for buffer clear to send next
         goto       ___txWait             ; Block waiting to send

      banksel     TXREG                   ;Select TX buffer register and
      movwf       TXREG                   ; move work register value to TXREG
   endm

;------------------------------------------------------------------------------
; Function:    _serialSendByteL
; Purpose:     Sends a single byte to the peripheral to be transmitted over
;              the TX pin. The argument provided is a literal value
;
_serialSendByteL macro byte
   local ___txWait
___txWait
      banksel     PIR1                    ;Select bank
      btfss       PIR1, TXIF              ;Test for buffer clear to send next
         goto       ___txWait             ; Block waiting to send

      movlw       byte                    ;Load literal to working register
      banksel     TXREG                   ;Select TX buffer register and
      movwf       TXREG                   ; move work register value to TXREG
   endm

;------------------------------------------------------------------------------
; Function:    _serialSendBitsL
; Purpose:
;
_serialSendBitsL macro literal
   local ___bitLoop, ___sendMark, ___sendSpace, ___doneSend
      banksel     serialSendByte          ;Select temp register bank
      movlw       literal                 ;Load literal value to work register
      movwf       serialSendByte          ; Then store in file reg for shifting

      movlw       .8                      ;Loop counter for 8 bits
      banksel     serialLoop              ;Bank select
      movwf       serialLoop              ; and move literal 8 into counter reg

___bitLoop
      rlf         serialSendByte,f        ;Rotate first bit to carry flag
      btfss       STATUS, C               ;Test LSb for mark or space
         goto     ___sendSpace            ; Jump to space if not set
      goto        ___sendMark             ; Jump to mark if set

___sendMark
     _serialSendByteL 0x31                ;Send '1' byte
      goto        ___doneSend             ;Jump to loop check

___sendSpace
     _serialSendByteL 0x30                ;Send '1' byte

___doneSend
      banksel     serialLoop              ;Select bank for loop counter
      decfsz      serialLoop, f           ; Decrement and see if we're done
         goto     ___bitLoop              ;  Nope, keep shifting and sending...
   endm


;------------------------------------------------------------------------------
; Function:    _SerialSendTableL
; Scope:       Public
; Purpose:     Writes a table to the serial device
;
_SerialSendTableL macro table
      banksel     serialTableHi           ;Select Bank
      movlw       HIGH table              ;Get the high bits of the table addr
      movwf       serialTableHi           ;Store them in our file register
      movlw       LOW table               ;Get the low bits
      movwf       serialTableLo           ;And store those
      call        SerialWriteTable        ;Call our function to send table data
   endm

;==============================================================================
; Function:    SerialLogWriteTable
; Scope:       Private
; Purpose:     This function is used by the _SerialLogTableL macro. Don't call
;              directly.
;
SerialWriteTable:
      call        SerialTableLookup       ;Call the lookup function to get next
      iorlw       0x00                    ;Check to see if it's a null
      btfsc       STATUS, Z               ;If zero set, then we're done
         goto     ___SerialWriteTable_end

     _serialSendByteW                     ;Send the next letter
      goto        SerialWriteTable        ;Loop back to function start

___SerialWriteTable_end
   return
   global SerialWriteTable

;==============================================================================
; Function:    SerialLogTableLookup
; Scope:       Private
; Purpose:     This function is used by the SerialLogWriteTable function.
;              Don't call directly.
;
SerialTableLookup:
      banksel     serialTableHi        ;Select bank
      movf        serialTableHi,w      ;Load hi bits to accumulator
      movwf       PCLATH               ;put this in the PCLATH register
      movf        serialTableLo,w      ;Load the low bits to accumulator
      incf        serialTableLo,f      ;Increment low data register for next
      btfsc       STATUS,Z             ;Skip the hi bank inc if no zero flag
      incf        serialTableHi,f      ;The next low addr crosses a page,
      movwf       PCL                  ;Jump to table, return is implicit
   global SerialTableLookup





I would talk about the file more, but it’s commented to death. If anyone would like a walkthrough of this file, simply leave a comment. The main.asm file that drives the example contains some boiler-plate initialization and then calls each macro in the include file:

;-----------------------------------------------------------------------------
; File:     main.asm
; Date:     02/17/2017
; Purpose:
;

   ERRORLEVEL     -202
   ERRORLEVEL     -205

   CONFIG   FOSC = INTOSC
   CONFIG   WDTE = OFF
   CONFIG   PWRTE = ON
   CONFIG   MCLRE = OFF
   CONFIG   BOREN = OFF
   CONFIG   IESO = OFF
   CONFIG   PLLEN = OFF

;.............................................................................
;Processor include
;
#include "p16f1828.inc"
#include "serialoutput.inc"

;.............................................................................
;Declare data local to this module. We just need a loop variable.
;
.main.data        udata
loop              res       .1

;-----------------------------------------------------------------------------
;Jump to start label at reset PC
;
.romStart         code 0x00
      goto        Start

;-----------------------------------------------------------------------------
;Jump to interrupt handler at interrupt vector
;
.romInterrupt     code 0x04
      goto        Interrupt

;=============================================================================
;Begin Code section
;
.main.code        code

;=============================================================================
;Declare program data tables for serial output
;
CRLF              dt    "\r\n",0
TESTTEXT          dt    "Outputting Byte",0

;=============================================================================
;Begin processing from 0x00
;
Start:
      ;.......................................................................
      ;Disable peripheral interrupts and global
      ;
      banksel     INTCON
      bcf         INTCON, GIE
      bcf         INTCON, PEIE

      ;.......................................................................
      ;Oscillator configuration
      ;
      banksel     OSCCON
      movlw       b'01110010'             ;8MHz oscillator frequency
      movwf       OSCCON

      banksel     OSCSTAT
___Main_oscwait
      btfss       OSCSTAT, HFIOFR         ;Wait for high freq oscillator
         goto     ___Main_oscwait

___Main_oscstable
      btfss       OSCSTAT, HFIOFS         ;Wait for HFO stable
         goto     ___Main_oscstable

      ;.......................................................................
      ;Disable analog
      banksel     ANSELA
      clrf        ANSELA
      clrf        ANSELB
      clrf        ANSELC

      ;.......................................................................
      banksel     ADCON0
      bcf         ADCON0,ADON             ;Disable A2D converters

      ;........................................................................
      ;Disable weak pullups
      ;
      banksel     OPTION_REG
      bcf         OPTION_REG, 7

      ;.......................................................................
      ;Call serial configuration function defined in serialout.inc
      ;1
      call        ConfigureSerial

      ;.......................................................................
      ;Set PORTA:5 as an output
      ;
      banksel     TRISC                   ;Bank select for tristate config
      bcf         TRISC, 7                ;Trigger pin output

;==============================================================================
;  Function:      Main
;  Purpose:
;
Main:

      ;.......................................................................
      ; Use PORTC:7 to trigger our oscilloscope
      ;
      banksel     PORTC
      bsf         PORTC, 7
      nop
      nop
      bcf         PORTC, 7

      ;.......................................................................
      ; Test the macros
      ;
     _SerialSendTableL TESTTEXT
     _serialSendByteL ' '
     _serialSendBitsL b'10101010'
     _SerialSendTableL CRLF

      ;.......................................................................
      ; Sleep for a short period
      ;
      banksel     loop
      movlw       0xff
      movwf       loop
___Main_sleep
      nop
      nop
      nop
      decfsz      loop, f
         goto     ___Main_sleep

      goto        Main                    ;Loop back to main


;==============================================================================
;  Function:      Interrupt
;  Purpose:
;
Interrupt:
   retfie                                 ;Return from interrupt handler

   end ;Stop assembly

Assembling these files will produce continuous output on RB7 (pin 10/TX). Note that I also use RC7 as a trigger so that I can show you this:

And, if you’re using a RS-232 to TTL device, such as a Maxim Integrated MAX233, you can send this output to a serial port:

Wiring your microcontroller to a PC serial port through an RS-232 to TTL line driver such as the MAX233A is very straight-forward:

Source code available for download here: Simple Serial Example

Leave a Reply

Your email address will not be published. Required fields are marked *

© Zillow, Inc., 2006-2016. Use is subject to Terms of Use
What's a Zestimate?