up student book

86
Microprocessors Project’s book Ainhoa Cort ´ es & Ibon Zalbide Donostia - San Sebasti´ an 2010

Upload: salah-dahouathi

Post on 04-Jun-2018

253 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 1/86

Page 2: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 2/86

Page 3: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 3/86

Contents

1 Using MPLAB IDE 1

2 Blink a LED 9

3 Displaying a digit on one 7-segment display 25

4 Simultaneous display on all four 7-segment displays 33

5 Four-digit counter with button read 45

6 LCD display 59

7 Counter with button read in mikroC 75

A PIC16F877A Datasheet Extract 83

B MPASMTM /MPLINKTM PICmicro R MCU Quick Chart 83

C MPLAB IDE Quick Chart 83

D Quick Reference Guide for C language 83

E Pointers 83

F Creating First Project in mikroC for PIC 83

Page 4: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 4/86

Chapter 1

Using MPLAB IDE

In this chapter you will learn:

•  how to use MPLAB Integrated Development Environment,

•  how to create and build a project using assembly source file.

Microprocessors - Using MPLAB IDE 1 - 1

Page 5: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 5/86

Create working directory 

LAB 1 : Using MPLAB IDE

Lab 1 procedure

Create working directory 

1. Create the lab1 directory

Before we start using MPLAB IDE we will create the directory to work in. Using [F7] in

Total Commander create lab1 directory under   C:\uP\labs.

Copy the files 

2. Copy the files that will be used for the project

MPLAB IDE  comes with a number of useful files that we can use for our first project.

This will allow us avoiding starting from a blank page. We are going to copy three files

to the lab1 directory. These are:

(a) The first one is the template file for the actual program. Copy the file 16F877ATMPO.ASM

(16F877A template file) located in C:\Program Files\Microchip\MPASM Suite\

Template\Object\ and rename it to lab1.asm.

The template files are simple files that can be used to start a project. They have the

essential sections for any source file, and have information in them that will helpyou write and organize your code.

(b) The second file is the  P16F877A.INC   and it is located in   C:\Program Files\

Microchip\MPASM Suite\. This is a header file that defines configurations, reg-

isters, and other useful bits of information for the PIC16F877A microcontroller.

(c) The third file is the linker script named 16f877a.lkr   located in the  C:\Program

Files\Microchip\MPASM Suite\LKR\ directory.

After you have copied all three files you’re ready to create the project.

1 - 2 Microprocessors - Using MPLAB IDE  

Page 6: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 6/86

Start MPLAB IDE 

Start MPLAB IDE 

3. Start MPLAB IDE using the desktop icon

1010110101010001011010111010101010100010010101000110101101010100010110101110101010101000100101010001

MPLAB Desktop

Create the Lab1 project 

4. Choose PICmicro device

Choose Select Device from the Configure menu.

In the Device window, select 16F877A from the list as the processor.

1010110101010001011010111010101010100010010101000110101101010100010110101110101010101000100101010001

Select The Device

Note the red and green lights. These indicate which MPLAB components support this

device. A green light indicates support.

Microprocessors - Using MPLAB IDE 1 - 3  

Page 7: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 7/86

Create the Lab1 project 

Sometimes you might see a yellow light. This would indicate minimal support for an

upcoming part that might not yet be fully supported by the MPLAB  component. Usage

of components with a yellow light instead of a green light is often intended for earlyadopters of new parts who need quick support and understand that some operations or

functions may not be available.

5. Create a new project

Next, we’ll create a project using the  Project Wizard. A project is the way your files

are organised to be compiled and assembled. We will use a single assembly file for this

project. Choose the Project Wizard from the Project menu.

The Project Wizard will start. Press Next to continue.

1010110101010001011010111010101010100010010101000110101101010100010110101110101010101000100101010001

Wizard – Confirm Device

6. Select Language Tools

After confirming the  16F877A device, you must select the  Microchip MPASMTM Tools

Suite and note that  MPLAB IDE  already knows the location of the assembler and the

linker. You should not have to browse to set these up. If the location is not set, the default

location for the assembler and linker are shown here.

Click Next to continue.

1 - 4 Microprocessors - Using MPLAB IDE  

Page 8: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 8/86

Create the Lab1 project 

1010110101010001011010111010101010100010010101000110101101010100010110101110101010101000100101010001

Wizard – Select Language Tools

7. Add files to project

You must choose a Project Name and a Project Directory. These are lab1 and C:\uP\

labs\lab1 respectively.

1010110101010001011010111010101010100010010101000110101101010100010110101110101010101000100101010001

Wizard – Name Project

When you are asked which files you want to add to project, select the three files discussedabove: lab1.asm, P16F877A.INC and 16f877a.lkr. Use the Add button to add these three

files to the list on the right side, then check the boxes to the left of the names.

Microprocessors - Using MPLAB IDE 1 - 5  

Page 9: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 9/86

Create the Lab1 project 

1010110101010001011010111010101010100010010101000110101101010100010110101110101010101000100101010001

Wizard – Select Template File

8. Summary Finish

The last slide in the Project Wizard shows the information on the project. Click Finish to

exit to the MPLAB IDE desktop.

1010110101010001011010111010101010100010010101000110101101010100010110101110101010101000100101010001

Wizard – Summary Finish

PIC16F877A

9. Check if the project builds correctly

Build the project by pressing [Ctrl + F10] or   [Project → Build All].

Current files don’t have any of our code in them yet, but this just assures that the project

is set up correctly and there are no errors.

1 - 6 Microprocessors - Using MPLAB IDE  

Page 10: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 10/86

Create the Lab1 project 

1010110101010001011010111010101010100010010101000110101101010100010110101110101010101000100101010001

Project – Output Window

You’re done.

Microprocessors - Using MPLAB IDE 1 - 7  

Page 11: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 11/86

Create the Lab1 project 

1 - 8 Microprocessors - Using MPLAB IDE  

Page 12: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 12/86

Chapter 2

Blink a LED

In this chapter you will learn:

•  how to create variables,

•  how to increment or decrement a variable,

•  how to perform conditional jumps (branching),

•  how to create a loop,

•  how to configure a port (and being aware of banking),

•   how to write to a port,

•  how to simulate your code using MPLAB built-in simulator,•  how to watch special function registers (SFRs) and variables,

•  how to load a program onto the  EasyPIC3,

•  how to run the program and view the results.

Microprocessors - Blink a LED 2 - 9  

Page 13: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 13/86

Create working directory 

LAB 2 : Blink a LED

The objective of this lab is to have a LED blinking on  PORTB.

Lab 2 procedure

Create working directory 

1. Create the lab2 directory

Using [F7] in Total Commander create lab2 directory under   C:\uP\labs.

Copy the files 

2. Copy the files from the previous lab

Using Total Commander  again copy all the files from the previous lab and rename the

lab*.*   files to the current lab. You can select the files by pressing   [numeric +]  and then

write lab  in front of  *.*. Then press [Shift + F6] and enter  lab2.*.

3. Create new project and workspace files

(a) Delete the project file lab2.mcp and the workspace file  lab2.mcw.

(b) Now create new project and workspace files using the project wizard in the MPLAB IDE

exactly as in the previous lab.

(c) When you are asked which files you want to add to project, select these three files:

lab2.asm, P16F877A.INC and 16f877a.lkr.

You are going to repeat this procedure in every subsequent lab.

4. Check if the project builds correctly

Build the project by pressing [Ctrl + F10] or   [Project → Build All]. It should do so as we have

not changed anything. If not, go back and correct any errors in the project configurationuntil lab2 assembles correctly.

Design the program flow in pseudocode 

We want to periodically switch on and off one of the LEDs attached to  PORTB. So the

pseudocode of the program should go like this:

PORTB = output

while ( 1 ) {

PORTB.bit1 = 1

PORTB.bit1 = 0

}

2 - 10 Microprocessors - Blink a LED  

Page 14: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 14/86

Implement the design 

Implement the design 

In the area beneath the label Main, we add the new code.Comments are put in the code after semicolons. The assembler will ignore everything on

a line after a semicolon.

Remember the PIC assembly format:

•  1st column: labels

•  2nd column: code

5. Remove example code

Remove all example code residing between  start and  END.

6. Configure PORTB

The first thing to do then, is to configure the PORTB. As we already know this is done us-

ing TRISB register. TRISB is the data direction register for PORTB, determining whether

each pin is used as an output or as an input.

There are eight bits in TRISB and  PORTB register corresponding to 8 different pins on

the  PIC16F877A. A value of zero in the  TRISB bit corresponding to a given pin means

that that pin will work as an output, a value of one means that it will work as an input.

To configure all pins of  PORTB as output we have to set all bits of  TRISB to zero.

There is no instruction to move a value directly into  TRISB or PORTB, so we need to usethe accumulator to load the data and move it to these registers. This accumulator is called

working register in PICmicro MCUs, and denoted W or WREG.

The first step then is to move a value of zero into the  PIC16F877A’s WREG:

 movlw   0x00

where x in 0x00 stands for hexadecimal notation, which is typical in assembly. You’ll get

accustomed to think hexadecimal and binary soon, you’ll see  ̈.

The next move (no pun intended) is to move the contents of the W register to TRISB.

 movwf   TRISB

As we put a value of zero in TRISB, we make all pins of  PORTB work as ouputs.

7. Another way of configuring PORTB

As it is explained in the section above, we want to configure all pins of PORTB as outputs

 by setting all bits of TRISB register to zero. We have already done this, as we have loaded

a value of zero in WREG and then move the content of  WREG to TRISB.

WREG is the only register that allows to store any posible value by giving it directly in

the program line with movlw instruction. In the special case of making zero all bits of any

register we can also use  clrf instruction. To make zero all bits of  TRISB register we can

write.

clrf   TRISB

Microprocessors - Blink a LED 2 - 11

Page 15: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 15/86

Set up an endless loop 

This is the one-instruction expression equivalent to the former two-instruction expres-

sion:

 movlw   0x00

 movwf   TRISB

8. Configure the bank bits

At start-up, PIC16F877A is located in bank 0 of memory locations. As TRISB register is

implemented in bank 1, we have to move to that bank by changing  RP1  and  RP0 bits in

the STATUS register. To work with bank 1, they should be set to 0 and 1 respectively. So

it is enough that we set the RP0 bit.

We do this before the code we have written until now and will use  bsf  instruction or bit

set in file.

 bsf   STATUS,RP0

After writting in TRISB register we come back to bank 0 by clearing RP0  bit. There is a

complementary instruction bcf instruction or bit clear in file.

 bcf   STATUS,RP0

Set up an endless loop 

There is no operating system on the PIC. The processor is going to execute whatever it

has in the memory. When it finishes with executing the instructions we placed in the

memory it is going to execute those in the subsequent memory locations.

To prevent the PIC from executing who-knows-what rubbish we have to setup and end-

less loop. This is the corresponding part of the pseudocode:

LOOP

...

goto   LOOP

9. Place a label

First we have to add a label that will mark the memory location we want to come back to

when we are done with the loop code. We will call it LOOP and we place it immediatelyafter the initialisation part we have written so far:

...

 bcf   STATUS,RP0

LOOP

10. Loop the loop

Next we have to put an instruction that will effectuate that jump back to the LOOP label.

We accomplish this by executing:

goto   LOOP

instruction, which causes the program to stay in this loop forever.

2 - 12 Microprocessors - Blink a LED  

Page 16: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 16/86

Toggling a  PORTB 

Toggling a PORTB 

11. Toggling a PORTB bit using bcf and  bsfNow it’s time to write code to alternately sending out 1s and 0s on pin  RB0. The code

goes of course inside the loop:

LOOP

; here goes your code

goto   LOOP

First we have to change first bit in  PORTB to 1. We can use the instruction we already

know that does exactly what we want, set a bit in a file register:

 bsf   PORTB, 0

The next instruction should bring the RB0 bit back low.

 bcf   PORTB, 0

12. Toggling PORTB bits using movlw and  movwf

We can also move a literal into the WREG register and then move the contents of  WREG

into PORTB. The following instructions set the RB0 bit high:

 movlw   0x01

 movwf   PORTB

The next instructions bring the RB0 bit back low:

 movlw   0x00

 movwf   PORTB

If we had used a pattern of 0xFF and 0x00 instead of 0x01 and 0x00, we would have

toggled all 8 pins on PORTB, not just the RB0 pin.

Finally we can revise if this is all we wanted to do in the pseudocode design. The original

pseudocode could have been written much better, but we did not want to advance the

events, particularly with the LOOP and  goto ideas.

