100% found this document useful (1 vote)
97 views127 pages

CPE 0907334 Embeddedlab Sep 2016 Fall

This document provides instructions for an experiment introducing MPLAB and the QL200 development kit. It describes how to start MPLAB, create a project, and write and simulate a simple program. It also explains how to use the QL-PROG software to program a PIC microcontroller using the QL200 kit by erasing the chip, loading a file, setting configuration bits, and programming the PIC. The objectives are to familiarize students with MPLAB, code simulation, the QL200 kit, and the process of programming a PIC microcontroller.

Uploaded by

Sitt Nyein Soe
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (1 vote)
97 views127 pages

CPE 0907334 Embeddedlab Sep 2016 Fall

This document provides instructions for an experiment introducing MPLAB and the QL200 development kit. It describes how to start MPLAB, create a project, and write and simulate a simple program. It also explains how to use the QL-PROG software to program a PIC microcontroller using the QL200 kit by erasing the chip, loading a file, setting configuration bits, and programming the PIC. The objectives are to familiarize students with MPLAB, code simulation, the QL200 kit, and the process of programming a PIC microcontroller.

Uploaded by

Sitt Nyein Soe
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 127

University of Jordan

Faculty of Engineering and Technology


Department of Computer Engineering
Embedded Systems Laboratory 0907334

Experiment 0: Introduction to MPLAB


and QL200 development kit

Objectives
The main objectives of this experiment are to familiarize you with:
 Microchip MPLAB Integrated Development Environment (IDE) and the whole process of building a
project, writing simple codes, and compiling the project.
 Code simulation
 QL200 development kit
 QL-PROG software and learn how to program the PIC using it

1
Starting MPLAB

After installation, shortcut of this software will appear on desktop.

Create asm file using MPLAB

a) Double click on the “MPLAB” program icon found on the desktop.

Note: All programs written, simulated and debugged in MPLAB should be stored in files with .asm
extension.
b) To create asm, follow these simple
steps:
i. File  New
ii. File  Save as, in the save
dialog box; name the file as
“myFirstFile.asm” WITHOUT
THE DOUBLE QUATATIONS
MARKS, this will instruct
MPLAB to save the file in .asm
format.

NOTE: All your files should be stored in a short path:

The total number of characters in a path should not exceed 64 Char No.
C:\ or D:\ or … 3 
D:\Embedded\ 12 
D:\Embedded\Lab 15 
D:\Engineer\Year_Three\Summer_Semester\Embedded_Lab\Experiment_1\MyProgram.asm 78 
Any file on Desktop 

2
Create a project in MPLAB by following these simple steps:
1. Select the Project  Project Wizard menu item  Next

2. In the device selection menu, choose 16F84A (or your target PIC)  Next

3
3. In the Active Toolsuite, choose Microchip MPASM Toolsuite  Click next.
DO NOT CHANGE ANYTHING IN THIS SCREEN

4. Browse to the directory where you saved your ASM file. Give your project a name  Save  Next.

4
5. If, in Step 4, you navigated correctly to your file destination you should see it in the left pane
otherwise choose back and browse to the correct path. When done Click add your file to the project
(here: myFirstFile.asm). Make sure that the letter A is beside your file and not any other letter 
Click next Click Finish.

6. You should see your ASM file under Source file, now you are ready to begin
Double click on the myFirstFile.asm file in the project file tree to open. This is where you will write
your programs, debug and simulate them.

CORRECT WRONG

5
Now we will simulate a program in MPLAB and check the results
In MPLAB write the following program:

Movlw 5 ; move the constant 5 to the working register


Movwf 01 ; copy the value 5 from working register to TMR0 (address 01)
Movlw 2 ; move the constant 2 to the working register
Movwf 0B ; copy the value 2 from working register to INTCON (address 0B)
Movf 01, 0 ; copy back the value 5 from TMR0 to working register
Nop ; this instruction does nothing, but it is important to write for now
End ; every program must have an END statement
After writing the above instructions we should build the project, do so by pressing build

Click Absolute

An output window should


show:
BUILD SUCCEDDED

6
QL-PROG – How to Program
Prepared by Eng. Enas Jaara

After installation, shortcut of this software will appear on desktop.

1. Connect hardware and power up the kit, run the programming software QL-PROG (Double
click it to run the software) which will automatically search programmer hardware. It will
appear as shown in the below diagram

2. Select Chip Family and Chip model


Choose All Chip from the chip family and choose 16F877A from the chip select

3. Press Erase button on programming software panel to Erase the chip data

4. Load File to Program


Press “Load” button on programming software panel to load machine code file (HEX file) of the
chip you desire to program. load the LCD1.hex found on D:\Experiment0

7
5. Set Configuration Bit
You may set or change the configuration bit of chip by running pressing “Fuses” button on
software panel. After running the command software, pop-up window to set configuration bit will
appear as shown in below diagram. Set the options according to your requirement and click “OK”
button.

If any of the above option differs, it is because you have chosen the wrong PIC, so go to chip select
and choose your appropriate PIC.
6. Program the PIC
Press "Program" button to begin programming. After completion, there will be messages of
"Programming complete".

8
University of Jordan
Faculty of Engineering and Technology
Department of Computer Engineering
Embedded Systems Laboratory 0907334

1 Experiment 1: MPLAB and Instruction Set


Analysis 1

Objectives

The main objectives of this experiment are to familiarize you with:


 The MOV instructions
 Writing simple codes, compiling the project and Code simulation
 The concept of bank switching
 The MPASM directives
 Microcontroller Flags
 Arithmetic and logical operations
Pre-lab requirements

Before starting this experiment, you should have already acquired the MPLAB software and the related PIC
datasheets from drive D on any of the lab PC’s. You are encouraged to install the latest version of MPLAB
(provided in the lab) especially if you have Windows Vista

Starting up with instructions


Movement instructions

You should know by now that most PIC instructions (logical and arithmetic) work through the working register
“W”, that is one of their operands must always be the working register “W”, the other operand might be either a
constant or a memory location. Many operations store their result in the working register; therefore we can
conclude that we need the following movement operations:

1. Moving constants to the working register (Loading)


2. Moving values from the data memory to the working register (Loading)
3. Moving values from the working register to the data memory (Storing)

INSTRUCTIONS ARE CASE INSENSITIVE: YOU CAN WRITE IN EITHER SMALL OR CAPITAL
LETTERS

 MOVLW: moves a literal (constant) to the working register (final destination). The constant is
specified by the instruction. You can directly load constants as decimal, binary, hexadecimal, octal and
ASCII. The following examples illustrate:

DEFAULT INPUT IS HEXADECIMAL

1. MOVLW 05 : moves the constant 5 to the working register


2. MOVLW 10 : moves the constant 16 to the working register.
3. MOVLW 0xAB : moves the constant ABh to the working register
4. MOVLW H’7F’ : moves the constant 7Fh to the working register
5. MOVLW CD : WRONG, if a hexadecimal number starts with a character, you
should write it as 0CD or 0xCD or H’CD’
6. MOVLW d’10’ : moves the decimal value 10 to the working register.
7. MOVLW .10 : moves the decimal value 10 to the working register.
8. MOVLW b ’10011110’ : moves the binary value 10011110 to the working register.
9. MOVLW O ’76’ : moves the octal value 76 to the working register.
10. MOVLW A’g’ : moves the ASCII value g to the working register.

 MOVWF: COPIES the value found in the working register into the data memory, but to which
location? The location is specified along with the instruction and according to the memory map.

So what is the memory map?


A memory map shows all available registers
(in data memory) of a certain PIC along with
their addresses, it is organized as a table
format and has two parts:

1. Upper part: which lists all the Special


Function Registers (SFR) in a PIC, these
registers normally have specific functions
and are used to control the PIC operation
2. Lower part: which shows the General
Purpose Registers (GPR) in a PIC; GPRs
are data memory locations that the user is
free to use as he wishes.
Memory Maps of different PICs are
different. Refer to the datasheets for the
appropriate data map

Examples:
1. MOVWF 01 : COPIES the value found in W to
TMR0
2. MOVWF 05 : COPIES the value found in W to
PORTA
3. MOVWF 0C : COPIES the value found in W to
a GPR (location 0C)
4. MOVWF 32 : COPIES the value found in W to
a GPR (location 32)
5. MOVWF 52 : WRONG, out of data memory
range of the PIC 16F84a (GPR range is from 0C-
4F and 8C to CF)

 MOVF: COPIES a value found in the data


memory to the working register OR to itself.

Therefore we expect a second operand to specify


whether the destination is the working register or
the register itself.

For now: a 0 means the W, a 1 means the register


itself.

Examples:

1. MOVF 05, 0 : copies the content of PORTA to the working register


2. MOVF 2D, 0 : copies the content of the GPR 2D the working register
3. MOVF 05, 1 : copies the content of PORTA to itself
4. MOVF 2D, 1 : copies the content of the GPR 2D to itself
Now we will simulate a program in MPLAB and check the results
In MPLAB write the following program:

Movlw 5 ; move the constant 5 to the working register


Movwf 01 ; copy the value 5 from working register to TMR0 (address 01)
Movlw 2 ; move the constant 2 to the working register
Movwf 0B ; copy the value 2 from working register to INTCON (address 0B)
Movf 01, 0 ; copy back the value 5 from TMR0 to working register
Nop ; this instruction does nothing, but it is important to write for now
End ; every program must have an END statement

After writing the above instructions we should build the project, do so by pressing build

An output window should show: BUILD SUCCEDDED


BUILD SUCCEED DOES NOT MEAN THAT YOUR PROGRAM IS CORRECT, IT SIMPLY MEANS THAT
THERE ARE NO SYNTAX ERRORS FOUND, SO WATCH OUT FOR ANY LOGICAL ERRORS YOU
MIGHT MAKE.

Notice that there are several warnings after building the file, warnings do not affect the execution of the
program but they are worth reading. This warning reads: “Found opcode in column 1”, column 1 is
reserved for labels; however, we have written instructions (opcode) instead thus the warning.

TO SOLVE THIS WARNING SIMPLY TYPE FEW BLANK SPACES BEFORE EACH INSTRUCTION OR PRESS
TAB

Preparing for simulation

Go to View Menu  Watch

From the drop out menu choose the registers we want to


watch during simulation and click ADD SFR for each one
Add the following:
 WREG: working register
 TMR0
 INTCON
You should have the following:

Notice that the default format is in hexadecimal, to change it (if you need to) simply right-click on the
row  Properties and choose the new format you wish.
From the Debugger Menu  choose Select Tool  then MPLAB SIM

Now new buttons will appear in the toolbar:

Step Into Reset

1. To begin the simulation, we will start by resetting the PIC; do so by pressing the yellow reset
button. A green arrow will appear next to the first instruction.

The green arrow means that the program counter is pointing to this instruction which has not
been executed yet.

Notice the status bar below:

Keep an eye on the value of the program counter (pc: initially 0), see how it changes as we
simulate the program

2. Press the “Step Into” button one at a time and check the Watch window each time an instruction
executes; keep pressing “Step Into” until you reach the NOP instruction then STOP.

Compare the results as seen in the Watch window with those expected.
Directives
Directives are not instructions. They are assembler commands that appear in the source
code but are not usually translated directly into opcodes. They are used to control the
assembler: its input, output, and data allocation. They are not converted to machine code
(.hex file) and therefore not downloaded to the PIC.

The “END” directive

If you refer to the Appendix at the end of this experiment, you will notice that there is no end instruction
among the PIC 16 series instructions, so what is “END”?

The “END” command is a directive which tells the MPLAB IDE that we have finished our program. It has
nothing to do with neither the actual program nor the PIC.

The END should always be the last statement in your program


Anything which is written after the end command will not be executed and any variable names will be
undefined.

Making your program easier to understand: the “equ” and “include” directives

As you have just noticed, it is difficult to write, read, debug or understand programs while dealing with
memory addresses as numbers. Therefore, we will learn to use new directives to facilitate program
reading.

The “EQU” directive

The equate directive is used to assign labels to numeric values. They are used to DEFINE CONSTANTS or to
ASSIGN NAMES TO MEMORY ADDRESSES OR INDIVIDUAL BITS IN A REGISTER and then use the name
instead of the numeric address.

Timer0 equ 01
Intcon equ 0B
Workrg equ 0
Movlw 5 ; move the constant 5 to the working register
Movwf Timer0 ; copy the value 5 from working register to TMR0 (address 01)
Movlw 2 ; move the constant 2 to the working register
Movwf Intcon ; copy the value 2 from working register to INTCON (address 0B)
Movf Timer0, Workrg ; copy back the value 5 from TMR0 to working register
Nop ; this instruction does nothing, but it is important to write it for now
End

Notice how it is easier now to read and understand the program, you can directly know the actions
executed by the program without referring back to the memory map by simply giving each address a
name at the beginning of your program.

DIRECTIVES THEMSELVES ARE NOT CASE-SENSITIVE BUT THE LABELS YOU DEFINE ARE. SO YOU
MUST USE THE NAME AS YOU HAVE DEFINED IT SINCE IT IS CASE-SENSITIVE.
As you have already seen, the GPRs in a memory map (lower part) do not have names as the SFRs
(Upper part), so it would be difficult to use their addresses each time we want to use them. Here,
the “equate” statement proves helpful.

Num1 equ 20 ;GPR @ location 20


Num2 equ 40 ;GPR @ location 40
Workrg equ 0
Movlw 5 ; move the constant 5 to the working register
Movwf Num1 ; copy the value 5 from working register to Num1 (address 20)
Movlw 2 ; move the constant 2 to the working register
Movwf Num2 ; copy the value 2 from working register to Num2 (address 40)
Movf Num1, Workrg ; copy back the value 5 from Num1 to working register
Nop ; this instruction does nothing, but it is important to write it for now
End

When simulating the above code, you need to add Num1, Num2 to the watch window, however,
since Num1 and Num2 are not SFRs but GPRs, you will not find them in the drop out menu of the
“Add SFR”, instead you will find them in the drop out menu of the “Add symbol”.

The “INCLUDE” directive

Suppose we are to write a huge program that uses all registers. It will be a tiresome task to define all
Special Function Registers (SFR) and bit names using “equate” statements. Therefore we use the include
directive. The include directive calls a file which has all the equate statements defined for you and ready to
use, its syntax is

#include “PXXXXXXX.inc” where XXXXXX is the PIC part number

Older version of include without #, still supported.

Example: #include “P16F84A.inc”


The only condition when using the include directive is to use the names as Microchip defined them which
are ALL CAPITAL LETTERS and AS WRITTEN IN THE DATA SHEET. If you don’t do so, the MPLAB will tell
you that the variable is undefined!

#include “P16F84A.inc”
Movlw 5 ; move the constant 5 to the working register
Movwf TMR0 ; copy the value 5 from working register to TMR0 (address 01)
Movlw 2 ; move the constant 2 to the working register
Movwf INTCON ; copy the value 2 from working register to INTCON (address 0B)
Movf TMR0, W ; copy back the value 5 from TMR0 to working register
Nop ; this instruction does nothing, but it is important to write it for now
End

The “Cblock” directive


You have learnt that you can assign GPR locations names using the equate statements to facilitate dealing
with them. Though this is correct, it is not recommended by Microchip as a good programming practice.
Instead you are instructed to use cblocks when defining and declaring GPRs. So then, what is the use of the
“equ” directive?

From now on, follow these two simple programming rules:


1. The “EQU” directive is used to define constants
2. The “cblock” is used to define variables in the data memory.
The cblock defines variables in sequential locations, see the following declaration

Cblock 0x35
VarX
VarY
VarZ
endc

Here, VarX has the starting address of the cblock, which is 0x35, VarY has the sequential address 0x36 and
VarZ the address of 0x37

What if I want to define variable at locations which are not sequential, that is some addresses are at 0x25
others at 0x40?

Simply use another cblock statement, you can add as many cblock statements as you need

The Origin “org” directive

The origin directive is used to place the instruction which exactly comes after it at the location it
specifies.
Examples:

Org 0x00
Movlw 05 ;This instruction has address 0 in program memory
Addwf TMR0 ;This instruction has address 1 in program memory
Org 0x04 ;Program memory locations 2 and 3 are empty, skip to address 4 where it contains
Addlw 08 ;this instruction

Org 0x13 ;WRONG, org only takes even addresses


In This Course, Never Use Any Origin Directives Except For Org 0x00 And 0x04, Changing Instructions’
Locations In The Program Memory Can Lead To Numerous Errors.

The Concept of Bank Switching

Write, build and simulate the following program in your MPLAB editor. This program is very similar to the
ones discussed above but with a change of memory locations.

#include “P16F84A.inc”
Movlw 5 ; move the constant 5 to the working register
Movwf TRISA ; copy the value 5 from working register to TRISA (address 85)
Movlw 2 ; move the constant 2 to the working register
Movwf OPTION_REG ; copy 2 from working register to OPTION_REG (address 81)
Movf TRISA, W ; copy back the value 5 from TRISA to working register
Nop ; this instruction does nothing, but it is important to write it for now
End

After simulation, you will notice that both TRISA and OPTION_REG were not filled with the values
5 and 2 respectively! But why?

Notice that the memory map is divided into two columns, each column is called a bank, here we
have two banks: bank 0 and bank 1. In order to access bank 1, we have to switch to that bank first
and same for bank 0. But how do we make the switch?

Look at the details of the STATUS register in the figure below, there are two bits RP0 and RP1,
these bits control which bank we are in:

 If RP0 is 0 then we are in bank 0


 If RP0 is 1 then we are in bank 1

We can change RP0 by using the bcf/bsf instructions

 BCF STATUS, RP0 RP0 in STATUS is 0  switch to bank 0


 BSF STATUS, RP0 RP0 in STATUS is 1  switch to bank 1

BCF: Bit Clear File Register (makes a specified bit in a specified file register a 0)
BSF: Bit Set File Register (makes a specified bit in a specified file register a 1)
Try the program again with the following change and check the results:

#include “P16F84A.inc”
BSF STATUS, RP0
Movlw 5 ; move the constant 5 to the working register
Movwf TRISA ; copy the value 5 from working register to TRISA (address 85)
Movlw 2 ; move the constant 2 to the working register
Movwf OPTION_REG ; copy 2 from working register to OPTION_REG (address 81)
Movf TRISA, W ; copy back the value 5 from TRISA to working register
BCF STATUS, RP0
Nop ; this instruction does nothing, but it is important to write it for now
End

The “Banksel” directive

When using medium-range and high-end microcontrollers, it will be a hard task to check the memory map
for each register we will use. Therefore the BANKSEL directive is used instead to simplify this issue. This
directive is a command to the assembler and linker to generate bank selecting code to set the bank to the
bank containing the designated label

Example:
BANKSEL TRISA will be replaced by the assembler, which will automatically know which bank the register
is in and generate the appropriate bank selection instructions:
Bsf STATUS, RP0
Bcf STATUS, RP1

In the PIC16F877A, there are four banks; therefore you need two bits to make the switch between any of
them. An additional bit in the STATUS register is RP1, which is used to make the change between the
additional two banks.
One drawback of using BANKSEL is that it always generates two instructions even when the switch is
between bank0 and bank1 which only requires changing RP0. We could write the code above in the same
manner using Banksel

#include “P16F84A.inc”
Banksel TRISA
Movlw 5 ; move the constant 5 to the working register
Movwf TRISA ; copy the value 5 from working register to TRISA (address 85)
Movlw 2 ; move the constant 2 to the working register
Movwf OPTION_REG ; copy 2 from working register to OPTION_REG (address 81)
Movf TRISA, W ; copy back the value 5 from TRISA to working register
Banksel PORTA
Nop ; this instruction does nothing, but it is important to write it for now
End

Check the program memory window to see how BANKSEL is replaced in the above code and the difference
in between the two codes in this page.
FLAGS
The PIC 16 series has three indicator flags found in the STATUS register; they are the C, DC, and Z flags. See
the description below. Not all instructions affect the flags; some instructions affect some of the flags while
others affect all the flags. Refer to the Appendix at the end of this experiment and review which instructions
affect which flags.
The MOVLW and MOVWF do not affect any of the flags while the MOVF instruction affects the zero flag.
Copying the register to itself does make sense now because if the file has the value of zero the zero flag will
be one. Therefore the MOVF instruction is used to affect the zero flag and consequently know if a register
has the value of 0. (Suppose you are having a down counter and want to check if the result is zero or not)

