CPE 0907334 Embeddedlab Sep 2016 Fall
CPE 0907334 Embeddedlab Sep 2016 Fall
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
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.
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:
Click Absolute
6
QL-PROG – How to Program
Prepared by Eng. Enas Jaara
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
3. Press Erase button on programming software panel to Erase the chip data
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
Objectives
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
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:
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:
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.
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)
Examples:
After writing the above instructions we should build the project, do so by pressing build
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
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
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.
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.
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.
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 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.
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”.
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 “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
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 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
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:
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
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:
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
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.
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
Run Animate
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
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:
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
Objectives
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
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.
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.
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?
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)
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.
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.
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
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
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.
9
1. After building the project, go to View Hardware Stack
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
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:
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
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
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
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
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:
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
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
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
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
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 above steps prove essential as programs get harder and larger. Next we will present a real life
example from the industrial automation field.
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
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
4
Examples:
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
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:
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.
7
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Test_and_Process subroutine;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
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
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)
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 delay
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
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.
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
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
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.
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)
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!
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.
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.
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.
*******************************************************************************************************
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
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
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
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
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
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
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.
{
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
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
}
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()
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.
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;
C Operators
Arithmetic operators
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.
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.
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
FOR loop
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:
RB0 = 1 //Pin B0 on
RB7 = 0 //Pin B7 off
void main()
{
//Initialize PORTD -> RD0 as Output
TRISD=0b11111110;
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.
Delay functions
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
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.
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.
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
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:
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)
whereXxYxZ=
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
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
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 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.
9
All the necessary settings are controlled from with T2CON Register
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.
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.
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.
14
University of Jordan
Faculty of Engineering and Technology
Department of Computer Engineering
Embedded Systems Laboratory 0907334
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).
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)
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.
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)
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
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:
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:
10
RECEIVER TESTING
11
So far we have not yet transmitted the data to do so
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.
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).
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:
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
Objectives
To familiarize you with the built-in A/D hardware module.
Pre-lab requirements
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.
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.
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.
• Channel selection
The CHS bits of the ADCON0 register.
•Results formatting
The ADFM bit of the ADCON1 register controls the output format.
• 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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
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
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:
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.
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