External Device Interface Macros - Programming and Customizing PICmicro® Microcontrollers
Myke's Home Page
Book CD-ROM Home
File Copying/Harddrive Setup
Development Tools
Experiments
Projects
Useful Code Snippets and Macros
Useful Snippets and Macros
16 Bit Arithmetic
Internal Peripheral Interface Code and Macros
External Device Interface Macros
Rentron Articles
Introduction to Electronics
Introduction to Programming
Datasheets
PCBs
Links
External Device Interface Macros
Here are a set of three macros that I use for interfacing with common devices
using the PICmicro. Each set of macros is quite large, but you will find that they are quite
general and can save you hours trying to figure out how to come up with the LCD, Serial Port and
I2C Device Interfaces.
LCD Interface Macros
NRZ Serial I/O
Mid-Range I2C Device Interface
1. LCD Interface Macros
Here are some LCD Interface Macros that can be used to interface with Hitachi
44780 based LCDs. More information about these Macros and how they are used can be found in the
book.
The first is an 8 bit interface with "Busy Flag" polling. This interface
will be the fastest of the three presented - but will also require the most PICmicro® MCU
Interface pins.
LCD8Poll Macro DataPort, EPort, EPin, RSPort, RSPin, RWPort, RWPin,Freq
variable Dlay5Value, Dlay160Value, Dlay160Bit1 = -1, Dlay160Bit2 = -1
variable BitCount = 0
variable Value = 128, Bit = 7
errorlevel 0,-224
Dlay5Value = ((5007 * (Freq / 1000) / 4000) / 7) + 256
Dlay160Value = (163 * (Freq / 1000) / 4000) / 3
while (Bit >= 0) ; Find the Number of Bits and their
; Positions in “Dlay160Value"
if ((Dlay160Value & Value) != 0)
if (Dlay160Bit1 == -1) ; Set the Upper Bit
Dlay160Bit1 = Bit
else
if (Dlay160Bit2 == -1)
Dlay160Bit2 = Bit
endif
endif
BitCount = BitCount + 1
endif
Value = Value >> 1
Bit = Bit - 1
endw
if (BitCount > 2) ; Just Want max two Bits
if ((Dlay160Bit1 - 1) == Dlay160Bit2)
Dlay160Bit1 = Dlay160Bit1 + 1 ; Shift Top up by 1
Dlay160Bit2 = -1 ; Delete Second
else
Dlay160Bit2 = Dlay160Bit2 + 1 ; Shift Bottom up by 1
endif
endif
Dlay5 ; Delay 5 msecs
movlw (Dlay5Value & 0x0FF00) >> 8
movwf Dlay
movlw Dlay5Value & 0x0FF
subwf Dlay, w
xorlw 0x0FF
addwf Dlay, w
btfsc STATUS, Z
decfsz Dlay, f
goto $ - 5
return
LCDPORTInit ; Initialize the I/O Ports
bsf STATUS, RP0 ; ONLY used by mid-range
movlw 0x000
movwf DataPort
bcf EPort, EPin
bcf RSPort, RSPin
bcf RWPort, RWPin
bcf STATUS, RP0
bcf EPort, EPin
bcf RSPort, RSPin
bcf RWPort, RWPin
return
LCDIns ; Send the Instruction to the LCD
movwf Dlay
movlw 0x0FF ; Read the "BF" Flag
tris DataPort
bcf RSPort, RSPin ; Read the Instruction Register
bsf RWPort, RWPin
goto $ + 1
bsf EPort, EPin
nop
movf DataPort, w ; Read the Data Port Value
nop
bcf EPort, EPin
andlw 0x080 ; Is the High Bit Set?
btfss STATUS, Z
goto $ - 7
bcf RWPort, RWPin
movlw 0 ; Put the DataPort Back into Output Mode
tris DataPort
movf Dlay, w ; Get the Saved Character
movwf DataPort
if (Freq > 8000000) ; Make Sure Proper Delay is In Place
if (Freq < 16000000)
nop
else
goto $ + 1
endif
endif
bsf EPort, EPin
if (Freq > 8000000) ; Make Sure Proper Delay is In Place
if (Freq < 16000000)
nop
else
goto $ + 1
endif
endif
bcf EPort, EPin
return
LCDChar ; Send the Character to the LCD
movwf Dlay
movlw 0x0FF ; Read the "BF" Flag
tris DataPort
bcf RSPort, RSPin ; Read the Instruction Register
bsf RWPort, RWPin
goto $ + 1
bsf EPort, EPin
nop
movf DataPort, w ; Read the Data Port Value
nop
bcf EPort, EPin
andlw 0x080 ; Is the High Bit Set?
btfss STATUS, Z
goto $ - 7
bsf RSPort, RSPin
bcf RWPort, RWPin
movlw 0 ; Put the DataPort Back into Output Mode
tris DataPort
movf Dlay, w ; Get the Saved Character
movwf DataPort
if (Freq > 8000000) ; Make Sure Proper Delay is In Place
if (Freq < 16000000)
nop
else
goto $ + 1
endif
endif
bsf EPort, EPin
if (Freq > 8000000) ; Make Sure Proper Delay is In Place
if (Freq < 16000000)
nop
else
goto $ + 1
endif
endif
bcf EPort, EPin
return
LCDInit ; Do the 8 Bit Initialization
call Dlay5 ; Wait 15 msecs
call Dlay5
call Dlay5
movlw 0x030
movwf DataPort
if (Freq > 8000000) ; Make Sure Proper Delay is In Place
if (Freq < 16000000)
nop
else
goto $ + 1
endif
endif
bsf EPort, EPin
if (Freq > 8000000) ; Make Sure Proper Delay is In Place
if (Freq < 16000000)
nop
else
goto $ + 1
endif
endif
bcf EPort, EPin ; Send the Reset Instruction
call Dlay5
if (Freq > 8000000) ; Make Sure Proper Delay is In Place
if (Freq < 16000000)
nop
else
goto $ + 1
endif
endif
bsf EPort, EPin
if (Freq > 8000000) ; Make Sure Proper Delay is In Place
if (Freq < 16000000)
nop
else
goto $ + 1
endif
endif
bcf EPort, EPin ; Send the Reset Instruction
bsf Dlay, Dlay160Bit1 ; Delay 160 usecs
if (Dlay160Bit2 != -1)
bsf Dlay, Dlay160Bit2
endif
decfsz Dlay, f
goto $ - 1
movlw 0x030
call LCDIns
movlw 0x038 ; Set Interface Length
call LCDIns
movlw 0x010 ; Turn Off Display
call LCDIns
movlw 0x001 ; Clear Display RAM
call LCDIns
movlw 0x006 ; Set Cursor Movement
call LCDIns
movlw 0x00E ; Turn on Display/Cursor
call LCDIns
return
errorlevel 0,+224 ; Enable "TRIS" Indicators
endm
The second Interface Method uses a 4 bit data interface:
LCD4 Macro DataPort, DataBit, EPort, EPin, RSPort, RSPin, RWPort,
RWPin, Freq
variable Dlay5Value, Dlay160Value, Dlay160Bit1 = -1, Dlay160Bit2 = -1
variable BitCount = 0
variable Value = 128, Bit = 7
Dlay5Value = ((5007 * (Freq / 1000) / 4000) / 7) + 256
Dlay160Value = (163 * (Freq / 1000) / 4000) / 3
if ((DataBit != 0) && (DataBit != 4))
error "Invalid 'DataBit' Specification - Can only be '0' or '4'"
endif
while (Bit >= 0) ; Find the Number of Bits and their
; Positions in "Dlay160Value"
if ((Dlay160Value & Value) != 0)
if (Dlay160Bit1 == -1) ; Set the Upper Bit
Dlay160Bit1 = Bit
else
if (Dlay160Bit2 == -1)
Dlay160Bit2 = Bit
endif
endif
BitCount = BitCount + 1
endif
Value = Value >> 1
Bit = Bit - 1
endw
if (BitCount > 2) ; Just Want max two Bits
if ((Dlay160Bit1 - 1) == Dlay160Bit2)
Dlay160Bit1 = Dlay160Bit1 + 1 ; Shift Top up by 1
Dlay160Bit2 = -1 ; Delete Second
else
Dlay160Bit2 = Dlay160Bit2 + 1 ; Shift Bottom up by 1
endif
endif
Dlay5 ; Delay 5 msecs
movlw (Dlay5Value & 0x0FF00) >> 8
movwf Dlay
movlw Dlay5Value & 0x0FF
subwf Dlay, w
xorlw 0x0FF
addwf Dlay, w
btfsc STATUS, Z
decfsz Dlay, f
goto $ - 5
return
LCDPORTInit ; Initialize the I/O Ports
bsf STATUS, RP0 ; ONLY used by mid-range
if (DataBit == 0)
movlw 0x0F0
else
movlw 0x00F
endif
movwf DataPort
bcf EPort, EPin
bcf RSPort, RSPin
bcf RWPort, RWPin
bcf STATUS, RP0
bcf EPort, EPin
bcf RSPort, RSPin
bcf RWPort, RWPin
return
LCDIns ; Send the Instruction to the LCD
movwf LCDTemp ; Save the Value
if (DataBit == 0)
swapf LCDTemp, w ; Most Significant Nybble First
andlw 0x00F
else
andlw 0x0F0
endif
movwf DataPort
bcf RSPort, RSPin
if (Freq > 8000000) ; Make Sure Proper Delay is In Place
if (Freq < 16000000)
nop
else
goto $ + 1
endif
endif
bsf EPort, EPin
if (Freq > 8000000) ; Make Sure Proper Delay is In Place
if (Freq < 16000000)
nop
else
goto $ + 1
endif
endif
bcf EPort, EPin
if (DataBit == 0)
movf LCDTemp, w
andlw 0x00F
else
swapf LCDTemp, w ; Least Significant Nybble Second
andlw 0x0F0
endif
movwf DataPort
bcf RSPort, RSPin
if (Freq > 8000000) ; Make Sure Proper Delay is In Place
if (Freq < 16000000)
nop
else
goto $ + 1
endif
endif
bsf EPort, EPin
if (Freq > 8000000) ; Make Sure Proper Delay is In Place
if (Freq < 16000000)
nop
else
goto $ + 1
endif
endif
bcf EPort, EPin
bsf Dlay, Dlay160Bit1 ; Delay 160 usecs
if (Dlay160Bit2 != -1)
bsf Dlay, Dlay160Bit2
endif
decfsz Dlay, f
goto $ - 1
movf LCDTemp, w
andlw 0x0FC ; Have to Delay 5 msecs?
btfsc STATUS, Z
call Dlay5
return
LCDChar ; Send the Character to the LCD
movwf LCDTemp ; Save the Value
if (DataBit == 0)
swapf LCDTemp, w ; Most Significant Nybble First
andlw 0x00F
else
andlw 0x0F0
endif
movwf DataPort
bsf RSPort, RSPin
if (Freq > 8000000) ; Make Sure Proper Delay is In Place
if (Freq < 16000000)
nop
else
goto $ + 1
endif
endif
bsf EPort, EPin
if (Freq > 8000000) ; Make Sure Proper Delay is In Place
if (Freq < 16000000)
nop
else
goto $ + 1
endif
endif
bcf EPort, EPin
if (DataBit == 0)
movf LCDTemp, w
andlw 0x00F
else
swapf LCDTemp, w ; Least Significant Nybble Second
andlw 0x0F0
endif
movwf DataPort
bsf RSPort, RSPin
if (Freq > 8000000) ; Make Sure Proper Delay is In Place
if (Freq < 16000000)
nop
else
goto $ + 1
endif
endif
bsf EPort, EPin
if (Freq > 8000000) ; Make Sure Proper Delay is In Place
if (Freq < 16000000)
nop
else
goto $ + 1
endif
endif
bcf EPort, EPin
bsf Dlay, Dlay160Bit1 ; Delay 160 usecs
if (Dlay160Bit2 != -1)
bsf Dlay, Dlay160Bit2
endif
decfsz Dlay, f
goto $ - 1
return
LCDInit ; Do the 8 Bit Initialization
call Dlay5 ; Wait 15 msecs
call Dlay5
call Dlay5
if (DataBit == 0) ; Send the Reset Instruction
movlw 0x003
else
movlw 0x030
endif
movwf DataPort
if (Freq > 8000000) ; Make Sure Proper Delay is In Place
if (Freq < 16000000)
nop
else
goto $ + 1
endif
endif
bsf EPort, EPin
if (Freq > 8000000) ; Make Sure Proper Delay is In Place
if (Freq < 16000000)
nop
else
goto $ + 1
endif
endif
bcf EPort, EPin
call Dlay5
bsf EPort, EPin ; Send Another Reset Instruction
if (Freq > 8000000) ; Make Sure Proper Delay is In Place
if (Freq < 16000000)
nop
else
goto $ + 1
endif
endif
bcf EPort, EPin
bsf Dlay, Dlay160Bit1 ; Delay 160 usecs
if (Dlay160Bit2 != -1)
bsf Dlay, Dlay160Bit2
endif
decfsz Dlay, f
goto $ - 1
bsf EPort, EPin ; Send the Third Reset Instruction
if (Freq > 8000000) ; Make Sure Proper Delay is In Place
if (Freq < 16000000)
nop
else
goto $ + 1
endif
endif
bcf EPort, EPin
bsf Dlay, Dlay160Bit1 ; Delay 160 usecs
if (Dlay160Bit2 != -1)
bsf Dlay, Dlay160Bit2
endif
decfsz Dlay, f
goto $ - 1
if (DataBit == 0) ; Send the Data Length Specification
movlw 0x002
else
movlw 0x020
endif
movwf DataPort
if (Freq > 8000000) ; Make Sure Proper Delay is In Place
if (Freq < 16000000)
nop
else
goto $ + 1
endif
endif
bsf EPort, EPin
if (Freq > 8000000) ; Make Sure Proper Delay is In Place
if (Freq < 16000000)
nop
else
goto $ + 1
endif
endif
bcf EPort, EPin
bsf Dlay, Dlay160Bit1 ; Delay 160 usecs
if (Dlay160Bit2 != -1)
bsf Dlay, Dlay160Bit2
endif
decfsz Dlay, f
goto $ - 1
movlw 0x028 ; Set Interface Length
call LCDIns
movlw 0x010 ; Turn Off Display
call LCDIns
movlw 0x001 ; Clear Display RAM
call LCDIns
movlw 0x006 ; Set Cursor Movement
call LCDIns
movlw 0x00E ; Turn on Display/Cursor
call LCDIns
return
endm
The last LCD Interface macro is designed for the two-wire LCD interface which is
shown in the graphic below:
LCD2 Macro ClockPort, ClockPin, DataPort, DataPin, Freq
variable Dlay5Value, Dlay160Value, Dlay160Bit1 = -1, Dlay160Bit2 = -1
variable BitCount = 0, i
variable Value = 128, Bit = 7
Dlay5Value = ((5007 * (Freq / 1000) / 4000) / 7) + 256
Dlay160Value = (163 * (Freq / 1000) / 4000) / 3
while (Bit >= 0) ; Find the Number of Bits and their
; Positions in "Dlay160Value"
if ((Dlay160Value & Value) != 0)
if (Dlay160Bit1 == -1) ; Set the Upper Bit
Dlay160Bit1 = Bit
else
if (Dlay160Bit2 == -1)
Dlay160Bit2 = Bit
endif
endif
BitCount = BitCount + 1
endif
Value = Value >> 1
Bit = Bit - 1
endw
if (BitCount > 2) ; Just Want max two Bits
if ((Dlay160Bit1 - 1) == Dlay160Bit2)
Dlay160Bit1 = Dlay160Bit1 + 1 ; Shift Top up by 1
Dlay160Bit2 = -1 ; Delete Second
else
Dlay160Bit2 = Dlay160Bit2 + 1 ; Shift Bottom up by 1
endif
endif
Dlay5 ; Delay 5 msecs
movlw (Dlay5Value & 0x0FF00) >> 8
movwf Dlay
movlw Dlay5Value & 0x0FF
subwf Dlay, w
xorlw 0x0FF
addwf Dlay, w
btfsc STATUS, Z
decfsz Dlay, f
goto $ - 5
return
LCDPORTInit ; Initialize the I/O Ports
bsf STATUS, RP0 ; ONLY used by mid-range
bcf ClockPort, ClockPin
bcf DataPort, DataPin
bcf STATUS, RP0
bcf ClockPort, ClockPin
bcf DataPort, DataPin
return
LCDIns ; Send the Instruction to the LCD
movwf LCDTemp ; Save the Value
movlw 6 ; Clear the Shift Register
movwf Dlay
bsf ClockPort, ClockPin
bcf ClockPort, ClockPin
decfsz Dlay, f
goto $ - 3
movwf Dlay ; w still equals 6
movf LCDTemp, w ; Shift out the Upper 4 Bits
swapf LCDTemp, f
bsf LCDTemp, 5 ; Make LCDTemp Correct for Shifting
bcf LCDTemp, 4 ; This is "RS" Bit
bcf DataPort, DataPin ; Shift Out Each Bit
btfsc LCDTemp, 5 ; 5 is the Current MSB
bsf DataPort, DataPin ; Shift Out the Next Highest Bit
bsf ClockPort, ClockPin
bcf ClockPort, ClockPin
rlf LCDTemp, f
decfsz Dlay, f
goto $ - 7
bsf DataPort, DataPin ; Latch in the Data
if (Freq > 8000000) ; Make Sure Proper Delay is In Place
if (Freq < 16000000)
nop
else
goto $ + 1
endif
endif
bcf DataPort, DataPin
bsf Dlay, 2 ; Dlay = 6 for Shift Out
bsf Dlay, 1
bsf ClockPort, ClockPin ; Clear the Shift Register
bcf ClockPort, ClockPin
decfsz Dlay, f
goto $ - 3
movwf LCDTemp ; Shift out the Low Nybble
bsf Dlay, 2 ; Dlay = 6 for Shift Out
bsf Dlay, 1
bsf LCDTemp, 5 ; Make LCDTemp Correct for Shifting
bcf LCDTemp, 4 ; This is "RS" Bit
bcf DataPort, DataPin ; Shift Out Each Bit
btfsc LCDTemp, 5 ; 5 is the Current MSB
bsf DataPort, DataPin ; Shift Out the Next Highest Bit
bsf ClockPort, ClockPin
bcf ClockPort, ClockPin
rlf LCDTemp, f
decfsz Dlay, f
goto $ - 7
bsf DataPort, DataPin ; Latch in the Data
if (Freq > 8000000) ; Make Sure Proper Delay is In Place
if (Freq < 16000000)
nop
else
goto $ + 1
endif
endif
bcf DataPort, DataPin
bsf Dlay, Dlay160Bit1 ; Delay 160 usecs
if (Dlay160Bit2 != -1)
bsf Dlay, Dlay160Bit2
endif
decfsz Dlay, f
goto $ - 1
andlw 0x0FC ; Have to Delay 5 msecs?
btfsc STATUS, Z
call Dlay5
return
LCDChar ; Send the Character to the LCD
movwf LCDTemp ; Save the Value
movlw 6 ; Clear the Shift Register
movwf Dlay
bsf ClockPort, ClockPin
bcf ClockPort, ClockPin
decfsz Dlay, f
goto $ - 3
movwf Dlay ; w still equals 6
movf LCDTemp, w ; Shift out the Upper 4 Bits
swapf LCDTemp, f
bsf LCDTemp, 5 ; Make LCDTemp Correct for Shifting
bsf LCDTemp, 4 ; This is "RS" Bit
bcf DataPort, DataPin ; Shift Out Each Bit
btfsc LCDTemp, 5 ; 5 is the Current MSB
bsf DataPort, DataPin ; Shift Out the Next Highest Bit
bsf ClockPort, ClockPin
bcf ClockPort, ClockPin
rlf LCDTemp, f
decfsz Dlay, f
goto $ - 7
bsf DataPort, DataPin ; Latch in the Data
if (Freq > 8000000) ; Make Sure Proper Delay is In Place
if (Freq < 16000000)
nop
else
goto $ + 1
endif
endif
bcf DataPort, DataPin
bsf Dlay, 2 ; Dlay = 6 for Shift Out
bsf Dlay, 1
bsf ClockPort, ClockPin ; Clear the Shift Register
bcf ClockPort, ClockPin
decfsz Dlay, f
goto $ - 3
movwf LCDTemp ; Shift out the Low Nybble
bsf Dlay, 2 ; Dlay = 6 for Shift Out
bsf Dlay, 1
bsf LCDTemp, 5 ; Make LCDTemp Correct for Shifting
bsf LCDTemp, 4 ; This is "RS" Bit
bcf DataPort, DataPin ; Shift Out Each Bit
btfsc LCDTemp, 5 ; 5 is the Current MSB
bsf DataPort, DataPin ; Shift Out the Next Highest Bit
bsf ClockPort, ClockPin
bcf ClockPort, ClockPin
rlf LCDTemp, f
decfsz Dlay, f
goto $ - 7
bsf DataPort, DataPin ; Latch in the Data
if (Freq > 8000000) ; Make Sure Proper Delay is In Place
if (Freq < 16000000)
nop
else
goto $ + 1
endif
endif
bcf DataPort, DataPin
bsf Dlay, Dlay160Bit1 ; Delay 160 usecs
if (Dlay160Bit2 != -1)
bsf Dlay, Dlay160Bit2
endif
decfsz Dlay, f
goto $ - 1
return
LCDInit ; Do the 8 Bit Initialization
call Dlay5 ; Wait 15 msecs
call Dlay5
call Dlay5
movlw 0x023 ; Initialize the I/O Port
movwf LCDTemp ; Save the Value
movlw 6 ; Clear the Shift Register
movwf Dlay
bsf ClockPort, ClockPin
bcf ClockPort, ClockPin
decfsz Dlay, f
goto $ - 3
movwf Dlay
bcf DataPort, DataPin ; Shift Out Each Bit
btfsc LCDTemp, 5 ; 5 is the Current MSB
bsf DataPort, DataPin ; Shift Out the Next Highest Bit
bsf ClockPort, ClockPin
bcf ClockPort, ClockPin
rlf LCDTemp, f
decfsz Dlay, f
goto $ - 7
bsf DataPort, DataPin ; Latch in the Data
if (Freq > 8000000) ; Make Sure Proper Delay is In Place
if (Freq < 16000000)
nop
else
goto $ + 1
endif
endif
bcf DataPort, DataPin
call Dlay5
bsf DataPort, DataPin ; Send another 0x03 to the LCD
if (Freq > 8000000) ; Make Sure Proper Delay is In Place
if (Freq < 16000000)
nop
else
goto $ + 1
endif
endif
bcf DataPort, DataPin
bsf Dlay, Dlay160Bit1 ; Delay 160 usecs
if (Dlay160Bit2 != -1)
bsf Dlay, Dlay160Bit2
endif
decfsz Dlay, f
goto $ - 1
bsf DataPort, DataPin ; Send another 0x03 to the LCD
if (Freq > 8000000) ; Make Sure Proper Delay is In Place
if (Freq < 16000000)
nop
else
goto $ + 1
endif
endif
bcf DataPort, DataPin
bsf Dlay, Dlay160Bit1 ; Delay 160 usecs
if (Dlay160Bit2 != -1)
bsf Dlay, Dlay160Bit2
endif
decfsz Dlay, f
goto $ - 1
movlw 0x022 ; Initialize the I/O Port
movwf LCDTemp ; Save the Value
movlw 6 ; Clear the Shift Register
movwf Dlay
bsf ClockPort, ClockPin
bcf ClockPort, ClockPin
decfsz Dlay, f
goto $ - 3
movwf Dlay
bcf DataPort, DataPin ; Shift Out Each Bit
btfsc LCDTemp, 5 ; 5 is the Current MSB
bsf DataPort, DataPin ; Shift Out the Next Highest Bit
bsf ClockPort, ClockPin
bcf ClockPort, ClockPin
rlf LCDTemp, f
decfsz Dlay, f
goto $ - 7
bsf DataPort, DataPin ; Latch in the Data
if (Freq > 8000000) ; Make Sure Proper Delay is In Place
if (Freq < 16000000)
nop
else
goto $ + 1
endif
endif
bcf DataPort, DataPin
bsf Dlay, Dlay160Bit1 ; Delay 160 usecs
if (Dlay160Bit2 != -1)
bsf Dlay, Dlay160Bit2
endif
decfsz Dlay, f
goto $ - 1
movlw 0x028 ; Set Interface Length
call LCDIns
movlw 0x010 ; Turn Off Display
call LCDIns
movlw 0x001 ; Clear Display RAM
call LCDIns
movlw 0x006 ; Set Cursor Movement
call LCDIns
movlw 0x00E ; Turn on Display/Cursor
call LCDIns
return
endm
2. NRZ Serial I/O
The two macros which follow will give you two different ways of providing NRZ
serial communications using your PICmicro application. These macros will provide you with
serial transmit and receive functions which work as both positive and negative logic.
The first macro provides a straight "bit-banging" serial interface. Note that
it requires the DlayMacro.
NRZSerialNI Macro TXPort, TXPin, RXPort, RXPin, Polarity, Rate, Frequency
variable BitDlay
BitDlay = Frequency / (4 * Rate)
SerialRX ; Receive 8-N-1
if (Polarity == Pos)
btfsc RXPort, RXPin ; Wait for a Bit to Come in
else
btfss RXPort, RXPin
endif
goto $ - 1
DlayMacro BitDlay / 2 ; Wait 1/2 a Bit to Confirm
if (Polarity == Pos)
btfsc RXPort, RXPin ; Confirm Data is Correct
else
btfss RXPort, RXPin
endif
goto SerialRX ; If Just a "Glitch", Restart Start Bit
; Poll
movlw 8 ; Wait for 8 Bits
SRXLoop
if ((BitDlay - 10) > 770) ; Check to See if Value is Too Large
DlayMacro 770 ; Put in a "Double" Delay
DlayMacro BitDlay - (770 + 10)
else
DlayMacro BitDlay - 10 ; Wait for the Middle of the Next Bit
endif
bcf STATUS, C ; Check the Incoming Data
if (Polarity == Pos)
btfsc RXPort, RXPin
else
btfss RXPort, RXPin
endif
bsf STATUS, C
rrf NRZTemp, f ; Shift in the Bit
subwf NRZTemp, w ; Decrement and End if == 0
xorlw 0x0FF
addwf NRZTemp, w
btfss STATUS, Z
goto SRXLoop
if ((BitDlay - 9) > 770) ; Check to See if Value is Too Large
DlayMacro 770 ; Put in a "Double" Delay
DlayMacro BitDlay - (770 + 9)
else
DlayMacro BitDlay - 9 ; Wait for the Middle of the Next Bit
endif
if (Polarity == Pos) ; Is there a Stop Bit?
btfss RXPort, RXPin
else
btfsc RXPort, RXPin
endif
goto SerialRX ; No, Start All Over Again
movf NRZTemp, w ; Return the Received Byte
return ; Note - Zero Returned in Low-End
; Devices
SerialTX
movwf NRZTemp ; Save the Byte to Output
movlw 10
bcf STATUS, C ; Start with Sending the Start Bit
STXLoop
if (Polarity == Pos) ; Output the Current Bit
btfsc STATUS, C
else
btfss STATUS, C
endif
goto $ + 4 ; 6 Cycles Required Each Time
nop
bcf TXPort, TXPin ; Output a "Low"
goto $ + 3
bsf TXPort, TXPin ; Output a "High"
goto $ + 1
if ((BitDlay - 15) > 770) ; Check to See if Value is Too Large
DlayMacro 770 ; Put in a "Double" Delay
DlayMacro BitDlay - (770 + 15)
else
DlayMacro BitDlay - 15 ; Wait for the Middle of the Next Bit
endif
subwf NRZTemp, w ; Decrement the Bit Counter
xorlw 0x0FF
addwf NRZTemp, w
btfsc STATUS, Z
return ; Can Return to Caller
bsf STATUS, C ; Shift Down the Next Bit
rrf NRZTemp, f
goto STXLoop
endm
If you are working with a mid-range PICmicro, you can also invoke the
“NRZSerialNISetup" macro that creates the “SerialSetup" subroutine. This subroutine puts the
TX pin in “output mode" and drives an “idle" output. This subroutine should be executed as
early as possible after the application has started. This will ensure that the PICmicro does
not inadvertently cause the receiver to process invalid data by missing the first “Start Bit".
NRZSerialNISetup Macro TXPort, TXPin, Polarity
SerialSetup ; Setup the Serial I/O Bits
bsf STATUS, RP0
bcf TXPort, TXPin ; Make TX Pin an Output
bcf STATUS, RP0
if (Polarity == Pos)
bsf TXPort, TXPin ; Transmit "idle"
else
bcf TXPort, TXPin
endif
return
endm
The last macro will provide you with a TMR0 based interrupt polling routine which
will run in the "background" of your application. This is the recommended macro for mid-range
PICmicro MCU Serial Communications:
NRZSerialI Macro TXPort, TXPin, RXPort, RXPin, Polarity, Rate, Frequency
variable BitDlay, Prescaler, TMR0Reset
BitDlay = (Frequency / (3 * 4 * Rate)) - 10
TMR0Reset = BitDlay / 2 ; Using TMR0, Calculate the Timer Reset Value
Prescaler = 0 ; And the Prescaler
while (TMR0Reset > 0x0FF) ; Find the Proper Reset Value
TMR0Reset = TMR0Reset / 2
Prescaler = Prescaler + 1
endw
if (Prescaler > 7) ; Can't Use TMR0
error "Bit Delay cannot use TMR0 for Polling Clock"
endif
TMR0Reset = 256 - TMR0Reset ; Get the TMR0 Reset Value
goto AfterInt ; Jump to After Interrupt
org 4
Int ; Interrupt Handler
movwf _w
movf STATUS, w
bcf STATUS, RP0 ; Make Sure in Bank 0
movwf _status
bcf INTCON, T0IF ; Reset the Timer Interrupt
movlw TMR0Reset
movwf TMR0
; First, Check for a Received Character
Int_RX
movlw 0x004 ; Check for Bit?
addwf RXCount, f
btfss STATUS, DC ; DC Not Affected by "clrf
goto _RXNo ; Nothing to Check for (Yet)
movf RXCount, w ; Everything Read Through?
xorlw 0x091
btfsc STATUS, Z
goto _RXAtEnd ; Yes, Check for Stop Bit
bcf STATUS, C ; Read the Current State
if (Polarity == Pos)
btfsc RXPort, RXPin ; Sample at 10 Cycles
else
btfss RXPort, RXPin
endif
bsf STATUS, C
rrf RXByte, f
bsf RXCount, 2 ; Start Counting from 4
_RXEnd13
nop
goto _RXEnd ; End 15 Cycles From "Int_RX" - Finished Receiving Bit
_RXEnd8 ; Finished - 8 Cycles to Here
goto $ + 1
nop
goto _RXEnd13
_RXNo ; 5 Cycles from "Int_RX" - No Bit to Receive
btfsc RXCount, 0 ; Something Running?
goto _RXEnd8 ; End 8 Cycles from "Int_RX" - Yes, Skip Over
btfsc RXCount, 3 ; Checking Start Bits?
goto _RXStartCheck
if (Polarity == Pos)
btfsc RXPort, RXPin ; If Line Low - "Start" Bit
else
btfss RXPort, RXPin
endif
bcf RXCount, 2 ; Don't Have a "Start" Bit
goto _RXEnd13 ; End 18 cycles from "Int_RX"
_RXStartCheck ; 10 Cycles to Here
if (Polarity == Pos)
btfsc RXPort, RXPin
else
btfss RXPort, RXPin
endif
movlw 0x0FF ; Nothing - Clear "RXCount"
addlw 1
movwf RXCount
goto _RXEnd ; 16 Cycles to End
_RXAtEnd ; 9 Cycles from "Int_RX" - Check Last
; Bit
if (Polarity == Pos)
btfsc RXPort, RXPin
else
btfss RXPort, RXPin
endif
bsf RXFlag
clrf RXCount ; Finished - Reset Check - 12 Cycles
goto $ + 1
goto _RXEnd
_RXEnd
; Next, Check for Transmitting a Character - Intrinsic Dlay 22 Cycles
Int_TX
movlw 0x004 ; Interrupt Transmit Increment Value
addwf TXCount, f
btfss STATUS, DC ; Send the Next Byte?
goto _TXSendDlayCheck
bsf TXCount, 2 ; Want to Increment 3x not Four for each Bit
bsf STATUS, C
rrf TXByte, f
movf TXPort, w ; Send Next Bit
andlw 0x0FF ^ (1
Wyszukiwarka
Podobne podstrony:
Oblique Abdominis Externus testDretske s Qualia Externalisminstrukcja isdn ta 128 externalfunction xml set external entity ref handlerexternalrepresenting interfaces to external systems?6D84D3freelance guide? Leitfaden für externe Übersetzerinnen und ÜbersetzerHow to make an inexpensive external GPS AntennaOblique Abdominis Externus testSCRoption external image list urlwięcej podobnych podstron