13
Types of Logical and Arithmetic Instructions and Result Destination

The PIC16 series logical and arithmetic instructions are easy to understand by just reading the instruction,
for from the name you readily know what this instruction does. There are the ADD, SUB, AND, XOR, IOR
(the ordinary Inclusive OR). They only differ by their operands and the result destination. The following
table illustrates:

Type I – Literal Type Type II – File Register Type


Syntax xxxLW k xxxWF f, d
where k is constant where f is file register and
d is the destination (F, W)
Instructions Addlw, sublw, andlw, iorlw and Addwf, subwf, andwf, iorwf, xorwf
xorlw
Operands 1. A literal (given by the 1. A file register in the data
instruction) memory (either SFR or GPR)
2. The working register 2. The working register
Result destination The working register only Two Options:
1. W: the Working register
2. F: The same File given in the
instruction
How does it work? W = L operation W F = F operation W
The value of F is overwritten by the
result, original value lost
W = F operation W
The value of F is the original value,
result stored in working register
instead

The order is important in the subtract operation


Examples xorlw 0BB Andwf TMR0, W
(assuming you are W = W ^ 0BB W = TMR0 & W
using the include
statement and sublw .85 addwf NUM1, F
appropriate equ W = 85d – W NUM1 = NUM1 + W
statements for
defining GPRs) Subwf PORTA, F
PORTA = PORTA - W
Notice that in subtraction, the W has the minus sign

Many other instructions of the PIC16 series instruction set are of Type II; refer back to the Appendix at the
end of this experiment for study.

14
Starting Up with basic programs

Program One: Fibonacci Series Generator

In mathematics, the Fibonacci numbers are the following sequence of numbers:


0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89
The first two Fibonacci numbers are 0 and 1, and each remaining number is the sum of the previous two

1 include "p16f84a.inc"
2 Fib0 equ 20 ;At the end of the program the Fibonacci series numbers from 0 to 5 will
3 Fib1 equ 21 ;be stored in Fib0:Fib5
4 Fib2 equ 22
5 Fib3 equ 23
6 Fib4 equ 24
7 Fib5 equ 25
8
9 Clrw ;This instruction clears the working register, W = 0
10 clrf Fib0 ;The clrf instruction clears a file register specified, here Fib0 = 0
11 movf Fib0, w ;initializing Fib1 to the value 1 by adding 1 to Fib0 and storing it in Fib1
12 addlw 1
13 movwf Fib1
14
15 movf Fib0, W ; Fib2 = Fib1 + Fib0
16 addwf Fib1, W
17 movwf Fib2
18
19 movf Fib1, W ; Fib3 = Fib2 + Fib1
20 addwf Fib2, W
21 movwf Fib3
22
23 movf Fib2, W ; Fib4 = Fib3 + Fib2
24 addwf Fib3, W
25 movwf Fib4
26
27 movf Fib3, W ; Fib5 = Fib4 + Fib3
28 addwf Fib4, W
29 movwf Fib5
30 nop
31 end

1. Start a new MPLAB session, add the file example1.asm to your project
2. Build the project
3. Select Debugger  Select Tool MPLAB SIM
4. Add the necessary variables and the working register to the watch window (remember that user
defined variables are found under the “Add Symbol” list)

15
5. Simulate the program step by step, analyze and study the function of each instruction. Stop at the
“nop” instruction
6. Study the comments and compare them to the results at each stage and after executing the
instructions
7. As you simulate your code, keep an eye on the MPLAB status bar below (the results shown in this
status bar are not related to the program, they are for demo purposes only)

The status bar below allows you to instantly check the value of the flags after each instruction is executed
In the figure above, the flags are z, DC, C
 A capital letter means that the value of the flag is one; meanwhile a small letter means a value of
zero. In this case, the result is not zero; however, digit carry and a carry are present.

Another faster method of simulation: Run and break points


Many times you will need to make some changes to your code, additions, omissions and bug fixes. It is not
then flexible to step into your code step by step to observe the changes you have made especially when
your program is large. It would be a good idea to execute your code all at once or up to a certain point and
then read the results from the watch window.

Now suppose we want to execute the Fibonacci series code at once - to do so, follows these steps:
1. Double click on the “nop” instruction (line 30), a red circle with a letter “B” inside is shown to the
left of the instruction. This is called a breakpoint. Breakpoints instruct the simulator to stop code
execution at this point. All instructions before the breakpoint are only executed

2a. Now press the run button

Run Animate

2b. Alternatively, you can instruct the IDE to


automatically step into the code an
instruction at a time by simply pressing
“animate”
You can control the speed of simulation as
follows:
1. Debugger  Settings  Animation/ Real time Updates
2. Drag the slider to set the speed of simulation you find convenient

16
Program Memory Space Usage
Though we have written about 31 lines in the editor, the total number of program memory space occupied
is far less, remember that directives are not instructions and that they are not downloaded to the target
microcontroller. To get an approximate idea of how much space does the program occupy: Select View 
Program Memory  Symbolic tab

Note that the last instruction


written is “nop” (end is a directive).
The total space occupied is only 18
memory locations

The “opcode” field shows the actual


machine code of each instruction
which is downloaded to the PIC

Program Two: Implementing the function Result = (X + Y) Z


This example is quite an easy one, initially the variable X, Y, Z are loaded with the values which
make the truth table

1 include "p16F84A.inc"
2
3 cblock 0x25
4 VarX
5 VarY
6 VarZ
7 Result
8 endc
9
10 org 0x00
11 Main ;loading the truth table
12 movlw B'01010101' ;ZYX Result
13 movwf VarX ;000 0 (bit7_VarZ, bit7_VarY, bit7_VarX)
14 movlw B'00110011' ;001 1 (bit6_VarZ, bit6_VarY, bit6_VarX)
15 movwf VarY ;010 1 .
16 movlw B'00001111' ;011 1 .
17 movwf VarZ ;100 1 .

17
18 ;101 0 .
19 ;110 0 .
20 ;111 0 (bit0_VarZ, bit0_VarY, bit0_VarX)
21 movf VarX, w
22 iorwf VarY, w
23 xorwf VarZ, w
24 movwf Result
25 nop
26 end

1. Start a new MPLAB session, add the file example2.asm to your project
2. Build the project
3. Select Debugger  Select Tool MPLAB SIM
4. Add the necessary variables and the working register to the watch window (remember that user
defined variables are found under the “Add Symbol” list)
5. Simulate the program step by step, analyze and study the function of each instruction. Stop at the
“nop” instruction
6. Study the comments and compare them to the results at each stage and after executing the
instructions

18
Appendix A: Instruction Listing

19
Appendix B: MPLAB Tools

Another method to view the content of data memory is through the File Registers menu:

 Select View Menu  File Registers

After building the Example1.asm codes, start looking at address 20


(which in our code corresponds to Fib0), to the right you will see the
adjacent file registers from 21 to 2F.

Observe that after code execution, these memory locations are filed
with Fibonacci series value as anticipated.

20
University of Jordan
Faculty of Engineering and Technology
Department of Computer Engineering
Embedded Systems Laboratory 0907334

2 Experiment 2: Instruction Set Analysis 2


& Modular Programming Techniques

Objectives

The main objectives of this experiment are to familiarize you with:


 Program flow control instructions
 Conditional and repetition structures
 The concept of modular programming
 Macros and Subroutines

1
Pre-lab requirements
Before starting this experiment, you should have already familiarized yourself with MPLAB software and how to
create, simulate and debug a project.

Introducing conditionals

The PIC 16series instruction set has four instructions which implement a sort of conditional statement: btfsc ,
btfss, decfsz and incfsz instructions.
1. btfsc checks for the condition that a bit is clear: 0 (Bit Test File, Skip if Clear)
2. btfss checks for the condition that a bit is set one: 1 (Bit Test File, Skip if Set)
3. Review decfsz and incfsz functions from the datasheet

Example 1: movlw 0x09


btfsc PORTA, 0
movwf Num1
movwf Num2

The above instruction tests bit 0 of PORTA and checks whether it is clear (0) or not
 If it is clear (0), the program will skip “movwf Num1” and will only execute “movwf Num2”
Only Num2 has the value 0x09
 If it is set (1), it will not skip but execute “movwf Num1” and then proceed to “movwf Num2”
In the end, both Num1 and Num2 have the value of 0x09

You have seen above that if the condition fails, the code will continue normally and both instructions will be
executed.

Example 2: movlw 0x09 Firstcondition, secondCondition, and


btfsc PORTA, 0 Proceed are called Labels, Labels are
goto firstcondition
goto secondCondition
used to give names for a specific block
Proceed of instructions and are referred to as
…….. your remaining code shown above to change the program
firstcondition execution order.
movwf Num1
goto Proceed
secondCondition
movwf Num2
goto Proceed

Example 2 is basically the same as Example 1 with one main difference:


 If it is clear (0), the program will skip “goto firstcondition” and will only execute “goto
secondCondition”, the program will then execute “movwf Num2” and then “gotoProceed”
Only Num2 has the value 0x09
 If it is set (1), it will not skip but execute “goto firstcondition”, the program will then execute
“movwf Num1” and then “gotoProceed”
Only Num1has the value 0x09

2
Conditional using Subtraction and how the Carry/Borrow flag is affected?
The Carry concept is easy when dealing with addition operations but it differs in borrow operations
according to Microchip implementation.

Carry is a physical flag; you will find it in the STATUS register,


Borrow is not implemented; it is in your mind 
In the following examples we will show the status of the Carry/Borrow flag and how it differs between
addition and subtraction operations:

Ex1) 99-66 Ex 2) 66 – 99
Expect no Expect
10011001 – 01100110-
01100110 borrow since 10011001 borrow since
99 > 66 66 < 99
10011001+ 01100110+
10011010 2’s complement of 66 01100111
100110011 011001101

There is carry (C = 1), since Borrow is the complement There is no carry (C = 0), since Borrow is the
of Carry, then Borrow is 0 (No borrow) which is complement of Carry, then Borrow is 1 (There is
correct borrow) which is correct

Program One: Check if a value is greater or smaller than 10, if greater Result will have the ASCII value G, if
smaller, it will have the ASCII value S.
1 include "p16F84A.inc"
2 cblock 0x25
3 testNum
4 Result
5 endc
6 org 0x00
7 Main
8 movf testNum, W
9 sublw .10 ;10d - testNum
10 btfss STATUS, C
11 goto Greater ;C = 0, that's B = 1, then testNum > 10
12 goto Smaller ;C = 1, that's B = 0, then testNum < 10
13 Greater
14 movlw A'G'
15 movwf Result
16 goto Finish
17 Smaller
18 movlw A'S'
19 movwf Result
20 Finish
21 nop
22 end

3
1. Start a new MPLAB session, add the file example3.asm to your project
2. Build the project
3. Select Debugger  Select Tool MPLAB SIM
4. Add the necessary variables and the working register to the watch window (remember that user defined
variables are found under the “Add Symbol” list)
5. Enter values into testNum, simulate the program step by step, concentrate on what happens at lines10-12
6. Keep an eye on the Flags at the status bar below while simulating the code
7. Enter other values lesser and greater and observe how the code behaves

 What is the value stored in Result when testNum = 10? Is this correct? Can you think of a solution?

Program Two: Counting the Number of Ones in a Register’s Lower Nibble


Introducing simple conditional statements

This program will take a hexadecimal number as an input in the lower nibbles (bits 3:0) in a register called
testNum.The number will be masked by anding it with 0F, (remember that 0 & Anything = 0, while 1 & anything
will remain the same), we used masking because if the user accidentally wrote a number in the higher nibble
(bits 3:0), it will be forced to zero. The number in the lower nibble will not be affected (anded with 1). The
masked result will be saved in a register called tempNum.
Now tempNum will be rotated to the right, bit0 (least significant bit) will move to the C flag of the STATUS
register after rotation. Then it will be tested whether it 0 or 1. If it is 1, the numOfOnes register will be
incremented. Else the program proceeds. This operation will continue for 4 times (because the number of bits in
the lower nibble is 4)

1 include "p16f84a.inc" Byte 8 bits


2 7 6 5 4 3 2 1 0
3 cblock 0x20 Higher 4 bits Lower 4 bits
4 testNum ;GPR @ location 20 Upper Nibble Lower Nibble
5 tempNum ;GPR @ location 21
6 endc
7
8 cblock 0x30
9 numOfOnes ;GPR @ location 30
10 endc
11
12 org 0x00
13 clrf numOfOnes ;Initially number of ones is 0
14 movf testNum, W ;Since we only need to test the number of ones in the lower nibble, we
15 ;mask them by 0F (preserve lower nibble and discard higher nibble)
16 andlw 0x0F ;in case a user enters a number in the upper digit. Save masked result
17 movwf tempNum ;in tempNum
18 rrf tempNum, F ;rotate tempNum to the right through carry, that is the least
19 ;significant bit of tempNum (bit0) goes into the C flag of the
20 ;STATUS register, while the old value of C flag goes into bit 7 of
21 ;tempNum

4
22 btfsc STATUS, C ;tests the C flag, if it has the value of 1, increment number of ones and
23 incf numOfOnes, F;proceed, else proceed without incrementing
24 rrf tempNum, F
25 btfsc STATUS, C ;Same as above
26 incf numOfOnes, F
27 rrf tempNum, F
28 btfsc STATUS, C
29 incf numOfOnes, F
30 rrf tempNum, F
31 btfsc STATUS, C
32 incf numOfOnes, F
33 nop
34 end
As you can see in the above program, we did not write instructions to load testNum with an initial value to test;
this code is general and can take any input. So, how do you test this program with general input?

After building your project, adding variables to the watch window and selecting MPLAB SIM simulation tool,
simply double click on testNum in the watch window and fill in the value you want. Then Run the program.

Change the value of testNum and re-run the program again, check if numOfOnes hold the correct value.

Coding for efficiency: The repetition structures

You have observed in the code above that instructions from 18 to 32 are simply the same instructions repeated
over and over four times for each bit tested.

Now we will introduce the repetition structures, similar in function to the “for” and “while” loops you have
learnt in high level languages.

Program Three: Counting the Number of Ones in a Register’s Lower Nibble


Using a Repetition Structure
1 include "p16f84a.inc"
2 cblock 0x20
3 testNum
4 tempNum
5 endc
6
7 cblock 0x30
8 numOfOnes
9 counter ;since repetition structures require a counter, one is declared
10 endc
11
12 org 0x00
13 clrf numOfOnes
14 movlw 0x04 ;counter is initialized by 4, the number of the bits to be tested

5
15 movwf counter
16 movf testNum, W
17 andlw 0x0F
18 movwf tempNum
19 Again
20 rrf tempNum, F
21 btfsc STATUS, C
22 incf numOfOnes, F
23 decfsz counter, F ; The contents of register counter are decremented then test :
24 goto Again ; if the counter reaches 0, it will skip to “nop” and program ends
25 nop ; if the counter is > 0, it will repeat “goto Again”
26 end

Introducing the Concept of Modular Programming


Modular programming is a software design technique in which the software is divided into several separate
parts, where each part accomplishes a certain independent function. This “Divide and Conquer” approach allows
for easier program development, debugging as well as easier future maintenance and upgrade.

Modular programming is like writing C++ or Java functions, where you can use the function many times only
differing in the parameters. Two structures which are similar to functions are Macros and Subroutines which
are used to implement modular programming.

Subroutines
Subroutines are the closest equivalent to functions
 Subroutines start with a Label giving them a name and end with the instruction return
Examples:
doMath Process
Instruction 1 Instruction 1
Instruction 2 Instruction 2
. .
. .
Instruction n Calculate
return Instruction 7
Instruction 8
return
This is still one subroutine, no matter the number
of labels in between

 Subroutines can be written anywhere in the program after the org and before the end directives
 Subroutines are used in the following way: Call subroutineName
 Subroutines are stored once in the program memory, each time they are used, they are executed from
that location

6
 Subroutines alter the flow of the program, thus they affect the stack
Example:
Main
Instruction1
Instruction2
Call doMath
Instruction4
Instruction5
Nop
Nop

doMath
Instruction35
Instruction36
Instruction37
return
So what is the stack and how is it used?

Initially the program executes sequentially; instructions 1 then 2 then 3, when the instruction Call doMath is
executed, the program will no longer execute sequentially, instead it will start executing Instructions35, then 36
then 37, when it executes return, what will happen? Where will it go and what instruction will be executed?

When the Call doMath instruction is executed, the address of the next instruction (which as you should already
know id found in the program counter) Instruction4 is saved in a special memory called the stack. When the
return instruction is executed, it reads the last address saved in the stack, which is the address of Instruction4
and then continues from there.
----Read section 2.4.1 of the P16F84A datasheet for more information regarding the stack----

Macros
Macros are declared in the following way (similar to the declaration of cblocks)
macroName macro
Instruction 1
Instruction 2
.
.
Instruction n
endm

 Macros should be declared before writing the code instructions. It is not recommended to declare macros
in the middle of your program.
 Macros are used by only writing their name: macroName
 Each time you use a macro, it will be replaced by its body, refer to the example below. Therefore, the
program will execute sequentially, the flow of the program will not change. The Stack is not affected

7
Programs Four and Five
The following simple program demonstrates the differences between using macros and subroutines. They
essentially perform the same operation: Num2 = Num1 + Num2

Example4 using Macro Example5 using Subroutine


1 include "p16f84a.inc" 1 include "p16f84a.inc"
2 2
3 cblock 0x30 3 cblock 0x30
4 Num1 4 Num1
5 Num2 5 Num2
6 endc 6 endc
7 7
8 Summation macro 8
9 movf Num1, W ;Macro Body 9
10 addwf Num2, F 10
11 endm 11
12 12
13 org 0x00 13 org 0x00
14 Main 14 Main
15 Movlw 4 15 Movlw 4
16 Movwf Num1 16 Movwf Num1
17 Movlw 8 17 Movlw 8
18 Movwf Num2 18 Movwf Num2
19 Summation 19 Call Summation
20 Movlw 1 20 Movlw 1
21 Movwf Num1 21 Movwf Num1
22 Movlw 9 22 Movlw 9
23 Movwf Num2 23 Movwf Num2
24 Summation 24 Call Summation
25 25 goto finish
26 finish 26
27 nop 27 Summation
28 end 28 movf Num1, W
29 addwf Num2, F
30 return
31 finish
32 nop
33 end

Analyzing the two programs and highlighting the differences


For both applications, go to View  Program Memory, let’s see the differences:

8
Figure 1. The example using macros
In the program memory window, notice that the macro name is replaced by its body. The instructions movf
Num1, W and addwf Num2, F replace the macro name @ lines 19 and 24. Using macros clearly affects the space
used by the program as it increases due to code copy.

Figure 2. The example using subroutines


Now notice that the subroutine is only stored once in the program memory. No code replacement is present.
You can also observe from the program memory that the program utilizing the macro executes sequentially from
start to end, while the second program alters the program flow.
For Program Two, do the following:

9
1. After building the project, go to View  Hardware Stack

2. Simulate the program up to


the point when the green
arrow points to the first Call
Summation instruction.

3. Look at the status bar below


your MPLAB screen, what is
the value of pc (program
counter) (Note that the
program counter has the
address of the next
instruction to be executed, that is Call Summation, Remember the instruction the arrow points to is not
yet executed)
4. Now execute (use Single step) the Call Summation instruction.
 After doing step4, what is the address of PC?
 What is now stored at the TOS (Top of Stack)? (Refer to the Hardware Stack window)
 How many levels of stack are used?
5. Now, continue simulating the program (subroutine). After executing the return instruction
 What is the address of PC?
 What is now stored at the TOS?
 How many levels of stack are used?
6. Repeat the steps above for the second Call Summation instruction?

The operation of saving the address on the stack - and any other variables - when calling a subroutine
and later retrieving the address – and variables if any - when the subroutine finishes executing is called
context switching.

Important Notes:
1. Assuming both a macro and a subroutine has the exact same body (same instructions), the execution of
the subroutine takes slightly more time due to context switching.
2. You can use macro inside a macro, call a subroutine inside a subroutine, use a macro inside a subroutine
and call a subroutine inside a macro
Further Simulation Techniques: Step Over and Step Out

“Step Over” “Step Out


Step Over is used when you want to execute the subroutine as a whole unit without seeing how each individual
instruction is executed. It is usually used when you know that that the subroutine executes correctly and you are
only interested to see the results.

