Let’s begin assembly
Micro controllers are very fast devices. In the assembly, you can rule every instruction cycle. For example, with a 4MHz x-tal, you can rule to 1us(micro second) instructions. You can do processes in micro seconds and calculations high-precisely.
PIC micros have RISC(Reduced Instruction Set Computer) architecture. They have only 35 instructions. We can implement very complex programs just by using these 35 instructions.
There are 8bit,16bit,32bit microcontrollers. The most common ones are 8bit. If you need fast processing and wide bandwidth you can use 16bit or 32bit. We will continue with 8bit ones.
Before starting PIC assembly, it is useful to be familiar with a high level language.
The structure of a PIC assembly code
My_REG EQU h’20′
;Part that do work
END ;End of program
You don’t have to understand what this program does for now. Just see the structure of the program. At the begining we do defines. For every, PIC microcontroller, microchip publish a define file in which there are general definitions for that microcontroller. We usually need general purpose registers to use in our programs. We do also this general purpose register definitions at the begining of our code.
After definitions, we need some configurations. These configurations can be IO, interrupt, peripheral configurations. Actually, configurations can be done while running code.
Our working part is usually a loop. In this loop, we can put codes to interact with outside world or just wait in a loop for interrupts. The interrupt routine handles program when an interrupt has been occured. We will discuss interrupts later.
If you compile and run the above code, you will see the LEDs connected to PORTB will light like 0-1-0-1-0-1-0-1 (where ones on and zeros off).
Notice that “;” signs are used for commenting.
Assembly code flow
In assembly, the program flows step by step. That means two different code blocks cannot be processed at the same time. In other words, there is no multitasking in assembly. (Actually, in high level languages, multitasking ability is provided by operating system.)
Program starts from beginning and implements every instruction step by step.
While programming microcontrollers, we only deal with registers. For example, if we want to turn on a led which is connected to fifth pin of PortA, we send a binary 00100000 (The least significant bit is right most bit and we start to count from zero) to PortA register. This will turn on 5. led and turn off all the others. All ports, status, configurations are defined by special registers.
Therefore, when we define a general purpose register, we can use it bit by bit.
Mostly, we use Accumulator, PORT registers, for port input/output configuration TRIS registers, STATUS register which holds the status of process results, OPTION_REG register for configuration and interrupt registers.
Every microcontroller has a register map. In this map you can find where these registers are present in the memory. Furthermore, in this map you will see BANKS. To use a register from memory, you should switch to related BANK. Then you will be able to use the register.
8 bit microcontrollers have 4 BANKS. You can switch them by setting the 5. and 6. bit of STATUS register. For 00 points bank0, 01 points bank1, 10 points bank2, 11 points bank3.
To set one bit in a register we use BSF command and to clear we use BCF.
This command will set the 3. bit of PORTA.
To switch BANK we can use this command:
These two commands switch to BANK1.
To change register at once we use two command :
This will move binary 00001111 to PORTB register. In assembly, we cannot put a number to a register directly. To do this we use accumulator(W) register. MOVLW means “move literal to accumulator”. The literal is the constant comes after command (here b’00001111′). And, MOVWF means “move accumulator to register”. The name of register comes after command (here PORTB).
Another way to change the value of register is calculation. For example, we can add two numbers and send the result to a register.
The first command move binary 00000001 to accumulator. The second command will add accumulator and PORTA, then, move result to PORTA. (If we use W instead of F in the ADDWF command, the result will be moved to accumulator.)
Reading the status of a bit (if condition in assembly)
There are two command for reading status; BTFSS and BTFSC. These commands are “if” structure of assembly. BTFSS means “Bit test, if set(1), skip next instruction” and BTFSC “Bit test, if clear(0), skip next instruction”.
In the example, 1. bit of PortA is tested and if it is clear(0), the next instruction (in the example it is BSF PORTB,0) will be ignored and the program continues with the next instruction (here MOVLW b’01010101′). If 1. bit of PortA is set(1), it will do nothing and continue with next instruction (here BSF PORTB,0).
Before using IO ports, we need to define them as inputs and outputs. To do this we use TRIS registers.
In the example, we set the first 4 bits as inputs and last for bits as outputs.
An example case
Let’s implement a case with the knowledge we gained so far.
Let’s say we have a water tower which collects rain. The tower consists 2 water level sensor and 1 button to empty tower. And it has 2 valve.
Case 1 : When the water level reaches the level sensor X, valve A will be opened.
Case 2 : when the water level reaches the level sensor Y, both valve A and B will be opened.
Case 3 : when the empty button is pressed, valve B will be opened.
Let’s say :
X = PORTB.0,
Y = PORTB.1 and
Button = PORTB.2
A = PORTB.3
B = PORTB.4
BCF STATUS,6 ;Switch to
BSF STATUS,5 ;BANK1 (TRIS registers are in the BANK1)
MOVLW b’00000111′ ;We need 3 inputs and 2 outputs.
MOVWF TRISB ;Configure PORTB
BCF STATUS,6 ;Switch back to
BCF STATUS,5 ;BANK0LOOP:
BTFSC PORTB,0 ;Check if sensor X is active otherwise skip next
BSF PORTB,3 ;if sensor X is active, set valve A
BTFSC PORTB,1 ;Check if sensor Y is active otherwise skip next
BSF PORTB,4 ;if sensor Y is active, set valve B
BTFSC PORTB,2 ;Check if Button is pressed otherwise skip next
BSF PORTB,4 ;if Button is active, set valve B
GOTO LOOP ;Stay in loop
In the example program, when an output is set(1), it will never be reset(0) again. Outputs will save their status. Because, we didn’t put any instruction to reset the outputs. Actually, the above program is useless for real life. Because, you can implement the same scenario by using only a few logic gates. But, when you need more complex scenarios, time delays, or serial communications, it is better to use a microcontroller instead of really big PCB with a lot of logic gates.