100% found this document useful (1 vote)
2K views

Microcontrollers Cheat Sheet

This document provides a cheat sheet for programming microcontrollers. It covers basic microcontroller concepts like memory architecture, programming fundamentals like input/output ports and assembly language, and specific aspects of programming the PIC16F887 microcontroller like its instruction set and memory organization. The document is intended to concisely summarize essential information for someone looking to learn microcontroller programming in a quick reference format.

Uploaded by

Aditya
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (1 vote)
2K views

Microcontrollers Cheat Sheet

This document provides a cheat sheet for programming microcontrollers. It covers basic microcontroller concepts like memory architecture, programming fundamentals like input/output ports and assembly language, and specific aspects of programming the PIC16F887 microcontroller like its instruction set and memory organization. The document is intended to concisely summarize essential information for someone looking to learn microcontroller programming in a quick reference format.

Uploaded by

Aditya
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 32

Cram It!

Microcontrollers Cheat Sheet


Table of Contents:
Section 1: Basic Microcontroller Knowledge
1. Memory Architectures
2. More Basic Knowledge
3. Important Diagrams, Instruction Set, Other Useful Documents
a. PIC16F887 Instruction Set
b. Diagram of All Memory Locations in PIC16F887
c. Status Register Diagram
4. Oscillators Explained
5. Memory Organization
6. Status Register
7. W - Registers

Section 2: Actual Programming


8. Input and Output Ports
9. Assembly Language and Intel HEX Format (Include Little Edian and Big Edian)
10. Labels and Radix
11. Memory Addressing
12. Directives (Including Directive List) → Not allowed for programming problems
a. Configuration Bits
13. Macros
14. Delay Loops
15. Subroutines
16. Look Up Tables (Not on Exam)
17. Boolean Logic Instructions Masking
18. Interrupts
19. Timer 0
20. Timer 1 (Not on the test)

Work on this document was done by: Big Chungus's Revenge#7618

IMPORTANT: Note that data memory refers to memory holding data, while program memory refers to memory holding
instructions for the microcontroller. In the PIC16F887, both program memory and data memory are stored within one
memory device.
Section 1: Basic Microcontroller Knowledge

Memory Architectures:
● Harvard:
○ The Harvard Architecture uses separate memory storage for code and data. This means that a computer
using Harvard Architecture stores its program memory and data memory in the same memory device.
○ Harvard Architecture is more complex, and thus also more costly.
○ The processor requires only one clock cycle as it has separate buses to access both data and code.
● Princeton (Von Neumann):
○ Princeton Architecture has a single memory which has to be shared by data memory and program
memory.
○ It is simpler, and thus cheaper for certain applications.
○ The processor requires two clock cycles, one to fetch the code and one to fetch the data, because it has
one bus to fetch both code and memory.
More Basic Knowledge:
Program Memory Fetch Cycle:
- This is just the method that the microcontroller uses to fetch instructions from the program memory. There are 3
“steps” involved in this. Do not worry though, this will not be very important
- PC: Program Counter, which is counter that points to each memory location in the program memory, will
increment. Thus, the microcontroller will begin execution of the next instruction as the program counter
will point to the next memory location.
- IR: Unsure, fill in later
- Sequence Controller: Unsure, fill in later

Data Memory Write Cycle:


- This is just the method that the microcontroller uses to write to the data memory.

ALU (Arithmetic Logic Unit):


- The arithmetic logic unit is the place in the
microcontroller where all the operations are done.
Depending on which microcontroller, the ALU can do a
variety of arithmetic and logical operations, including
Add, Subtract, And, Or, Multiply, or Divide. Check the
microcontroller manual for information on which
operations can be done.