1. Simulate program two up to the point when the green arrow points to the first Call Summation
instruction.
2. Press Step Over, observe how the simulation runs

10
Step Out resembles Step Over, the only difference is that you use it when you are already inside the
subroutine and you want to continue executing the subroutine as a whole unit without seeing how each
remaining individual instruction is executed.
1. Simulate the program up to the point when the green arrow points to the first instruction inside the
Summation subroutine: movf Num1, W
3. Press Step Out, , observe how the simulation runs
In both cases, the instruction are executed but you only see the end result of the subroutine

Time Calculation

To calculate the total time spent in executing the whole program or a certain subroutine, do the following:

1. Set the oscillator (external clock speed) as follows:

2. Set the processor frequency to 4MHz

This means that each instruction cycle time is 4MHz/4 = 1MHz and T = 1/f = 1/MHz = 1µs

3. Now set breakpoints at the beginning and end of the code you want to calculate time for

4. Go to Debugger  Stop Watch

11
5. Now run the program, when the pointer stops at the first breakpoint  Press Zero
6. Run the program again. When the pointer reaches the second breakpoint, read the time from the
stopwatch. This is the time spent in executing the code between the breakpoints.

Modular Programming
How to think Modular Programming?
Initially, you will have to read and analyze the problem statement carefully, based on this you will have to
1. Divide the problem into several separate tasks,
2. Look for similar required functionality
Non Modular and Modular Programming Approachs: Read the following problem statement
A PIC microcontroller will take as an input two sensor readings and store them in Num1 and Num2, it will then
process the values and multiply both by 5 and store them in Num1_5, and Num2_5. At a later stage, the program will
multiply Num1 and Num2 by 25 and store them in Num1_25 and Num2_25 respectively.

Analyzing the problem above, it is clear that it has the following functionality:
 Multiply Num1 by 5
 Multiply Num2 by 5
 Multiply Num1 by 25
 Multiply Num2 by 25

12
As you already know, we do not have a multiply instruction in the PIC 16F84A instruction set, so we do it by
addition since:
2x3=2+2+2 ; add 2 three times
7 x 9 = 7 + 7 + 7 + 7 + 7 + 7 + 7 + 7 + 7 ; add 7 nine times

So we write a loop as follows (example 4 x 9, add four nines), initially one nine is placed in W then we construct a
loop to add the remaining 8 nines:

movlw .8 ; because we put the first 4 in W, then we add the remaining 8 fours to it
movwf counter
movf temp, w ; 1st four in W
add
addwf temp, w
decfsz counter, f ; decrement counter, if not zero keep adding, else continue
goto add
; continue with code

A Non Modular Programming Approach Modular Programming Approach


Write multiply code for each operation above Write one “Multiply by 5” code, use it two times
Write one “Multiply by 25” code, use it two
times
Note that you do not need to write the “Multiply
by 25” code from scratch, since 25 is 5x5, you
can simply use “Multiply by 5” two times!
Code lines: 38 Code lines: 27
get Num1 1 get Num1 1
Write whole code to multiply Num1 by 5 7 call “multiply by 5” function 1
Store in Num1_5 1 Store in Num1_5 1
get Num2 1 get Num2 1
Write whole code to multiply Num2 by 5 7 call “multiply by 5” function 1
Store in Num2_5 1 Store in Num2_5 1
get Num1 1 get Num1 1
Write whole code to multiply Num1 by 25 7 call “multiply by 25” function 1
Store in Num1_25 1 Store in Num1_25 1
get Num2 1 get Num2 1
Write whole code to multiply Num2 by2 5 7 call “multiply by 25” function 1
Store in Num2_25 1 Store in Num2_25 1
goto finish 1 goto finish 1
nop 1 nop 1

A single Multiply by 5 function


8
A single Multiply by 5 function 5

13
include "p16f84a.inc" include "p16f84a.inc"
cblock 0x30 cblock 0x30
Num1 Num1
Num2 Num2
Num1_5 Num1_5
Num2_5 Num2_5
Num1_25 Num1_25
Num2_25 Num2_25
temp temp
counter counter
endc endc

org 0x00 org 0x00


Main Main
movf Num1, w ;Num1 x 5 movf Num1, w ;Num1 x 5
movwf temp call Mul5
movlw .4 movwf Num1_5
movwf counter
movf temp, w movf Num2, w ;Num2 x 5
add1 call Mul5
addwf temp, w movwf Num2_5
decfsz counter, f
goto add1 movf Num1, w ;Num1 x 25
movwf Num1_5 call Mul25
movwf Num1_25

movf Num2, w ;Num2 x 5 movf Num2, w ;Num2 x 25


movwf temp call Mul25
movlw .4 movwf Num2_25
movwf counter goto finish
movf temp, w
add2 Mul5
addwf temp, w movwf temp
decfsz counter, f movlw .4
goto add2 movwf counter
movwf Num2_5 movf temp, w
add
movf Num1, w ;Num1 x 25 addwf temp, w
movwf temp decfsz counter, f
movlw .24 goto add
movwf counter return
movf temp, w
add3 Mul25
addwf temp, w movwf temp
decfsz counter, f call Mul5
goto add3 movwf temp

14
movwf Num1_25 call Mul5
return
movf Num2, w ;Num2 x 25 finish
movwf temp nop
movlw .24 end
movwf counter
movf temp, w
add4
addwf temp, w
decfsz counter, f
goto add4
movwf Num2_25
goto finish
finish
nop
end

Notes on passing parameters to subroutines


Subroutines and macros are general codes; they work on many variables and generate results. So how do we tell
the macro/subroutine that we want to work on this specific variable?
We have two approaches:

Place the input at the working register Store the input(s) in external variables
Take the output from the working register Load the output(s) from external variables

Example: Example:

Main Movf Num1, W ;load Num with Num1


Movlw 03 ;input to W Movwf Num
Call MUL_by4 Call MUL_by4
Movwf Result1 ;output from W Movf Result, W ;read the result and store
Movlw 07 ;input to W Movwf Result1 ;it in Result1
Call MUL_by4 Movf Num2, W ;load Num with Num2
Movwf Result2 ;output from W Movwf Num
Nop Call MUL_by4
. Movf Result, W ;read the result and store
. Movwf Result2 ;it in Result2
MUL_by4
Movwf temp MUL_by4
Rlf temp,F Rlf Num,F
Rlf temp, F Rlf Num, W
Movf temp, W ;place result in W Movwf Result ;place result in W
Return Return

15
In this approach, the MUL_by4 subroutine takes the In this approach the MUL_by4 subroutine expects to
input from W (movwf), processes it then places the find the input in Num and saves the output in Result.
result back in W. Notice that we initially load W by Therefore, before calling the subroutine we load
the numbers we work on (here 03 and 07) then we Num by the value we want (here Num1) and then
take their values from W and save them in Result1 take the value from Result and save it in Result1.
and Result2 respectively The same is repeated for Num2
This approach is useful when the subroutine/macro This approach is useful when the subroutine/macro
has only one input and one output takes many inputs and produces multiple outputs

Special types of subroutines: Look up tables

Look up tables are a special type of subroutines which are used to retrieve values depending on the input they
receive. They are invoked in the same as any subroutine: Call tableName
They work on the basis that they change the program counter value and therefore alter the flow of instruction
execution
The retlw instruction is a return instruction with the benefit that it returns a value in W when it is executed.

Syntax:

lookUpTableName
addwf PCL, F ;add the number found in the program counter to PCL (Program counter)
nop
retlw Value ;if W has 1, execute this
retlw Value ;if W has 2, execute this
retlw Value

retlw Value

Value can be in any format: decimal, hexadecimal, octal, binary and ASCII. It depends on the application
you want to use this look-up table in.

16
Program Six: Displaying the 26 English Alphabets
This program works as follows:
Counter is loaded with the number 1 because we want to get the first letter of the alphabet, when we call the
look-up table, it will retrieve the letter ‘A’. The counter is incremented by 1 and then checked if we have reached
the 26th letter of the alphabet (27 – the initial 1), if not we proceed to display the second letter ‘B’ and the third ‘C’
and so on. When we have displayed all the alphabets, counter will have the value 27 after which the program
exits.
1 include "p16f84a.inc"
2 cblock 0x25
3 counter ;holds the number of Alphabet displayed
4 Value ;holds the alphabet value
5 endc
6 org 0x00
7 Main
8 movlw 1 ;Initially no alphabet is displayed
9 movwf counter
10 Loop
11 movf counter, W
12 call Alphabet ;display Alphabet
13 movwf Value
14 incf counter, F ;Each time, increment the counter by 1
15 movf counter, w ;if counter reaches 27, exit loop else continue
16 sublw .27
17 btfss STATUS, Z
18 goto Loop
19 goto finish
20 Alphabet
21 addwf PCL, F
22 nop
23 retlw 'A'
24 retlw 'B'
25 retlw 'C'
26 retlw 'D'
27 retlw 'E'
28 .
29 .
30 retlw 'Z'
31 finish
32 nop
33 end

1. Complete the look-up table above with the missing alphabet


2. Add both counter and value to the watch window.
3. Place a breakpoint @ instruction 14: incf counter, F
4. Run the program, keep pressing run and observe the values of the variables in the Watch window

17
Appendix A: Documenting your program

It is a good programming practice to document your program in order to make it easier for you or others to read
and understand it. For that reason we use comments. A proper way of documenting your code is to write a
functional comment, which is a comment that describes the function of one or a set of instructions. Comments
are defined after a semicolon (;) and are not read by MPLAB IDE

BSF STATUS, RP0


; Switch to Bank 1 Good comment √
; Set the RP0 bit in the Status Register to 1 Bad Comment, no new added info X

How to professionally document your program?

At the beginning of your program, you are encouraged to add the following header which gives an insight to your
code, its description, creator, version, date of last revision, etc… Most importantly, it is encouraged to document
the necessary connections and classify them as input/output.

;**************************************************************************************************
; * Program name: Example Program
; * Program description: This program …….
;*
; * Program version: 1.0
; * Created by Embedded lab engineers
; * Date Created: September 1st, 2008
; * Date Last Revised: September 16th, 2008
;**************************************************************************************************
; * Inputs:
;* Switch 0 (Emergency) to RB0 as interrupt
;* Switch 1 (Start Motor) to RB1
;* Switch 2 (Stop Motor) to RB2
;* Switch 3 (LCD On) to RB3
; * Outputs:
;* RB4 to Motor
;* RB5 to Green LED (Circuit is powered on)
;**************************************************************************************************
1. Your code declarations go here: includes, equates, cblocks, macros, origin, etc…
2. Your code goes here…
3. When using subroutines/macros, it is advised to add a header like this one before each to properly
document and explain the function of the respected subroutine/macro.
;**************************************************************************************************
;* Subroutine Name: ExampleSub
;* Function: This subroutine multiplies the value found in the working register by 16
;* Input: Working register
;* Output: Working register * 16
;***************************************

18
Appendix B: Instruction Listing

19
University of Jordan
Faculty of Engineering and Technology
Department of Computer Engineering
Embedded Systems Laboratory 0907334

3
Experiment 3: Basic
Embedded System Analysis
and Design

Objectives
 Empowering students with logical and analytical skills to solve real life system design
problems
 To become familiar with the process of system requirement analysis and definition,
system and subsystem design, flow analysis and flowchart design, software design and
optimization
 Stressing software and hardware co-design techniques by introducing the Proteus IDE
package

Written by Eng. Enas Jaara1and Eng. Ashraf Suyyagh |


Starting-Up System Design

When we attempt to design a system that is required to perform complex tasks, it is essential that one
thinks about the design flow and establish an overall system design before immediately jumping into
implementation and coding in order for the program be written flawlessly and smoothly and the system
functions correctly. In this way you don't waste time writing a flawed incomplete program, or which
addresses the wrong problem or which is missing some flow scenarios.

A well-established diagramming technique is the flow chart which tracks down system execution flow.
A flowchart is a schematic representation of an algorithm, showing the steps as different shapes, and
their order by connecting them with unidirectional arrows. Flowcharts are used in designing or
documenting programs. As programs get more complex, flowcharts help us follow and maintain the
program flow. This makes a program easier to write, read, and understand. Other techniques used are
state machines which are not covered in this course.

Complex systems need be broken into smaller pieces where each carries out few simple related tasks of
the overall system. The system is thus built from these simple subsystems. One need only care about
how these subsystems interface with each other. Subroutines allow the programmer to divide the
program into smaller parts which are easier to code. In system design methodology, this is called the
“Divide and Conquer” approach.

The basic steps in system design are:


Step 1: Requirements Definition
1. Reading the problem statement for what is needed to do, divide if it is complex.
2. What do I need to solve? Should I do it in software or hardware …
3. Determine the inputs and outputs for the hardware.
Step 2: System and Subsystem Design
4. Partition overall architecture into appropriate sub-systems.
5. Draw a detailed flowchart for each sub-systems
Step 3: Implementation
6. Translate flowcharts into code
7. Integrate subsystem into one code/design
Step 4: System Testing and Debugging
8. Run the program/hardware and see if it works correctly. If not, attempt to fix the program by
reviewing the above steps and refining your design along with it

The above steps prove essential as programs get harder and larger. Next we will present a real life
example from the industrial automation field.

Example – An Industrial Filling Machine

Problem Statement
We are to design an embedded system which controls a filling machine that works as follows:
Empty bottles move on a conveyer belt, when a bottle is detected, the conveyor belt stops, a
filling machine starts working for a specified period of time after which the filling machine
stops. The total number of filled bottles is increased by one and shown on a common cathode
7-Segments display, the conveyor belt starts again and the machine does the same thing for the
next bottle and so on. When the total number of bottles reaches nine the machine stops for

2
manual packaging. Meanwhile, one LED lights on an 8-LED-row and moves back and forth.
The conveyor belt does not start again until the resume button is pressed. Moreover, the LED
array turns off! See the figure on the next page for the machine layout:

or
A Typical Filling Machine

Step1: Requirements Definition and Analysis


Now we will analyze the problem statement above and determine the required hardware and
their role as input or output.

Output means a signal need be sent from the PIC to external hardware for control purposes.
Input means a signal is received from external hardware into the PIC for processing. Processing
means a certain code which does the job is required; this subroutine is internal processing and
doesn’t interact with the outside world!

Empty bottles move on a conveyer belt, when a bottle is detected, the conveyor belt stops
 There is a motor which controls the conveyor: “conveyor motor”. Output
 There is a sensor which detects the presence of a bottle: “bottle sensor”. Input
A filling machine starts working for a specified period of time after which the filling machine
stops
 There is a pump/motor which is turned on/off to fill the bottle: “filling motor”. Output
 We need a mechanism to calculate this time period. Processing

3
The total number of filled bottles is increased by one and shown on a common cathode 7-
Segments display
 Clearly we need some sort of a counter. Memory location (GPR) reserved
 We need to output the value of this counter to a 7-segment display. Output
The conveyor belt starts again and the machine does the same thing for the next bottle and so on.
When the total number of bottles reaches nine the machine stops for manual packaging.
 Continuously check for counter value if it reaches 9. Processing
Meanwhile, one LED lights on an 8-LED-row and moves back and forth. The conveyor belt does
not start again until the resume button is pressed. Moreover, the LED array turns off!
 We need a code to control the LED lights. Output
 We need a mechanism to check for the resume button key press. Input

As you have seen above, we need to interact with external components; outputs like the
motors, 7-Segments and the LEDs, as well as inputs from sensors or switches. Almost any
embedded system needs to transfer digital data between its CPU and the outside world. This
transfer achieved through input /output ports.

A quick look to the 16F84A or 16F877A memory maps will reveal multiple I/O ports: PORTA
and PORTB for the 16F84A, and the additional PORTC, PORTD and PORTE for the 16F877A.
Each port has its own TRISx Register which controls whether this PORTx will be an input port,
output port, or a combination of both (individual bits control).

Ports A and E have a special configuration. Instructions needed to configure all PORTA
and E pins as general digital I/O pins :
PORTA pins are multiplexed with analog inputs
for the A/D converters. The operation of each pin is BANKSEL ADCON1
selected by clearing/setting the appropriate control MOVLW 06H ;set PORTA as general
bits in the ADCON1. MOVWF ADCON1 ;Digital I/O PORT

PIC microcontrollers’ ports are general-


purpose bi-directional digital ports. The state
of TRISx Register controls the direction of the
PORTx bits. A logic one in a bit position
configures the PIC to act as an input and if it
has a zero to act as an output. However, a
pin can only act as either input or output at
any one time but not simultaneously. This
means that each pin has a distinct direction
state.

4
Examples:

Movlw 0x0F Clrf TRISC Clrf TRISD Movlw B’00110011’


Movwf TRISB Comf TRISD, F Movwf TRISB
The high nibble of Whole PORTC as output Whole PORTD as input Bits 2, 3, 6, 7 as output
PORTB is output, low Bits 0, 1, 4, 5 as input
nibble is input

How to decide whether microcontroller’s ports must be configured as inputs or outputs?


Input ports “Get Data” from the outside world into the microcontroller while output
ports “Send Data” to the outside world.

 LEDs, 7-Segment displays, motors, and LCDs (write mode) that are interfaced to
microcontroller’s ports should be configured as output.
 Switches, push buttons, sensors, keypad and LCDs (read mode) that are interfaced to
microcontroller’s ports should be configured as input.

For the above filling machine example, we will use the following configuration.
Inputs:
 RA2: Bottle sensor
 RA3: Resume button
Outputs:
 RB0 to RB7: LEDs
 RC0: Machine motor ON/OFF
 RC1: Filling machine ON/OFF
 RD0 to RD6: 7-Segments outputs from “a” to “g” respectively

Step 2: System and Subsystem Design


Divide the overall system into appropriate sub-systems. The design of a subsystem includes:
(a) Defining the processes/functions that are carried out by the subsystem.
(b) Determining the input and output of the subsystem (Subsystem Interface)

Commonly, programs have “Initial” and “Main” subroutines, the Initial subroutine is used
to initialize all ports, SFRs and GPR’s used in the program and thus is only executed once
at system startup, the Main subroutine contains all the subroutines which perform the
functions of the system, many applications require that these functions be carried out
repeatedly, thus the program loops through the Main subroutine code infinitely.

Note: when designing a system, expect not that you should reach the same system design as
your friends/colleagues. Each one of you has her/his own thinking style and therefore
designs the system differently; some might divide a certain problem into two subsystems,
others into three or four. As long as you achieve a simple, easy to understand, maintainable and
correct fully working system, then the goal is achieved! Therefore, the following subsystem
design of the above problem is not the only one to approach and solve the problem. You may
divide your subsystems differently depending on the philosophy and system structure you deem
as appropriate.

5
Step 3: Implementation
As introduced before, the system should start with an initial subroutine. The nature of the
system requires it runs continuously, consequently, the program code will loop through
specific subroutines which implement the system function, we have decided to implement the
code in three Major and two Minor subroutines – aside from the Initial subroutine:

Major Subroutines (in body of the Main):


Update_Seven_Seg subroutine: reads the total number of bottles filled and displays it
on the 7-segment display.
Test_and_Process subroutine: waits for bottle, stops the conveyor, fills the bottle, and
restarts the conveyor.
Test_Resume subroutine: checks if total number of bottles filled is nine, if so, stops the
machine, continuously rotates the LEDs and tests for resume button press (this is done
by calling the LEDs subroutine)

Minor Subroutines (outside the body of Main, called by those inside):


LEDs: moves the LED in the LED array back and forth and testing for the resume button
press meanwhile.
Simplest_Delay: introduces a software delay used to give enough time for the LED to be
seen as on.
The Initial and Main Codes:
Main
Call Initial ; Initialize Ports, SFRs and GPR’s
Main_Loop
Call Update_Seven_Seg ; Test the number of Bottles and displays it on the 7-Seg.
Call Test_and_Process ; Keep testing the bottle sensor, if bottle found, process it,
; else wait until a bottle is detected
Call Test_Resume ; Check if No. of bottles is 9, if yes test if resume button is
; pressed, else skip and continue code
goto Main_Loop ; Do it again
Initial
CLRF BottleNumber ; Start count display from zero
BANKSEL TRISD ; Set register access to bank 1
CLRF TRISC ; Set up all bits of PORTC as outputs
CLRF TRISD ; Set up all bits of PORTD as outputs, connected to
; Common Cathode 7- Segments Display
CLRF TRISB ; Set up all bits of PORTB as outputs, connected to
; LED array
MOVLW 0x0C ; Set up bits (1-2) of PORTA as inputs; RA3:
MOVWF TRISA ; resume button, RA2: bottle sensor, others not used
BANKSEL ADCON1
MOVLW 06H
MOVWF ADCON1 ;set PORTA as general Digital I/O PORT
BANKSEL PORTA
CLRF PORTB ; Initially, all LEDs are off
BSF PORTC, 0 ; Start conveyer motor
RETURN

