PIC Assembler Modules
For Non-Commercial Use
Only
I would appreciate to be credited within your project, if you
use any of the source code below. If you have an interesting
project going on, I'll be glad about feedback.
The software below comes with no guarantee or warranty
except for my good intentions. Further the use of this code
implies that the user has a fundamental understanding of
electronics and its risks. I'm not responsible for any harm or
damage, caused by inproper use of any of the code below.
Any commercial use of parts or all of this code requires the
permission of the
author.
Table of Contents [Toc]
General
Standard Modules
Dot Matrix LCD Modules
LCD Display Driver
Routines
LCD Display Conversion
Routines
Serial Communication Modules
(RS232)
Notes on Modules
General
Recommendations
Known Limitations of
MPLAB IDE
Technical Hints
General [Toc] [Top]
Modules are source code blocks, which can be included in the
MAIN PROGRAM by simply adding the command line:
#include "C:/ (...) m_bank.asm"
The code will be inline expanded. All you need is to specify the
necessary registers in front of the include statement in the main
program, e.g.
LCDtris equ
TRISB
LCDport equ PORTB
CONSTANT BASE = 0x0C ; 16F84 base address of user file
registers
#include "..\m_bank.asm"
#include "..\m_wait.asm"
#include "..\m_lcd.asm" |
Standard Modules [Toc] [Top]
Assembler include files: General macros to ease assembler
handling, e.g. macros BANK0, BANK1
m_bank.asm |
V2.01 (2010/05/05)
Standard macros for bank and page handling and
conditional branching.
Device-specific customization of macros by using #DEFINE
TWO_BANKS for adjusting macro instructions to small PIC
MCUs with only two register banks in order to optimize
the corresponding code size.
Bank/page handling: BANK0, BANK1, BANK2, BANK3
Branch macros (to simplify 'IF THEN ELSE'
queries):
BEQ val: branch on equal w and val
BNE val: branch on not equal w and val
BREG val: branch on equal or greater w than
val
(...)
|
m_wait.asm |
V1.02 (20.08.2004)
Parameterizable wait function, which performs a "busy"
wait.
Suitable for all Microchip PIC MCUs with an internal
8-bit timer (routine uses TMR0).
Implemented standard delay (@ 4 MHz):
WAIT 0x01 is equal to 1 unit == 1.02 ms
The assigned standard prescaler for TMR0 is PRESCstd =
b'00000001'
Relationship between TMR0 prescaler and unit delay
(WAIT 0x01):
b'00000000' == 1:2 ==> 0.512 ms
b'00000001' == 1:4 ==> 1.02 ms (standard)
b'00000111' == 1:256 ==> 65.3 ms
Declarations required in main program:
CONSTANT
BASE = 0x0C ; 16F84 base address of user file
registers
#include "..\m_bank.asm"
#include "..\m_wait.asm"
; Call of implemented procedures with:
; WAIT 0x01 ; standard delay, 1.02 ms
; WAITX d'16',d'7' ; 1.045 s @ 4 MHz, extended with
specific prescaler |
|
m_beep.asm |
V1.00 (16.02.2003)
Beep function on parameterizable output port.
Declarations required in main program:
#define
BEEPport PORTA,0x0
#define BEEPtris TRISA,0x0
CONSTANT BASE = 0x0C ; 16F84 base address of user
file registers
#include "..\m_bank.asm"
#include "..\m_beep.asm"
; Call of implemented procedures with:
; BEEPinit ; initialization to set output port
; BEEP 0xFF,0x0 |
|
m_16bit.asm
|
V1.01 (2010/05/14)
Provides basic 16-bit arithmetic routines such as CLR16,
MOV16, NEG16, ADD16, SUB16, CMP16, LSR16, LSL16, INC16, and
DEC16 operations and the corresponding immediate
operations, if applicable.
|
For demo-programs or complete
applications, refer to 'Projects' [click here]
Dot Matrix LCD Modules [Toc] [Top]
Assembler include files: Display drivers for dot matrix LCD
displays. (Hitachi HD44780 compatibles)
Dot Matrix LCD Display
4 lines x 20 characters
|
Dot Matrix LCD Display 2 lines x
40 characters
|
LCD Display Driver Routines
[Toc] [Top]
Download the PDF schematic to illustrate the connectivity
of both 'classes' of LCD driver routines:
- Modules m_lcd.asm, m_lcdx.asm,
m_lcd_bf.asm, m_lcdxbf.asm: working only on entire
ports ([1..7], without [0]),
e.g. PortB[1..7] on PIC16F84,
or PortB[1..7], PortC[1..7], PortD[1..7] on PIC16F77
- Modules m_lcde.asm, m_lcde_bf.asm,
m_lcdexbf.asm: working on separate, customizable
ports,
e.g. PortB[0..2] for control lines & PortA[0..3] for data
lines on PIC16F84, PIC16F7x,
or PortC[5..7] for control lines & PortD[0..3] for data
lines on PIC16F77, or ...
Note that the data lines have to be on the low nibble of the
port.
m_lcd.asm |
V2.06 (26.12.2004)
7 wires, 4 bit LCD interface
Based on timing constraints, no busy flag check. So if
LCD fails, system is still running stable. Higher
reliability, because less critical system components.
No portable code for higher clock frequencies.
R/W connection to LCD supported for compatibility to
m_lcd_bf.asm, but can be put to GND at LCD side =>
You'll get one interrupt pin more on the processor
side.
Schematic of LCD connection (PDF)
Declarations required in main program:
LCDtris equ
TRISB ; could also be PORTC, PORTD on 16F77
LCDport equ PORTB
; LCD data ports D4-D7 are on LCDport,0x1-0x4
; LCD_EN is on LCDport,0x05 ; Enable
; LCD_RW is on LCDport,0x06 ; Read/Write
; LCD_RS is on LCDport,0x07 ; Register Select
CONSTANT BASE = 0x0C ; 16F84 base address of user
file registers
#include "..\m_bank.asm"
#include "..\m_wait.asm"
#include "..\m_lcd.asm" |
|
m_lcdx.asm |
V2.26 (26.12.2004)
7 wires, 4 bit LCD interface
Extended m_lcd.asm with the ability to define your own
characters (max. 8) in macro LCDspecialChars. Based
on timing constraints, specifications as m_lcd.asm.
Schematic of LCD connection (PDF)
Declarations required in main program:
LCDtris equ
TRISB ; could also be PORTC, PORTD on 16F77
LCDport equ PORTB
; LCD data ports D4-D7 are on LCDport,0x1-0x4
; LCD_EN is on LCDport,0x05 ; Enable
; LCD_RW is on LCDport,0x06 ; Read/Write
; LCD_RS is on LCDport,0x07 ; Register Select
CONSTANT BASE = 0x0C ; 16F84 base address of user
file registers
; complete/replace macro LCDspecialChars to define
your own characters
#include "..\m_bank.asm"
#include "..\m_wait.asm"
#include "..\m_lcdx.asm"
|
|
m_lcd_bf.asm |
V3.09 (26.12.2004)
7 wires, 4 bit LCD interface
Extended m_lcd.asm which reads busy flag of LCD
(bi-directional communication between controller and
LCD)
R/W connection to LCD needed for hand shaking.
Clock independent program code, successfully tested up to
10 MHz on PIC 16F84 and up to 20 MHz on PIC 16C74A!
Gives the best performance, but system fails, if LCD
(-connection) fails, because processor is waiting for
ready signal from LCD to send next character. =>
Deadlock, but should not occur under normal
circumstances!
Solution:
Connect an auxilliary 10k resistor from DB7 to GND, so if
the LCD is disconnected, the microprocessor will read an
inactive busy flag. (That's needed because of the
internal weak pull ups of the microprocessor ports. Maybe
also required if you switch them off.)
Schematic of LCD connection (PDF)
Declarations required in main program:
LCDtris equ TRISB ; could also be PORTC, PORTD
on 16F77
LCDport equ PORTB
; LCD data ports D4-D7 are on LCDport,0x1-0x4
; LCD_EN is on LCDport,0x05 ; Enable
; LCD_RW is on LCDport,0x06 ; Read/Write
; LCD_RS is on LCDport,0x07 ; Register Select
CONSTANT BASE = 0x0C ; 16F84 base address of
user file registers
FLAGreg equ BASE+d'7' ; general flag register
#define LCDbusy FLAGreg,0x0 ; LCD busy flag
#define LCDcflag FLAGreg,0x1
#include "..\m_bank.asm"
#include "..\m_wait.asm"
#include "..\m_lcd_bf.asm"
|
|
m_lcdxbf.asm |
V3.29 (26.12.2004)
7 wires, 4 bit LCD interface
Basically the same as m_lcd_bf.asm, but with the ability
to define your own characters (max. 8) in macro
LCDspecialChars. Bi-directional communication, reads busy
flag of LCD.
Schematic of LCD connection (PDF)
|
m_lcde.asm |
V2.12e (17.08.2004)
7 wires, 4 bit LCD interface
Basically as m_lcd.asm, but with the ability to
independently configure LCD control and data lines, i.e.
LCD data on low nibble of any port, LCD command lines on
any port bits (even different port).
Schematic of LCD connection (PDF)
Declarations required in main program:
LCDtris equ
TRISA ; LCD data on low nibble of portA
LCDport equ PORTA
#define LCD_ENtris TRISB,0x01 ; EN on portB,1
#define LCD_EN PORTB,0x01
#define LCD_RStris TRISB,0x02 ; RS on portB,2
#define LCD_RS PORTB,0x02
#define LCD_RWtris TRISB,0x03 ; RW on portB,3
#define LCD_RW PORTB,0x03
CONSTANT BASE = 0x0C ; 16F84 base address of user
file registers
#include "..\m_bank.asm"
#include "..\m_wait.asm"
#include "..\m_lcde.asm"
|
|
m_lcde_bf.asm |
V4.03e (17.08.2004)
7 wires, 4 bit LCD interface
Extended m_lcde.asm which reads busy flag of LCD
(bi-directional communication between controller and
LCD)
Schematic of LCD connection (PDF)
Declarations required in main program:
LCDtris equ TRISA ; LCD data on low nibble of
portA
LCDport equ PORTA
#define LCD_ENtris TRISB,0x01 ; EN on portB,1
#define LCD_EN PORTB,0x01
#define LCD_RStris TRISB,0x02 ; RS on portB,2
#define LCD_RS PORTB,0x02
#define LCD_RWtris TRISB,0x03 ; RW on portB,3
#define LCD_RW PORTB,0x03
CONSTANT BASE = 0x0C ; 16F84 base address of
user file registers
FLAGreg equ BASE+d'7' ; general flag register
#define LCDbusy FLAGreg,0x6 ; LCD busy
flag
#define LCDcflag FLAGreg,0x7 ; LCD command/data
flag
#include "..\m_bank.asm"
#include "..\m_wait.asm"
#include "..\m_lcde_bf.asm"
|
|
m_lcdexbf.asm |
V4.23e (17.08.2004)
7 wires, 4 bit LCD interface
Basically the same as m_lcde_bf.asm, but with the ability
to define your own characters (max. 8) in macro
LCDspecialChars. Bi-directional communication, reads busy
flag of LCD.
Schematic of LCD connection (PDF)
|
For demo-programs or complete
applications, refer to 'Projects' [click here]
LCD Display Conversion Routines
[Toc] [Top]
m_lcdv08.asm |
V1.02 (20.08.2004)
8 bit binary to decimal conversion routine for LCD
output, stringent for any numeric display output or
interaction.
Declarations required in main program:
CONSTANT BASE = 0x0C ; 16F84 base address
of user file registers
FLAGreg equ BASE+d'4' ; general flag register
LO equ BASE+d'5'
LO_TEMP set BASE+d'6'
#define BCflag FLAGreg,0x0 ; blank checker for
preceding zeros
#include "..\m_bank.asm"
#include "..\m_wait.asm"
#include "..\m_lcd.asm"
#include "..\m_lcdv08.asm"
; Call of implemented procedure with:
LCDval_08 ; result is stored in register
LO
|
|
m_lcdv16.asm |
V1.02 (20.08.2004)
16 bit binary to decimal conversion routine for LCD
output, stringent for any numeric display output or
interaction.
Declarations required in main program:
CONSTANT BASE = 0x0C ; 16F84 base address
of user file registers
FLAGreg equ BASE+d'4' ; general flag register
LO equ BASE+d'5'
HI equ BASE+d'6'
LO_TEMP set BASE+d'7'
HI_TEMP set BASE+d'8'
#define BCflag FLAGreg,0x0 ; blank checker for
preceding zeros
#include "..\m_bank.asm"
#include "..\m_wait.asm"
#include "..\m_lcd.asm"
#include "..\m_lcdv16.asm"
; Call of implemented procedure with:
LCDval_16 ; result is stored in registers HI,
LO
|
|
m_lcdb08.asm |
V1.00 (20.08.2004)
8 bit binary LCD output routine for debugging of
registers and bitstreams, e.g. to visualize the binary
output of an A/D converter to check the magnitude of its
LSB toggling due to digital quantization and/or
noise.
Declarations required in main program:
CONSTANT BASE = 0x0C ; 16F84 base address
of user file registers
b08_cnt equ BASE+d'4' ; counter
LO equ BASE+d'5'
LO_TEMP set BASE+d'6'
#include "..\m_bank.asm"
#include "..\m_wait.asm"
#include "..\m_lcd.asm"
#include "..\m_lcdb08.asm"
; Call of implemented procedure with:
LCDbin_08 ; value in register LO, output to
LCD
|
|
m_lcdb16.asm |
V1.00 (20.08.2004)
16 bit binary LCD output routine for debugging of
registers and bitstreams, e.g. to visualize the binary
output of an A/D converter to check the magnitude of its
LSB toggling due to digital quantization and/or
noise.
Declarations required in main program:
CONSTANT BASE = 0x0C ; 16F84 base address
of user file registers
b16_cnt equ BASE+d'4' ; counter
LO equ BASE+d'5'
HI equ BASE+d'6'
LO_TEMP set BASE+d'7'
HI_TEMP set BASE+d'8'
#include "..\m_bank.asm"
#include "..\m_wait.asm"
#include "..\m_lcd.asm"
#include "..\m_lcdb16.asm"
; Call of implemented procedure with:
LCDbin_16 ; value in registers HI, LO, output to
LCD
|
|
For demo-programs or complete
applications, refer to 'Projects' [click here]
Serial Communication Modules
(RS232) [Toc]
[Top]
Software-based RS232 communication for all Microchip PIC MCUs.
The routines handle all aspects of RS232 transmission and
reception, based on busy waits. The MCU port for RS232
transmission can be selected freely by the user, provided it is
an active digital push/pull port. The MCU port for RS232
reception is limited to PORTB,0 due to its own interrupt
flag.
Note that for external interrupts, such as the RB0/INT pin or
PORTB change interrupt, the latency will be three to four
instruction cycles. The exact latency depends on when the
interrupt occurs. The latency is the same for both one and two
cycle instructions.
(=> see Microchip PIC16/17 Microcontroller Databook.)
For those, who are not familiar with interfacing a PIC to the
RS232 using a MAX232 : RS232-Interface.pdf
(9.7 kB)
m_rs096.asm |
V1.03 (2010/05/04) Completely
software handled RS232 Interface for interrupt featured
PICs (PIC16C84, PIC16F84,...).
Specifications: 9600 baud, 8 bit, no parity, 1 stopbit
(9600-8-N-1) @ 4 MHz / 1 MIPS
Declarations required in main program:
#define TXport PORTA,0x00 ; RS232 output port,
could be
#define TXtris TRISA,0x00 ; any active push/pull
port
; RS232 input port is RB0, because of its own
interrupt flag
CONSTANT BASE = 0x0C ; 16F84 base address of user
file registers
TXD equ BASE+d'7' ; TX data register, for
transmission
RXD equ BASE+d'8' ; RX data register, for
storage
#include "..\m_bank.asm"
#include "..\m_rs096.asm"
; Call of implemented procedures with:
; RS232init ; initialization
; SEND 'c' ; sends character 'c'
; SENDw ; sends content of working register
; RECEIVE ; macro in ISR: receive from RS232, store
in register RXD
|
|
m_rs012.asm |
V1.03 (2010/05/04)
Functionality as m_rs096.asm
Specifications: 1200 baud, 8 bit, no parity, 1 stopbit
(1200-8-N-1) @ 4 MHz / 1 MIPS |
m_rs024.asm |
V1.03 (2010/05/04)
Functionality as m_rs096.asm
Specifications: 2400 baud, 8 bit, no parity, 1 stopbit
(2400-8-N-1) @ 4 MHz / 1 MIPS |
m_rs048.asm |
V1.03 (2010/05/04)
Functionality as m_rs096.asm
Specifications: 4800 baud, 8 bit, no parity, 1 stopbit
(4800-8-N-1) @ 4 MHz / 1 MIPS |
m_rs192.asm |
V1.03 (2010/05/04)
Functionality as m_rs096.asm
Specifications: 19200 baud, 8 bit, no parity, 1 stopbit
(19200-8-N-1) @ 4 MHz / 1 MIPS |
m_rs7n1.asm |
V1.03 (2010/05/04)
Functionality as m_rs096.asm
Developed for an old and heavy matrix needle printer with
RS232 Interface.
Specifications: 9600 baud, 7 bit, no parity, 1 stopbit
(9600-7-N-1) @ 4 MHz / 1 MIPS
|
For demo-programs or complete
applications, refer to 'Projects' [click here]
Notes on Modules [Toc] [Top]
- Code is optimized for 1. small memory, and 2. optimal
execution time.
- If extern sources are called from within modules, set
absolute pathes. Please refer to Known Limitations of MPLAB IDE
below.
- Declare and define the required registers and statements in
the MAIN.
- Declarations of constants and registers need to be listed
in the MAIN before the '#include' statement of the modules,
since most modules are referencing specific constants and
registers (therefore they need to be defined at that time).
General Recommendations
[Toc] [Top]
- Manage the used registers in a clear structured way.
- For prototyping, it is recommended to introduce temporarily
dedicated registers for each new code segment. At the end, when
everything runs properly, you can start to optimize the design
in order to share as much registers as possible to save memory
space. Otherwise, you run the risk of getting data consistency
problems - bugs, which are really hard to solve!
- Provide dedicated registers for interrupt handling! Do not
call subroutines from the interrupt service routine (ISR),
which are executed during normal operation. If so, ensure to
perform a complete context save of all registers being used
within this subroutine.
- Interrupt flags have to be cleared by software before
re-enabling the 'global interrupt enable bit' (GIE) and exiting
the ISR. Otherwise, recursive ISR calling occurs.
- At the beginning of the ISR, disable the 'global interrupt
enable bit' (GIE). In case different interrupt sources have to
be served, it may be necessary to disable also the related
'interrupt source enable bit' (e.g. INTF of RB0/INT pin).
See next recommendation
- On ISR exit, make sure to reset the interrupt flag of the
source, which triggered the interrupt. But perform re-enabling
of the corresponding 'interrupt source enable bit' at the end
of the corresponding service routine:
- If the interrupt service is entirely completed within the
ISR, re-enabling is suitable at the end of the ISR. This
applies only, if disabling of the interrupt source has been
carried out at the beginning of the ISR.
- If parts of the interrupt service are carried out during
normal operation, re-enabling the interrupt source is most
suitable at the end of the external code part. In this case,
disabling the specific interrupt source at the beginning of the
ISR is stringent, otherwise we face lost of data or a system
crash due to return address stack overflow. This method owns a
further advantage: A subsequent interrupt already having been
triggered before re-activation and originating from the same
source is not omitted and will immediately be served. (This is
because the interrupt flag bit gets still set, when the
interrupt is temporarily disabled. When unmasking this
interrupt source, the interrupt is immediately triggered.)
Known Limitations of MPLAB
IDE [Toc]
[Top]
- Previously MPLAB worked with the old MS DOS 8.3 naming
convention (so set paths with old notation).
However, recent MPLAB versions seem to accept also longer file
names. That's why my include files sometimes have strange
names.
- Note that there is also an upper limitation on the amount
of characters for file names including path.
Without using the MPLINK linker, i.e. for
absolute/non-relocationable assembler code, file names
including path are somehow restricted to 62 characters (see
MPLAB IDE Quick Reference, p.3).
- '#include' - commands in the MAIN could be relative
(..\..\xyz.asm) or absolute paths (C:\abc\xyz.asm).
- '#include' - commands in extern source have to be absolute
paths (if extern source is called with relative path).
Technical Hints [Toc] [Top]
- If you use 'set' instead of 'equ', you can assign different
labels to the same register. This is very useful, if you need
temporary registers in different modules. But be careful that
you do not write and access the same register alternating in 2
different procedures. This results in nice bugs or possibly
endless loops.
- 'equ' is exclusive, i.e. it accepts only one label for each
register.
- If you want to create a 16 bit assembler look-up table very
quickly, have a look at the
Automatic Table Generator for MPLAB
Assembler.
- To specify exact periods (e.g. with busy wait module
m_wait.asm), I use quite often the MPLAB stopwatch. This is a
very convenient cycle-accurate timer, which takes both 1 cycle
(e.g. incf) and 2 cycle instructions (e.g. goto) as well as
special instruction mnemonics (e.g. bnc, subcf) into
account.
I just let the simulator run to the wait statement or loop to
be measured, reset the stopwatch, and let it again simulate
until after the wait statement:
The Stopwatch
having been reset at 'bsf TXport' and run to the 'nop'
statement. The latency of the 'WAITX d73,d2' statement
within this setup (4 MHz clock, PIC16F84) turns out
to be approximately 150 ms.
|
Last updated: 2010/05/14
[Toc] [Top]