Microcontrollers Cheat Sheet
Microcontrollers Cheat Sheet
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
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).
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.
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.
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
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:
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.
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.
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.
❏ 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.
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.
; [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?
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
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…
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