Types of Processors:
- RISC (Reduced Instruction Set Computer):
- This type of processor / computer has very few possible instructions. The instruction set does not have
many commands that ease writing code.
- Thus, this type of processor makes it easier for the hardware designer, as they only have to
accommodate limited instructions, but harder for the software designer, as they must create longer
programs with more simple instructions from a limited instruction set.
- Less costly
- CISC (Complex Instruction Set Computer):
- This type of processor / computer has a very large instruction set.
- Makes it easier for software designer, but harder for hardware designer, as the hardware designer must
accommodate harder, more complex instructions, but the addition of these complex instructions allow
the software designer to create shorter programs form a larger instruction set.
- More costly
Important Documents, Instruction Set, Other Useful Documents
Oscillators Explained:
- The oscillator is a part of the circuit
that controls the frequency that the
clock of the microcontroller is running
at. This is important because it can tell
one how many clock cycles are
happening per second.
- When calculating delay loops, know
the frequency is important.
- Setting frequency is easy, and one can
simply do this using the Oscillator
Control register (OSCCON).

- When writing code for the


microcontroller, one can set the
OSCCON Oscillator Control register to
one of 8 values. It is possible to see
these values to the right.
Memory Organization
How exactly are data and program instructions stored within the PIC16F887?
- Data memory and program memory are stored in the same memory device. This memory storage device is 8K x
14, which just means that there are 8K memory locations, with each memory location storing 14 bits.
- The memory device has 4 banks, each of which have a memory storage of 2K x 14.
- Data memory and program memory are both stored
within the 8K x 14 memory device.

How does the microcontroller know which instruction to execute?


- There is a program counter, which is 13 bits, which is capable of addressing the program memory space.
Essentially, the value of the program counter tells one about which memory location the microcontroller is
looking at, and give the address of the current instruction.

Where are the reset vectors and interrupt vectors?


- Reset Vector: At memory location 0000h
- Interrupt Vector: At memory location 0004h

What are special function registers (SFR) and general purpose register (GPR)?
- In the data memory, there needs to be some memory locations which hold important values that control input /
output in the microcontroller, that control the oscillator, and that control some of the microcontroller’s
functions. Then, there needs to be some memory locations that hold data. Thus, there are special function
registers and general purpose registers.
- Special Function Registers: Found at the beginning of every bank. They store important information for
registers like TRISA / B / C / D, PORTA / B / C / D, and other registers like the STATUS register. We will get
into what these registers mean and what they do in a couple pages.
- General Purpose Registers: Found throughout the middle and end of every bank. They store data.

Introduction to the STATUS Register?


- The STATUS register is a register that stores information about the last operation done by the ALU. This makes it
very useful, as it can tell information about an arithmetic operation, such as whether the result was zero,
whether there was a carry, whether a carry occurred form the most significant bit, or other information that can
be found on the status register datasheet page.
W Register Explained:
What is the W Register?
- The W Register is the Working Register, and many
consider it to be the most important register. After
an operation is done in the ALU, the result can be
stored in the W Register. In addition, numbers
must be brought to the W - Register, where they
can be performed operations on.
- For example, in order to move data from A to B,
the data has to be moved from location A to W
and then from W to B. → A quote from Mr.
Paterno’s slides

How does the data bit work?


- When performing an instruction that uses the W register, usually there is also a data bit. This data bit controls
where the result of the instruction operation goes.
- Data Bit:
- 1: Data goes forward into the memory location specified
- 0: Data goes back into the W Register and is stored there

Let’s say I am analyzing some instructions. Then, it


turns out that there is nothing where the data bit is
supposed to be written. What do I do?
- It is important to remember that the data bit d
defaults to 1, which means that if there is no value
specified, data goes forward into the memory location
specified, and nothing happens to the W register (the
last put value into the W Register stays there)...
Section 2: Actual Programming

Input and Output Ports:


What are input and output ports?
- Input and output ports are located on the microcontroller in order to allow the microcontroller to interface with
the outside world. This specific microcontroller, the PIC16F887, has 5 ports (A → E) each with 8 pins except PORT
E with 3 pins

How do we set up these ports?


- In this study guide, we will look at the different registers that exist and how they function and need to be set in
order to get the input and output working on a microcontroller