MAINPORTB = output

LOOP

PORTB.bit0 = 1

PORTB.bit0 = 0

goto   LOOP

Remember that we number bits from 0 to 7, so bit 0 is the first bit.

Now we can compile and test the code.

13. Differences between using bcf/bsf and using movlw/movwf

One of the differences is that using  movlw and  movf we program all bits in the PORTBregister. So if we want to change more than two bits, using  mov  instruction will be more

efficient than a number of  bsf’s.

Microprocessors - Blink a LED 2 - 13  

Page 17: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 17/86

Debug code 

Another difference is that the initial state of  PORTB bits is unknown, they can be on or

off. Therefore, if we want to use  bcf/bsf instructions and make sure that the remaining

 bits are zero, it is necessary first to initialize  PORTB with movlw/movf. movlw   0x00

 movwf   PORTB

and next use  bcf/bsf instructions to toggle bits of  PORTB.

Debug code 

14. Set MPLAB SIM as debugger

In order to test the code, we need a debug tool. MPLAB SIM is a debug tool. It is software

simulator that can be used to test code on the PC.Go to Debugger menu and click on Select Tool option to pull down another menu and

choose the third option MPLAB SIM.

1010110101010001011010111010101010100010010101000110101101010100010110101110101010101000100101010001

MPLAB SIM As Debugger

2

3

1

4

2 - 14 Microprocessors - Blink a LED  

Page 18: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 18/86

Debug code 

These are the MPLAB SIM short cut icons:

1010110101010001011010111010101010100010010101000110101101010100010110101110101010101000100101010001

Debug Short Cut Icons

15. Start the debugger

Next we select [Reset → Processor Reset] from the   [Debugger] pull down menu (or press [F6]

directly), and a green arrow shows us where our program will begin. This was part of 

the template file. The first instruction in memory jumps to the label called Main, where

we put our code. This allows us to jump over the vector areas in memory.

1010110101010001011010111010101010100010010101000110101101010100010110101110101010101000100101010001

Debug – Reset

Microprocessors - Blink a LED 2 - 15  

Page 19: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 19/86

Debug code 

Be careful with the difference between O and 0. The default font configuration of MPLAB IDE

makes thos two characters virtually indistinguishable.

1010110101010001011010111010101010100010010101000110101101010100010110101110101010101000100101010001

Be Careful with O and 0

To solve this problem go to  [Edit → Properties → Text [Tab] → Select Font [Button] → Change

Size to 12pt → OK [Button] → Apply [Button] → OK [Button]].

2 - 16 Microprocessors - Blink a LED  

Page 20: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 20/86

Debug code 

16. Step Into

Now we can press the Step-Into icon or choose Step-Into from the Debugger menu to

single step to our code.

Continuing to press Step-into, we can step through the code.

. . . step . . . step . . . step . . . step ..step and we go back to the start of the infinite loop.

1010110101010001011010111010101010100010010101000110101101010100010110101110101010101000100101010001

Debug – Step Into

Microprocessors - Blink a LED 2 - 17  

Page 21: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 21/86

Configure the oscillator 

17. Watch window

In order to see if our code executes properly we need to see what’s happening on PORTB.

Go to View menu and choose the ninth option Watch.

We will add PORTB to the Watch Window  with Add SFR button. After we press reset

we end up back at the start of the program and the value on PORTB is 0.

1010110101010001011010111010101010100010010101000110101101010100010110101110101010101000100101010001

Watch – Select PORTB

After the instruction that sends a 1 to PORTB, the value of  PORTB changes. Note that

when single stepping, you are stopped at the instruction before it executes, so we didn’t

see a change until we executed the instruction.

As we step further, we see  PORTB with a value of 0. Continually following through the

loop will show PORTB changing from 0 to 1 and back again.

Configure the oscillator 

If our application is running correctly, we are almost ready to program it into the PIC16F877A

sitting on the EasyPIC3 board. The last step is to configure the oscillator.

By default the template file comes with the RC oscillator configured. However the PIC

processor in the EasyPIC3 is driven by a cristal oscillator. This must be reflected in the

configuration bits of the processor.

18. Change the oscillator configuration bit

Open the source file lab2.asm and find the line that begins like:

 __CONFIG _CP_OFF & _WDT_OFF & _BODEN_OFF & [...]

The oscillator bit   RC OSC  should be changed to   HS OSC, where   HS   stands for High

Speed, i.e. crystal oscilator.

2 - 18 Microprocessors - Blink a LED  

Page 22: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 22/86

Program the PIC 

Program the PIC 

19. Connect the EasyPIC to the PCUsing the USB cable provided connect the EasyPIC3 board to your PC.

20. Open the programmer

On the desktop find the icon for the PICFLASH2 programmer and double-click it to run

the application. Alternatively you can run it directly from  C:\uP\programmer  direc-

tory.

21. Select the processor type

In the upper right corner of the programmer window select the correct processor from

the [Device]

 drop down list and select the HS oscillator type.22. Load the program into the programmer

Press the  [Load HEX] button and navigate to  C:\uP\labs\lab2 directory and select the

lab2.hex file.

23. Confirm the oscillator type

If you’re oscillator changed to HS as we set it up in the previous step, you’re doing well.

Otherwise you have most probably chosen the wrong processor type.

24. Download the program

Press the [Write] button to program the PIC.

Note:   If for some reason (for example you have discovered errors in your pro-

gram and needed to recompile it) you want to reload the hex file into the

PICFLASH2 programmer it is enough to press the  [Reload HEX] button to

refresh the hex file.

The RB0 LED should go on. If you were using bcf  and  bsf  and you wonder why more

than one LED is on, go back to step 13 and find out.

What’s wrong? 

Why the LED is not blinking?

Let’s calculate how long it takes for the microcontroller to execute each instruction.

The microcontroller’s operating speed is set by the clock input. A single-cycle instruction

executes during four periods of the input clock signal. All instructions are single-cycle

except jumps (goto) that take two cycles to execute.

On our board the PIC is driven by the 8 MHz cristal oscillator. Therefore a single-cycle

instruction takes:

T clock   = 4 · 1/8 MHz = 0.5 µs.

To switch on the LED we only need two instructions!! First, load the value into W register

and then send it from W  register to PORTB. That corresponds to one microsecond. The

Microprocessors - Blink a LED 2 - 19  

Page 23: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 23/86

Make the LED blink slower 

same time is necessary for switching it off and the jump lasts another microsecond. Sum-

ming all this numbers up we get 3 µs. Well, this is something like 333 kHz. “Slightly” too

fast as for a human eye, we could say  ̈.

Make the LED blink slower 

The solution is to make the PIC16F877A microcontroller to “waste time” between switch-

ing the LED on and off. We need a delay.

One idea, it would be to make a subroutine that places some number in a register and

keeps decrementing it in a loop until it reaches zero. We could call this subroutine be-

tween toggling theRB0 bit up and down. If we initialise our register with 0xFF, which is

255 in human language, and keep decrementing it, we will execute the instructions inside

the loop 255 times. Inside the loop we have an arithmetic operation which lasts one clockcycle and a jump with comparison condition which lasts two cycles. This is what we get

when we calculate approximately how long it takes to execute the subroutine:

Time = (3 · 0.5 µs) · 255 = 382.5 µs

which is still not long enough. The LED would seem to be on all the time.

25. Make the LED blink even slower

Using two registers we can create two nested loops, one inside another. Now the inner

(255 times) loop itself can be run as many as 255 times. If we initialise both registers with

0xFF we could get as far as:

Time = ((3 · 0.5 µs) · 255) · 255 = 97537.5 µs   100 ms = 0.1 sec

If we call the delay subroutine after switching on the LED and again after switching it off,

we will have the LED five times switched on and off during every second. That speed is

visible and we can see how the LED blinks.

26. Design the delay subroutine

The general idea is similar to counting down a two-digit number. Lets start from 99, then

comes 98, 97... 91, 90, 89. When the less significant digit reaches zero, in the next step,

the more significant is decremented and the lower digit is “reloaded” with the maximumvalue again.

The case with our delay counter is similar if we think of them as if one counter were one

digit. First we initialise both variables to 255. Then we keep decrementing delay counter 1

- the less significant “digit”. When it reaches zero we reload it with 255 again and decre-

ment delay counter 2.

The subroutine may run as follows (pseudocode)

DELAY

delay_counter_1 = 255

delay_counter_2 = 255

 _decrement_counter_1

counter1 = counter1 - 1

2 - 20 Microprocessors - Blink a LED  

Page 24: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 24/86

Modify the variable declaration area 

if   ( counter1 != 0 )   goto   _decrement_counter_1

counter1 = 255   // counter reload 

counter2 = counter2 - 1

if   ( counter2 != 0 )   goto   _decrement_counter_1

return

Modify the variable declaration area 

27. Modify the source file

At the beginning of the assembly source file there are examples of different data sections:

;***** VARIABLE DEFINITIONS (examples)

; example of using Shared Uninitialized Data SectionINT_VAR   UDATA_SHR    0x71

w_temp   RES   1   ; variable used for context saving 

status_temp   RES   1   ; variable used for context saving 

pclath_temp   RES   1   ; variable used for context saving 

; example of using Uninitialized Data Section

TEMP_VAR   UDATA 

temp_count   RES   1   ; temporary variable (example)

; example of using Overlayed Uninitialized Data Section

; in this example both variables are assigned the same GPR location by linker G_DATA   UDATA_OVR    ; explicit address can be specified 

flag   RES   2   ; temporary variable (shared locations - G_DATA)

G_DATA   UDATA_OVR 

count   RES   2   ; temporary variable (shared locations - G_DATA)

Overlayed data section G DATA can be removed together with all its variables. We will

not use0 overlayed variables.

We will rename TEMP VAR data section to VARIABLES:

VARIABLES   UDATA 

We will also remove the explicit address from the  INT VAR  section declaration so that

linker will be also free to place shared variables in memory:

INT_VAR   UDATA_SHR 

28. Examine the linker file

If you look into the linker file you will see this code:

DATABANK NAME=gpr0   START=0x20   END=0x6F

DATABANK NAME=gpr1   START=0xA0   END=0xEF

DATABANK NAME=gpr2   START=0x110   END=0x16F

DATABANK NAME=gpr3   START=0x190   END=0x1EF

SHAREBANK NAME=gprnobnk   START=0x70   END=0x7F

Microprocessors - Blink a LED 2 - 21

Page 25: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 25/86

Write the delay subroutine 

SHAREBANK NAME=gprnobnk   START=0xF0   END=0xFF

SHAREBANK NAME=gprnobnk   START=0x170   END=0x17F

SHAREBANK NAME=gprnobnk   START=0x1F0   END=0x1FF

Memory addresses declared as  DATABANK  will be used for storing the variables we

will declared in section of the type  UDATA. Currently we have one such section, named

VARIABLES.

Memory addresses declared as  SHAREBANK will be used for storing the variables we

will declare in section of the type  UDATA SHR. The directive UDATA SHR   is used to

declare variables that are allocated in RAM that is shared across all RAM banks (i.e. un-

 banked RAM). Currently we have one such section, named INT VAR that is used to store

variables used for context saving during interrupts (more on this in step 10 on page 51).

29. Declare the variables

First, we declare the registers in the variable definition section of our program:

delay_counter_1   RES   1

delay_counter_2   RES   1

Write the delay subroutine 

The subroutine should be placed at the end of the program, i.e., after the final goto LOOP

statement.

30. Initialise counter variables inside the DELAY subroutine

DELAY

 movlw   0xFF

 movwf   delay_counter_1

 movwf   delay_counter_2

31. Set up decrement loop for the “lower digit” variable

We will use  decfsz  which decrements the file register and skips the next instruction

(which is jump back) in case the result is zero.

 _DELAY_COUNTER_1

decfsz   delay_counter_1, f

goto   _DELAY_COUNTER_1

Note the f placed after the file name to be decremented. It means the decremented value

will be placed back in the same file register. Otherwise it would be placed in WREG. In

fact, when we do not specify the destination explicitly, the default destination depends

on the compiler settings or on our default settings for the project. But it is always a good

practice to not rely on defaults and specify the destination explicitly.

32. Set up decrement loop for the “upper digit” variable

After the lower counter is gone to zero, we reload it and the decrement the upper counterand check if it also is gone to zero. If not we continue decrementing the lower counter

and otherwise we have both counter at zero, so we return.

2 - 22 Microprocessors - Blink a LED  

Page 26: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 26/86

Write the delay subroutine 

 movlw   0xFF

 movwf   delay_counter_1