6
Subsystem Flow Chart Analysis and Code Implementation

Clearly, the signals sent to the 7-Segments display are not decimal values but according to the 7Segments
layout. Refer to the Hardware Guide for more information.

We have to translate the decimal number of bottles found in the bottle counter: BottleNumber to the
appropriate common cathode 7-Segments number representation.

To do so we define the representations as constants and use a Look-up table to get the correct representation
for each bottle number.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Update_Seven_Seg subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


This subroutine returns the appropriate common cathode 7-Segments
representation of the number of bottles in order for it to be displayed by the
consecutive subroutine

; Assuming the order is dp g f e d c b a

Zero equ B’00111111’ Update_Seven_Seg


One equ B’00000110’
Twoo equ B’01011011’ Movf BottleNumber,W
.
Addwf PCL, F
.
Retlw Zero
Nine equ B‘01101111’
Retlw One
Retlw Two
Retlw Three
Retlw Four
Retlw Five
Retlw Six
Retlw Seven
Retlw Eight
Retlw Nine

7
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Test_and_Process subroutine;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

This subroutine tests if a bottle is present or not, if a bottle is detected, the


conveyor motor stops, the filling machine starts working for a specified period of
time after which the filling machine stops. The conveyor belt starts moving
again. Finally the number of bottles is incremented

Test_and_Process
movwf PORTD ; display on the 7-Seg
Display number of bottles on the seven poll
segments display btfss PORTA,2 ; Test the bottle sensor
goto poll
bcf PORTC,0 ; stop conveyer motor
bsf PORTC,1 ; start filling motor
NO call Simplest_Delay ;Insert delay
bcf PORTC,1 ; stop filling motor
Has a bottle bsf PORTC,0 ; start conveyer motor
been detected? incf BottleNumber,F
Is RA2 =1? return

YES

Stop conveyer motor

Start filling motor

Wait a specified delay

Stop filling motor

Start conveyer motor

Increment the number of bottles

Exit

8
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Test_Resume Subroutine;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
This subroutine checks if the total number of bottles reaches nine, if not it will exit,
if yes the conveyer motor stops for manual packaging. Meanwhile one LED lights
on an 8-LED-row and moves back and forth. The conveyor belt does not start again
until the resume button is pressed

Test_Resume
movf BottleNumber, w
sublw .9
NO btfss STATUS, Z
goto fin1
Is Bottle No. == 9? call Update_Seven_Seg
movwf PORTD ; display on the 7-seg
bcf PORTC, 0 ; stop conveyer motor
bsf PORTB, 0 ; light 1 LED
YES bcf STATUS,C
Exit clrf BottleNumber ; Reset System
call LEDs ; rotate LEDs
fin1
return
Update 7-Seg with the
number of the last bottle (9)

Stop conveyer motor

Light one LED on from the 8-LEDs

Clear the No. of bottles to start over

Call LEDs subroutine

9
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; LEDs Subroutine;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

This subroutine lights one LED on an 8-LED-row and continuously moves back and
forth in this fashion. In between, the resume button is checked. If pressed, the
conveyor motor starts again and the LED array turns off else the LEDs keep rotating
and the resume button checked.

Insert enough time for the LEDs


Rotate_Left
LED to be visually seen
Call Simplest_Delay
rlf PORTB, F
Rotate LED one location to the left btfsc PORTA, 3 ;check Resume button
goto fin
btfss STATUS, C
goto Rotate_Left
Rotate_Right
YES call Simplest_Delay
Has the Resume rrf PORTB , F
button been btfsc PORTA, 3 ;check Resume button
pressed? Is RA3=1 goto fin
btfss STATUS, C
goto Rotate_Right
NO goto Rotate_Left
fin
clrf PORTB
NO Is C flag=1 ( has YES return
LED’s rotate 8
times)

Insert delay

Same as above but the


rotation now is to the
right direction

Turn off the LEDs

Exit

10
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Simplest_Delay Subroutine;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
This subroutine inserts delay to be used as a digit delay in 7-seg
multiplexing and as LED delay in the LED's array

Load MSD with 0xFF Simplest_Delay


Movlw 0xFF
movwf msd
clrf lsd
loop2
Clear LSD decfsz lsd, f
goto loop2
decfsz msd, f
goto loop2
Decrement LSD return

NO YES

Is LSD = 0?

Decrement MSD

NO YES
YES
Is MSD =
0?

Exit

11
How to Simulate This Code in MPLAB?
You have learnt so far that in order to simulate inputs to the PIC, you usually entered them through the
Watch window. However, this is only valid and true when you are dealing with internal memory
registers. In order to simulate external inputs to the PIC pins, we are to use what is called a Stimulus.

There are multiple actions which you can apply to an input pin, choose whatever you see as appropriate
to simulate your program. Here we have chosen to simulate the button press as a pulse.

1. Add RA2(AN2) and RA3(AN3) to the Stimulus window and BottleNumber to Watch window.

12
2. Place a break point at Instruction BTFSS PORTA,2 in the Test_and_Process
subroutine. This will allow us to change the reading of the bottle sensors.

3. Place another break point at Instruction BTFSC PORTA, 3 in the LEDs subroutine. This will allow
us to change the reading of the resume button.

4. Run your code, you will go to the First break point then press “Step Into” you will observe that
you have stuck in loop.

5. Now Press “Fire”, the arrow next to the RA2 in the Stimulus pin, what do you observe?

6. Now press “Step Into” again , observe how the value of BottleNumber change.

7. press “Run” then “fire” again, observe how the value in BottleNumber changes whenever you
reach the first breakpoint.
Note: You will reach the second breakpoint when nine bottles were detected.

8. Press “Step Into “ you will observe that you have stuck in loop.

9. Now Press “Fire”, the arrow next to the RA3 in the Stimulus pin.

10. Now press “Step Into” again, observe how the value of BottleNumber changes to ZER

13
University of Jordan
Faculty of Engineering and Technology
Department of Computer Engineering
Embedded Systems Laboratory 0907334

4 Experiment 4: LCD

Objectives
 To become familiar with HD44780 controller based LCDs and how to use them
 Knowing the various modes of operation of the LCD (8-bit/4-bit interface, 2-lines/1-line, CG-ROM).
 Distinguishing between the commands for the instruction register and data register.
 Stressing software and hardware co-design techniques by using the Proteus IDE package to
simulate the LCD.

Written by Eng. Ashraf Suyyagh and Eng. Enas Jaara | 1|Page


Introduction
What is an LCD?
A Liquid Crystal Displays (LCD) is a thin, flat display device made up of any number of color or
monochrome pixels arrayed in front of a light source or reflector. It is often utilized in battery-powered
electronic devices because it uses very small amounts of electric power.

LCDs have the ability to display numbers, letters, words and a variety of symbols. This experiment
teaches you about LCDs which are based upon the Hitachi HD44780 controller chipset. LCDs come in
different shapes and sizes with 8, 16, 20, 24, 32, and 40 characters as standard in 1, 2 and 4–line
versions. However, all LCD’s regardless of their external shape are internally built as a 40x2
format. See Figure 2 below

Figure 1: A typical LCD module

Figure 2: Different LCD modules shapes and sizes

Figure 3: Display address assignments for HD44780 controller based LCDs

2|Page
LCD I/O

Most LCD modules conform to a standard interface specification. A 14-pin access is provided
having eight data lines, three control lines and three power lines as shown below. Some LCD
modules have 16 pins where the two additional pins are typically used for backlight purposes

Note: This image might differ from the


actual LCD module, the order can be from
left to right or vice versa therefore you
should pay attention, pin 1 is marked to
avoid confusion (printed on one of the
pins).

Powering u p the LCD requires connecting


three lines: one for the positive power Vdd
(usually +5V), one for negative power (or
ground) Vss. The Vee pin is usually
connected to a potentiometer which is used
to vary the contrast of the LCD display. We
will connect this pin to the GND. Figure 4: LCD pin-out

As you can see from the figure, the LCD connects to the microcontroller through three control lines: RS,
RW and E, and through eight data lines D0-D7.

With 16-pin LCDs, you can use the L+ and L- pins to turn the backlight (BL) on/off.

Figure 5: LCD pin-out details

3|Page
Figure 6: A typical interfacing between a PIC16F877A and an LCD module

When powered up, the LCD display should show a series of dark squares. These cells are actually in
their off state. When power is applied, the LCD is reset; therefore we should issue a command to set it
on. Moreover, you should issue some commands which configure the LCD. (See the table which lists all
possible configurations below in the code and the explanation to each field)

Sending Commands/Data to the LCD


Using an LCD is a simple procedure once you learn it. Simply put you will place a value on the LCD lines
D0-D7 (this value might be an ASCII value (character to be displayed), or another hexadecimal value
corresponding to a certain command). So how will the LCD differentiate if this value on D0-D7 is
corresponding to data or command?

Observe the figure below, as you might see the only difference is in the RS signal (Register Select), this is
the only way for the LCD controller to know whether it is dealing with a character or a command!

Figure 7: Necessary control signals for Data/Commands

Setting the necessary control signals in software:


For this experiment assume that RS (Register Select) is connected to PORTA1 , R/W (Read/Write) to
PORTA2 (In this lab experiment we are only writing to the LCD, reading from the LCD is left to the
student as home study)and E(Enable) is connected to PORTA3. Moreover, assume that the LCD lines D0-
D7 are directly connected to PORTD.

4|Page
we will introduce two subroutines; one will set the necessary control signals for sending a character
(send_char), the other for sending a command (send_cmd).

send_char send_cmd
1 movwf PORTD 1 movwf PORTD
2 bsf PORTA,1 2 bcf PORTA, 1
3 bsf PORTA, 3 3 bsf PORTA, 3
3 nop 3 nop
3 bcf PORTA, 3 3 bcf PORTA, 3
4 bcf PORTA, 2 4 bcf PORTA,2
call delay call delay
return return
Steps to send character to LCD Steps to send a command to LCD
1.Place the ASCII character on the D0-D7 lines 1.Place the command on the D0-D7 lines
2. Register Select (RS) = 1 to send characters 2. Register Select (RS) = 0 to send commands
3. "Enable" Pulse (Set High – Delay – Set Low) 3. "Enable" Pulse (Set High – Delay – Set Low)
4. Delay to give LCD the time needed to display the 4. Delay to give LCD the time needed to carry out the
character command
Table 1: Sending Characters/Commands Steps

Displaying Characters
All English letters and numbers (as well as
special characters, Japanese and Greek
letters) are built in the LCD module in such a
way that it conforms to the ASCII
standard. In order to display a character,
you only need to send its ASCII code to the
LCD which it uses to display the character.

To display a character on the LCD simply


move the ASCII character to the working
register (for this experiment) then call
send_char subroutine.

Notice that from column 1 to D, the


character resolution is 5 pixels wide x 7
pixels high (5x7) (column 0 is a special
case, it is 5x8, but considered as 5x7, more
on this later) whereas the character
resolution of columns E and F is 5 pixels
wide x 10 pixels high (5x10). We should
change the resolution if we are to use
characters from different resolution
columns, this can be done using a command
discussed later.

Figure 8: LCD Characters Map

5|Page
Figure 9: LCD command control codes

To issue any of these commands to the LCD, all you have to do is place the command value in the
working register, then issue the instruction “Call Send_cmd”

;***************************************************************************************************
; Explaining the commands and their parameters in the LCD command table
;*************************************************************************************************

Clear Display
Moving the value 01 to the working register followed by “call send_cmd” will clear the LCD display,
however the cursor will remain at it last position, so any future character writes will start from the last
location, to reset the cursor position use the Display and Cursor Home command.

Display and Cursor Home


Resets cursor location to position 00 of the LCD screen (Figure 3), future writes will start at the first
location of the first line.

Character Entry Mode


This command has two parameters 1/D and S:
1/D: By default, the cursor is automatically set to move from location 00 to 01 and so on (Increment
mode). Suppose now that you are to write from right to left (as in the Arabic language), then you have to
set the cursor to the Decrement mode.
S: Accompanies the D/C parameter, explained below

Display On/OFF and Cursor


This command has three parameters:
D: Turns on the display (when you see the black dots on the LCD, it means that it is POWERED on, but
not yet ready to operate), this command activates the LCD in order to be ready to use.
U: This displays the cursor (in the form of a horizontal line at the bottom of the character) when it is
high and turns the cursor off when it is low
B: If the underline cursor option is enabled, this will blink the cursor if high.

6|Page
Display/Cursor Shift
All LCDs based on the HD44780 format - whatever their actual physical size is - are internally built in to
be 40 characters x 2 lines with the upper row having the display addresses 0-27H (27H = 39D  0-39 =

40 Characters!!) and the lower row from 40 H -67H. Now suppose you bought an LCD with the physical
size of 20 char. x 2 lines, when you start writing to the LCD and the cursor reaches locations 20D, 21 D,
and 22 D …, you will not see them BUT don’t worry, they are not lost. They were written in their
respective locations but you could not see them because your bought LCD is 20 visible Characters wide
from the outside and 40 from the inside. All you have to do is shift the display. So all you do is
1. Determine the direction of the shift (R/L)
2. Issue the shift Command D/C
R/L: Determines the direction of the shift, this might be useful if you are writing Arabic characters …
D/C: if this bit has a value of 0, the display is not shifted and the cursor moves the same way it was, if
the its value is logic high, the display is shifted once, you might need to issue this command multiple
times in order to shift the display by multiple locations!

Function Set
This command has three parameters:
8/4: Eight/Four bits mode
8 – Bit interface: you send the whole command/character (8 bits) in one stage to the D0-D7 lines
4 – Bit interface: you send the command/character in two stages as nibbles to D4-D7 lines.
When to use the 4-bit mode?
1. Interfacing LCD with older devices which have 4-bit wide I/O Bus
2. You don’t have enough I/O pins remaining, or you want to conserve the I/O pins for other HW
2/1: Line mode, determines whether you want to use the upper line of the LCD or both lines

10/7: Dot format, based on the LCD built-in characters table, note the following:
* 5x7 format (Default) is used whenever you use the characters found in columns 1 to D
* 5x7 format is also used whenever you use the built in characters in CG-RAM (EVEN THOUGH
THE CG-RAM CHARACTERs ARE 5X8!!!)
* 5x10 format is only used when displaying the characters found in columns E and F

******************************************************************************************************* In
LCD initialization, we normally set “Clear Display”, “Display and Cursor Home”, “Display On/OFF”
and “Cursor, and Function Set”, we place the value of the command then use the call send_cmd
instruction.
*******************************************************************************************************

Set Display Address command


Syntax: 1AAAAAAA
This command allows you to move the cursor to whichever location you want, suppose you want to
start writing in the middle of the display (assuming the visible width of the LCD screen is 20), then from
Figure 2 you will observe that location 06 is approximately in the middle so you replace the A’s with 06:
1AAAAAAA 100001100x86
Moreover, suppose you wish to move to the second line which starts at location 40, same as above
1AAAAAAA  11000000  0xC0
After calculating this value, you place it in the working register and then use the call send_cmd
instruction.

7|Page
1 ;*******************************************************************************
2 ; EXAMPLE CODE 1
3 ;*******************************************************************************
4 ; This code displays on the first “upper” row of the LCD the 26 English letters in alphabetical order
5 ; The code starts with LCD initialization commands such as clearing the LCD, setting modes and
6 ; display shifting.
7 ;
8 ; Outputs:
9 ; LCD Control:
10 ; RA1: RS (Register Select)
11 ; RA3: E (LCD Enable)
12 ; LCD Data:
13 ; PORTD 0-7 to LCD DATA 0-7 for sending commands/characters
14 ; Notes:
15 ; The RW pin (Read/Write) - of the LCD - is connected to RA2
16 ; The BL pin (Back Light) – of the LCD – is connected to potentiometer
17 ;*******************************************************************************
18 include "p16f877A.inc"
19 ;*******************************************************************************
20 cblock 0x20
21 tempChar ;holds the character to be displayed
22 charCount ;holds the number of the English alphabet
23 lsd ;lsd and msd are used in delay loop calculation
24 msd
25 endc
26 ;*******************************************************************************
27 ; Start of executable code
28 org 0x000
29 goto Initial
30 ;*******************************************************************************
31 ; Interrupt vector
32 INT_SVC org 0x0004
33 goto INT_SVC
34 ;*******************************************************************************
35 ; Initial Routine
36 ; INPUT: NONE
37 ; OUTPUT: NONE
38 ; RESULT: Configure I/O ports (PORTD and PORTA as output, PORTA as digital)
39 ; Configure LCD to work in 8-bit mode, with two lines of display and 5x7 dot format.
40 ; Set the cursor to the home location (location 00), set the cursor to the visible state
41 ; with no blinking
42 ;*******************************************************************************
43 Initial
44 Banksel TRISA ;PORTD and PORTA as outputs
45 Clrf TRISA
46 Clrf TRISD
47 Banksel ADCON1 ;PORTA as digital output
48 Movlw 07
49 mowf ADCON1
50 Banksel PORTA
51 Clrf PORTA
52 Clrf PORTD
53 movlw d'26'
54 Movwf charCount ; initialize charCount with 26 Number of Characters in the English language
8|Page
55 Movlw 0x38 ;8-bit mode, 2-line display, 5x7 dot format
56 Call send_cmd
57 Movlw 0x0e ;Display on, Cursor Underline on, Blink off
58 Call send_cmd
59 Movlw 0x02 ;Display and cursor home
60 Call send_cmd
61 Movlw 0x01 ;clear display
62 Call send_cmd
63 ;*******************************************************************************
64 ; Main Routine
65 ;********************************************************************************
66 Main
67 Movlw 'A'
68 Movwf tempChar
69 CharacterDisplay ; Generate and display all 26 English Letters
70 Call send_char
71 Movf tempChar ,w ; ‘A’ has the ASCII code of 65 decimal (0x41), by
72 Addlw 1 ; adding 1 to it we have 66, which is B. Therefore, by
73 movwf tempChar ; continuously adding 1 to tempChar we are cycling
74 movf tempChar ,w ; through the ASCII table (here: alphabets)
75 decfsz charCount
76 goto CharacterDisplay
77 Mainloop
78 Movlw 0x1c ;This command shifts the display to the right once
79 Call send_cmd
80 Call delay
81 Goto Mainloop ; This loop makes the character rotate continuously
82 ;**********************************************************************************
83 send_cmd
84 movwf PORTD ; Refer to table 1 on Page 5 for review of this subroutine
85 bcf PORTA, 1
86 bsf PORTA, 3
87 nop
88 bcf PORTA, 3
89 bcf PORTA, 2
90 call delay
91 return
92 ;**********************************************************************************
93 send_char
94 movwf PORTD ; Refer to table 1 on Page 5 for review of this subroutine
95 bsf PORTA, 1
96 bsf PORTA, 3
97 nop
98 bcf PORTA, 3
99 bcf PORTA, 2
100 call delay
101 return
102 ;**********************************************************************************
103 delay
104 movlw 0x80
105 movwf msd
106 clrf lsd
107 loop2
107 decfsz lsd,f
9|Page
109 goto loop2
110 decfsz msd,f
111 endLcd
112 goto loop2
113 return
114 ;**********************************************************************************
115 End
116

Set CG-RAM Address command


Syntax: 01AAAAAA

If you give a closer look at Figure 8, you will clearly see that the table only contains English and
Japanese characters, numbers, symbols as well as special characters! Suppose now that you would like
to display a character not found in the built-in table of the LCD (i.e. an Arabic Character). In this case we
will have to use what is called the CG-RAM (Character Generation RAM), which is a reserved memory
space in which you could draw your own characters and later display them.

Observe column one in Figure 8, the locations inside this column are reserved for the CG-RAM. Even
though you see 16 locations (0 to F), you only have the possibility to use the first 8 locations 0 to 7
because locations 8 to F are mirrors of locations 0 – 7.