PORT A:
- TRISA Register: Setting a 1 will make the corresponding PORTA pin an input, clearing a 0 will make the
corresponding PORTA pin an output.
- PORTA Register: Each bit on the PORTA register corresponds with the corresponding PORTA pin on the
microcontroller. All this means that if there is 5V on the 4th PORTA pin, the 4th bit of the PORTA register will be a
1. Simple
- ANSEL Register: Setting a 1 will cause all digital reads to be read as a 0 and allow analog functions to work
correctly. The state of ANSEL bits have no affect on digital output functions. The default is analog (1).

How does one initialize the PORTA so that it is ready for use?
- Here is an example initialization of PORTA. Essentially what is happening is we first select the bank the PORTA
register is in (you must always be in the correct bank). Then we clear it to set all the pins to 0 to start the
program off.
- After selecting ANSEL’s bank, we clear it to all 0s, meaning we plan to use PORTA for digital reasons.
- Finally, we decide to move the literal 0Ch, which means 0C hex, to the W Register. Remember that 0C in binary is
00001100. This means that when we move the W Register contents to TRISA (last line), the bits <1:0> and <7:4>
(they mistyped in the picture) are outputs, and bits <3:2> are outputs.
PORT B:
- TRISB Register: 1 will be input, 0 will be output
- PORTB Register: The PORTB register holds the value of all the ports of PORTB on the microcontroller. For
example if the PORTB pin 3 was on, then the PORTB register’s 3rd bit would be 1 as well.
- ANSELH Register: Setting a 1 will cause all digital reads to be read as a 0 and allow analog functions to work
correctly. The state of ANSEL bits have no affect on digital output functions. The default is analog (1).
- WPUB Register: Setting a 1 will allow the weak pull - up attached to each PORTB input / output pin to be turned
on. Clearing a 0 will make disable the pull - up.
- IOCB Register: Setting a 1 will turn on the interrupt - on - change, while clearing a 0 will turn off the interrupt -
on change.

How does one initialize PORT B?


- Self Explanatory, refer to the explanation for PORTA
and the picture found to the right here.

What is Interrupt - On - Change?


- Unsure, fill in later

PORT C:
- PORTC Register: Similar to PORTB and PORTA register, but for PORTC
- TRISC Register: Similar to TRISB and TRISA register, but for PORTC

PORT D:
- PORTD Register: Similar to PORTB and PORTA register, but for PORTD
- TRISD Register: Similar to TRISB and TRISA register, but for PORTD

PORT E:
- PORTE Register: Similar to PORTB and PORTA register, but for PORTE
- TRISE Register: Similar to TRISB and TRISA register, but for PORTE
Assembly Language and Intel HEX Format:
What is assembly language?
- Assembly Language is a set of instructions that can be used to cause the microcontroller to do things
- You can find out information about each type of instruction on the PIC16F887 Instruction Set

Source Code vs. Machine Code?


- Source code is the code that is written by a human. This would be the assembly language instructions. However,
the computer can not understand words. Thus, the assembly language instructions, or source code, need to be
converted into machine code, or code found in 0s and 1s. This machine code can be read by a computer.
- Source code is converted to machine code through an assembler, which converts assembly level code to
machine code.
- A compiler converts high level code (Python, Java, C++) to machine code.

What is Intel Hex Format (.hex files)?


- Intel Hex format is a format for
storing microcontroller programs in
a .hex file
- Each Intel Hex format File has
multiple lines, with each line being
in the format below.
- Little Edian: When each memory location is 8 bits, or one byte, and we are trying to store a multibyte integer,
such as one that is 16 bits or 2 bytes, or 32 bits or 4 bytes, we put the LSB first and the MSB next.
- Big Edian: When we are attempting to store a multibyte integer when each memory location is only one byte, we
put the MSB first and the LSB next.

Little Edian Big Edian


Radix and Labels
What are labels?
- Labels can be thought of as “tags” within the program. You can place a label anywhere in your program, and the
label can be called anything as long as its name begins with an alpha character or an underscore, and contains
alphanumeric characters.
- Labels make it so that the program “jumps” to certain parts of the code in certain circumstances. For example, if
my code is split up into three sections, and each section is called label1:, label2:, and label3: respectively, then if
the program is currently in label2, and needs to perform code in label1:, a GOTO label1 instruction will force the
code to start executing label1.
- The easiest way to understand labels is through an example.

Explaining an Example Use of Labels:


- As you can see on the right, labels in this small example program are main, label1, random,
label3, and isr. Using GOTO statements, it is possible to jump to different labels, and thus,
different points in the code.

You may be wondering, but what if I want to jump to a part of the code based on something? Kind
of like an if statement; if this, then jump to label1, else, jump to random?
- Although we can’t use if statements directly in assembly code (there is something we will
learn about later, macros, but for now, ignore that), we do have some instructions that allow
us to jump to different parts of the code based on a conditional.
- These instructions include BTFSC, BTFSS, DECFSZ, and INCFSZ.

Radix:
- Radix just refers to the number system
that will be used in the code. For example,
if we want to represent a hex number, we
can put the hex number inside of single
quotations like this: H’9f’ or following 0x,
like this: 0x9f.
- We are able to set the radix for all
numbers at the beginning of the code as
well, if we want to use one number system
throughout. This can be done with the
command:

list radix = dec → Sets radix to decimal for example


Memory Addressing:
There are four different types of addressing. These include immediate addressing, register addressing, direct
addressing, and indirect addressing. We will go over what all of these different types of addressing are. Addressing refers
to how the microcontroller knows what piece of data it should access

Immediate Addressing:
- Immediate addressing occurs when the CPU does not access memory. This means that it does not require
accessing any data memory to complete an instruction. For example, the instructions MOVLW, ANDLW, ADDLW,
SUBLW, IORLW, and XORLW use immediate addressing because all of them do not access memory, instead doing
an action on a literal number value. In addition, after the operation is done, the result is put into the W -
Register, so no data memory is accessed or written into throughout the whole program.

Register Addressing:
- Register addressing occurs when

Direct Addressing:
- Direct addressing occurs when the CPU uses a file register to specify the memory location. All this means is that
when an instruction is executed by the microcontroller, a memory location is accessed. A memory location can
be accessed in two ways: Either the memory location is specified using a hex value 0x20, for example, or by
stating the name of a register, like PORTA, TRISA, or PORTB.
- It is important to note that direct addressing occurs when instructions such as CLRF, DEF, MOVF, MOVWF, and
INCF are used

Indirect Addressing:
- In order to understand indirect addressing, there are two terms you must be made familiar with. These are FSR
register and INDF register.
- FSR Register: The FSR register holds a literal value that points to a memory location.
- INDF Register: The INDF Register is not a physical register. It just refers to the data stored in the memory location
pointed to by the FSR pointer.

Let’s have a look at an example of indirect addressing:


Let’s assume that in the GPRs (General Purpose Registers) there are 3 memory locations that have 3 literal values.
Memory location 0x20 has literal value 0xAA stored, memory location 0x21 has 0xBB stored, 0x22 has 0xCC stored.

Now, let us assume that we set FSR = 0x20. As the INDF register refers to the data stored in the memory location
pointed to by the FSR pointer, INDF = 0xAA.
If we do INCF FSR (increases FSR by one), that makes FSR = 0x21, and will mean that INDF = 0xBB, and memory
location 0x21 is still 0xBB. Do you see what just happened there?
Finally, let’s do INCF INDF (increases INDF by one), that makes FSR still = 0x21. However, INDF = 0xBC, and
memory location 0x21 is now 0xBC. This tells us that when performing actions on the INDF register, we are actually also
changing the value inside of the memory location that FSR is pointing to, which is the value stored in the INDF register.
Directives
- Directives are commands that produce assembly code at assembly time based on the nature of the directive.
- They are not instructions, and do not convert directly into OPCODES, and are not casesensitive.
- There are six different types of directives. For the cases and purposes of this study guide, I will focus and mention
only the ones that come up during the MCA ECET Course.

NOTE: Only important (according to me) directives are included in the text below. For a list of all directives, please check
page 129 and later of the slides on Microcontrollers

● Configuration bits are special bits that are stored in the microcontroller that store the state of different
microcontroller processes. For example, one microcontroller process is called the watchdog timer. Oftentimes, it
hinders the function of a program, and oftentimes, it is necessary to turn it off. This can be done using the
__config directive

Control Directives:
● __config
○ Allows you to specify which of the configuration bits will be turned off and which will be turned off.
■ Example: __CONFIG _CONFIG1, _HS_OSC & _WDT_OFF & _PWRTE_ON
● #include
○ Allows you to include a file in your program. For the purposes of microcontroller programming, and in
this case, programming the PIC16F887, every file in MPLAB X or MPLAB 8.5 must have the below line to
get the program to assemble properly.
■ Example: #include <p16f887.inc> → Include at top of program
● banksel
○ Allows you to select the bank of the register or memory location you will be working with. Note that if
you access any register, you must select the bank of that register using banksel. Once you’re in, say Bank
1, then you do not have to specify banksel until you are about to access a memory location or register
that is not in Bank 1.
■ Example: banksel PORTB → Selects Bank 1, as PORTB is in Bank 1
● equ
○ Sets some “variable” word equal to a number literal. Note that this word, whenever used in the code, is
replaced by the number literal. For example, amount EQU 5 will set amount = 5, so whenever amount is
written in the code, you can mentally replace it with 5.
■ Example: count EQU 0x20 → count = 0x20 when used in code
● org
○ Forces program execution at this address and sets the program origin to this value. Essentially, if you see
an org directive in code, all it means is
Conditional Assembly Directives:
- The thing you need to know about conditional
assembly directives, which include else, endif,
endw, if, ifdef, ifndef, and while, are that they do
not function the same way as similar functions
in coding languages like Python, C ++, or Java, or
Javascript. These functions, when used in
assembly language, control what code is
compiled. This means that they decide what
code even makes it to the microcontroller.

- In order to explain this better, I will provide an example to the right.


- This code seems simple. If the input is 1, we move 0xAA to the W
register. However, this is not how it works. The thing you need to
know about conditional assembly directive is that they don’t
function at run time, instead functioning at compile time. This
means that even before the program has started running, the
program will look at input. If it is 1, then it will move MOVLW
0xAA to the next line, and discard MOVLW 0xBB altogether from
the program. Then MOVLW 0xBB will never show up again. Thus,
it is not possible to use conditional assembly directives regularly in assembly language code. You should
not try to do this.
- However, that being said, it is important to note that there is one case in which these conditional
assembly directives do work: IN MACROS
- We will get to macros later

Data Directives:
● cblock
○ The cblock directive starts a section of the code in which names can be
given to a block of constants.
● endc
○ The endc directive ends a section of the code in which names can be given
to a block of constants.

Listing Directives: Not often used


Macro Directives: Wait for next section
Object File Directives: Not often used
Macros
What are Macros?
● Macros are user - defined sets of instructions and
directives. They consists of sequences of
assembler instructions and directives, and can be
created to use arguments as well. Note that these
are NOT functions.
● When a program with one macro or multiple
macros is executed, what happens is that the call
to the macro in the assembly language code is
replaced in line by assembly language code that
represents the function of the macro.

How are MACROS created:


label macro[arg1, arg2, … argn]



endm

Where are MACROS located in the code?


- Macros are located at the beginning of the code,
and must be defined before they are used (forward
references to macros are not permitted).
- As a macro is not completely assembly code, it is
allowed to have if and while and for loops that
work like they would in most high level
programming languages (Python, C++, Java,
Javascript). Thus, it can have arithmetic operators,
which can be found in the charts below.

Let’s Look at an example of a macro:


Important Note about Macros: An important thing to know about macros are that they are a preprocessor. What this
means is while the program is compiling and assembling and getting put into the microcontroller, the macro is not
getting put with it. Instead, the assembly language code that represents the macro is getting physically put into the
assembly language code wherever the macro is called. What this means is that bit test instructions such as BTFSS or
BTFSC, when called before a call to a macro, do not skip the whole macro, but only the first line of the macro. This
means that it is best (and not possible lol) to not use bit test instructions to skip macros. That would work for
subroutines, but not for macros, which we will get into later.
Delay Loops:
❏ Delay loops are pieces of code found within a microcontroller program that allow the microcontroller to “wait” a
certain amount of time (in seconds or milliseconds) until executing the next instruction. The way they work is
quite simple.

How do delay loops even work?


❏ We know that each instruction takes a certain amount of instruction cycles to complete, and that each
instruction cycle takes 4 periods of the clock in the PIC16F887 microcontroller. If we know the frequency, ie.
number of clock cycles per section, we can devise a formula to find the number of seconds it takes to complete
one instruction cycle.
❏ fOSC = frequency, in number of clock cycles per second
❏ 4 = number of clock cycles per instruction cycle
❏ TCYC = time per instruction cycle in seconds

❏ Thus, now that we know the time taken per instruction cycle, let’s get into how delay loops actually work in
assembly language. Essentially, what happens is first, we divide the amount of time we want to delay for by TCYC.
This is how many instruction cycles we want to “skip,” or “waste,” or “wait out.” Turns out there is a perfect
instruction to “skip” and do nothing for one instruction cycle: NOP
❏ Now, we create a loop full of NOPs, with one NOP for each instruction cycle we need to skip.

Nested Loops:
❏ When creating a delay loop, there are three values you need to know and adjust. First, is the number of
instruction cycles per one iteration of the delay loop. This is equal to the number of NOPs, plus two for the GOTO
statement (the GOTO statement takes two instruction cycles), plus one for the DECFSZ command. The number of
instruction cycles for each instruction can be seen on the instruction set for the PIC16F887 microcontroller.

Build a Delay Loop Workshop:


Similar to build - a - bear workshop, but this one will help you get a degree…
1. Find TCYC, also known as the time per instruction cycle in seconds, by dividing frequence in number of clock cycles
per second by 4, the number of clock cycles per instruction cycle. Equation: TCYC = 4 / fOSC
2. Find the number of instruction cycles needed by dividing the amount of time the delay loop needs to delay for by
TCYC
3. Divide by 250 or some number less than 255 that is easily divisible (if applicable). If the resulting number is
smaller than 10 (or the maximum number of NOPs you want in your inner loop), create a delay loop that loops
for the number you divided by, with 3 less NOPs than the result, a DECFSZ statement, and a GOTO statement
inside of the loop.
4. If 3 didn’t work out, it means you need a nested loop. a. Divide the number of instruction cycles from 2 by some
easily divisible, large number less than 255. b. Divide this number by an easily divisible, large number less than
255. The resulting number minus 3 is how many NOPs you need in inner loop, along with a DECFSZ and GOTO.
Inner loop will be repeated the result of b. number of times, and the outer loop repeated result of a. times.
Confused? You may skip to the example if you’re not: OK, OK, I get it. You’re didn’t understand the build a delay loop
workshop. Too many words. Well, let’s try an example of creating a delay loop together. In addition, I will include the
general format for creating a delay loop that ALWAYS works. Let’s try it.

Example Question: Let’s say I want to create a delay loop that delays the code for half a second (0.5 seconds). Create the
code for this. The microcontroller in question will be the PIC16F887, which has a frequency of 4MHz.

1. TCYC = 4 / fOSC → 4 / 4MHz = 1 us instruction cycle, or a 0.000001 second instruction cycle


2. 0.5 second / 0.000001 second instruction cycle = 500000 instruction cycles in one second
3. What we need to do is create a delay loop that goes through 500000 instruction cycles. Let’s use 2 loops, an
inner and outer loop.
a. 500000 instruction cycles / 250 cycles = 2000
b. 2000 instruction cycles / 250 cycles = 8
4. The inner loop will be repeated 250 times, the outer loop (which includes the inner loop) will be repeated 250
times, and there will be 5 NOPs (5 instruction cycles), 1 DECFSZ (1 instruction cycle), and 1 GOTO (2 instruction
cycles) statement inside of the delay loop. 5 + 1 + 2 = 6 + 2 = 8

; [prior code]
MOVLW .250 ; Moving 250 decimal value (specified by the . before the 250) to the W register
MOVWF 0x40 ; We are storing the 250 count for the outer delay loop in memory location 0x40
CALL delayloop1
; [after code]

delayloop1:
BANKSEL 0x41
MOVLW .250 ; Moving 250 decimal value to the W register
MOVWF 0x41 ; We are storing the 250 count for the inner delay loop in memory location 0x41
delay1:
NOP ; First out of five NOPs
NOP ; Second out of five NOPs
NOP ; Third out of five NOPs
NOP ; Fourth out of five NOPs
NOP ; Fifth out of five NOPs
DECFSZ 0x41 ; Decrement 250 count value in 0x41 memory location, if it is not 0, redo inner loop
GOTO delay1 ; Return to beginning of inner loop → Skipped if 0x41 is 0
DECFSZ 0x40 ; Decrement 250 count value in 0x40 memory location, if it is not 0, redo outer loop
GOTO delayloop1 ; Return to beginning of outer loop → Skipped if 0x40 is 0
RETURN ; return to where the program “left off,” right after the CALL delayloop1
Boolean Logic Masking Instructions
- Essentially, what the point of this chapter subunit is to instruct you on how to compare two binary values in the
PIC16F887 microcontroller. This microcontroller does not have a “compare” instruction, so in order to compare
two values, a variety of different methods must be used.
- In addition, sometimes in a program, the writer of the program may wish to compare only certain bits from the
binary numbers - for example comparing the 2nd and 3rd bit - which is done through a process called masking.
How to compare two 8 - bit numbers in the PIC16F887 Microcontroller?

1. The Subtract Instruction (SUBLW or SUBWF)


a. Using the SUBWF instruction, it is possible to subtract the two 8 - bit numbers
that you wish to compare from each other. For example, take number a and
number b.
i. If a > b, you subtract a - b, you get a positive number
ii. If a = b, you subtract a - b, you get 0
iii. If a < b, you subtract a - b, you get a negative number

b. The STATUS Register gives information about the result from an arithmetic
computation. After using the SUBWF instruction, look to the STATUS register to
check if the result is positive, a 0, or negative. Based on this, it is possible to tell
which number, a or b, was larger.
i. Result Z C
ii. Positive: 0 1
iii. Zero: 1 1
iv. Negative: 0 0

2. The XOR Instruction (XORWF or XORLW) → Only tests for equality


a. The XOR instruction is also particularly useful for comparing two 8 - bit
numbers in the PIC16887. The way it works is that when some number, for
example 10101010, is XOR’d with the same number, 10101010, because of
the XOR truth table (see to the right), the result becomes 0.

b. Thus, if the result of XOR’ing two


numbers is 0, the STATUS register’s Z bit
will become a 1. Thus, you know that the
two numbers are the same.
How do we compare only specific bits in the 2 8 - bit numbers we are attempting to compare?
1. Bitwise Logical And Masking
a. Decide which bits you want to compare in the two 8 - bit
numbers. Then, clear the rest of the bits in the numbers
to 0, and clear them to 0. The way we can do this is using
the And function.
b. Example: Let’s say we want to compare the 2nd and 3rd
bits of 2 8 - bit numbers. Let’s start by Anding each 8 - bit
number with binary value of B’00001100’ which sets all
the rest of the bits in each of the 8 - bit numbers to 0,
while retaining the original value of the 2nd and 3rd bits.
Then, simply use the XOR or Subtract comparison
techniques given on the last page of this document.

2. Bitwise Logical Or Masking


a. Decide which bits you want to compare in the two 8 - bit
numbers. Then, set the rest of the bits in the numbers to
1. The way we can do this is using the Or Function.
b. Example: Let’s say we want to compare the 2nd and 3rd
bits of 2 8 - bit numbers. Let’s start by Oring each 8 - bit
number with binary value of B’11110011’ which sets all
the rest of the bits in each fo the 8 - bit numbers to 1,
while retaining the original value of the 2nd and 3rd bits.
Then, simply use the XOR or Subtract comparison
techniques given on the last page of this document.

How do we ROTATE the bits in a number:


1. RLF: Rotate left through carry

2. RRF: Rotate right through carry


Interrupts and Timer0

What is an interrupt?
- An interrupt is an event in the microcontroller that forces the microcontroller to stop execution of the normal
program and go to the isr (interrupt service routine) and perform some service related to the event.
- When an interrupt occurs, the microcontroller will automatically go to the ISR (Interrupt Service Routine)

IMAGES FOR REFERENCE MATERIAL :::: SCROLL BELOW TO ACCESS REST OF CHAPTER
What are the above two register descriptions there for?
● They are there for easier access while reading the material below…

What are the different interrupts we will be forced to accommodate?

RB0/INT Interrupt:
● The RB0/INT Interrupt is an interrupt that occurs when the value on the RB0 port, also labelled as INT, changes.
An interrupt can be forced to occur when RB0 / INT goes from low to high, or when it goes from high to low, BUT
NOT BOTH. We can control this using the register shown above:
○ TRISB Resister → Bit 0 → Set to 1 for input, only then will RB0/INT Interrupt work
○ INTCON Register → INTE → Set to 1 to enable RB0 / INT Interrupts
○ OPTION_REG Register → INTEDG → 1 for rising edge interrupt, clear to 0 for falling edge interrupt
○ INTCON Register → INTF → Becomes 1 if interrupt occurs, 0 if not, must be set to
0 again
during the interrupt ISR sequence

PORT B Interrupt:
● The Port B interrupt is an interrupt that occurs when any of the values on the PORTB input / output strip change
at all.
○ TRISB Register → All bits → Set to 1 for input, only then will PORTB interrupt work
○ INTCON Register → RBIE → Set to 1 to enable PORT B INTERRUPT, clear to 0 else
○ INTCON Register → RBIF → Becomes 1 if interrupt occurs, 0 if not, must be set to 0 again
during the interrupt ISR sequence

TIMER 0 Interrupt:
● Timer 0 is a timer that runs while the program is running. Once it reaches 255, also known as when it overflows,
Timer 0 causes an interrupt sequence, and the program goes to the ISR. We are able to set the TIMER0 counter
so that it causes an interrupt sequence every x seconds.
○ TMR0 → 8 bits → 8 bits that hold the value of TIMER 0
○ OPTION_REG → T0CS → Select 0 to use TIMER 0, 1 for external counter (for ECET, 0)
○ OPTION_REG → PSA → 0 to use TIMER0
○ OPTION_REG → PS<2:0> → 3 bits that decide the prescaler rate
○ INTCON Register → T0IF → Becomes 1 if interrupt occurs because TIMER0 overflowed, 0
if not, must be set to 0 again during the interrupt ISR
sequence

● As you can see to the right, that is the TI5MER0 delay


equation. N is the prescaler value, see OPTION_REG.
Designing an ISR Routine:
● Something important you need to know while designing an ISR routine is that you need to focus on one ISR
routine at a time. You can’t have the program going through an ISR routine and have interrupts happening at the
same time. This is why you must disable all interrupts while in the ISR routine, and after leaving the ISR
routine, turn all interrupts on again.
● This can be done by turning GIE, or the Global Interrupt Enable Bit, to a 0 at the start of the ISR Routine, and
turning it to a 1, while leaving (which is accomplished through the RETFIE Instruction)...
● Remember to use GOTO and not CALL for the ISR

You might also like