decfsz   delay_counter_2, f

goto   _DELAY_COUNTER_1

return

33. Call the subroutine

Call the subroutine after updating the  PORTB state:

LOOP

 movlw   0x01

 movwf   PORTB

call   DELAY

 movlw   0x00 movwf   PORTB

call   DELAY

goto   LOOP

34. Build your application

Build, debug, simulate and download your program into the PIC. The LED should be

 blinking with approximate frequency of 5 Hz.

35. Make the delay equal 1 second

Now try your hand at extending the delay to reach 1 s, so that your LED would toggle its

state every 1 s. If you’re thinking of adding another loop, you guessed right. Add another

variable, create another outer loop, calculate the initial values for all three variables and

give a try to your programming skills in assembly.

Microprocessors - Blink a LED 2 - 23  

Page 27: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 27/86

Write the delay subroutine 

2 - 24 Microprocessors - Blink a LED  

Page 28: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 28/86

Chapter 3

Displaying a digit on one 7-segment display

In this chapter you will learn:

•  how a 7-segment display works,

•  how the PIC is interfaced to the 7-segment display on the EasyPIC3 board,

•  how to compare two variables,

•  how to set up a table look-up subroutine.

Microprocessors - Displaying a digit on one 7-segment display 3 - 25  

Page 29: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 29/86

Create working directory 

LAB 3 : Displaying a digit on a 7-segment display

Lab 3 procedure

Create working directory 

1. Create the lab3 directory

Using [F7] in Total Commander create lab3 directory under   C:\uP\labs.

Copy the files 

2. Copy the files from previous labs

Using Total Commander  again copy all the files from the previous lab and rename the

lab*.*   files to the current lab. You can select the files by pressing   [numeric +]  and then

write lab  in front of  *.*. Then press [Shift + F6] and enter  lab3.*.

3. Create new project and workspace files

(a) Delete the project file lab3.mcp and the workspace file  lab3.mcw.

(b) Now create new project and workspace files using the project wizard in the MPLAB IDE.

(c) When you are asked which files you want to add to project, select these three files:

lab3.asm, P16F877A.INC and 16f877a.lkr.

4. Add files to project

5. Check if the project builds correctly

Build the project by pressing [Ctrl + F10] or   [Project → Build All]. It should do so as we have

not changed anything. If not, go back and correct any errors in the project configuration

until lab3 assembles correctly.

Display a digit on the first 7-segment display 

In this part of the lab we will display a digit on the first 7-segment display. The code will

 be placed after the Main and before the forever loop. We place it before the loop as in this

step we are not going to change neither the 7-segment display used nor the digit.

6. Configure the ports

The PORTA register controls the selection of anyone of the four 7-segment displays avail-

able on the board. The PORTB register controls what will be shown on the selected dis-

play. Hence both ports should be configured for output using  TRIS  instruction and we

can reuse the code from the previous lab.

7. Select the 7-segment display

To select the first 7-segment display we have to set the first bit in  PORTA. This can be

accomplished by:

3 - 26 Microprocessors - Displaying a digit on one 7-segment display  

Page 30: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 30/86

Page 31: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 31/86

Revise the initialisation of  PORTA

Figure 3.2: The SW switch.

Revise the initialisation of PORTA

There is a high probability that your program is not functioning properly. Apart from any

 bugs you might have introduced, there is a “small” hidden detail about  PORTA

12. Read the documentation

Read carefully page 41 in the printed extract from the  PIC16F87XA datasheet. Alterna-

tively you can go to  C:\uP\docs\Microchip\Datasheets to read the pdf file.

Pay particular attention to how PORTA is configured to run as analog or digital.

Analyse   EXAMPLE 4-1: INITIALIZING PORTA  and copy the appropriate instructions

that were missing in your program.

Revise LVP configuration bit 

Another detail that is going to catch you is the Low Voltage Programming bit.

13. Read the documentation

This time read carefully the page 158 about In-Circuit Serial Programming.

Pay particular attention to how RB3 bit is affected by Low Voltage Programming mode.

Read Note 2.

14. Change the LVP configuration bit

Find again the config line in your source file that begins like:

 __CONFIG _CP_OFF & _WDT_OFF & _BODEN_OFF & [...]

and change the LVP bit.

Use table lookup for digit to 7-segment conversion 

In this part of the lab we will learn how to create table lookup in which by providing the

location of the element we retrieve the value of that element.

15. Putting the table lookup into a subroutine

The idea is to retrieve the 7-segment code provided by providing a digit. We will imple-

ment it as a subroutine.

As we already know the general structure of a program calling a subroutine is:

3 - 28 Microprocessors - Displaying a digit on one 7-segment display  

Page 32: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 32/86

Use table lookup for digit to 7-segment conversion 

Main

...

call   Subroutine_Label

...

Subroutine_Label

...   ; here goes the code of the subroutine

return   ; return from the subroutine

But instead of using  return  we will use  retlw  which loads the  WREG  with a literal

upon return. This will be the 7-segment code we will use to program the  PORTA register.

16. Table lookup

To implement the lookup we will code a series of  retlw’s and increment the program

counter register PCL to jump to the appropriate location. Let’s call our subroutine SEVEN SEGMENT COD

We assume that the arguments and return values are passed through the WREG.

We add the digit to the value contained in the  PCL register and we place it back into the

PCL effectively performing a jump to a location  digit bytes ahead. At the time we do the

addition the PCL  is already pointing to the next location so if we want to  jump  to that

next location we have to add 0.

SEVEN_SEGMENT_CODE:

retlw   0x__ ; case 0

...

retlw   0x__ ; case 9

Now using the Seven Segment Convertor in the mikroC complete the table for all ten

digits.

Instead of placing a 7-segment equivalent of a digit we place the desired digit into the

WREG and then call the subroutine:

 movlw   0x01

call   SEVEN_SEGMENT_CODE

After the return from the subroutine is completed we find in the  WREG  the 7-segment

code corresponding to the digit that was in WREG before the call was executed.

Note:   Remember to write your table addressing using PCLATH to avoid prob-

lems in case your table is spread across page boundaries. And read com-

plete discussion on page 30 of the datasheet.

17. Build the project

Build and debug your project using the simulator and check in the watch window that

the PORTB is set to the correct value.

Microprocessors - Displaying a digit on one 7-segment display 3 - 29  

Page 33: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 33/86

Make the display more alive 

18. Program the microcontroller

Program the PIC16F877A and check if your program works correctly after all the modifi-

cation. Correct any errors if required.

Make the display more alive 

In this final part of the lab we will be constantly changing the digits — from 0 to 9.

19. The delay subroutine

We will reuse the delay subroutine from the first lab. We will call it each time we change

the digit.

Designing the counter 

Now we have to implement the counter. We will be counting from 0 to 9 and when we

reach 9 we reset the counter back to zero. But how do we check that the digit is equal

9? Please take time to think about it and check with the Instruction Set Summary what

instruction or instructions would come handy. Come up with your own solution before

you continue

Suggested solution 

The solution we suggest is to use the subwf instruction and check the Z flag in the STA-TUS   register. Counting up can be easily accomplished by using   incf. The   incf   is

operating on a file register so we will have to create a variable to store the counter.

20. Memory reservation

To reserve some memory for a variable we will have to issue the following directive some-

where before the code:

digit   RES   1

21. Counter increment

Inside the loop we will have to increment the counter.

incf   digit, f

As we remember the final f is to instruct the processor to put the incremented value back

into the file register and no to the WREG.

22. Counter increment

Now the only thing left is to reset the counter when it reaches nine. We are going to

use  subwf  instruction, so first we have to put into the  WREG  the value which will be

subtracted from the variable (meant as the content of a file register).incf   digit, f

3 - 30 Microprocessors - Displaying a digit on one 7-segment display  

Page 34: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 34/86

Suggested solution 

23. Subtract a constant from a file register

To subtract to numbers, one has to be place in a file register an the other in the  WREG.

As the digit is already located in the file register then we have to put 10 (decimal) in theWREG. Then we perform the actual subtraction. :

 movlw   D’10’

subwf   digit, W

We placed the result back in the  WREG. Why? Remember that we only want to check

(compare) if the digit is equal 9, or as we do it here: to check if the difference is zero. So

the actual result of subtraction is of no interest to us and we can discard it.

24. Check the result of the subtraction

Now we have to test whether the result is zero. We check the Z  flag in the   STATUSregister. If it is set we reset the counter, otherwise we jump to the delay call:

 btfss   STATUS, Z

goto   CONTINUE;

 movlw   .0   ; First digit to display 

 movwf   digit   ;

CONTINUE:

call   DELAY

goto   LOOP   ; Stay in this loop forever 

25. Build the project

At this point your program should be counting from 0 to 9 on the first display. Build anddebug your project using the simulator and check in the watch window that it does so.

Again, to avoid lengthy pauses you can comment out the call to the delay subroutine.

The remember to remove the comment before writing to the PIC.

26. Program the microcontroller

Now use PICFLASH2 to program the PIC16F877A and check if your program works

correctly. If not, go back and correct the errors, before you proceed.

Microprocessors - Displaying a digit on one 7-segment display 3 - 31

Page 35: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 35/86

Suggested solution 

3 - 32 Microprocessors - Displaying a digit on one 7-segment display  

Page 36: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 36/86

Chapter 4

Simultaneous display on all four 7-segment displays

In this chapter you will learn:

•  how the multiplexed display is accomplished

•  how interrupts work

•  how periodic interrupts can be generated using a timer

•  how indirect addressing can be used to easily address an array of variables

Microprocessors - Simultaneous display on all four 7-segment displays 4 - 33 

Page 37: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 37/86

Create working directory 

LAB 4 : Multiplexed display

Lab 4 procedure

Create working directory 

1. Create the lab4 directory

Using [F7] in Total Commander create lab4 directory under   C:\uP\labs.

Copy the files 

2. Copy the files from previous labs

Using Total Commander  again copy all the files from the previous lab and rename the

lab*.*   files to the current lab. You can select the files by pressing   [numeric +]  and then

write lab  in front of  *.*. Then press [Shift + F6] and enter  lab4.*.

3. Create new project and workspace files

(a) Delete the project file lab4.mcp and the workspace file  lab4.mcw.

(b) Now create new project and workspace files using the project wizard in the MPLAB IDE.

(c) When you are asked which files you want to add to project, select these three files:lab4.asm, P16F877A.INC and 16f877a.lkr.

You are going to repeat this procedure in every subsequent lab.

4. Add files to project

5. Check if the project builds correctly

Build the project by pressing [Ctrl + F10] or   [Project → Build All]. It should do so as we have

not changed anything. If not, go back and correct any errors in the project configuration

until lab4 assembles correctly.

Display a number on all four 7-segment displays 

In this part of the lab we will display a digit on all four 7-segment displays. We will do

that by expanding the code we have written for the previous lab.

6. Multiplexing

Look again at the slide that depicts how the PIC microcontroller is connected with the

7-segment displays.

To be able to display a digit on each 7-segment display independently of what is beingshown on other displays we have to synchronise changes in the content of  PORTA with

changes on PORTB.

4 - 34 Microprocessors - Simultaneous display on all four 7-segment displays 

Page 38: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 38/86

Display a number on all four 7-segment displays 

7. Select the 7-segment display

Every time we change the display we have to expose the corresponding digit value on

PORTB. So a good solution would be to use a variable that will contain the selection of the display. Let’s call it display and reserve some memory for it:

display   RES   1

8. Initialise the display variable

In the initialisation part of our program (located between the start and the LOOP labels)

we initialise the display variable instead of programming PORTA directly. Hence:

 movlw   0x01   ; Select first display 

 movwf   PORTA   ;

should be changed to:

 movlw   0x01   ; Select first display 

 movwf   display   ;

9. Use the display variable to control PORTA

Now we have to use the newly create variable to control the  PORTA. To maintain the

highest possible synchronisation with changes on  PORTB it is obvious that the instruc-

tions changing the content of  PORTA  should be as close as possible to the instructions

controlling PORTB. So just after the code snippet that writes to PORTB:

 movf   digit, W

call   SEVEN_SEGMENT_CODE

 movwf   PORTB

we place code that will write to PORTA:

 movf   display, W

 movwf   PORTA

10. Rotate the display

Similarly to incrementing the digit we have to add code to change the  display variable so

each time the digit is changed, the display is also changed.First we put the desired value into the WREG and only then we can transfer it to PORTA.

This is because, as we remember from the previous lab, there is no instruction to put a

literal directly into a file register (RAM).

We will place the code for changing the contents of  PORTA  after   movwf digit  and

 before the  CONTINUE label. We will label that part of the code  NEXT DISPLAY.

We have to adjust the  goto  instruction in the code preceding the   NEXT DISPLAY  so it

 jumps to  NEXT DISPLAY instead of  CONTINUE:

 btfss   STATUS, Z

goto   CONTINUE

  ← this instruction has to be adjustedThe first instruction we add after the   NEXT DISPLAY  labels is the shift instruction that

will operate on the display variable:

Microprocessors - Simultaneous display on all four 7-segment displays 4 - 35 

Page 39: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 39/86

Our program has to be  multirate

rlf   display, f

The shifting operation should reset to the display after it reaches the last one. We can itin a similar way when we were resetting the counter to 0 after it has reached 9. We check

if the next value is binary  00010000 by putting this value in the  WREG, subtracting it

from  display  and checking the  Z bit in the  STATUS  register. If the Z is set we reset the

display to  0x01, otherwise we go to CONTINUE.

Try to write this part of the program yourself.

11. Build the project, debug and write to PIC

At this point your program should be counting from 0 to 9 and changing the display with

every digit.

Note:   Please save a copy of your project and files to lab4a?

Our program has to be  multirate

As we already know the multiplexing of displays should be fast enough to “trick” the eye

so it will see a continuous display of four digits. But at the same we want to change the

digits somewhat slower to be able to see those changes!

We will do it in two steps. First we will display four different digits simultaneously andthen we will make them change.

12. Making it multirate

Now we proceed to configure set up the generation of interrupts in such a way that the

displays would change quickly enough for our eyes to not to see it.

Let’s fix the rate of changing the displays at 100 Hz. We have four displays so we will

need to do it four times quicker, that is at 400 Hz. This gives us a period of 2.5 ms.

Now we have to calculate what has to be programmed into the TMR0 register. The initial

timer value must be a number of clock cycles to overflow the timer after the requested

period, which in our case is 2.5 ms:

TMR0 initial  = 256 − interrupt cycles [expressedinclockcycles ]

Remember, however, that resonator frequency is internally divided by 4, and additionally

TMR0 divides it further by the prescaler value. So interrupt cycles can be computed as

follows:

interrupt cycles  = (interrupt period [s ]   ∗   resonator frequency /4)/prescaler

= 2.5ms   ∗   2MHz/prescaler,

where resonator frequency  = 8 MHz (quartz crystal).

4 - 36 Microprocessors - Simultaneous display on all four 7-segment displays 

Page 40: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 40/86

Our program has to be  multirate

Well, the number you get (hopefully right) does not seem to fit well into a 8 bit register if 

the prescaler is 1. So we have to use a higher prescaler which will increment the TMR0

not every second clock tick but every  n-th clock tick, where n is the prescaler divisor.

13. Using the prescaler

You select the prescaler divisor based on the table located on page 54 of the PICF87XA

datasheet. The prescaler has to make interrupt cycles  less than 256. Please neglect any

rounding errors.

When you are done read the corresponding bit values of  PS2:PS0 that you have to set in

the OPTION REG register.

The code you write should be placed before all other initialisations, just after the  start

label.

14. Making sure the prescaler is assigned to TMR0 and not to WDT

Note that you have to clear bit PSA in the OPTION REG register to assign the prescaler

to TMR0.

To make the life easier the MPLAB assembler allows some nice abstraction in program-

ming. To create a binary value for storing in register we can set individual bits by telling

whether it is 0 or 1 and specifying (symbolically) what is the position of the bit within the

register. Let’s say we want to set bit PSA in the OPTION REG register. The correspond-

ing code will be:

 movlw   0 << PSA movwf   OPTION_REG

Here we are using the   x << y   macro. It means: shift the value x  by  y  positions to the

right. After this operation  0  will be shifted to the position of the bit  PSA. PSA  is simply

the number (position) of the bit PSA in the register, as defined in the  P16F877A.INC we

have included on our project:

[...]

;----- OPTION_REG Bits -----------------------------------------------------

NOT_RBPU   EQU   H’0007’

INTEDG   EQU   H’0006’

T0CS   EQU   H’0005’

T0SE   EQU   H’0004’

PSA   EQU   H’0003’

PS2   EQU   H’0002’

PS1   EQU   H’0001’

PS0   EQU   H’0000’

[...]

If we want to configure more than one bit, it is enough to OR them like this : movlw   ( 0 << T0CS ) | ( 1 < < T0SE ) | (0 < < PSA)

 movwf   OPTION_REG

Microprocessors - Simultaneous display on all four 7-segment displays 4 - 37 

Page 41: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 41/86

Allowing TMR0 to generate interrupts 

In this case we have 0 on the position of the T0CS bit, 1 on the position of the T0SE and

0 on the position of the PSA bit. After combining this together into one byte, it is written

to WREG register, as we can see in the resulting  .lst file:[...]

00002f 3010   MOVLW    0x10   movlw   ( 0 << T 0CS ) | ( 1 < < T0SE ) | ( 0 < < PSA )

[...]

And remember that OPTION REG  is mapped to Bank 1  so you have to change the  RP

 bits in the STATUS register accordingly before you attempt to write to the OPTION REG

and restore those bits afterwards.

15. Initialise the timer

Once you are done with all the calculations, you have to write the initial value to theTMR0.

Again remember that this time  TMR0  register is is mapped to  Bank 0  so you have to

change the RP bits in the STATUS register accordingly before you attempt to write to the

TMR0 and restore those bits afterwards.

Allowing TMR0 to generate interrupts 

First read about interrupts on pages 153 and 154 in the PICF87XA datasheet. All the code

you are going to write in the following steps should be placed before configuring the

timer.

16. Enable interrupts

The register that controls generation of interrupt is INTCON (see page 24 in the PICF87XA

datasheet for details). You have to set  GIE bit to enable all interrupts.

; Set up interrupts

 bsf   INTCON, GIE; Enable interrupts

17. Enable interrupts from TMR0

In this same register you have to enable   TMR0   to generate interrupts by setting bit

TMR0IE.

 bsf   INTCON, TMR0IE; Enable interrupts from TMR0

18. Check bank switching

After you are done with setting up the timer and the interrupts, please simulate your

code and check in the watch window that you have your banking right and INTCON,

OPTION REG and TMR0 get configured as desired.

19. Build the project, debug and write to PIC

At this point your program should be behaving exactly as before. Although we have in-troduced many changes and added interrupts, we still do not handle them. The program

should be counting from 0 to 9 and changing the display with every digit.

4 - 38 Microprocessors - Simultaneous display on all four 7-segment displays 

Page 42: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 42/86

Setting up the ISR 

20. Does it work?

Is your program working OK or it is only showing a zero on the first display or all you

can see is just four empty displays? Well, you have probably forgot to acknowledge theinterrupt. Or to put it another way, to clear the  TMR0IF in the INTCON register. Every

time you return from interrupt using the   retfie   instruction the microprocessor finds

that it has the  TMR0IF flag set so it means another interrupt is waiting (but in fact this

is the old interrupt we have just returned from but simply forgot to clear that flag) so

it immediately jumps to interrupt vector address. So the program stops with the first

interrupt and there is only servicing interrupts (one unacknowledged interrupt, to be

precise) all the time. So from what we see the main code could only get as far as to

display the first zero on the first display.

Slightly below   INT VECTOR CODE 0x004 you will find a line that says   ; isr code

can go here or be located as a call subroutine elsewhere. Place hereyour instruction for clearing the discussed bit.

Setting up the ISR 

Now we have to write the actual code to handle the interrupt (Interrupt Service Routine).

21. Move the display switching code

Our code is going to handle the switching of the digits. So the code we have already

written should be placed just below the instruction that clears the TMR0IF flag.

This is the code we have to move there:

NEXT_DISPLAY   ;

 bcf   STATUS, C   ; clear CARRY bit

rlf   display, f

 movlw   B’00010000’

subwf   display, W

 btfss   STATUS, Z

goto   CONTINUE

 movlw   0x01

 movwf   display

The CONTINUE label is now wrong. Change it to CONTINUE ISR and place a label of such

name exactly after the code you have moved into the ISR. In this way, when the display

does not need to be reset, the ISR will continue its normal course.

22. Update PORTA inside the ISR

The code where we change the displays (write the  PORTA) also needs to be moved to the

ISR.

Find the following fragment in your program

 movf   display, W

 movwf   PORTA

and place it just after the  CONTINUE ISR label.

Microprocessors - Simultaneous display on all four 7-segment displays 4 - 39 

Page 43: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 43/86

Displaying four different digits 

23. Reload the timer

Finally we have to reload the timer to its initial value. You can put the appropriate code

 just after writing to PORTA.

24. Use name alias

Now we have two places were we initialise (or in fact initialise and then reinitialise) the

TMR0 register to some predefined value. It would be much easier to change that value

if we could do it in just one place. To this end we can use the EQU assembler directive

to assign decimal 100 to some symbolic name. Let’s call this  TMR0 INITIAL  and place

it just before the block of  RES  directives. We use all capitals for this name to remember

that it is a constant, whose value will stay unchanged throughout the execution of the

program.

It will be a good practice then to give that initial value some symbolic name. For exampleTMR0 INITIAL.

The original call to   NEXT DISPLAY (see the code fragment below) should be changed to

CONTINUE to reflect the changes we have just made.

NEXT_DIGIT

incf   digit, f

 movlw   D’10’;

subwf   digit, W

 btfss   STATUS, Z

goto   NEXT_DISPLAY <-- This has to be changed to CONTINUE

 movlw   .0   ; First digit to display 

 movwf   digitCONTINUE

call   DELAY

goto   LOOP   ; Stay in this loop forever 

The code that has been moved to the ISR should not be referenced from the main program

anymore.

25. Build the project, debug and write to PIC

Now you have to be able to see digits changing on all four displays simultaneously. If you

want to see that the displays are being multiplexed, you can do that easily by increasing

the prescaler value. Set it for example to 128 and you’ll notice flickering of the displays.To make the effect even more pronounced change TMR0 INITIAL to 0. You see how easy

it was to do that adjustment. Only one place to remember about.

Displaying four different digits 

Note:   Copy to lab4b

26. Create variables for digitsBefore we had only one variable for storing the digit. Now we will need to reserve mem-

ory for four digits. We rename the digit to digit 1 and add three more variables:

4 - 40 Microprocessors - Simultaneous display on all four 7-segment displays 

Page 44: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 44/86

Use indirect addressing 

digit_1   RES   1

digit_2   RES   1

digit_3   RES   1

digit_4   RES   1

27. Initialise variables for digits

In the place where you were initialising the  display variable now you have to initialise all

four variables. We suggest you assign them different values. Otherwise we will see all

four digits equal again.

28. Move display of digits to ISR

Writes to PORTB should also be moved to ISR. Every time we change the display we also

have to change the digit X  variable we show on that display.

The code fragment in question is located just after the  LOOP label:

LOOP

 movf   digit, W

call   SEVEN_SEGMENT_CODE

 movwf   PORTB

Place just after where you reload the timer. And read on to see how we are going to cycle

through the digit 1  to  digit 4 to display each of them on the appropriate seven segment

display.

Use indirect addressing 

The digit variable no longer exists. Each time we change the display we need to read a

different variable. We will make it the clever way using indirect addressing. The FSR

register acts as a pointer. First we make it point to the  digit 1 location and than we will

increment it. When we reset the display from the last one to the first one, we will also

reset the FSR register to point to the  digit 1 location again.

Important:   The FSR register points to the location of the digit 1, or inother words, it contains the  address  of where the  digit 1

variable is in the memory. It does not  store the value of 

that variable.

Note:   Have you realised what the indispensable condition for the location in

memory of  digit X  variables is? They have to be placed one after another

in the memory, exactly in the order in which the corresponding displays

are selected. Otherwise the pointer would point to memory locations

occupied by other variables or not occupied at all (not valid data) andwe will be displaying some nonsense values.

Microprocessors - Simultaneous display on all four 7-segment displays 4 - 41

Page 45: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 45/86

Use indirect addressing 

29. Initialise the FSR

In the initialisation part of your program write (as the last instructions, just before the

LOOP label) the FSR set-up:

 movlw   digit_1

 movwf   FSR

30. Simulate your program

Build and simulate your program to verify that indeed  FSR  is loaded with the  address of 

the digit 1 variable. Open the watch window and add the FSR register and digit 1 symbol.

31. Increment the pointer

The instruction of incrementing the FSR  register should be placed as the first instructionafter the NEXT DISPLAY label.

32. Reinitialise the pointer

In the ISR after the goto CONTINUE ISR instruction we have placed the code that resets

the selected display back to the first one. This is a perfect place to put our FSR   reset

together with resetting the display and the digit pointer. Use the same instructions you

used to originally initialise the FSR register in the main part of the program.

33. Read through indirect addressing

Now go to the ISR again and find the following code: movf   digit, W

call   SEVEN_SEGMENT_CODE

 movwf   PORTB

Now we will read through the  INDF register the contents of the memory location speci-

fied by the FSR register:

 movf   INDF, W

34. Rename the digit  variable

In the main part of the program the logic that was counting-up still refers to the original

digit. Rename all those references to  digit 1.

35. Build, debug and burn your program into the PIC

Now you should see the digits chosen by you being displayed and the first digit being

incremented as before.

36. We are almost done

What is missing yet from our program is that the main part should be modified to reflect

the recent recent changes, and in particular that we are having four digits now. We will

do this the easy way in this lab.

The part of your program that was originally incrementing the one-digit counter:

4 - 42 Microprocessors - Simultaneous display on all four 7-segment displays 

Page 46: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 46/86

Use indirect addressing 

incf   digit, f

 movlw   D’10’;

subwf   digit, W

 btfss   STATUS, Z

goto   CONTINUE

 movlw   .0   ; First digit to display 

 movwf   digit

Copy it three times, so that you have four copies altogether, one for each digit. After

you’re done, is the result that only one digit is counting? Well, check where you jump to

after you check digit 1. Do you want to jump to there or rather to check  digit 2? Revise

the code for all other digits.

Now you are left on your own  ̈ Good luck!

37. The end of the lab

When your’re done, your program should be displaying four different digits, all incre-

menting simultaneously.

Microprocessors - Simultaneous display on all four 7-segment displays 4 - 43 

Page 47: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 47/86

Use indirect addressing 

4 - 44 Microprocessors - Simultaneous display on all four 7-segment displays 

Page 48: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 48/86

Chapter 5

Four-digit counter with button read

Microprocessors - Four-digit counter with button read 5 - 45  

Page 49: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 49/86

Create working directory 

LAB 5 : Developing a counter

This lab has four goals:

•  Develop a counter

•  Switch off non-significant zeros on the display

•  Detect the button pressed

•  Increment the counter every time a button is pressed

Lab 5 procedure

Create working directory 

1. Create the lab5 directory

Using [F7] in Total Commander create lab5 directory under   C:\uP\labs.

Copy the files 

2. Copy the files from previous labs

Using Total Commander  again copy all the files from the previous lab and rename the

lab*.*   files to the current lab. You can select the files by pressing   [numeric +]  and then

write lab  in front of  *.*. Then press [Shift + F6] and enter  lab5.*.

3. Create new project and workspace files

(a) Delete the project file lab5.mcp and the workspace file  lab5.mcw.

(b) Now create new project and workspace files using the project wizard in the MPLAB IDE.

(c) When you are asked which files you want to add to project, select these three files:

lab5.asm, P16F877A.INC

 and 16f877a.lkr

.

You are going to repeat this procedure in every subsequent lab.

4. Add files to project

5. Check if the project builds correctly

Build the project by pressing [Ctrl + F10] or   [Project → Build All]. It should do so as we have

not changed anything. If not, go back and correct any errors in the project configuration

until lab4 assembles correctly.

Design the counter 

Below is the figure that shows again the flowchart of the counter.

5 - 46 Microprocessors - Four-digit counter with button read  

Page 50: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 50/86

Design the counter 

Every time we have an overflow on the least significant digit we have to check all the

way up to the most significant digit whether this caused another overflow (or overflows)

in some other digit (or digits). The pseudocode for the counter could go like this:

DIGIT_CHECK_OVERFLOW:

digit_1++;

if   ( digit_1 > 9 ) {

digit_1 = 0;

digit_2++;

}

else   {

goto   DIGIT_CHECK_OVERFLOW;

}

if   ( digit_2 > 9 ) {

digit_2 = 0;

digit_3++;

}

else   {

Microprocessors - Four-digit counter with button read 5 - 47  

Page 51: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 51/86

Design the counter 

goto   DIGIT_CHECK_OVERFLOW;

}

if   ( digit_3 > 9 ) {

digit_3 = 0;

digit_4++;

}

else   {

goto   DIGIT_CHECK_OVERFLOW;

}

if   ( digit_4 > 9 ) {

digit_4 = 0;

goto   DIGIT_CHECK_OVERFLOW;

}else   {

goto   DIGIT_CHECK_OVERFLOW;

}

*p_digit > 9

 p_digit = &digit_1;

*p_digit++;

DIGIT_CHECK_

OVERFLOW

*p_digit > 9

*p_digit = 0;

 p_digit++;

*p_digit > 9

*p_digit > 9

digit_4 = 0;

*p_digit = 0;

 p_digit++;

*p_digit = 0;

 p_digit++;

NO

NO

NO

NO

Now as the FSR  register can be used with no problems within the main part of our pro-

gram, we will increment all digits in a similar way that we used inside the ISR, but this

5 - 48 Microprocessors - Four-digit counter with button read  

Page 52: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 52/86

Design the counter 

time using pointers. In the figure above you can see the flowchart of the counter imple-

mented this way.

digit *p_digit;   // a pointer to digits

DIGIT_CHECK_OVERFLOW:

p_digit = &digit_1;   // set the pointer to first digit

*p_digit++;

if   ( *p_digit > 9 ) {

*p_digit = 0;

p_digit++;

*p_digit++;

}

else   {

goto   DIGIT_CHECK_OVERFLOW;

}

if   ( *p_digit > 9 ) {

*p_digit = 0;

p_digit++;

*p_digit++;

}

else   {

goto   DIGIT_CHECK_OVERFLOW;}

if   ( *p_digit > 9 ) {

*p_digit = 0;

p_digit++;

*p_digit++;

}

else   {

goto   DIGIT_CHECK_OVERFLOW;

}

if   ( *p_digit > 9 ) {digit_4 = 0;

goto   DIGIT_CHECK_OVERFLOW;

}

else   {

goto   DIGIT_CHECK_OVERFLOW;

}

Now we see that the three first blocks are identical and we can execute them three times

inside a loop:

digit *p_digit;   // a pointer to digits

DIGIT_CHECK_OVERFLOW:

Microprocessors - Four-digit counter with button read 5 - 49  

Page 53: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 53/86

Do not use  FSR  directly in the ISR 

p_digit = &digit_1;   // set the pointer to first digit

digit_1++;

for( i = 0 ; 0 < i < 3 ; i + + ) {

if   ( *p_digit > 9 ) {

*p_digit = 0;

p_digit++;

*p_digit++;

}

else   {

goto   DIGIT_CHECK_OVERFLOW;

}

}

if   ( *p_digit > 9 ) {

digit_4 = 0;

goto   DIGIT_CHECK_OVERFLOW;

}

else   {

goto   DIGIT_CHECK_OVERFLOW;

}

We have to be sure that the flowchart (and the the corresponding pseudocode) is correct

 before we even start to think about writing code.

6. Zero the counterWe start counting from zero so all the  digit X  variables should be now initialised to zero.

make the necessary changes to your code.

Do not use FSR directly in the ISR 

Before we use the  FSR  register in the main part of the program we have to fix one more

thing.

We have a problem that we can not use  FSR  directly in the main body of our program.

This is so as we are already using the  FSR in the ISR. This means we can not use it easily

in other parts of the program as they will be overwriting each other in the settings of FSR.We would need to store the content of  FSR in some temporary register and than restore it

when we are done. This is, however, bad programming style, which can provoke easily

errors in the code and makes us constantly check if we can use that register or not.

A better solution will be to use some variable to store the current content of the  FSR to be

used by the ISR. That is, the ISR, as a subroutine has to take care not to mess around. Such

approach makes our program more robust and clear and less prone to errors.

7. Create a variable to store current FSR content

Let’s call it FSR digit ISR and allocate memory for it using RES.

8. Change initialisation in main

Instead of initialising the FSR directly initialise only the FSR digit ISR variable.

5 - 50 Microprocessors - Four-digit counter with button read  

Page 54: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 54/86

Use  FSR  to manage the counter 

9. Change references to FSR

Now the instructions in your program that were referencing to FSR should now all refer-

ence to FSR digit ISR.

10. Add context saving and restoring for FSR

Read section   14.12 Context Saving During Interrupts   together with  Example 14-1: Saving

STATUS, W and PCLATH registers in RAM  on page 154 of the datasheet for complete dis-

cussion.

Inside the ISR have a look on how  PCLATH, STATUS and  WREG are saved in tempo-

rary variables so that we can change them inside the ISR without affecting the original

contents of those registers.

Create a variable fsr temp and add saving and restoring of  FSR in the way other registers

are preserved.

11. Use FSR digit ISR for reading the digits

Now the address of  digit 1 is stored in FSR digit ISR. We have to retrieve it to write it into

the FSR before we read the INDF register:

 movf   FSR_digit_ISR, W

 movwf   FSR

 movf   INDF, W

12. Use pointer (INDF) for variable read and write

That part of the code that is responsible for incrementing a digit, should be rewritten to

use the  INDF register instead of any particular  digit X  file register.   INDF works as if it

were any other file register, for example:

 movf   INDF, W

or

incf   INDF, f

13. Setup the pointer

To make reads and writes to  INDF operate on the correct memory locations we can set

up it for the first digit like this:

 movf   FSR_digit_ISR, W

 movwf   FSR

and increment it simply like any other register:

incf   FSR, f

Use FSR to manage the counter 

Now the content of the FSR register is stored in the  FSR digit ISR variable. It means that

the value of  FSR  will be remembered between successive calls to the ISR. This allows as

to freely use the  FSR  register in the main body of the program. There will be no conflictas the original  FSR content from the main program will be preserved in  fsr temp  when

the ISR is called and the ISR will store the FSR value it needs in FSR digit ISR.

Microprocessors - Four-digit counter with button read 5 - 51

Page 55: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 55/86

OPTIONAL: Switch off the insignificant zeros 

14. Create a loop

As we already are aware of, the first three digits can be checked for overflow using the

same code placed in a loop. To construct a loop we can reserve memory for a runningvariable, let’s call it i, set it to the number of times we want the body of the loop to run,

decrement it after each loop pass and test for zero as the loop condition. For example

something like this would serve:

int   i;

i = 3 ;

DIGIT_CHECK_OVERFLOW

...   // here comes the code for digit increment and overflow check

i--;

if   ( i ! = 0 ) {

goto   DIGIT_CHECK_OVERFLOW

}

which could be translated into assembly:

i   RES   1

 movlw   3

 movwf   i

DIGIT_CHECK_OVERFLOW

...   ; here comes the code for digit increment and overflow check

decfsz   i

goto   DIGIT_CHECK_OVERFLOW

15. Build the project, debug and write to PIC

At this point your program should be counting from 0 to 9999.

16. Making it more human-readable

One funny thing you noticed is that the digits are reversed. Now with us using indirectaddressing, accounting for this is very easy. We have to simply start with  digit 4 and the

decrement the pointer instead of incrementing it. So the first digit to change will be the last

one (on the display). After you’re done with modifications everything should be working

well and nice. If you want to make your counter work faster to see if it wraps up correctly

at 9999 decrease the delay in the  DELAY subroutine.

Note:   Copy to lab5a

OPTIONAL: Switch off the insignificant zeros 

5 - 52 Microprocessors - Four-digit counter with button read  

Page 56: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 56/86

OPTIONAL: Switch off the insignificant zeros 

We have to polish (no pun intended) a bit more our counter to make it work as we are

accustomed to and remove the display of the insignificant zeros.

17. An empty character solution

This is very easy. We simply add another character, as if it were the eleventh digit, to

the SEVEN SEGMENT CODE table that will display nothing:  0x00, and we will modify the

 beginning of our pogram to initialise all the digits of the counter with that value.

 movlw   0x0A

 movwf   digit_1   ; First digit to display 

...

18. Build the project and write to PIC

Check if the solution works.

19. Why the solution failed?

Well, nothing seems to happen. Let’s think about this. The starting value for the first

digit is 10 (0x0A), so when it is incremented for the first time it is 11. So when the

SEVEN SEGMENT CODE is called with a value that points beyond the look-up table some

unknown value is read from the uninitialised part of the processors memory, which is

most probably equal zero as we can not see anything on the display.

You can easily test this hypothesis by self-copying the  SEVEN SEGMENT CODE  table to

make it twice the long. You’ll see the last digit start from zero and then disappear when

they reach 9.

20. Undo all the changes

Undo all modification to your program to revert it to the state before we attempted to

switch off the zeros.

21. Switch off the zeros at the display stage

So we can not modify the counter variables directly as they are being used for counting.

What a coincidence  ̈. Then we can remove the displays of zero at the display stage

(inside the ISR) so the original counter variables would not be affected. We can do that by

testing if the digit to be displayed is equal zero and if so, modify it to be equal 10 (0x0A)- our secret empty digit.

We go to the ISR code and find the following:

 movf   FSR_digit_ISR, W

 movwf   FSR

 movf   INDF, W

call   SEVEN_SEGMENT_CODE

 movwf   PORTB

And between the movf INDF, W and the call SEVEN SEGMENT CODE we insert switch-

ing off the zeros. To find out if the digit to display is zero we proceed as usual and test theZ bit in the STATUS register. Seems reasonable, but can we do it with out any subtraction

to compare it with zero?

Microprocessors - Four-digit counter with button read 5 - 53  

Page 57: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 57/86

The  SOLUTION then? 

When you look at the instruction set on page 160 of the PIC16F8X7A data sheet you’ll see

that:

Which means that the  movf instruction affects the Z  bit in the STATUS register and this

is exactly what we need.

Without any further complications we can write the test code:

 btfss   STATUS, Z

goto   _ISR_SEVEN_SEGMENT_CODE

where   ISR SEVEN SEGMENT CODE  is the internal ISR label place just before the   call

SEVEN SEGMENT CODE instruction. Of course when the condition is true we modify the

digit to be displayed.

22. Build the project and write to PIC

Check if the new solution works.

23. Why the solution failed either?

Well, now we are switching off  ALL the zeros, they are significant or not.

24. Undo all the changesAgain undo all modification to your program to revert it to the state before we attempted

to switch off the zeros.

The  SOLUTION then? 

Now it is clear that we have to decouple the variables we use for counting from the

variables we use for the display. The ones used for counting can not be touched (the easy

way) but those used for display can be modified at will.

25. Create a second set of variables

Now we create a second set of variables. In order to avoid too many changes in the

program, as we use them both in the main program and in the ISR, the new variables will

 be used for counting and then we copy them to the variables we used before.

Call the new variables digit counter X , where X  goes from 1 to 4 and reserve memory for

them.

26. Rename the variables for initialisation

In the initialisation part of the program change the setting of four digits to zero to the

new digits, as they will be our new counter.

27. Rename the variables in the main part of the program

Rename the variables used for counting to the new variables we are going to use. Every-

thing in the main part of the program.

5 - 54 Microprocessors - Four-digit counter with button read  

Page 58: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 58/86

The  SOLUTION then? 

28. Test (if you want)

If you build your project now and burn it into the PIC, you’ll see the counter showing

something but it is not changing. This is OK. The counter is now not the same as thedisplay.

29. Copy the counter variables into the display variables

But where do we place the code? Do we place it after we modify each counter digit? Or

maybe after we modify whole four counter digits? But what if we loose synchronisation?

Let’s say that the interrupt comes in an unhandy moment when the counter has advanced

and the display digits not?

Let’s try and do it every time we pass through all the counting code. Just after the LOOP

label place your code to copy the digits.

30. Build the project and write to PIC

It should be working exactly as before, counting numbers from 0 to 9999.

31. Design the flowchart for switching off of non-significant zeros

A possible solution may go like this. First we check the most significant digit, if it is zero

we check the next one, if not we stop. We move from left to right.

digit_3 = empty

Start

digit_1 == 0

digit_2 == 0

digit_3 == 0

Stop

NO

digit_1 = empty

digit_2 = empty

NO

NO

digit_1 is the most

significant digit

32. Pseudocode

The pseudocode could go something like this:

Start:

if   ( digit_1 == 0 ) {

digit_1 = empty;

if   ( digit_2 == 0 ) {

Microprocessors - Four-digit counter with button read 5 - 55  

Page 59: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 59/86

Counting the number of times a button has been pressed 

digit_2 = empty;

if   ( digit_3 == 0 ) {

digit_3 = empty;

}

else   {

goto   Stop;

}

}

else   {

goto   Stop;

}

}

else   {

goto   Stop;

}

Stop

33. Implementing the design

Now it is your turn to implement this algorithm. You should place your code just after

the code that does the copying of the four digits to form counter to display variables.

34. Build the project, debug and write to PIC

After you’re done you program should be counting as before but this time with all in-

significant zeros switched off.

Counting the number of times a button has been pressed 

Instead of incrementing a counter automatically in a loop, we will read increment the

counter only when a button is pressed (and released). We are going to read bit 4 on

PORTA (usually denoted as RA4).

35. Reconfigure PORTA

Currently all pins of  PORTA  are configured as output. To be able to read from some

pins of that port we need to change its configuration. So in the initialisation part of the

program leave the four least significant bits as before — they are used for display so wecan not use them anyway — and configure the four most significant bits in  PORTA as

input.

We want to know if the button has been pressed. It is even better to know if it has been

pressed and released again. Otherwise pressing the button would advance the counter

many times. Let’s do it the latter way first so you can see with your own eyes how many

times the counter can advance when you are pressing the button. You know that you’re

not likely to get below 3000 without any extra trick!?

36. Remove the delay

Go and find the following lines in your code: movlw   digit_counter_4

 movwf   FSR

5 - 56 Microprocessors - Four-digit counter with button read  

Page 60: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 60/86

Counting the number of times a button has been pressed 

; call DELAY  

incf   INDF, f

 movlw   3

 movwf   i

Comment out the  call DELAY instruction which will not be needed anymore.

37. Check if the button has been pressed

 Just before the code fragment shown above will place the code to read the button. The

code should loop until the button corresponding to  RA4  is pressed. So the counter can

only advance when the button is pressed otherwise it will get “stuck” in the loop.

BUTTON_DOWN_RA4

 btfss   PORTA, 4

goto   BUTTON_DOWN_RA4

38. Simulate user input

MPLAB IDE allows simulation of user input on microprocessor’s ports. First select the

Simulator as your tool:  [Debugger → Select Tool → MPLAB SIM] and then open the Stimulus

window:  [Debugger → Stimulus].

Create two stimuli: one for setting the  RA4  high and the other for setting the  RA4 pin

low as shown on the screenshot below:

By pressing the button [Fire] you can easily simulate the high or low logical state on the

RA4 pin.

39. Build, simulate and debug your project

40. Download the application to PIC

Download the application to PIC and play with the program. What is your record?  ̈

41. Check if the button has been pressed and released

It seems that checking only if the button has been pressed is not the best way to increment

the counter. Add another loop similar to the one above that waits for the button to go up

again.

42. Build your project

Build the project and play again with the enhanced version. Does it work correctly now?

From time to time it may advance the counter by 2, but we won’t give it a greater impor-tance at this stage.

Microprocessors - Four-digit counter with button read 5 - 57  

Page 61: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 61/86

Counting the number of times a button has been pressed 

5 - 58 Microprocessors - Four-digit counter with button read  

Page 62: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 62/86

Chapter 6

LCD display

In this chapter you will learn:

•  how the LCD display is connected to the PIC

•  how the LCD display works

•  how to initialise and configure the LCD display

•  how to print text on LCD display

Microprocessors - LCD display 6 - 59  

Page 63: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 63/86

Create working directory 

LAB 6 : Developing a counter

Lab 6 procedure

Create working directory 

1. Create the lab6 directory

Using [F7] in Total Commander create lab6 directory under   C:\uP\labs.

Copy the files 

2. Copy the files from previous labs

Using Total Commander again copy all the files from the lab 5 and rename the lab*.* files

to the current lab. You can select the files by pressing   [numeric +]  and then write   lab   in

front of  *.*. Then press  [Shift + F6] and enter  lab6.*.

3. Add delays library to working directory

To do this lab, we’ll use a new library call  DELAYS.INC. This library has a number of 

subroutines that allow us to make PIC16F877A wait from a few microseconds to several

seconds until next instruction is executed.

4. Create new project and workspace files

(a) Delete the project file lab6.mcp and the workspace file  lab6.mcw.

(b) Now create new project and workspace files using the project wizard in the MPLAB IDE.

(c) When you are asked which files you want to add to project, select these four files:

lab6.asm, P16F877A.INC, DELAYS.INC and 16f877a.lkr.

5. Add files to project

6. Check if the project builds correctly

Open  lab6.asm  file by making double click on it in project tree. Go to the end of the

program and just before the line:

END   ; directive ’end of program’

Write the following code that includes the delays subroutines into the main program.

#include <DELAYS.INC>   ; delays library 

END   ; directive ’end of program’

Build the project by pressing  [Ctrl + F10]

 or  [Project → Build All]

. It should compile with-out errors. If not, go back and correct any errors in the project configuration until lab6

assembles correctly.

6 - 60 Microprocessors - LCD display  

Page 64: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 64/86

LCD display description 

LCD display description 

The LCD display used in this lab can display two lines of 16 alphanumeric characters,each made up of 5x7 pixels. The LCD display communicates with the microcontroller via

a 4-bit data bus.

The LCD display has 14 pins described in the following table.

The following figure shows how the LCD display is connected to the PORTB of the PIC

microcontroller. Four pins of the LCD, which are the data lines, are connected to thehigher nibble of  PORTB (higher nibble is the upper half of the byte, which means in the

case of  PORTB the four pins RB4  to  RB7). In addition RB2  is also connected to  RS  pin

Microprocessors - LCD display 6 - 61

Page 65: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 65/86

LCD display working modes 

and RB3 to Enable pin. The R/W line is connected to ground, that means that we always

have a value of zero in  R/W entry and therefore we can only write in the LCD display

and not read from it.The main advantage of this connection is that we are using the minimum number of pins

of the microcontroller to control the LCD display.

7. Name PORTB pins as aliases to LCD input pin names

At the beginning of the file lab6.asm, there are some configuration instructions. Just Af-

ter CONFIG directive we can assign the  PORTB pins that are connected to LCD display

to LCD entry names with the DEFINE directive. This allows us to refer to PORTB pins

with a name that describes the corresponding LCD entry.

#DEFINE LCD_RS PORTB,2

#DEFINE LCD_E PORTB,3

#DEFINE LCD_D4 PORTB,4#DEFINE LCD_D5 PORTB,5

#DEFINE LCD_D6 PORTB,6

#DEFINE LCD_D7 PORTB,7

LCD display working modes 

The LCD display has two main working modes:

•  Command mode:

LCD display is working in this mode when it is receiving instructions like ”ClearDisplay”, ”Display ON/OFF”,”Entry Mode Set”,”Function Set”...

To work in command mode, RS entry must be set to ’0’.

6 - 62 Microprocessors - LCD display  

Page 66: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 66/86

Page 67: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 67/86

LCD display working modes 

- The  LCD WriteCommand  subroutine takes the 8-bit command mode instruction

stored in W register and send it to LCD display.

- The  LCD WriteCharacter   subroutine takes the 8-bit ASCII character stored in  Wregister and writes it on the screen of LCD display.

As we have a 4-bit data bus and both command mode instructions and character mode

instructions are 8-bit long, we send first the high nibble of the specific instruction and

later the low nibble. This is a repetitive action, therefore we will also write the subrou-

tine LCD Write that sends a specific nibble to LCD display. That subroutine is called by

LCD WriteCommand and LCD WriteCharacter subroutines.

The process of writing  LCD WriteCommand  and  LCD WriteCharacter   subroutines is

described below, each step correspond to one instruction. The  LCD Write  subroutine is

already done and will be copied to file lab6.asm.The three subroutines will be written at the end of file   lab6.asm  and just before the

include directive.

•   LCD WriteCommand subroutine

1 - Set RS entry to ’0’.

2 - Get the value stored in W register and save it in a new variable called LCD Byte.

This variable should be previously registered at the beginning of our program

in the variable definitions section.

LCD_Byte   RES   1

We copy the value of  W  register in the  LCD Byte  register because we cannottest bit values in W register.

3 - Now, we have to send to the LCD display the high nibble of the value stored in

LCD Byte  register (and afterwards the low nibble). For that purpose, we will

call to the subroutine  LCD Write. That subroutine always sends high nibble of 

LCD Byte register.

4 - Swap between the high nibble and the low nibble in  LCD Byte   register with

swapf instruction.

5 - Recall to LCD Write subroutine.

6 - The last but one step in  LCD WriteCommand  subroutine is to call to the suit-

able delay subroutine to make sure that the command instruction is correctlyexecuted.

•   LCD WriteCharacter subroutine

1 - Sets RS entry to ’1’.

2 - Get also the value stored in W register and save it in the variable  LCD Byte.

3 - Call LCD Write subroutine to send high nibble of the value stored in LCD Byte

register.

4 - Swap between the high nibble and the low nibble in LCD Byte register.

5 - Recall to LCD Write subroutine to complete the character instruction.

6 - The last but one step in  LCD WriteCharacter  subroutine is to call to the suit-able delay subroutine to make sure that the character instruction is correctly

executed.

6 - 64 Microprocessors - LCD display  

Page 68: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 68/86

LCD display command descriptions 

•   LCD Write subroutine

The subroutine LCD Write writes high nibble of  LCD Byte register and is called by

LCD WriteCommand  and  LCD WriteCharacter  subroutines to send command orcharacter instructions respectively to the LCD display.

LCD_Write

 bcf   LCD_D7

 bcf   LCD_D6

 bcf   LCD_D5

 bcf   LCD_D4

 btfsc   LCD_Byte,7

 bsf   LCD_D7

 btfsc   LCD_Byte,6

 bsf   LCD_D6 btfsc   LCD_Byte,5

 bsf   LCD_D5

 btfsc   LCD_Byte,4

 bsf   LCD_D4

 bsf   LCD_E   ; Enable pulse

 bcf   LCD_E

return

LCD display command descriptions 

In this section the main commands that can be sent to LCD display are explained:

•  Clear Display

RS R/W DB7   DB6   DB5   DB4   DB3   DB2   DB1   DB0

=== === === === === === === === === ===

0 0 0 0 0 0 0 0 0 1

Clears all display and returns the cursor to the home position (the left edge of the

first line).

Sets I/D = 1 (Increment Mode) of Entry Mode.

•  Entry Mode Set

RS R/W DB7   DB6   DB5   DB4   DB3   DB2   DB1   DB0

=== === === === === === === === === ===

0 0 0 0 0 0 0 1   I/D S  

Sets the cursor move direction and specifies or not to shift the display.

I/D: Increments (I/D = 1) or Decrements (I/D = 0) the cursor position by 1 when acharacter is printed on the screen. The cursor moves to the right when incremented

 by 1 and to the left when decremented by 1.

Microprocessors - LCD display 6 - 65  

Page 69: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 69/86

LCD display command descriptions 

S: Shifts the entire display either to the right or to the left when S is 1; to the left

when I/D = 1 and to the right when I/D = 0.

•  Display ON/OFF

RS R/W DB7   DB6   DB5   DB4   DB3   DB2   DB1   DB0

=== === === === === === === === === ===

0 0 0 0 0 0 1   D C B

Sets the display and the cursor ON or OFF. Makes also the cursor position character

 blink or not.

D: The display is ON when D = 1 and OFF when D = 0. When off due to D = 0,

display data remains in memory and it can be displayed by setting D = 1.

C: The cursor displays when C = 1 and does not display when C = 0.B: The character indicated by the cursor blinks when B = 1.

•  Cursor and Display Shift

RS R/W DB7   DB6   DB5   DB4   DB3   DB2   DB1   DB0

=== === === === === === === === === ===

0 0 0 0 0 1   S/C R/L   ∗ ∗

Shifts cursor position or display to the right or left without writing display data.

S/C R/L=== ===

0 0   Shif ts the cursor position to the left

(Address Counter is decremented by  1)

0 1   Shif ts the cursor position to the right

(Address Counter is incremented by  1)

1 0   Shif ts the entire display to the left

T he cursor f ollows the display shif t

1 1   Shif ts the entire display to the right

T he cursor f ollows the display shif t

•  Set Cursor Position

RS R/W DB7   DB6   DB5   DB4   DB3   DB2   DB1   DB0

=== === === === === === === === === ===

0 0 1   A A A A A A A

Sets the cursor position on the screen without writing display data.The cursor position is specified by the seven-bit memory direction AAAAAAA,

which is called DD RAM Adress of the LCD display. For the first line, write an

6 - 66 Microprocessors - LCD display  

Page 70: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 70/86

LCD display initialisation 

hexadecimal value between 00h and 27h. For the second line, write an hexadecimal

value between 40h and 67h.

As each line of the LCD display can only display 16 alphanumeric characters, the00h value sets the cursor at the first position of the first line and the 0Fh value sets

the cursor at the last position of the first line. Analogously, the 40h value sets the

cursor at the first position of the second line and the 4Fh sets the cursor at the last

position of the second line.

9. Make command subroutines

We can add also a number of subroutines that can be called to send command instructions

to LCD display. This subroutines merely stored the corresponding value in W  register

 before going to  LCD WriteCommand subroutine.

Some examples are shown below:

; Clear screen and send cursor to the start or first line LCD_ClearDisplay 

 movlw   b’00000001’

call   LCD_WriteCommand

; Cursor in incremental mode LCD_CursorIncr 

 movlw   b’00000110’

call   LCD_WriteCommand

; Set cursor position to start of first line (00h of DDRAM) LCD_Line1

 movlw   b’10000000’

call   LCD_WriteCommand

; Set cursor position to start of second line (40h of DDRAM) LCD_Line2

 movlw   b’11000000’

call   LCD_WriteCommand

Make subroutines  LCD DisplayOn and LCD DisplayOff  to switch on and off the dis-

play, LCD LinePosition to set the cursor position to the value of W register and LCD CursorOn

and LCD CursorOff to show and hide the cursor.

LCD display initialisation 

To start working with LCD display it must be initialised and configured. This is done by

sending specific instructions in the correct order and respecting minimum delays between

any two subsequent instructions.

10. The LCD initialise subroutine

The flowchart of the initialisation protocol is shown below:

Microprocessors - LCD display 6 - 67  

Page 71: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 71/86

LCD display initialisation 

The waiting times in the flowchart set the minimum threshold. The actual delays cannot

 be shorter but they can be longer. In case there is no subroutine of the required delay in

the DELAYS.INC library we should use the next longer delay.

11. Write the LCD initialise subroutine

In this subroutine first instructions are 4-bit long (if we exclude RS and R/W inputs), thatis they are defined with only one nibble. Afterwards, all remaining instructions are 8-bit

long and we have to send two nibbles to define any instruction.

6 - 68 Microprocessors - LCD display  

Page 72: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 72/86

Display your name on the LCD 

Remember that we have to send an enable pulse to the  E input after each nibble has been

sent to cause each instruction to execute.

 bsf   LCD_E   ; Enable pulse bcf   LCD_E

When one specific instruction is being executed, LCD display doesn’t realize if any other

instruction has been sent. For that reason, to create this subroutine we have to respect

minimum delays between instructions. This is done by calling the appropiate delay sub-

routine in DELAYS.INC file. First delays are specified in the flowchart and after the 100-

microsecond delay we have to respect at least 1.64 miliseconds between any two subse-

quent instructions.

For example, to fulfil the 4.1 miliseconds requirement we will choose a 5 miliseconds

delay subroutine. This subroutine is called after sending the enable pulse.

call   Delay_5ms

Now, go to the end file lab6.asm and write the subroutine. You can use the LCD display

commands described earlier to speed up the process and better structure your program.

Lab 6 A – Display your name on the LCD

Remove the unneeded code from the program 

Once all the subroutines necessary to control the operation of the LCD are in place, weare ready to modify the main body of the program. In the previous labs the 7-segment

displays were controlled by the interrupts generated by TMR0. All the variables and code

that were involved in that task will not be needed any more and should be removed.

12. Clean up the ISR

All the code that we added to the ISR should be removed including the fsr temp variable

and its declaration.

13. Remove interrupt enable code

This is the code that was configuring the  INTCON register:

14. Remove TMR0 setup code

Remember to remove TMR0 INITIAL as well.

15. Remove variables used in the ISR

This will include FSR digit ISR and display variables.

16. Remove PORTA intialisation

The LCD display is controlled using only PORTB.

Display some text on the LCD 

Before we start “interfacing” our counter to the LCD first we will try to display some text

on the LCD to see if the subroutines that we have written are bug-free.

Microprocessors - LCD display 6 - 69  

Page 73: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 73/86

Display some text on the LCD 

17. “Desactivate” the rest of the program

After the LOOP label we will place the instruction:

goto   LOOP

This will prevent the rest of the programm from running as it will loop back before any

counter instruction is executed. Only the initialisation part will be carried out.

18. Initialise the LCD

All the new code we are going to write in this part of the lab should be placed just before

the LOOP label.

The first subroutine to call is of course  LCD Initialise. This subroutine will initialise and

configure the LCD.

19. Sending the characters to the LCD

Once the LCD is ready, we can send some text for displaying. Suppose we want to display

a message: “I love PICs!” on the LCD. The easiest way to do so will be to write a character

to the W  register, then call the  LCD WriteCharacter subroutine and repeat this for all

the characters in the string. This would go like this:

 movlw   ’I’

call   LCD_WriteCharacter

 movlw   ’ ’

call   LCD_WriteCharacter

 movlw   ’l’

call   LCD_WriteCharacter movlw   ’o’

call   LCD_WriteCharacter

 movlw   ’v’

call   LCD_WriteCharacter

 movlw   ’e’

call   LCD_WriteCharacter

 movlw   ’ ’

call   LCD_WriteCharacter

 movlw   ’P’

call   LCD_WriteCharacter

 movlw   ’I’

call   LCD_WriteCharacter

 movlw   ’C’

call   LCD_WriteCharacter

 movlw   ’s’

call   LCD_WriteCharacter

 movlw   ’!’

call   LCD_WriteCharacter

Write this code, or any similar — in fact any message will do. If it works, you might be

almost sure that the routines you have written are correct.

Note:   Do not continue until your code works correctly.

6 - 70 Microprocessors - LCD display  

Page 74: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 74/86

Page 75: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 75/86

Display the counter on the LCD 

Of course the number of instruction generated by the assembler and the memory usage

will still be the same. The  dt  directive only makes the coding easier.

22. Send the message to the LCD

Now it is your turn to write a loop that would read the characters from the table using

this code snippet:

 movf   i, W

call   MESSAGE

and send them to the LCD.

And remember that the final character in the string has to be 0 (and not ’0’)!

Lab 6 B – Display the counter on the LCD

“Reactivate” the counter code 

23. “Activate” the main body of the program again

Remove the   goto LOOP  instruction you have placed after the   LOOP   label. In this way

the counter code becomes operative again.

24. “Desactivate” displaying of the message

Add a goto LOOP

 instruction before the code you have written to display a message sothe processor will jump over it directly to the counter code.

Display the counter on the LCD 

25. Clear the display

Each time the counter is updated we have to clear the LCD display. This is achieved by

calling the LCD ClearDisplay subroutine. Each digit must be printed on the LCD screen

in order from the most significant to the least significant using LCD WriteCharacter sub-

routine.

This can be done in a new section called PRINT DIGIT between EMPTY DIGIT CHECKand  BUTTON DOWN RA4   sections.   PRINT DIGIT   section clears display, calls the

ASCII CODE subroutine to obtain ASCII code of each digit and calls LCD WriteCharacter

subroutine to print it on the screen.  EMPTY DIGIT CHECK section ”goto” instructions

must jump to PRINT DIGIT section.

SEVEN SEGMENT CODE section must be changed to return ASCII code of each digit.

This can be easily accomplished by placing each digit between quotes, as shown below.

The empty character becomes now a simple space. Note the name of the subroutine has

also been changed to reflect its new functionality.

ASCII_CODE:

addwf   PCL, f

retlw   ’0’; case 0

retlw   ’1’; case 1

6 - 72 Microprocessors - LCD display  

Page 76: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 76/86

Display the counter on the LCD 

retlw   ’2’; case 2

retlw   ’3’; case 3

retlw   ’4’; case 4

retlw   ’5’; case 5

retlw   ’6’; case 6  

retlw   ’7’; case 7  

retlw   ’8’; case 8

retlw   ’9’; case 9

retlw   ’ ’; empty  

26. Build the project, debug and write to PIC

After you’re done with debugging, your program should increment the counter every

time you press the RA4 button and display it on the LCD screen.

Microprocessors - LCD display 6 - 73  

Page 77: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 77/86

Display the counter on the LCD 

6 - 74 Microprocessors - LCD display  

Page 78: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 78/86

Chapter 7

Counter with button read in mikroC

In this chapter you will learn:

•  how to create projects using mikroC IDE

•  how to use libraries provided by mikroC

•  how to port assembly code to C

•  how to set up interrupts

•  how to port to C the counter applications you developed in previous labs

Microprocessors - Counter with button read in mikroC 7 - 75  

Page 79: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 79/86

Create working directories 

LAB 7 : Developing a counter in mikroC

Lab 7 procedure

Create working directories 

1. Create the lab7 directory

Using [F7] in Total Commander create lab7 directory under   C:\uP\labs.

2. Create the example, 7seg and lcd subdirectories

Using  [F7] in  Total Commander  create example,  7seg and  lcd  subdirectories under  C:\

uP\labs\lab7.

Developing the first project in mikroC 

3. Open microC IDE

Open mikroC IDE by making double click on its desktop shortcut.

4. Change the Code Editor’s colours

The Code Editor is set to a particular scheme of colours and the default scheme in mikroC

has the background black. As it is more confortable to work with a white background,we are going to change it.

Click   [Tools → Options] from the drop-down menu, or click  [F12], to open the Preferences

window.

Choose  [Editor → Colors] from the left tree menu and go to the Scheme section in the mid-

dle of the window. Change the default scheme “Zedar” for the more confortable “mikro-

Dream” in the drop-down list next to the “Scheme:” label. Finally, click on the “OK”

 button to make changes take effect.

Example 

5. Read the documentation and develop the first project

Read the document “Creating First Project in mikroC compiler”. Follow carefully each

step to create a new project, write the code, built it and test the results.

In the Step 4 the main properties of our project are defined. Choose the following settings:

•  Project Name:  MyProject

•  Project Path:  C:\uP\labs\lab7\example

•   Device: P16F877A

•   Clock: 008.000000 (8MHz)

•  Device Flags:  Default Settings (by clicking on Default button).

7 - 76 Microprocessors - Counter with button read in mikroC  

Page 80: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 80/86

Counter on 7-segment displays written in C 

In the Step 5  the example’s code is written. The first code to be executed by the micro-

processor is always inside the main function.

void    main() {

// First code to be executed 

}

If we examine the example’s code, we can realize that:

•   It is not necessary to worry about in which memory bank each register is. We can

easily configure all pins of PORTB as outputs, by assigning a zero to TRISB register

without worrying about its location.

TRISB = 0

•  There are built-in functions.For example, with the Delay ms() function, we specify how many miliseconds we

want the program to wait without having to write a delay subroutine.

•  To write loops, we use C structures such as do{...}while(condition)

Lab 7 A – Counter on 7-segment displays written in C

In this section of the lab7, we will develop the same counter as in lab5 but using mikroC

instead of assembly language.

6. Create a new project

Create a new project with the following settings:

•  Project Name:  lab7 7seg

•  Project Path:  C:\uP\labs\lab7\7seg

•   Device: P16F877A

•   Clock: 008.000000 (8MHz)

•  Device Flags:  Default Settings

7. Write the project header

At the beginning of the code we will write a header that allows us to easily identify the

project.

/*

* Project name:

lab7_7seg (7-segment display)

* Test configuration:

MCU: PIC16F877A

Dev.Board: EasyPIC3

Oscillator: HS, 08.0000 MHz

SW: mikroC v8.2

*/ 

Microprocessors - Counter with button read in mikroC 7 - 77  

Page 81: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 81/86

Counter on 7-segment displays written in C 

8. Variable definitions

Before writing any function in mikroC (main function included), it is compulsory to de-

fine the variables we are going to use in our program.

To see the data types used in mikroC go to page 33 in “mikroC Language reference” user’s

manual.

In lab5, we had the following variables:

unsigned short   digit[4];

unsigned short   digit_counter[4];

unsigned short   display;

unsigned short   digit_int;

unsigned short   i;

We define all variables as unsigned short type because they are one byte length in lab5

and we work with positive numbers.

The digit and digit counter variables are arrays. They replace the variables digit 1 to digit 4

and digit counter 1 to digit counter 4 from lab 6 respectively.

The  digit counter  array stores the four-digit number of the counter, meanwhile the  digit

array stores the same number with blanks instead of non-significant zeros. Each of them

represent an array of four elements of the  unsigned short   type, from digit counter[0]  to

digit counter[3] and from digit[0] to digit[3] respectively.

To learn more about arrays types read pages 37 to 39 in “mikroC Language reference”

user’s manual.

The identifier TMR0 INITIAL fixes the timer interrupt’s period as explained in lab 6. We

use the preprocessor command define to assign a constant value to TMR0 INITIAL.

#define   TMR0_INITIAL value

When we compile the project, any occurrence of  TMR0 INITIAL in the rest of the code

is replaced by its corresponding value.

9. Write the main program code

The main program is written below the variable declarations and inside the function

main():

void    main() {

// Code of function main

}

In lab7 (as in lab5), the main program configures and enables timer interrupts, configures

PORTA and  PORTB and finally, inside a loop, checks if button 4 of  PORTA  is pressed

and increments a counter if that is the case.

To enable and configure timer interrupts we change the value of INTCON, OPTION REG

and TMR0 registers.

Go to page 35 in “mikroC Language reference” user’s manual to see how to access indi-

vidual bits of a register.

7 - 78 Microprocessors - Counter with button read in mikroC  

Page 82: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 82/86

Counter on 7-segment displays written in C 

Besides the identifiers F0, F1 , ... , F7 we can also access individual bits by their names.

Therefore we will enable interrupts in  INTCON  and  OPTION REG  registers with the

following code:// Enable interrupts

INTCON.GIE = 1;   // Enable all unmasked interrupts

INTCON.TMR0IE = 1;   // Enable timer 0 interrupts

// Configure TMR0

OPTION_REG = (0 << T0CS) | (1 << T0SE) | (0 << PSA);

OPTION_REG = OPTION_REG + 0b100;   // Add prescaler x32

Now, we have to set the  TMR0 register to the value of the constant  TMR0 INITIAL.

Next, configure PORTB with TRISB, PORTA with TRISA and set the 0x06 value to the

ADCON1 register.

To initialise the counter, we must set to zero all elements of the  digit counter array. We can

use the for instruction for that purpose.

for( i = 0 ; i < 4 ; i + + ) {

digit_counter[ i ] = 0;

}

The next initialization step is to choose the first display by assigning a value of 1 to the

display variable.

The infinite loop in the main function can be done with the  do...while structure as in

the example code.

Inside the loop, we write the following steps:

•   Copy digit counter array variables to their corresponding in the  digit array.

•  Remove non-significant zeros in the digit array variables and replace them with the

’10’ value. This can be done with  if (condition) or   while (condition) in-

structions.

•  Check if button RA4 is pressed.

As we have to check it constantly, we need an infinite loop to detect if it is pressed.

For that purpose we can use a while loop:

while   ( PORTA.F4 == 0 ) {}

•  Check if button RA4 has been released.

•  Increment less significant digit in  digit counter array.

•  Check digits overflow in digit counter array.

If that is the case, make zero the overflow digit and increment the following more

significant digit. If four digits of the counter overflow, reset the counter by making

all of them zero.

10. Write the interrupt function

Go to page 36 in “mikroC Language reference” user’s manual and read the section about

interrupts.

Microprocessors - Counter with button read in mikroC 7 - 79  

Page 83: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 83/86

Counter on LCD display written in C 

The first thing we have to do inside the interrupt function is to clear the flag TMR0IF of 

the INTCON register.

Next, we will increment the digit int variable and shift the display to be visualized withthe  <<  command in the  display  variable. (Go to pages 77 to 78 in “mikroC Language

reference” user’s manual to see how the bitwise operator  << works).

If the display variable has a value greater than the corresponding to the last display, it

must be initialised to ’1’ and the  digit int must be reset to ’0’.

Next, we will reload the timer TMR0 with the constant TMR0 INITIAL, assign PORTA

to the display variable and assign PORTB to the seven segment code of the corresponding

variable in digit array pointed by digit int.

As in  digit  array are stored decimal digits, we need a function (a subroutine in assem-

 bly language) to replace the decimal digit with its 7-segment code. This is done by the

SEVEN SEGMENT CODE function described in the following section.

11. Write SEVEN SEGMENT CODE function

The SEVEN SEGMENT CODE function takes a digit and convert it to 7-segment code.

A subroutine in assembly language is equal to a C function in mikroC.

The SEVEN SEGMENT CODE function must be defined before it is used. For that rea-

son, we write it between the variable definitions and the interrupt function because it is

used inside it.

The  SEVEN SEGMENT CODE  function has an input variable of  unsigned short  type

and has an output variable (the 7-segment code of the input digit) of the unsigned short

type also.

unsigned short   SEVEN_SEGMENT_CODE(   unsigned short   num ){

// code

}

In assembly language we have use the  retlw  instruction to return a different value de-

pending on the input value.

SEVEN_SEGMENT_CODE:

retlw   0x__ ; case 0

...

retlw   0x__ ; case 9

Use the switch...case command to do the same in C.

12. Compile and test the counter

Compile the lab7 7seg project by selecting [Project →Build] from drop-down menu or click-

ing [Ctrl+F9].

Select   [Tools   →  PicFlash Programmer]  from drop-down menu, or click   [F11], to test if the

counter is working properly.

Lab 7 B – Counter on LCD display written in C

In this section of the lab7 we will also develop a counter, but now the counter will be

displayed on the LCD display.

7 - 80 Microprocessors - Counter with button read in mikroC  

Page 84: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 84/86

Counter on LCD display written in C 

13. Create a new project

Create a new project with the following settings:

•  Project Name:  lab7 lcd

•  Project Path:  C:\uP\labs\lab7\lcd

•   Device: P16F877A

•   Clock: 008.000000 (8MHz)

•  Device Flags:  Default Settings

14. Write the project header

At the beginning of the code write the project header as follows.

/*

* Project name:

lab7_lcd (LCD display)

* Test configuration:

MCU: PIC16F877A

Dev.Board: EasyPIC3

Oscillator: HS, 08.0000 MHz

SW: mikroC v8.2

*/ 

15. Variable definitions

To develop the counter on the LCD display we will only need two variables:

•  An integer variable where the count value is stored.

As we want to develop a counter from “0” to “9999”, we go to page 33 in “mikroC

Language reference” user’s manual to search for a integral type that includes such a

range.

•  As we will see in the next section, we need a char array to write on the LCD display.

We will use the built-in  IntToStr function that returns a 6-byte char array from an

integer variable, that plus the end-of-string character makes up a total of 7-byte chararray.

16. Write the main program code

As we have seen in lab7, we need to use the  PORTB to control the LCD display. On the

other hand, the button RA4 belongs to the  PORTA.

Therefore, we will configure all pins of  PORTB   as outputs with  TRISB  and the RA4

 button of PORTA as input. We also have to assign the 0x06 value to ADCON1 register.

It is very easy to control the LCD display with a mikroC LCD library, which provides

many functions that allow us to program it. Before continuing with the lab, take a look at

pages 236 to 251 in “mikroC Language reference” user’s manual.

To initialise the LCD display you can adapt the code you can find on page 161.

Microprocessors - Counter with button read in mikroC 7 - 81

Page 85: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 85/86

Counter on LCD display written in C 

Now, we have to initialise the  count variable to zero and write an infinite loop using the

while( 1 ){...}  construct. Inside the loop we will have to perform the following

actions:

•  As it has been said before, the  IntToStr function converts the integer type count into

a 6-byte char array txt.

Go to the conversions library section in “mikroC Language reference” user’s man-

ual, and on page 360 you’ll find the description of how to use the IntToStr function.

•  Now, once the first 6 bytes of the txt  variable are assigned, it is necessary to set the

last character of the char array to the end-of-string character.

The end-of-string character it is equal to ’\0’.

•  The counter value is written to the LCD display by using the  LCD Out  function,

with the txt variable as the argument.Go to the LCD library section in “mikroC Language reference”y user’s manual to

see how to print the counter value on the screen.

•  Next, we have to wait for the button to be pressed and released.

•  At the end of the loop, we only have to increment the counter and check if it has

exceeded the upper limit, which is 9999. If that is the case, reset the counter to zero.

17. Compile and test the counter

Compile the lab7 lcd project by selecting   [Project → Build] from drop-down menu or click-

ing [Ctrl+F9]

.Select  [Tools → PicFlash] Programmer from drop-down menu, or click  [F11], to test if the

counter is working properly.

7 - 82 Microprocessors - Counter with button read in mikroC  

Page 86: uP Student Book

8/13/2019 uP Student Book

http://slidepdf.com/reader/full/up-student-book 86/86

Counter on LCD display written in C