So, to organize things, in order to use our own characters we have to do the following:
1. Draw and store our own defined characters in CG-RAM
2. Display the characters on the LCD screen as if it were any of the other characters in the table

Drawing and storing our own defined characters in CG-RAM


As stated earlier, we have eight locations to store our characters in. So how do we choose which
location out of these to start drawing and building our characters in?
The answer is quite simple; follow this rule as stated in the datasheet of the HD44780 controller
1. To write (build/store a character in location 00 (crossing of the row and column)), you send the
CG-RAM address command as follows: 01AAAAAA  01000000  0x40
2. However, to write in any location from 01 to 07, you have to skip eight locations (WHY?)
So the CG-RAM address command will send 0x48 (to store a character in location 1), 0x50 (to
store a character in location 2) and so on...

So up to this point we have defined where to write our characters but not how to build them!
This is the fun part, draw a 5x8 Grid and start drawing your character inside, then replace
each shaded cell with one and not shaded ones with zero. Append three zeros to the left (B5-B7)
and finally transform the sequence into hexadecimal format. This is the sequence which you will
fill in the CG-RAM SEQUENTIALLY once you have set the CG-RAM Address before.

10 | P a g e
B4 B3 B2 B1 B0 B7 B6 B5 B4 B3 B2 B1 B0
0 0 0 0 1 1 1 0 0x0E
0 0 0 1 0 0 0 1 0x11
0 0 0 0 1 1 1 0 0x0E
 0 0 0 0 0 1 0 0  0x04
0 0 0 1 1 1 1 1 0x1F
0 0 0 0 0 1 0 0 0x04
0 0 0 0 1 0 1 0 0x0A
0 0 0 1 0 0 0 1 0x11

1 Example
2 DrawStick1 Setting the CGRAM address at which we draw the stick man
3 Movlw 0x40 ;Here it is address 0x00 in Figure 8 which transforms into
4 Call send_cmd ; command 0x40
5 Movlw 0X0E Sending data that implements the Stick man
6 Call send_char ; Notice the address where to store the character in CG-RAM
7 Movlw 0X11 ;is a command thus use send_cmd, whereas the
8 Call send_char ;data bits of the stickman are sent as Data
9 Movlw 0X0E ;using send_char
10 Call send_char
11 Movlw 0X04
12
Call send_char
13
Movlw 0X1F
14
Call send_char
15
16 Movlw 0X04
17 Call send_char
18 Movlw 0X0A
19 Call send_char
20 Movlw 0X11
21 Call send_char
22 Return

Displaying the user generated (drawn) characters on the LCD screen

Simply,if we stored our character in location 0, we move 0 to the working register then issue the “call
send_char” command, if we stored it in location 2, move 2 to the working register and so on ….

11 | P a g e
1 ;**********************************************************************************
2 ; EXAMPLE CODE 2
3 ;**********************************************************************************
4 ; This code stores two shapes of a stickman, one in location 00 (of Figure 8), and another at location
5 ; 01. The first stickman is written on the leftmost location of the upper line, the second stick man
6 ; shape is also written above the first one, then the first stick man is rewritten on the same location
7 ; that is display: first stickman shape  second stickman shape  first stickman shape and so on ..
8 ; thus the stickman will appear as if it is moving ! 
9 ;
10 ; Outputs:
11 ; LCD Control:
12 ; RA1: RS (Register Select)
13 ; RA3: E (LCD Enable)
14 ; LCD Data:
15 ; PORTD 0-7 to LCD DATA 0-7 for sending commands/characters
16 ; Notes:
17 ; The RW pin (Read/Write) - of the LCD - is connected to RA2
18 ; The BL pin (Back Light) – of the LCD – is connected potentiometer
19 ;**********************************************************************************
20 include "p16f877A.inc"
21 ;**********************************************************************************
22 cblock 0x20
23 lsd ;lsd and msd are used in delay loop calculation
24 msd
25 endc
26 ;**********************************************************************************
27 ; Start of executable code
28 org 0x000
29 goto Initial
30 ;**********************************************************************************
31 ; Interrupt vector
32 INT_SVC org 0x0004
33 goto INT_SVC
34 ;**********************************************************************************
35 ; Initial Routine
36 ; INPUT: NONE
37 ; OUTPUT: NONE
38 ; RESULT: Configure I/O ports (PORTD and PORTA as output, PORTA as digital)
39 ; Configure LCD to work in 8-bit mode, with two lines of display and 5x7 dot format.
40 ; Set the cursor to the home location (location 00), set the cursor to the visible state
41 ; with no blinking
42 ;**********************************************************************************
43 Initial
44 Banksel TRISA ;PORTA and PORTD as outputs
45 Clrf TRISA
46 Clrf TRISD
47 Banksel ADCON1 ;PORTA as digital output
48 movlw 07
49 mowf ADCON1
50 Banksel PORTA
51 Clrf PORTA
52 Clrf PORTD
53 Movlw 0x38 ;8-bit mode, 2-line display, 5x7 dot format
54 Call send_cmd
12 | P a g e
55 Movlw 0x0e ;Display on, Cursor Underline on, Blink off
56 Call send_cmd
57 Movlw 0x02 ;Display and cursor home
58 Call send_cmd
59 Movlw 0x01 ;clear display
60 Call send_cmd
61 Call DrawStick1 ;The subroutines draw and store the Stick man inside the
62 Call DrawStick2 ;CG-RAM. This DOES NOT mean that the character is
63 ;displayed on the LCD, it was only stored inside the CG-RAM
64 ;of the LCD.
65 Movlw 0x01 ;the datasheet says you have to clear display command after
66 Call send_cmd ;storing the characters or the code will not work
67
68 ;**********************************************************************************
69 ; Main Routine
70 ;**********************************************************************************
71 Main
72 Movlw 0x00 ;Display character stored in location 00 (Figure 8), which in
73 Call send_char ;this case is our first stickman in CG-RAM
74 Movlw 0x02 ;Cursor Home Command
75 Call send_cmd
76 Movlw 0x01 ;Display character stored in location 00 (Figure 8), which in
77 Call send_char ;this case is our first stickman in CG-RAM
78 Movlw 0x02 ;Cursor Home Command
79 Call send_cmd
80 Goto Main ; This loop makes the character rotate continuously
81 ;**********************************************************************************
82 send_cmd
83 movwf PORTD ; Refer to table 1 on Page 5 for review of this subroutine
84 bcf PORTA, 1
85 bsf PORTA, 3
86 nop
87 bcf PORTA, 3
88 bcf PORTA, 2
89 call delay
90 return
91 ;**********************************************************************************
92 send_char
93 movwf PORTD ; Refer to table 1 on Page 5 for review of this subroutine
94 bsf PORTA, 1
95 bsf PORTA, 3
96 nop
97 bcf PORTA, 3
98 bcf PORTA, 2
99 call delay
100 return
101 ;**********************************************************************************
102 delay
103 movlw 0x80
104 movwf msd
105 clrf lsd
106 loop2
107 decfsz lsd,f
108 goto loop2
13 | P a g e
109 decfsz msd,f
110 endLcd
111 goto loop2
112 return
113 ;**********************************************************************************
114 DrawStick1 Setting the CGRAM address at which we draw the stick man
115 Movlw 0x40 ; Here it is address 0x00 in Figure 8 which transforms
116 Call send_cmd ; into command 0x40
117 Movlw 0X0E ;Sending data that implements the Stick man
118 Call send_char
119 Movlw 0X11
120 Call send_char
121 Movlw 0X0E
122 Call send_char
123 Movlw 0X04
124 Call send_char
125 Movlw 0X1F
126 Call send_char
127 Movlw 0X04
128 Call send_char
129 Movlw 0X0A
130 Call send_char
131 Movlw 0X11
132 Call send_char
133 Return
134 ;**********************************************************************************
135 DrawStick2 ;Setting the CGRAM address at which we draw the stick man
136 Movlw 0x48 ;Here it is address 0x01 in Figure 8 which transforms
137 Call send_cmd ; into command 0x48
138 Movlw 0X0E ;Sending data that implements the Stick man
139 Call send_char
140 Movlw 0X0A
141 Call send_char
142 Movlw 0X04
143 Call send_char
144 Movlw 0X15
145 Call send_char
145 Movlw 0X0E
146 Call send_char
147 Movlw 0X04
148 Call send_char
149 Movlw 0X0A
150 Call send_char
151 Movlw 0X0A
152 Call send_char
153 Return
154 ;**********************************************************************************
155 End

14 | P a g e
University of Jordan
Faculty of Engineering and Technology
Department of Computer Engineering
Embedded Systems Laboratory 0907334

6 Experiment 6: Using HI-TECH C


Compiler in MPLAB

Objectives
The main objectives of this experiment are to familiarize you with:
 Writing PIC programs in C
 Setting up MPLAB IDE projects to use the HI-TECH C compiler
 Becoming familiar with HI-TECH C primitives, built-in function in use with 10/12/16 MCU Family

Prepared by Eng. Enas Ja’ra

1
INTRODUCTION
So far in this lab course, PIC assembly programming has been introduced, however, in practice, most of the
industrial and control codes are written in High Level Languages (abbreviated as HLL) the most common of
which is the C programming language. The use of high level languages is preferred due to their simplicity
which allows for faster program development (especially for large and very complex programs), easier
debugging, and for easier future code maintainability, this will provide developers with shorter time to
market advantages in a world where competition is at its prime to introduce new commercial products. On
the other hand, HLLs assembled codes are often longer (due to inefficient compilers, aggressive and
advanced optimizing compilers are often used to yield better results). Longer codes are at a disadvantage
since memory space is limited in microcontrollers not to mention that longer codes take more time to
execute. Expert assembly programmers can rewrite certain pieces of code in a very optimized and short
fashion such that they execute faster, this is very important especially when real time applications are
concerned. This direct use of assembly language requires that the programmer knows the problem in hand
very well and that one is experienced in both software and target microcontroller hardwrae limitations.
Often, programmers combine in between the use of C and Assembly language in the same developed source
code.

There are many C compilers available commercially, such as mikroC, CCS and HI-TECH among others. This
experiment introduces the “free” Lite version C compiler from HI-TECH software bundled with MPLAB, in
contrast to the Pro versions of compilers commercially available from HI-TECH and others, the compiler
and assembler don’t use aggressive techniques and the resultant assembly codes are larger in size.

THIS PART ASSUMES YOU HAVE ALREADY SAVED A FILE WITH A C EXTENSION AND YOU HAVE ALREADY
INSTALLED THE HI-TECH C PRO FOR THE PIC10/12/16 MCU FAMILY COMPILER

Create a project in MPLAB in the same


steps as was shown in Exp 0, the only
difference is in the step of selecting a
language toolsuite; “ Active Toolsuite”
dialog box:

In this step where you get to specify the


toolsuite associated with the project,
you are not associating the project with
the MPASM compiler as previously done,
but instead we will be using the HI-
TECH C compilers for Microchip devices

In the Active Toolsuite drop down menu,


select HI-TECH Universal Toolsuite 
Click next.

The next steps will proceed as usual:

2
Browse to the directory where
you saved your C file. Give your
project a name  Save  Next. If
you navigated correctly to your
file destination you should see it
in the left pane otherwise choose
back and browse to the correct
path. When done Click add your
file to the project (here:
FirstCFile.c). Make sure that the
letter C is beside your file and not
any other letter  Click next
Click Finish.

As before, you should see your C file under Source file list, now you are ready to begin.
Double click on the FirstCFile.C file in the project file tree to open. This is where you will write your
programs, debug and simulate them.

CORRECT WRONG

3
The proceeding parts assume that you have basic knowledge of programming in C. We will present the C
language in general context then we’ll introduce it within the contest of use in PIC programming. The
following discussion attempts to write and simulate a simple C program in MPLAB and check the results

In MPLAB, inside your newly created project from above, write the following:

#include <htc.h>
void main(void) // every C program you write needs a function called main.
{

Notice that comments are indicated with // instead of ‘;’

After writing the above EMPTY program we should build the code to ensure that MPLAB IDE and HI-TECH
C are properly installed. Select Build from the Project menu, or choose any of MPLAB IDE’s shortcuts to
build the project — you can, for instance, click on the toolbar button that shows the HI-TECH “ball and
stick” logo, as shown in the figure below. You will notice that the Project menu has two items: Build and
Rebuild.

An output window
should show with
BUILD SUCCEDDED

The compiler has


produced memory
summary and there
is no message
indicating that the
build failed, so we
have successfully
compiled the project.
If there are errors
they will be printed
in Build tab of this
window. You can
double-click each
error message and MPLAB
IDE will show you the
offending line of code,
where possible. If you do get
errors, check that the
program is as it is written in
this document. BUILD SUCCEED DOES NOT MEAN THAT YOUR PROGRAM IS CORRECT, IT SIMPLY
MEANS THAT THERE ARE NO SYNTAX ERRORS FOUND, SO WATCH OUT FOR ANY LOGICAL
ERRORS YOU MIGHT MAKE.

4
Quick Review of the Basic Rules for Programming in C
1. Comments for only ONE line of code start with 2 slashes: //
// This is a one line comment
Remember to always document your code through the use of functional comments!
2. Comments for more than one line start with /* en end with */
/*
This is a comment.
This is another comment.
*/
3. At the end of each line with some instruction, a semi-colon (;) has to be placed.
a=a+3;
4. Parts of the program that belong together (functions, statements, etc.), are between { and }.
void main(void) //Function
{
//Add code
}

The Basic Structure of a C Program


The ordered structure of a program in C is as follows:
 Libraries
 Global Variables
 Function Prototypes
 Main Function
 Functions

 Adding libraries (the initial few lines of any C program)


Syntax: #include <filename.h>
Libraries such as “htc.h”, “math.h” and “stdlib.h” include many references to built-in variables and
functions to be used in programs, if the header files are not included, the built-in functions and
variables if used will not be defined which will result in build errors.
The htc.h file will be included in all our C programs which use the HI-TECH compiler, other
compilers have different header files, refer to their documentation when needed.
 Declaring “global” variables.
Define and declare the variables to be used throughout the program, this is in contrast to “local”
variables discussed later on.
 Defining prototypes of the functions.
A C program has a main function and possibly other functions as well which might be written below
the main function. If we are to call any of the other functions from inside the main subroutine, the
build will fail and indicate that the function is undefined. This is because the code is compiled line
by line and at the moment the compiler attempts to compile “call function”, it still has not known of
the existence of this function because it is declared later in the code “after main”. One solution is to
place all the functions before the main function. Another preferred method is the use of function
prototype. A prototype of a function ensures that the function can be called anywhere in the
program. It is simply copying only the header of the function, placing it before the main
subroutine and ending it with a semicolon ‘;’

5
 Main function.
This is the function that will be called first when starting your microcontroller. From there, other
functions are called. Every C program must have a main function.
 Functions.
Functions are a grouping of instructions which perform a certain task. They are the unit of
modularity and are very useful to make it easy to repeat tasks. They have input and output
variables.

Syntax: type identifier function name (type identifier identifier1, type identifier identifier2 ….)
{
//The body of the function
return identifier //only when return type is not void
}

Type identifier: could be int, long, short, char, void ….. etc
The output variable type precedes the function’s name, input variables follow the function name
and are placed in between brackets, a function can take as many input variables as needed but it
only returns one output variable.
testFunction1 has two input testFunction2 has one input testFunction3 takes no
parametres of type integer (x,y) parameter of type integer (x), it input or outputs.
but has no output, all processing returns an output which is the
is local inside the function and it square of the input number.
returns no values Notice, that value returning
functions end with a return
statement, omitting of which will
result in an error
void testFunction1(int x, int y) int testFunction2 (int x) void testFunction3 (void)
{ { {
int k; return x*x; //some code
k = x; } }
y = 2 + x;
}
How to call function: Examples
testFunction1(75,99) A = testFunction2(5) testFunction()

Since this type of functions Note that the brackets are


returns a value, the value need left empty when no
be stored in a previously defined arguments are passed
variable. The variable must be
defined as the same return
output type of the function, if the
function returns an integer, A
must be defined as integer, if the
function returns a character, A
need be defined as character …

6
Example Program 1: Typical Program Layout
// ExampleProgram1.c
#include <htc.h> //Always include this library when using HI-TECH C compiler
//Declaring global variables
int a, b, c;
char temp;
//Defining prototypes
int calc (int p);
//Main function
void main(void)
{
a=calc(3); //write main body code
}
//Functions
int calc (int p)
{
p=p+1; //write function body code
return p;
}
More on Variables
Variables can be classified into two main types depending on their scope:

Global Variables
These variables can be accessed (i.e. known) by any function comprising the program. They are
implemented by associating memory locations with variable names. They do not get recreated if the
function is recalled. In Example Program 1, (a, b, c, and temp) are GLOBAL VARIABLES
Local Variables
These variables only exist inside the specific function that creates them. They are unknown to other
functions and to the main program. As such, they are normally implemented using a stack. Local
variables cease to exist once the function that created them is completed. They are recreated each time
a function is executed or called. In Example Program 1, (p) is a LOCAL VARIABLE

Variable Types
The following table lists all possible variable types in C, the size they take up in memory and the
range of each.

Type Memory usage Possible values


bit 1 bit 0, 1
char 8 bits -128…127
unsigned char 8 bits 0…255
signed char 8 bits -128…127
int 16 bits -32k7…32k7
unsigned int 16 bits 0…65k5
signed int 16 bits -32k7…32k7
long int 32 bits -2G1…2G1
unsigned long int 32 bits 0…4G3
signed long int 32 bits -2G1…2G1
float 32 bits ± 10^(±38)
double 32 bits ± 10^(±38)

7
Default Input Is Decimal

Example Program 2:

#include <htc.h>
char Ch;
unsigned int X;
signed int Y;
int Z, a, b, c; // Same as "signed int"
unsigned char Ch1;
bit S, T;

void main (void)


{
Ch = 'a';
X = -5;
Y = 0x25;
Z =-5;
Ch1='b';
T = 0;
S = 81; //S=1 When assigning a larger integral type to a bit variable,
//only the Least Significant bit is used.
a = 15;
b = 0b00001111;
c = 0x0F;
// a, b, c will all have the same value which is 15

C Operators

 Relational and bit operators

> Greater than ~ Bitwise NOT


>= Greater than or similar to & Bitwise AND
< Less than | Bitwise OR
<= Less than or similar to ^ Bitwise XOR
== Equal to << Shift to left
!= Not equal to >> Shift to right

 Arithmetic operators

x--; This is the same as x = x – 1; + Addition


x++; This is the same as x = x + 1; - Subtraction
* Multiplication
/ Division
% Modulus ( remainder after division)

8
Operators Precedence Chart
Operator precedence describes the order in which C reads expressions. For example, the expression
a=4+b*2 contains two operations, an addition and a multiplication. Does the HI TECH compiler evaluate
4+b first, then multiply the result by 2, or does it evaluate b*2 first, then add 4 to the result? The operator
precedence chart contains the answers. Operators higher in the chart have a higher precedence, meaning
that the HI TECH compiler evaluates them first. Operators on the same line in the chart have the same
precedence, and the "Associativity" column on the right gives their evaluation order.

Operator Precedence Chart

Operator Type Operator Associativity


Primary Expression Operators () left-to-right
*/%
Binary Operators
+-
>> <<
< > <= >=
left-to-right
== !=
&
^
|

Example Program 3: Fibonacci series: 0, 1, 1, 2, 3, 5

#include <htc.h> // Library


unsigned int Fib (unsigned int Num1, unsigned int Num2); // Prototype
unsigned int F1, F2, F3, F4, F5, F6; // Global Variables

void main (void) // Main function


{
F1 = 0;
F2 = 1;
F3 = Fib (F1, F2);
F4 = Fib (F2, F3);
F5 = Fib (F3, F4);
F6 = Fib (F4, F5);
}
unsigned int Fib (unsigned int Num1, unsigned int Num2) //Function
{
return Num1 + Num2;
}

9
Preparing for Simulation

1. Start a new MPLAB session, add the file ExampleProgram3.c to your project
2. Build the project
3. Select Debugger  Select Tool MPLAB SIM
4. Go to View Menu  Watch (From the drop out menu choose the variables watch F1 through F6 we
want to inspect during simulation and click ADD Symbol for each one)

From the Debugger Menu  choose Select Tool  then MPLAB SIM
After the following buttons appears in the toolbar:

5. Press the “Step into” button one at a time and check the Watch window each time an
instruction executes; keep pressing “Step into” until you all the six terms of the series are
generated.
6. Reset the simulation, do step 5 above but this time use “Step Over”, note the difference
7. Reset the simulation, do step 5 above, this time place a break point at the last instruction in
main, press run. Inspect the variables in watch window.

Notes about simulating a code written in C in MPLAB


Stepping into codes written in C is not as direct as one would imagine, different compilers translate the
C code into assembly differently, a single line of code might be translated into multiple assembly lines,
for example a simple assignment statement “X = 5” where X has been defined as integer will be
translated into four assembly instructions.
Movlw 05
Movwf 0x70 //GPR address 0x70 chosen by compiler
Movlw 00
Movwf 0x71
Since X is an integer which reserves 2 bytes in memory (16 bits as specified in the table in page 7), it
need be saved as 0x0005, so two instructions are needed to load the first byte into location 0x70 and
another two to move the rest of the number into location 0x71.

If a simple one statement instruction was assembled like this, imagine how would complex statements
be translated like for loops and if statements. Moreover, some compilers are more efficient than others,
which give you optimized shorter assembly codes which might not be easy to understand.

Moreover, function placement spans through multiple pages in program memory, the code might not be
placed in consecutive order into memory by the compiler; further overhead instructions to switch
between pages are common.

In addition, the use of built-in library functions will further complicate stepping through assembly codes
line by line as these functions are often provided as a black box for the developer to use with no interest
in their details.

For this, it might be difficult for the inexperienced to understand the assembly code generated by
compilers, and stepping into assembly code one instruction at a time might be a headache. It is often
advised to place breakpoints at points of interest and run the program till it halts at the required
breakpoints and analyze the outputs in the watch window.

10
Control and Repetition Statements
 IF...ELSE statements

if (expression1)
{ Example Code 5:
statement 1;
. if (a==0) //If a is equal to 0
. {
statement n; b++; // increase b and c by 1
} c++;
else }
{ else
statement 1; {
. b--; //decrease b and c by 1
. c--;
statement n; }
}

 WHILE loop

while (expression) Example Code 6:


{
statement 1; while (a>=1 ) && (a <=10) //As long as 1<=a <= 10
statement 2; {
. b = b + 3;
. c = a%b;
statement n; }
}

 FOR loop

for (expr1; expr2; expr3) Example Code 7:


{
statement 1; for (i = 0 ; i < 100 ; i++) //loop 100 times
statement 2; {
. B = B + i + A%i;
. }
statement n;
}

11
C for PIC
The preceding discussion introduced the C language in a broad concept. Now, we will draw an example of
how to use C with the PIC microcontroller. Actually, it is fairly simple where besides user defined variables,
the PIC registers are also used in the context of programs.

The microcontroller is completely controlled by registers. All registers used in MPLAB HI-TECH have
exact the same name as the name stated in the datasheet. Registers can be set in different ways,
following are few examples:

TRISB = 0b00000000; //TRISB is output


PORTC = 255; //All pins of PORTC are made high
PORTD = 0xFF; //All pins of PORTD are made high
PORTB = 170; //Pin B7 on, B6 off, B5 on, B4 off, etc.
TRISB = 0b11110010; //Pin RB7, RB6, RB5, RB4 and RB1 are input, other bits are outputs.
OPTION=0xD4 //PSA assigned to TMR0, Prescalar = 32, TMR0 clock source is the internal instruction cycle
//clock, External interrupt is on the rising “refer to datasheet”
.
To set or reset one single bit in a register (one of the 8 bits), the pin name is used and, the names of the
bits are also as specified and used in the datasheet.
Some examples:

RB0 = 1 //Pin B0 on
RB7 = 0 //Pin B7 off

Example Program 8: Periodically switch a LED connected to RD0 on and off


#include <htc.h>
// if the whole function is placed before the main function, there is no need for a prototype
void Wait()
{
unsigned char i;
for(i=0; i<100; i++)
_delay(60000); //built in function .. more info next page
}

void main()
{
//Initialize PORTD -> RD0 as Output
TRISD=0b11111110;

//Now loop forever blinking the LED.


while(1)
{
RD0 = 1; //LED on
Wait();

RD0 = 0; //LED off


Wait();
}
}

12
To simulate the above example code, you can either select PORTD from the ADD SFR drop down menu
or choose _PORTDbits from the ADD SYMBOL drop list, click on the + sign to expand and see the
individual bits.

Place your break points on both Wait() instructions and run the code.

BUILT IN LIBRARY FUNCTIONS


The C standard libraries contain a standard collection of functions, such as string, math and
input/output routines. The declaration or definition for a function is found in the htc.h and other
libraries files which are to be included whenever necessary. Some of these functions are listed below,
the syntax of each and a brief description follows.

Delay functions

_DELAY __DELAY_MS, __DELAY_US

Synopsis Synopsis
#include <htc.h> __delay_ms(x) // request a delay in milliseconds
void _delay(unsigned long cycles); __delay_us(x) // request a delay in microseconds

Description Description
This is an inline function that is expanded by the As it is often more convenient request a delay in
code generator. The sequence will consist of code time-based terms rather than in cycle counts, the
that delays for the number of cycles that is macros __delay_ms(x) and __delay_us(x) are
specified as argument. The argument must be a provided. These macros simply wrap around
literal constant. _delay(n) and convert the time based request into
An error will result if the delay period requested instruction cycles based on the system frequency.
is too large. For very large delays, call this These macros require the prior definition of
function multiple times. preprocessor symbol _XTAL_FREQ. This symbol
should be defined as the oscillator frequency (in
Hertz) used by the system.

//Example //Example

#include <htc.h> #include <htc.h>


int A; int A;
#define _XTAL_FREQ 4000000

void main (void) void main (void)


{ {
A = A | 0x7f; A = A | 0x7f;
_delay(10); // delay for 10 cycles __delay_ms(10); // delay for 10 ms
A = A & 0x85; A = A & 0x85;
} }

13
Arithmetic functions
In addition to the htc.c library, other libraries such as Standard Library <stdlib.h> or C Math
Library <math.h> need be included in the project for making use of many useful built-in functions.
Make sure you include the appropriate header files for each library before making use of its
functions or else build errors will be present.

ABS, POW, LOG, LOG10, RAND, MOD, DIV, CEIL, FLOOR, NOP, ROUND, SQRT are required.. refer to the
datasheet for the documentation of the others

ABS POW

Synopsis Synopsis
#include <stdlib.h> #include <math.h>
int abs (int j) double pow (double f, double p)

Description Description
The abs() function returns the absolute value of The pow() function raises its first argument, f, to
the passed argument j. the power p.

LOG, LOG10 RAND

Synopsis Synopsis
#include <math.h> #include <stdlib.h>
double log (double f) int rand (void)
double log10 (double f)
Description
Description The rand() function is a pseudo-random number
The log() function returns the natural logarithm generator. It returns an integer in the range 0 to
of f. The function log10() returns the 32767, which changes in a pseudo-random
logarithm to base 10 of f. fashion on each call.

14
Trigonometric functions
SIN, COS, TAN, COS, ASIN, ATAN …… refer to the data sheet for the others

 SIN  COS

Synopsis Synopsis
#include <math.h> #include <math.h>
double sin (double f) double cos (double f)

Description Description
This function returns the sine function of its This function yields the cosine of its argument,
argument.it is very important to realize that C which is an angle in radians. The cosine is
uses radians, not degrees to perform these calculated by expansion of a polynomial series
calculations! If the angle is in degrees you must approximation.
first convert it to radians.
// Example:
#include <htc.h>
#include <math.h>
#include <stdio.h>
#define C 3.141592/180.0
double X,Y;
void main (void)
{
double i;
X=0;
Y=0;
for(i = 0 ; i <= 180.0 ; i += 10)
{X= sin(i*C);
Y= cos(i*C);
}
}

 define directive

You can use the #define directive to give a meaningful name to a constant in your program.

#define identifier constant

Example:
#define COUNT 1000

15
University of Jordan
Faculty of Engineering and Technology
Department of Computer Engineering
Embedded Systems Laboratory 0907334

7 Experiment 7:Timers

Objectives
 To become familiar with hardware timing modules provided by the PIC 16F877A 
 To become familiar with the concept of 7 segment multiplexing 

Written by Eng. Ashraf Suyyagh and Eng. Enas Jaara |


Pre-lab
You are required to review the following in order to be fully prepared for the experiment, refer back to both
your text book and the Microchip PIC datasheets whenever you find it necessary.
  The operation of the Timer0 Module and the related OPTION_REG settings 
  The Operation of Timer2 Module and its associated PR2 and T2CON registers 
  The External interrupt on RB0. 
 Context saving and retrieval while using interrupts. 

DO NOT COME TO THE LAB UNPREPARED

The Idea behind the Code

The code is simply a 2-digit stopwatch (Max. 60 seconds) which has its output in decimal format shown on
2 Seven segments displays, it simply does the following:
1. Initially, when the system is display 00 on the 2 seven segments displays.
2. The stopwatch remains in this condition until a (Start/Stop) button is pressed, after which you will
observe the following count:

00, 01, 02,03,04,05... 58, 59, 00, 01…

3. The stopwatch will count this way indefinitely until the (Start/Stop) button is pressed again
where the count display will remain as is (Hold). When the (Start/Stop) button is pressed
another time it will continue counting from its last count.

Counting Example:
00, 00 (Start/Stop), 01, 02, 03, 04, 05, (Start/Stop), 05, 05, 05, (Start/Stop), 06, 07, 08 ……

2
How did we write this code?

 In this experiment we will use PIC16877A microcontroller and an oscillator with a value of 4
 MHz 
 We made the decision to use TMR0 to count time (1 second), and to use the external interrupt
 RB0 as the Start_Stop button. 
 We have also defined a register: Start_Stop , which if it has the value 0x00, then the stopwatch
 will stop, if it has the value 0xFF then the stopwatch will count. 
 Now, the first problem, if Fosc is 4 MHz, then the instruction cycle is 1µs, at this speed the
maximum count of TMR0 at maximum pre-scalar settings is 256 x 256 x 1µs = 65.536 ms which
is far below the one second time needed (1,000,000 µs). 

So what do we do now? 

Since we need to count 1,000,000 µs, use your mind, calculator, sheet of paper, pencil
and luck to find three numbers X, Y, Z (all under 255, maximum register width)
whereXxYxZ=

1,000,000 µs and with the condition that one of the numbers should satisfy 2N (one of
the values of TMR0 pre-scalar) 

We have found that 250 x 32 x 125 = SEC_CALC No. of TMR0 Total Time
1,000,000 (notice that 32 = 25 ) so we do
Interrupts elapsed
the following: 0 0 0 ms
1 1 8 ms
2 2 16 ms
 Let 32 be the pre-scalar
3 3 24 ms
 250 be TMR0 count. (that is TMR0
4 4 32 ms
will be initialized to 256 – 250 = 6) 5 5 40 ms
---------------------------------------------
So each interrupt, TMR0 will count 124 124 992 ms
32 x 250 = 8000 µs = 8 ms. 125 125 1000 ms (1 second)
0 Cleared in order to count the next
 Each interrupt, a register which we second correctly
defined: SEC_CALC will be 1 126 0 ms
incremented, nd it will be checked 2 127 8 ms
for the value 125 to know whether we reached 1 second or not.

Notice in the flow chart below that in order for the clock digits to update two conditions should
be satisfied:
1. One second has elapsed (SEC_CALC = 125)
2. The clock should be in the counting mode (START_STOP = 0xFF)

If either condition fails, the clock will not count but hold its previous count on the display
unchanged

3
Start

Initialize I/O ports Initialization

Enable External and


TMR0 Interrupts Infinite Loop

Configure TMR0
Settings
Interrupt
Clear Variables

Context Saving

N N
Clear SEC_CALC Is External Is TMR0
Interrupt? Interrupt?
Y
Is Low Digit Y
=9 Complement Reinitialize TMR0
N Start_Stop Flag
Increment SEC_CALC
Y
Y Y
Is START_STOP = Is SEC_CALC =
Clear Low Digit 0xFF? 125?

Increment High N N
In other words,
Digit
has one second
Increment Low Digit
elapsed?
Is High
N
Digit = 6
Y
Clear High Digit
Display Clock

Context retrieval
and exit ISR

4
Do not forget to set the speed to 4MHz for this code in MPLAB

How to simulate this code in MPLAB?


You have learnt so far that in order to simulate inputs to the PIC, you usually entered them through the
Watch window. However, this is only valid and true when you are dealing with internal memory
registers. In order to simulate external inputs to the PIC pins, we are to use what is called a Stimulus.

There are multiple actions which you can


apply to an input pin, choose whatever
you see as appropriate to simulate your
program. Here we have chosen to
simulate the button press as a pulse.
1. Add Low_Digit, High_Digit
and Start_Stop to the
watch window.
2. Place a break point at line
79 (Instruction return).
This will allow us to see
the change to Start_Stop, if
0xFF the stopwatch
counts, else it stops.
3. Place another breakpoint
at line 105 (Instruction
return), this will allow us
to observe how Low_Digit
and High_Digit change
4. Run your code, you will
observe nothing except
that the values in the watch window are all zeros.

5. Now Press “Fire”, the arrow next to the RB0 in the Stimulus pin, what do you observe?
6. Now, press “run” again, observe how the values of Low_Digit and High_Digit change
whenever you reach the breakpoint.
7. Press “fire” again, how do the values in Low_digit and High_Digit change now?

5
Example Code

1 ;**********************************************************************************
2 ; Connections:
3 ; Input:
4 ; Pushbutton : RB0
5 ; Output:
6 ; 7-segment A-G: PortD 0-6
7 ; hardware requests : S6 set ON, first and second set ON,S1 ON ,S12 and S13 OFF
8 __CONFIG_DEBUG_OFF&_CP_OFF&_WRT_HALF&_CPD_OFF&_LVP_OFF&_BODEN_OFF&_PWRTE_O
9 FF&_WDT_OFF&_XT_OSC
10 ;**********************************************************************************
11 INCLUDE "P16F877A.INC"
12 ;**********************************************************************************
13 ; CBLOCK Assignments
14 ;**********************************************************************************
15 CBLOCK 0X20
16 Delay_reg
17 STATUSTEMP
18 LOW_DIGIT ; holds the digit to be displayed on first 7-segment
19 HIGH_DIGIT ; holds the digit to be displayed on second 7-segment
20 SEC_CALC ; used in calculating the elapse of one second
21 START_STOP ; user defined flag which if filled with 1’s the stop watch
22 ;counts, else halts
23 ENDC
24 ;**********************************************************************************
25 ORG 0X000
26 GOTO MAIN
27 ORG 0X004
28 GOTO ISR
29 ;**********************************************************************************
30 MAIN
31 CALL INITIAL
32 MAINLOOP
33 CALL DisplayClock
34 GOTO MAINLOOP
35 ;**********************************************************************************
36 INITIAL
37 BANKSEL TRISA
38 CLRF TRISA ;TRISA and TRISD as outputs
39 CLRF TRISD
40 MOVLW 01
41 MOVWF TRISB ;RB0 as input (External Interrupt enabled), RB1-RB7
42 ; as outputs
43 BSF INTCON, GIE ;TMR0 and External Interrupts Enabled, their
44 ; flags cleared
45 BSF INTCON, INTE
46 BSF INTCON, TMR0IE
47 BCF INTCON, INTF
48 BCF INTCON, TMR0IF
49 MOVLW 0XD4 ;PSA assigned to TMR0, Prescalar = 32, TMR0 clock source
50 ;is the internal
51 MOVWF OPTION_REG ;instruction cycle clock, External interrupt is on the
52 ;rising egde
6
53
54 BANKSEL ADCON1
55 MOVLW 06H
56 MOVWF ADCON1 ;set PORTA as general Digital I/O PORT
57
58 BANKSEL TMR0 ;TMR0 to update 256 – 6 = 250
59 MOVLW 0X06
60 MOVWF TMR0
61 CLRF LOW_DIGIT ;Initially, the number to be displayed is 00
62 CLRF HIGH_DIGIT
63 CLRF SEC_CALC ;0 ms has passed
64 CLRF START_STOP ;stopwatch is initially stopped
65 MOVLW 0FFH
66 MOVWF PORTD ;close all display
67 RETURN
68 ;**********************************************************************************
69 ISR
70 BTFSC INTCON, INTF ;External Interrupt has higher priority
71 CALL START_STOP_SUB
72 BTFSC INTCON, TMR0IF
73 CALL TMR0_CODE
74 RETFIE
75 ;**********************************************************************************
76 START_STOP_SUB
77 BCF INTCON, INTF ;clear external interrupt flag
78 COMF START_STOP, F ;thus halting or starting the stopwatch
79 RETURN
80 ;**********************************************************************************
81 TMR0_CODE
82 BCF INTCON, TMR0IF ;Clear TMR0 Flag
83 MOVLW 0X06 ;Reinitialize TMR0
84 MOVWF TMR0
85 INCF SEC_CALC, F
86 MOVLW .125 ;Assuming a clock of 4MHZ, we need
87 SUBWF SEC_CALC, W ; 250 * 32 * 125 = 1×106 µs = 1 sec
88 BTFSS STATUS, Z
89 GOTO ENDTMR0
90 BTFSC START_STOP, 0
91 CALL UPDATE_DIGITS ;if one second passed, update digits
92 ENDTMR0
93 RETURN
94 ;**********************************************************************************
95 UPDATE_DIGITS
96 CLRF SEC_CALC ;Cleared so as to count the next 1 sec correctly
97 MOVF LOW_DIGIT, W ; If previous low digit is not 9, increment low digit
98 ;by one
99 SUBLW 0X09 ; else, increment high digit by one and clear low digit
100 BTFSC STATUS, Z
101 GOTO UPDATE_HIGH_DIGIT
102 GOTO UPDATE_LOW_DIGIT
103 END_UPDATE
104 CALL DisplayClock ; Update clock display

7
105 RETURN
106 ;**********************************************************************************
107 UPDATE_LOW_DIGIT
108 INCF LOW_DIGIT, F
109 GOTO END_UPDATE
110 UPDATE_HIGH_DIGIT
111 CLRF LOW_DIGIT
112 INCF HIGH_DIGIT, F
113 MOVF HIGH_DIGIT, W
114 SUBLW 6 ; if high digit reaches 6 (that is number = 60, 1 Minute),
115 ;reset
116 BTFSC STATUS, Z
117 CLRF HIGH_DIGIT
118 GOTO END_UPDATE
119 ;**********************************************************************************
120 DisplayClock ;7 segment digit multiplexing ; see appendix 3
121 MOVF LOW_DIGIT,W
122 CALL Look_TABLE
123 MOVWF PORTD
124 BCF PORTA,1 ;enable first 7_segment Display
125 CALL DELAY
126 BSF PORTA,1
127 MOVF HIGH_DIGIT,W
128 CALL Look_TABLE
129 MOVWF PORTD
130 BCF PORTA,0 ;enable second 7_segment Display
131 CALL DELAY
132 BSF PORTA,0
133 RETURN
134 ;**********************************************************************************
135 Look_TABLE
136 ADDWF PCL , 1
137 RETLW B’11000000’ ;’0’
138 RETLW B’11111001’ ;’1’
139 RETLW B’10100100’ ;’2’
140 RETLW B’10110000’ ;’3’
141 RETLW B’10011001’ ;’4’
142 RETLW B’10010010’ ;’5’
143 RETLW B’10000010’ ;’6’
144 RETLW B’11111000’ ;’7’
145 RETLW B’10000000’ ;’8’
146 RETLW B’10010000’ ;’9’
147 ;*********************** delay subprogram ***************************************
148 Delay
149 MOVLW 0FFH
150 MOVWF Delay_reg
151 L1 DECFSZ Dealy_reg,F
152 GOTO L1
153 RETURN
154 END

8
Appendix 1 - Timer2 Module
Prepared by Eng. Enas Ja’ra

Timer2 is an 8-bit timer with a prescaler and a postscaler, , it is connected only to an


internal clock - (FOSC/4) and it has Interrupt on overflow feature.

Timer2 has 2 count registers: TMR2 and PR2. The size of each registers is 8-bit in which we
can write numbers from 0 to 255.The TMR2 register is readable and writable and is cleared
on any device Reset. PR2 is a readable and writable register and initialized to FFh upon
Reset.

Register TMR2 is used to store the “initial” count value (the value from which it begins to
count). Register PR2 is used to store the “ending” count value (the maximum value we
need/want to reach). ie: using Timer2 we can determine the started count value, the final
count value, and the count will be between these two values. The Timer2 increments from
00h until it matches PR2 and then resets to 00h on the next increment cycle.

Prescaler and Postscaler :


Each allows making additional division of the frequency clock source.
 Prescaler divides the frequency clock source BEFORE the counting takes place at the
register TMR2, thus the counting inside the TMR2 register is performed based on
 the divided frequency clock source by the Prescaler. 
 Postscaler divides the frequency that comes out of the Comparator again for the last
time. The match output of TMR2 goes through a 4-bit postscaler (which gives a 1:1
to 1:16 scaling) to generate a TMR2 interrupt if enabled (TMR2IF (PIR1 register bit
no 1)). 

TIMER2 BLOCK DIAGRAM

9
All the necessary settings are controlled from with T2CON Register

T2CKPS1:T2CKPS0: Timer2 Clock Prescale Select bits


T2CKPS1:T2CKPS0 (T2CON<1:0>).
00 = Prescaler is 1
01 = Prescaler is 4
1x = Prescaler is 16

TMR2ON: Timer2 On bit


TMR2ON (T2CON<2>)
1 = Timer2 is on
0 = Timer2 is off

TOUTPS3:TOUTPS0: Timer2 Output Postscale Select bits


TOUTPS3:TOUTPS0 (T2CON<6:3>).

0000 = 1:1 postscale


0001 = 1:2 postscale
0010 = 1:3 postscale



1111 = 1:16 postscale

10
Appendix 2- Watchdog Timer
Prepared by Eng. Enas Ja’ra

A watchdog timer (abbreviated to WDT) is a part of hardware that can be used to automatically
detect software anomalies and reset the processor if any occur. A watchdog timer can get a
system out of a lot of dangerous situations.

A watchdog circuit is a resistor/capacitor network inside the PIC. This provides a unique clock,
which is independent of any external clock that you provide in your circuit. Now, when the
Watchdog Timer is enabled, a counter starts at 00 and increments by 1 until it reaches FF. When
it goes from FF to 00 (which is FF + 1) then the PIC will be reset, irrespective of what it is
doing. The only way we can stop the WDT from resetting the PIC is to periodically reset the WDT
back to 00 throughout our program. Now you can see that if our program does get stuck for some
reason, the WDT will then reset the PIC, causing our program to restart from the beginning.

In order to use the WDT, we need to know three things. First, how long have we got before we
need to reset the WDT, secondly how do we clear it. Finally, we have to tell the PIC programming
software to enable the WDT inside the PIC.

WDT Times

The PIC data sheet specifies that the WDT has a period from start to finish of 18mS. This is
dependant several factors, such as the supply voltage, temperature of the PIC etc. The reason for
the approximation is because the WDT clock is supplied by an internal RC network. The time for
an RC network to charge depends on the supply voltage. It also depends on the component
values, which will change slightly depending on their temperature. So, for the sake of simplicity,
just take it that the WDT will reset every 18mS. We can, however, make this longer by
Prescaler. We can program this prescaler to divide the RC clock. The more we divide the RC clock
by, the longer it takes for the WDT to reset.

The prescaler is located in the OPTION register, bits 0 to 2 inclusive. Below is a table showing the
bit assignments with the division rates and the time for the WDT to time out, Remember these
times are irrespective of your external clock frequency.

By default the prescaler is assigned to the other internal timer” TIMR0” . This means that we
have to change the prescaler over to the WDT.

11
Bit 2,1,0 Rate WDT Time
0,0,0 1:1 18mS
0,0,1 1:2 36mS
0,1,0 1:4 72mS
0,1,1 1:8 144mS
1,0,0 1:16 288mS
1,0,1 1:32 576mS
1,1,0 1:64 1.1Seconds
1,1,1 1:128 2.3Seconds

Example:

Suppose we want the WDT to reset our PIC after about half a second as a failsafe.

From table the nearest we have is 576mS, or 0.576 seconds.

 We have to reset the “TMR0” to 0.


 reset the WDT and prescaler
 Assign the prescaler to the WDT.
 Select the appropriate prescaler.

Banksel TMR0 ; make sure we are in bank 0


clrf TMR0 ; TMR0=0;
Banksel OPTION ;switch to bank 1
clrwdt ;reset the WDT and prescaler
movlw b’00001101’ ;Select the new prescaler value
movwf OPTION ; and assign it to WDT

The CLRWDT instruction is used to clear the WDT before it resets the PIC. So, all we need to do is
calculate where in our program the WDT will time out, and then enter the CLRWDT command just
before this point to ensure the PIC doesn’t reset. If your program is long, bear in mind that you
may need more than one CLRWDT. For example, if we use the default time of 18mS, then we need
to make sure that the program will see CLRWDT every 18mS.

The CLRWDT instruction clears the WDT and the prescaler, if assigned to the WDT, and prevent it
from timing out and generating a device RESET condition.

12
Example:

This subroutine lights one LED on an 8-LED-row and continuously moves back and forth in this
fashion.

1 ;**********************************************************
2 include "p16f917.inc"
3 ;**********************************************************
4 COUNT1 equ 20H ; DELAY Loop register.
5 COUNT2 equ 21H ; DELAY Loop register.
6 COUNT equ 22H
7 ;**************************************************************
8 ORG 0x00
9 goto initial
10 ;************************************************************
11 initial
12 clrf TMR0 ;Clear TMR0
13 Banksel TRISB
14 clrwdt ;reset the WDT and prescaler
15 movlw b'00001011' ;Select the prescaler value and assign
16 movwf OPTION_REG ;it to WDT,WDT time to reset 144mS
17 bsf STATUS,RP0
18 movlw 00H
19 movwf TRISB
20 bcf STATUS,RP0
21 movlw 8
22 movwf COUNT
23
24 MAIN
25 movlw 01H
26 movwf PORTB
27
28 Rotate_Left ; Move the bit on Port B left, then right.
29 call DELAY
30 rlf PORTB, F
31 btfss STATUS, C
32 goto Rotate_Left
33 Rotate_Right
34 call DELAY
35 rrf PORTB, F
36 btfss STATUS, C
37 goto Rotate_Right
38 goto Rotate_Left
39 ; **************************************************
40 ; Subroutine to give a delay between bit movements.
41 ;Total of 42.7 mS
42 ; **************************************************
43 DELAY
44 MOVLW 0X6F
45 MOVWF COUNT2
46 L11 MOVLW 0X7F
47 MOVWF COUNT1
48
49 LOOP2
50 DECFSZ COUNT1, F
51 GOTO LOOP2
52 LOOP1
53
54 DECFSZ COUNT2, F
55 GOTO L11
56 CLRWDT ; This simply resets the WDT.
57 return ; Return from our original DELAY subroutine
58
59 END
60
61
62

13
 The instruction at Line 59 resets the WDT, Comment out or removes this command to see the
WDT in action. It should reset the PIC.
 If you comment out, or remove the CLRWDT command, you will find that the PIC will not go
past lighting the fifth LED. This is because the WDT is resetting the PIC. With the CLRWDT in
place, the program works as it should.

Appendix 3- 7 Segment Multiplexing

Some kits like QL 200 development kit provide multiplexed multi 7 segment digit displays
in single packages; Multiplexed displays are electronic displays where the entire display
is not driven at one time. Instead, sub-units of the display are multiplexed.

In multiplexed 7 segment applications (see Figure 1) the LED segments of all the digits
are tied together so if you send date to any one of the segment , it will displayed on both
segments to prevent that the common pins of each digit are turned ON separately by the
microcontroller. When each digit is displayed only for several milliseconds, the eye
cannot tell that the digits are not ON all the time. This way we can multiplex any number
of 7-segment displays together. For example, to display the number 24, we have to send 2
to the first digit and enable its common pin. After a few milliseconds, number 4 is sent to
the second digit and the common point of the second digit is enabled. When this process
is repeated continuously, it appears to the user that both displays are ON continuously.

The display can be controlled from the microcontroller as follows


 Send the segment bit pattern for digit 1 to segments a to g
 Enable digit 1.
 Wait for a few milliseconds.
 Disable digit 1.
 Send the segment bit pattern for digit 2 to
segments a to g
 Enable digit 2
 Wait for a few milliseconds
 Disable digit 2.
 Repeat the above process continuously

Figure 1: Two multiplexed 7-segment


displays

14
University of Jordan
Faculty of Engineering and Technology
Department of Computer Engineering
Embedded Systems Laboratory 0907334

Experiment 8:The USART


8
Objectives
 Introduce the USART module of the PIC 16series through an
industrial example. 

 To become familiar with the serial communications using PIC and
RS232 Protocol. 

 Become familiar with serial communication testing techniques
either in software and hardware 
Pre-lab
You are required to review the following in order to be fully prepared for the experiment, refer back to
both your text book and the Microchip PIC datasheets whenever you find it necessary.
 The general operation of the USART in asynchronous mode. 

 Familiarize yourself with the following registers and their individual
 bit functions: TXSTA, RCSTA, TXREG, RCREG, and SPBRG. 
  Calculating baud rate speeds. 
 The PIE and PIR registers in the 16F877A. 

The Idea behind the Code


In a certain factory, a modern computerized machine is serially connected to a control computer. Once
the machine is powered on, it sends a message to the control room indicating that it is ready to receive
commands. After reading the message, an operator sends commands to the machine through the control
computer. In this experiment, since the there is no physical machine to carry out the commands, the
commands will be simply displayed on 7 segments display.

The simple flow of the program is:


 Initialize I/O, enable interrupts, configure USART settings: baud rate, transmitter and receiver
settings
 Send message to control computer
 Wait continuously “Loop” until commands are received from control computer, when received
display them on 7 segments display

STEP 1: INITIALIZE I/O, ENABLE INTERRUPTS …

 RC6 is reserved by Microchip design specifications for serial data transmission, therefore
configure as o/p
 RC7 is reserved by Microchip design specifications for serial data reception, therefore configure
 as i/p 
 PORTD will be connected to the 7 segments display, so configure as o/p 

 Baud rate is agreed to be 9600 bps, review datasheet or do hand calculations to find that SPBRG
has to be filled by 25 and high baud rate will be enabled (BRGH = 1) in order to achieve this
 speed. 
  Enable serial port (SPEN = 1), enable receiver (CREN = 1), enable transmitter (TXEN = 1) 
 Since we want to use asynchronous mode (SYNC = 0). 

 We have agreed to use receiver interrupt to know whether the machine received commands
from the control station or not, so (GIE = 1), (PEIE = 1) and (RCIE = 1). 

STEP 2: SEND MESSAGE TO CONTROL COMPUTER


The machine status message which reads “Machine ready to receive commands” has a length of 33
characters and is sequentially stored in a look up table. Where the first entry in the table is the letter
“M”, the second is “a”, third is “c” and so on … To send the message, the look-up table is to be accessed
33 times with the first time adding 0 to PCL to retrieve “M”, the second time adding 1 to PCL to retrieve
“a”, the third time adding 2 to PCL to retrieve “c” and so on … The message length is stored in a variable
which is decremented each time the look up table is accessed and is checked to see if this variable
reached 0 or not to indicate end of message.

2
After each message character is retrieved from the look-up table it is sent to TXREG, assuming the
USART is configured properly, the character will be serially sent at the designated speed.

We can’t send the next character immediately to TXREG while there is data still being transmitted or
residing in the transmitter’s TXREG, this will overwrite the data to be transmitted and therefore be lost.
In consequence, we have four ways to detect if transmission of the previous frame has finished or not
before sending the next one:

1. Use Interrupts (when transmission is finished, program flow will be interrupted and you can send the
next character inside ISR)

2. Poll the TXIF interrupt flag found in PIR1 register

3. Poll the TRMT flag found in TXSTA register (which is the method employed in this experiment)

4. Insert a time delay calculated to be larger than the delay time needed to transmit the character
frame Ex. If speed is 9600 bps, this means the time needed to send a frame asynchronously is:
9600 1,000,000µs (1s)
10 X
X = 1041 µs = 1.041ms so insert a delay larger than this value before transmitting the next frame.

After the whole message is sent, the code goes into an infinite loop waiting to receive commands.

STEP 3: COMMANDS ARE RECEIVED FROM CONTROL COMPUTER


When characters are received from a control computer, the character frame will reside in the RCREG
register and the RCIF flag will be set high (Remember that interrupt flags are set high whenever their
event occurs regardless whether the sources were enabled or not). But how do we know the moment
the command is received and ensure that we get all commands without losing any of them?

Similar to what has been discussed above. We have three methods to ensure data is read at sufficient
time periods without any data loss:
1. Use Interrupts (which is the method employed in this experiment, when a command is received,
the program flow will be interrupted and you can read RCREG inside ISR)

2. Poll the RCIF flag found in PIR1 register

3. Periodically read RCREG at sufficient time intervals.

3
Another important issue is how to check if the date received is erroneous or not? There are two types of
errors in serial data communications which the PIC can detect and flag:

1. Framing errors occur due to the difference in the speed of communication between the
transmitter and receiver (not correctly set to match each other). This error is detected when a
stop bit is received as CLEAR and the framing error bit (FERR) in the RCSTA register is set to
indicate occurrence. The FERR pin is set/cleared for every frame received to indicate if there is
speed mismatch! Therefore, the FERR value will be updated with every coming frame and it is
necessary to read RCSTA value before RCREG to check if we are receiving the data correctly.

2. Overrun errors: The receiver module has a two-level deep buffer in which the received data is
stored. Data received in the RSR register ultimately fill the buffer. However, if the two buffer
locations are already occupied, and a third frame of data is being shifted into the RSR, once it is
complete, it will not be stored in the buffer and thus be lost, and hence an overrun error occurs.
Flag OERR in the RCSTA register is set to indicate this error occurrence. Once this OERR bit is
set, no further data is received! The FIFO buffer is cleared by reading data in the RCREG, that is,
it needs two RCREG reads to empty the buffer! Furthermore, once set, the OERR bit can only be
cleared in software by clearing and setting the CREN bit. To avoid overrun errors, the user
should always make sure to read data at appropriate speeds such that the buffers won’t become
full!

3. Parity Errors: used to detect odd number of erroneous bit transmissions. This is done by
enabling the 9th bit mode in the RCSTA register “RX9 bit”. However, no hardware is present to
calculate and check for parity, therefore, the sender should write appropriate code to calculate
desired parity (odd/even) and place the result in the TX9D pin in the TXSTA register before
sending the frame. An equivalent code should read the received parity RX9D from the RCSTA
register calculate parity and check for a match!

4
Code Example
1 Function
2 In a certain factory, a modern computerized machine is serially connected to a control computer.
3 Once the machine is powered on, it sends a message to the control room indicating that it is
4 ready to receive commands. After reading the message, an operator sends commands to the
5 machine through the control computer. In this experiment, since the there is no physical
6 machine to carry out the commands, the commands will be simply displayed on 7 segments
7 display.
8 ;
9 The simple flow of the program is:
10 1. Initialize I/O, enable interrupts, configure USART settings: baud rate, transmitter and
11 receiver settings
12 2. Send message to control computer
13 3. Wait continuously “Loop” and wait until commands are received from control computer,
14 when received display them on 7 Segments Display
15
16 ;Hardware Connections
17 Inputs
18 RC7: USART Receiver pin
19 Outputs
20 RC6: USART Transmitter pin
21 PORTD 0 -6: 7 segment display
22 RA0 is connected to 7-Segment Digit Enable
23 ;************************************************************************************************
24 include "p16f9877a.inc"
25 ;************************************************************************************************
26 ; User-defined variables
27 cblock 0x20
28 WTemp ; Must be reserved in all banks
29 StatusTemp ; reserved in bank0 only
30 Counter
31 BLNKCNT
32 MSG
33 endc
34 cblock 0x0A0
35 WTemp1
36 endc
cblock 0x120
37
WTemp2
38
endc
39
cblock 0x1A0
40
WTemp3
41
endc
42 ;************************************************************************************************
43
; Macro Assignments
44
push macro
45
movwf WTemp ;WTemp must be reserved in all banks
46
swapf STATUS,W ;store in W without affecting status bits
47 banksel StatusTemp ;select StatusTemp bank
48 movwf StatusTemp ;save STATUS
49 endm
50
51
52 pop macro
53 banksel StatusTemp ;point to StatusTemp bank
swapf StatusTemp,W ;unswap STATUS nibbles into W
5
54 movwf STATUS ;restore STATUS (which points to where W was
55 ;stored)
56 swapf WTemp,F ;unswap W nibbles
57 swapf WTemp,W ;restore W without affecting STATUS
58 endm
59 ;************************************************************************************************
60 ; Start of executable code
61 org 0x00 ; Reset Vector
62 goto Main
63 org 0x04 ; Interrupt Vector
64 goto IntService
65 ;************************************************************************************************
66 ; Main program
67 ; After Initialization, this code sends the message: "Machine ready to receive commands" then
68 ; goes into an infinite loop during which, the program is interrupted if data is received.
69 ;************************************************************************************************
70 Initial
71 movlw D'25' ; This sets the baud rate to 9600
72 banksel SPBRG ; assuming BRGH=1 and Fosc = 4.000 MHz
73 movwf SPBRG
74
75 banksel RCSTA
76 bsf RCSTA, SPEN ; Enable serial port
77 bsf RCSTA, CREN ; Enable Receiver
78
79 banksel TXSTA
80 bcf TXSTA, SYNC ; Set up the port for Asynchronous operation
81 bsf TXSTA, TXEN ; Enable Transmitter
82 bsf TXSTA, BRGH ; High baud rate used
83
84 banksel PIE1 ; Enable Receiver Interrupt
85 bsf PIE1,RCIE
86 banksel INTCON
87 bsf INTCON, GIE ; Enable global and peripheral interrupts
88 bsf INTCON, PEIE
89 banksel TRISD
clrf TRISD
90 ; PORTD is used to display the received commands
91 clrf TRISA
92 bcf TRISC, 6 ; Configuring pins RC6 as o/p, RC7 as i/p for
93 bsf TRISC, 7 ; serial communication
94 movlw 06
95 movwf ADCON1
96
97 banksel PORTD
98 clrf PORTD
99 clrf PORTA
100 return
101 ;***********************************************************************************************
102 Main
103 Call Initial
104 MainLoop ; Prepare to send first character in the message MSG = 0
105 Clrf MSG ; then incremented by on to access every character in
106 ;.look up table
107
6
108 SEND
109 movf MSG, W
110 call Message
111 movwf TXREG
112 TX_not_done
113 banksel TXSTA ; Polling for the TRMT flag to check
114 btfss TXSTA, TRMT ; if TSR is empty or not
115 goto TX_not_done
116 banksel MSG
117 incf MSG, F ; Move to next character in string
118 movlw .33 ; Check if the whole message has been sent
119 subwf MSG, W ; "Message length = 33"
120 btfss STATUS, Z
121 goto SEND
122 Loop
123 Goto Loop ; When whole message is sent, loop and wait
124 ; for receiver interrupts.
125 ;************************************************************************************************
126 ; Interrupt Service Routine
127 IntService
128 push
129 btfsc PIR1, RCIF ; Check for RX interrupt
call RX_Receive
130
pop
131
retfie
132
RX_Receive
133 bcf PIR1, RCIF ;Pass the value of RCREG to PORTD
134 ;************************************************************************************
135 ; Uncomment the following piece of code if error detection is required. Note that it is
136 ; recommended to detect for serial transmission errors
137 ;************************************************************************************
138 ;banksel RCSTA
139 ;btfsc RCSTA, FERR ;Check for framing error
140 ;goto FramingError
141 ;btfsc RCSTA, OERR ;Check for Overrun error
142 ;goto OverrunError
143 banksel RCREG
144 movf RCREG, W
145 banksel PORTD
146 CALL Look_TABLE
147 movwf PORTD
148 return
149
150 Look_TABLE
151 ADDWF PCL , 1
152 RETLW B’11000000’ ;’0’
153 RETLW B’11111001’ ;’1’
154 RETLW B’10100100’ ;’2’
155 RETLW B’10110000’ ;’3’
156 RETLW B’10011001’ ;’4’
157 RETLW B’10010010’ ;’5’
158 RETLW B’10000010’ ;’6’
159 RETLW B’11111000’ ;’7’
160 RETLW B’10000000’ ;’8’
161 RETLW B’10010000’ ;’9’
7
162 Message
163 addwf PCL, F
164 retlw A'M'
165 retlw A'a'
166 retlw A'c'
167 retlw A'h'
168 retlw A'i'
169 retlw A'n'
170 retlw A'e'
171 retlw A' '
172 retlw A'r'
173 retlw A'e'
174 retlw A'a'
retlw A'd'
retlw A'y'
retlw A' '
retlw A't'
retlw A'o'
retlw A' '
retlw A'r'
retlw A'e'
retlw A'c'
retlw A'e'
retlw A'i'
retlw A'v'
retlw A'e'
retlw A' '
retlw A'c'
retlw A'o'
retlw A'm'
retlw A'm'
retlw A'a'
retlw A'n'
retlw A'd'
retlw A's'
END
;************************************************************************************************
CODE TESTING
At first glance, you might think that you cannot test your code unless you have a physical control
PC and a machine at home!! Surely this is not feasible. Therefore we will now introduce you to
testing USART serial communication in MPLAB IDE.
MPLAB TRANSMITTER TESTING
After Building your project in MPLAB do
the following procedure:
1. Debugger  Select Tool  MPLAB SIM
2. Debugger  Settings Uart1 IO
3. The following screen will show up:
4. Select Enable Uart1 IO
5. Select the output to be shown in Window
6. Click Ok

Now, if the output window is not already


shown, go to View  Output

8
Notice that a new tab (SIM Uart1) has shown up as shown below:

Now run the program, you will see that the message has appeared in the Uart1 IO window which we
have already enabled. See screenshot below:

MPLAB RECEIVER TESTING


We will test the receiver the same way we used to test for external inputs: using stimulus.

The procedure will be revisited here again:


1. Debugger  Stimulus New Workbook
2. In the Async tab choose RCREG, and the action as Direct Message, in the Message field type
in the character you wish to send.
3. Press fire, by doing so the character “7” will be received in RCREG

4. Place a break point at instruction goto IntService .


3. Since the received character is displayed on 7 segment display which are connected to
PORTD, use the watch window, check if “11111000” has been actually sent to PORTD

IN-LAB TESTING PHASE 2


 TRANSMITTER TESTING

 Double click on the “Docklight” program icon found on the desktop. 



The “Docklight” program is a tool through which we can establish serial communication
between two PC’s or a PC with other devices. We can send, receive and view data in
different formats: ASCII, hex and decimal. 

  Press Ok to the message that appears then “Start with a blank project” 
  In “Docklight”, go to Tools  Project Settings 

9
A window will appear through which we can set the communication session settings, set the com port
used to COM1, configure the baud rate speed to match that used by the other device/PC, set number of
stop bits used and so on. Check the settings we will use in this experiment as seen below: 

 Press Ok to save your settings. 



 Now we have to start the communication session between the PC and the kit, press the play
button  or press F5 to open communication port. 







Download the program to the PIC16F877A; 
Connect the kit to the PC through a serial RS232 cable. Be sure to connect the cable to COM1
of the PC. In this scenario, the kit will act as the machine and the PC will act as the control
computer which will receive machine status and send commands. 

 Now the PC (control room) is configured properly to receive status and send commands 

 Switch back to “Docklight”, make sure that the window format is ASCII; you will be able to read
the message which has been sent by the PIC to the PC. WOW  

10
RECEIVER TESTING

To send data to the kit (machine) start with the following:


We will start with preparing the frame
 Double click on the space shown:

  Give the data sequence you want to send a name (optional) 


  Choose the data format you want to see (decimal) 
 Fill in the data you want to send then click OK 

11
 So far we have not yet transmitted the data to do so

Click on the fire button

12
Capturing the frame sent/received by the USART using a Digital
Oscilloscope

Digital oscilloscopes provide an easy way to capture signals using the “AutoSet” function provided with
most models. However, this function is not feasible for use with non periodic signals especially those
that are at high frequencies which is the case in this experiment; we are to capture and view a
transmitted or received frame at baud rates of 9600 or more. Even using manual setting and pressing
the “Stop” button will not be that easy as transmission and reception speed increases. Therefore, we are
to use the trigger function which modern oscilloscopes offer.

The trigger event is usually the input waveform reaching some user-specified threshold voltage in the
specified direction (going positive or going negative). Trigger circuits allow the display of non-periodic
signals such as single pulses or pulses that don't recur at a fixed rate.

The DS1150 Digital Oscilloscope

1. In this experiment, connect the oscilloscope probe to CH1 and use the hook at the other end to
connect to RC7 pin (Receiver) through a wire. Connect the probe GND to that of the
Mechatronics board (Optional). – See figure below!

13
2. Make sure that the orange slider of the oscillator’s probe is at X1 option.
3. Power on the oscilloscope
4. Press Autoset (if the probe is not connected to the circuit, this resets the oscilloscope)
5. Set Voltage/Div value on CH1 to 5 Volts using the knob.
6. Set the time division to 0.2 ms (remember that we have calculated above that the whole frame
will take 1.041 ms to be sent, therefore we need a smaller time division in order to see the
whole frame fit on the oscilloscope screen).

Oscilloscope Screenshot after settings

14
7. On the right side of the oscilloscope you will see a set of trigger buttons, press the “Source”
button as many times until you see that the trigger is on CH1 (Upper left corner of the screen)
8. Press the trigger’s “Menu” button then select the following options using the 5 blue buttons to
the right of the oscilloscope’s screen:

 Select CH1 (other options include


CH2, Line and EXTernal), you will
see your selection at the upper left
 corner of the oscilloscope’s screen. 
 Change the coupling to “DC”, 

 Edge to “Falling” (since we are to
detect the beginning of the frame,
which is a transition from idle state
to start bit state (Logic 1 to Logic 0 at
pin RC7) 

 Finally set the Mode to “Single” since
we are to detect only one frame. 

9. Make sure that all your connections are
correct and firmly fixed, review your
oscilloscope and Docklight settings, after
which use the Docklight program to send the
hex value 0x65 as an example.
10. The frame should now appear on the screen,
draw it here:

11. Now, you will notice that the screen has frozen to show this frame, to view other frames, press
the STOP/RUN button, now the oscilloscope is ready to receive and display new frames.

15
University of Jordan
Faculty of Engineering and Technology
Department of Computer Engineering
Embedded Systems Laboratory 0907334

Experiment 9: ANALOG-TO-DIGITAL

9 CONVERTER (A/D) MODULE

Objectives
 To familiarize you with the built-in A/D hardware module.

Pre-lab requirements

 Review the PIC16F877A datasheet section on the AD module.

Appendix A quickly reviews the AD module

Written by Eng. Enas Jaara |


Page 1 of 9
Overview

An analog to digital converter converts analog voltages to digital information that can be used by a
computer. In almost in all digital systems, there is a frequent need to convert analog signals
generated by peripheral devices such as microphones, sensors, and etc. into digital values that can
be stored and processed. As an example, temperature and brightness are changing continuously.
This experiment will focus on A/D conversion by using the PIC16F877A Analog-To-Digital
Converter.

The idea behind the code

Select RAO as input connected to potentiometer, get the result of a A/D conversion, convert the
result into the BCD format and finally the result (the only low 8 bits) will be displayed on three 7-
segment displays, The 7 segments display will use Time Division Multiplexing to display a 3-digit
values.

A Detailed View of the Interworking of the System


Based on the above discussion, we will further elaborate how this system works.
1. Initially, the system should be initialized as follows:
 We need to connect an analogue signal to the PIC, we shall use either one of PORTA or
PORTE, since both offer analogue input interfacing to the PIC. We will specify which
PORT and which exact pin of the port to be used as analogue or digital through the use
of the ADCON1 register. In this experiment we chose RA0 as the analogue input
(corresponding to channel 0 “AN0” of the AD module)
 We will configure the AD module as follows, power on the module (set ADON), and
choose the analogue channel 0 “AN0” as the analogue input of the AD module by setting
CH2, CH1 and CH0 as zeros. We will set the voltage references to be between 0 and 5
volts (why?) and finally the result is to be right justified, that is the lower 8-bits will
reside in ADRESL and the higher 2 bits will reside in ADRESH. In this program, we will
choose to ignore ADRESL and only deal with the 8-bit digitized value to simplify
program development.
 We chose a conversion speed of Fosc/8, therefore ADCON1 will have the value of 0x8E
 We implemented the code such that the main functionality is to convert analogue
signals into digital ones and save them into ADRESL in a continuous fashion such that
we will always have updated and recent values of the potentiometer, this is the code of
the main subroutine will have all other actions: CHANGE _To_BCD ,this subroutine is
used to convert the result of the conversion into BCD values (Units , Tens , Hundreds),
then display the result on the 7 segment display , Time Division Multiplexing used to
display a 3-digit values(Units , Tens , Hundreds).

2. As stated above, the main subroutine is to continuously update ADRESL register with a recent
digitized value of the potentiometer. The routine starts by starting the conversion process (bsf
ADCON0, GO), the value of ADRESL is not read until we are sure that the conversion process has
truly finished. This is done through polling the ADIF flag (remember that we have not enabled
the interrupt for AD, yet the flags of interrupts are set and cleared no matter whether they were
enabled or not, this is why polling is possible). When the conversion is finished, the value of
ADRESL is copied into TEMP register in order to display it on the 7 segment display!

Page 2 of 9
The steps should be followed for doing an A/D Conversion:

• Port configuration
The I/Os pin should be configured as analog by setting the associated TRIS and
PCFG3:PCFG0 bits.

• ADC module configuration

• Channel selection
The CHS bits of the ADCON0 register.

• ADC voltage reference selection


The PCFG bits of the ADCON1 register.

•Results formatting
The ADFM bit of the ADCON1 register controls the output format.

•Turn on ADC module


To enable the ADC module, the ADON bit of the ADCON0 register must be set to a ‘1’.

•ADC conversion clock source


The source of the conversion clock is software selectable via the ADCS bits of the
ADCON1 and ADCON0 registers.

• Interrupt control
The ADC module allows for the ability to generate an interrupt upon completion of
an Analog-to-Digital conversion, but we have chosen to use the ADC without
interrupts and use polling instead.

Page 3 of 9
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;Main Subroutine;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

This subroutine shows theA/D Conversion Procedure.

Start conversion by setting the GO/DONE bit. Poll the AD interrupt flag ADIF
(interrupts disabled) to check whether conversion has finished or not. Clear the ADC
interrupts flag (required). Finally Read ADC Result found in ADRESH and/or
ADRESL. Convert Result into BCD Format and display it on the 7 segments displays.

Initial
Main
MOVLW 8EH ;A/D data right justified
MOVWF ADCON1 ; RA0 is analogue input
Banksel PORTA ;BANK 0
ADC module
MOVLW 41H ;A/D enabled
configuration
MOVWF ADCON0 ;select CLOCK is fosc/,
CALL DELAY
Set the GO/DONE bit. BSF ADCON0,GO ;startup ADC divert
WAIT
BTFSS PIR1,ADIF ;Is the convert have finished?
NO GOTO WAIT ; wait for the convert finished
YES bcf PIR1, ADIF ; Clear the A/D flag
Is ADIF =1
Banksel TRISA
MOVF ADRESL,W ;read the result of convert
(Wait for conversion to Banksel PORTA
complete) MOVWF TEMP ; keep in temporary register
CALL CHANGE_To_BCD ; call result convert subr.
CALL DELAY
CALL DISPLAY ; call display subroutine
CALL DELAY
GOTO Initial ; Do it again
Clear the A/D interrupt flag

Get A/D Result

Convert A/D Result to BCD

Display Result on 7 segments displays

Page 4 of 9
1 ;****************************************************
2 ;Code Function:Select RAO as input connected to potentiometer,
3 ;get the result of a A/D conversion ,convert the result into the BCD format
4 ; and finally the result (the only low 8 bits) will be displayed on 7-segment displays.
5
6 #INCLUDE<P16F877a.INC>
7
8 TEMP EQU 20H ;temporary register
9 hundreds EQU 21H ;the hundred bit of convert result
10 tens EQU 22H ;the ten bit of convert result
11 units EQU 23H ;the ones bit of convert result
12 ;****************************************************
13 ORG 00H
14 NOP
15 GOTO Initial
16
17 ;*******************Initial subroutine*****************************
18 Initial
19 CLRF hundreds
20 CLRF tens
21 CLRF units
22 Banksel TRISA ;select bank 1
23 MOVLW 01H ;PORTA bit Number0 is INPUT
24 MOVWF TRISA
25 CLRF TRISD ;All of the PORTD bits are outputs
26 ;***********************MAIN program***********************
27 Main
28 MOVLW 8EH ;A/D data right justified
29 MOVWF ADCON1 ;only select RA0 as ADC PORT,the rest are data PORT
30 Banksel PORTA ;BANK 0
31 MOVLW 41H
32 MOVWF ADCON0 ;select CLOCK is fosc/8,A/D enabled
33 CALL DELAY ;call delay program,ensure enough time to sampling
34 BSF ADCON0,GO ;startup ADC divert
35 WAIT
36 BTFSS PIR1,ADIF ;is the convert have finished?
37 GOTO WAIT ;wait for the convert finished
38 Bcf PIR1, ADIF ; Clear the A/D flag
39 Banksel TRISA
40 MOVF ADRESL,W ;read the result of convert
41 Banksel PORTA
42 MOVWF TEMP ;keep Result in temporary register
43 CALL CHANGE_To_BCD ;call result convert subroutine
44 CALL DELAY
45 CALL DISPLAY ;call display subroutine
46 CALL DELAY
47 GOTO Initial ;Do it again
48 ;************************Convert subroutine********************
49 CHANGE_To_BCD
50 gen_hunds
51 MOVLW .100 ;sub 100,result keep in W
52 SUBWF TEMP,0
53 BTFSS STATUS,C ;judge if the result biger than 100
54 GOTO gen_tens ;no,get the ten bit result
55 MOVWF TEMP ;yes,result keep in TEMP
56 INCF hundreds,1 ;hundred bit add 1
57 GOTO gen_hunds ;continue to get hundred bit result
58 gen_tens
59 MOVLW .10 ;sub 10,result keep in W
60 SUBWF TEMP,0
61 BTFSS STATUS,C ;judge if the result biger than 10
62 GOTO gen_ones ;no,get the Entries bit result
63 MOVWF TEMP ;yes,result keep in TEMP
64 INCF tens,1 ;ten bit add 1
65 GOTO gen_tens ;turn to continue get ten bit

Page 5 of 9
66 gen_ones
67 MOVF TEMP,W
68 MOVWF units ;the value of Entries bit
69 RETURN
70
71 ;**************************Display subroutine********************
72 DISPLAY
73 MOVF hundreds,W ;display Hundreds bit
74 CALL TABLE
75 MOVWF PORTD
76 BCF PORTA,3
77 CALL DELAY
78 CALL DELAY
79 BSF PORTA,3
80
81 MOVF tens,W ;display Tens bit
82 CALL TABLE
83 MOVWF PORTD
84 BCF PORTA,4
85 CALL DELAY
86 CALL DELAY
87 BSF PORTA,4
88
89 MOVF units,W ;display Units bit
90 CALL TABLE
91 MOVWF PORTD
92 BCF PORTA,5
93 CALL DELAY
94 CALL DELAY
95 BSF PORTA,5
96 RETURN
97
98 ;*********************************************************
99 TABLE
100 ADDWF PCL, 1
101 RETLW B'11000000' ;'0'
102 RETLW B'11111001' ;'1'
103 RETLW B'10100100' ;'2'
104 RETLW B'10110000' ;'3'
105 RETLW B'10011001' ;'4'
106 RETLW B'10010010' ;'5'
107 RETLW B'10000010' ;'6'
108 RETLW B'11111000' ;'7'
109 RETLW B'10000000' ;'8'
110 RETLW B'10010000' ;'9'
111
112 ;***************************Delay subroutine***********************
113 DELAY
114 MOVLW 0xFF
115 MOVWF TEMP
116 L1 DECFSZ TEMP,1
117 GOTO L1
118 RETURN
119
120 ;*********************************************************
121 END ;program end
122

Page 6 of 9
Appendix A
Analog-to-Digital Conversion (ADC)
An analog-to-digital converter, or simply ADC, is a module that is used to convert an analog signal
into a digital code. In the real world, most of the signals sensed and processed by humans are
analog signals. Analog-to-digital conversion is the primary means by which analog signals are
converted into digital data that can be processed by Microcontroller for various purposes.

Sensors signals is an analog quantity, and digital systems often use signals to implement
measurement, control, and protection functions so it is the necessary to convert the analog signal
to digital information.

There's generally a lot of confusion about using the A/D inputs, but it's actually really very simple -
it's just a question of Extraction the information you need out of the datasheets.

There are four main registers associated with using the analogue inputs; these are summarized in
the following table:

Main registers used for Analog-to-Digital Conversion.


Name Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
ADRESH A/D Result Register - High Byte

ADRESL A/D Result Register - Low Byte

ADCON0 ADCS1 ADCS0 CHS2 CHS1 CHS0 GO/DONE - ADON

ADCON1 ADFM ADCS2 - - PCFG3 PCFG2 PCFG1 PCFG0

 ADCON0 and ADCON1 are the registers that control the A/D conversation process.
 ADRESH and ADRESL are the registers that return the 10-bit result of the analogue
to digital conversion, the only slightly tricky thing about them is that they are in
different memory banks.

RESULT FORMATTING:
The 10-bit A/D conversion result can be supplied in two formats, left justified or
right justified. The desired formatting is chosen by sitting the ADFM bit in the ADCON0
register.

Page 7 of 9
ADCON0 Details

ADON (bit 0), turns the A/D On (when = 1) or off (when = 0), thus saving the power it
consumes.

GO/DONE (bit 2), this bit has a dual function, the first is that by setting the bit it initiates
the start of the analogue to digital conversion process, the second is that when the bit is
automatically cleared when the conversion is complete, it can be polled to check if
conversion has ended before initiating a subsequent conversion.

CHS2, CHS1 and CHS0 (bits 3 - 5), the CHS2 CHS1 CHS0 Channel Pin
channel selection bits, choose one channel
among the available eight AD analogue 0 0 0 Channel0 RA0/AN0
channels and specify which one is to be used
as an input for the AD module for digitization. 0 0 1 Channel1 RA1/AN1
Be careful that the first five channels AN0-
0 1 0 Channel2 RA2/AN2
AN4 map to pins (RA0-RA3, RA5). Further
notice that AN4 uses digital pin RA5, not RA4
0 1 1 Channel3 RA3/AN3
as you would expect. And the remaining three
channels AN5-AN7 map to pins (RE0-RE2). 1 0 0 Channel4 RA5/AN4
See adjacent figure.
1 0 1 Channel5 RE0/AN5
ADCS1 and ADCS0 (bits 6 - 7): A/D
Conversion Clock Select bits (see ADCS2) 1 1 0 Channel6 RE1/AN6

1 1 1 Channel7 RE2/AN7

Page 8 of 9
ADCON1 Details

ADFM (bit 7), the Result Format Selection Bit, selects if the output is Right Justified (bit
set) or Left Justified (bit cleared). For full digitization precision, the whole 10 bits are to be
used.

ADCS2 (bit 6), which set the clock ADCON1 ADCON0


frequency used for the analogue to A/D Conversion Clock Select bits.
ADCS2 <ADCS1:ADCS0>
digital conversion, this clock is
divided down from the system clock 0 0 0 Fosc/2
(or can use an internal
oscillator), bit 4 and bit 5 0 0 1 Fosc/8
Unimplemented: Read as ‘0’.
0 1 0 FOsc/32

X 1 1 FRC (clock derived from a dedicated


Internal oscillator = 500 kHz max.)
1 0 0 Fosc/4

1 0 1 Fosc/16

1 1 0 Fosc/64

PCFG3:PCFG0 (bit
3:0): A/D Port
Configuration
Control bits

Example
If we make
ADCON1 = 0x80,
then we have 8
analog channels,
and Vref+ =
VDD, and Vref- =
Vss.

Page 9 of 9

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy