Program description
What does it do?
The count program counts in binary, displaying the count on the LEDs.
New instructions
During this activity, you will learn about these microcontroller instructions:
| btfss | 'bit test file register, skip if set' - checks if the specified bit of a file register is set (1). If the bit is clear (0), the line immediately following btfss is executed. If set (1), the line immediately following btfss is skipped. (btfsc is the complement to btfss, and is used to check if a bit is clear.) |
| movf | 'move file register' - move a number from a file register (RAM) to itself, or to W, the Working register |
| incf | 'increment file register' - increment (add one) to the contents of a file register (RAM location). (decf is the complement to incf, and subtracts one from the contents of a file register.) |
Jumper positions
The CHRP jumpers need to be set to the following positions in order for this program to function.
| J7 | - serial receive | - Ser. position |
| J8 | - serial transmit | - Ser. position |
| J9 | - analogue input | - n/a |
| J11 | - power select | - Cont. position |
| J13 | - voltage divider | - n/a |
Count programming activity
Did you try to make a flashing light pattern in the Output activity? The pattern you made would have changed so fast that you would not have been able to see it. This program uses a time delay to display a binary count—one that you can see—on the LEDs. It also introduces you to a common decision-making structure and one of the PIC's hardware timers.
What you should know before starting
Microcontroller related information
PIC microcontrollers contain one or more hardware timers. Timers are hardware circuits inside the microcontroller that count or time events so that your program software can do other things.
This program uses TMR0 (timer 0) to make its time delay. TMR0 is an 8-bit timer (256 states) with an optional 8-bit prescaler, meaning that it can actually count up to an equivalent of 65,536 (256 * 256) states. The prescaler divides the input by a binary factor before generating a TMR0 count. With a maximum prescaler of 256, for example, TMR0 will increment by one after the first 256 events have gone through the prescaler. TMR0 will remain at that count for the next 255 events—and increment again on the 256th event.
TMR0 trades the number of counts for resolution. With a prescaler of 1 (no prescaler), each event gets counted, but only up to a maximum of 256 events. Using a prescaler of two allows a count of 512 by counting only every other event. A prescaler of four counts to 1024 by counting every fourth event, and so on, up to the maximum count of 65,536.
Program requirements
To use this program you will need:
An assembled CHRP board, microcontroller, and power supply, a programming cable, and a Windows PC with the MPLAB IDE software and downloader software as described in the Output activity.
Create the program
The entire COUNT.ASM program is shown below. Create a new project in MPLAB, copy this code into it, and assemble the program.
;COUNT.ASM v1.2 Last modified on July 28, 2007 ;=============================================================================== ;Description: Count-up program. Display binary count on LEDs. ;Start of MPLAB and processor configuration. list p=16F876A ;Define processor type include "p16f876a.inc" ;Include processor definitions __CONFIG _CP_OFF & _DEBUG_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _PWRTE_ON & _WDT_OFF & _LP_OSC ;End of MPLAB and processor configuration. org 00h ;Start of program memory clrf PORTA ;Turn off all port outputs clrf PORTB clrf PORTC goto Init_Ports ;Jump to initialize subroutine org 05h Init_Ports ;Set Ports B and C to support CHRP digital circuitry. banksel TRISB ;Switch to TRISB register bank movlw 01010111b ;Enable Port B pull-ups, internal TMR0 movwf OPTION_REG ;clock with prescaler of 256 clrf TRISB ;Set all LED (Port B) pins as outputs movlw 10110000b ;Set up serial input and output pins, movwf TRISC ;and set motor pins as outputs banksel PORTB ;Return to PORTB register bank Set_Timer movlw 61 ;Preload TMR0 for 50ms time period movwf TMR0 Check_Timer movf TMR0,W ;Check TMR0 value btfss STATUS,Z goto Check_Timer ;Repeat until TMR0 = 0 incf PORTB,F ;Increase the count on the LEDs goto Set_Timer ;Reset TMR0 for the next count org 1F00h ;Start of bootloader code area res 256 ;Reserve memory for bootloader end
Download the program into the CHRP and verify its operation.
How the program works
Like Output, this program starts at program memory location 0, the Reset Vector. Unlike Output what follows is not a goto, but instructions to clear the Port A, B and C registers. Clearing the registers now actually has no effect (because the I/O pins default to input on power-up), but it does ensure that when the output pins become active, they will be at a known (zero) state.
org 00h ;Start of program memory clrf PORTA ;Turn off all port outputs clrf PORTB clrf PORTC goto Init_Ports ;Jump to initialize subroutine
The goto Init_Ports instruction follows clearing the ports. It causes the microcontroller to jump over the Interrupt Vector (memory location 4) exactly as in the Output program. Also, just as in Output, Init_Ports is located at program memory address 5, one location past the Interrupt vector. If you could look into the program memory, this is where you would find the instructions:
| Address | Contents |
|---|---|
| 00 | clrf PORTA |
| 01 | clrf PORTB |
| 02 | clrf PORTC |
| 03 | goto 05 |
| 04 | (empty - Reset Vector) |
| 05 | Init_Ports |
| . | . |
Initialization and TMR0 setup
The second and third lines of the Init_Ports subroutine will probably be a bit more clear to you now (you did read the 'What you should know before starting', above, didn't you?). One of the functions of OPTION_REG is controlling TMR0, the Timer 0 module. Some of the OPTION_REG bits enable TMR0 and select its input, and other bits set TMR0's prescaler. Refer to Microchip's PIC16F87xA data sheet for detailed information about the function of each OPTION_REG bit.
Init_Ports ;Set Ports B and C to support CHRP digital circuitry. banksel TRISB ;Switch to TRISB register bank movlw 01010111b ;Enable Port B pull-ups, internal TMR0 movwf OPTION_REG ;clock with prescaler of 256 clrf TRISB ;Set all LED (Port B) pins as outputs movlw 10110000b ;Set up serial input and output pins, movwf TRISC ;and set motor pins as outputs banksel PORTB ;Return to PORTB register bank
The remainder of the Init_Ports subroutine is the same as in the Output program. It prepares PORTB for output to the LEDs, and sets up PORTC for motor output and serial I/O.
Set_Timer movlw 61 ;Preload TMR0 for 50ms time period movwf TMR0
The Set_Timer subroutine starts the program by pre-loading the TMR0 register with the count of 61. TMR0 will count from 61 up to 256, incrementing by one for every 256 microcontroller clock cycles, before automatically wrapping the count in the TMR0 register back around to zero.
Deciding how long to wait
Check_Timer movf TMR0,W ;Check TMR0 value btfss STATUS,Z goto Check_Timer ;Repeat until TMR0 = 0 incf PORTB,F ;Increase the count on the LEDs goto Set_Timer ;Reset TMR0 for the next count
The Check_Timer subroutine waits for the TMR0 count to overflow to zero. movf TMR0,W copies the current TMR0 value into W, and btfss STATUS,Z checks if its value is zero.
How does the microcontroller know if something is zero? The STATUS register contains three arithmetic flag bits that indicate mathetatic results: the Z (zero) bit in the STATUS register becomes a one whenever a number is zero, and the C (carry) and DC (digit-carry) bits indicate mathematical carry or borrow operations (these will be described in more detail in the Math programming activity). So,
when a number or answer is 0, Z=1 (set), and
when a number or answer is not 0, Z=0 (clear).
Moving the TMR0 value into W activates a check for zero. If the micrcontroller detects that the value in TMR0 is zero, it sets the Z bit in the STATUS register. The btfss STATUS,Z (bit test file register, skip if set) instruction reads the Z bit and decides what to do next:
If Z=0 (clear), goto Check_Timer executes next, repeating the loop. Or,
If Z=1 (set), goto Check_Timer is skipped. incf PORTB,F executes next.
Play the computer
Imagine the program running. We know that TMR0 contains 61 on startup. We also know that it will take 256 microcontroller clock cycles (the prescaler value) before TMR0 goes up by one count to 62. Assuming that PIC microcontroller instructions take one or two clock cycles to execute, we can infer it will be a while until TMR0 reaches zero—roughly 50,000 clock cycles in this case, or about 50ms in a 4MHz PIC.
When TMR0 eventually does reach zero, the Z bit in the Status register will be set, goto Check_Timer is skipped, and incf PORTB,F adds one to the value in the PORTB register (the ',F' at the end of the instruction makes the result stay in PORTB—the other option, ',W' would move the result into W). Since the PORTB RAM register is also connected to the eight CHRP LEDs, we see the register's value as it counts. Every 50ms (20 times per second) the count changes, which is slow enough for us to see it.
After the increment instruction, the program starts again from Set_Timer by reloading TMR0 with 61, checking for zero over and over again for another 50ms, and finally incrementing PORTB to the next count.
Test your knowledge
- Use the simulator to verify the time delay between counts. Set a breakpoint at the incf PORTB,F line of the program and run the program up to there. Then clear the stopwatch and re-run the program. How many clock cycles does it take between successive PORTB increments?
- Will this program ever stop? Why or why not.
- Set the timer starting value in the Set_Timer subroutine to 1 instead of 61. Re-build the program and determine how much time it takes to increment PORTB now. Compare this result to the first question. Why are they different?
- What will happen if the Set_Timer routine sets TMR0 to 0 instead of 61?
Apply your skills
- Modify the Count program to display a flashing pattern. The easiest way to do this is to move a pattern into PORTB before the loop started by the Set_Timer subroutine. Then, replace the incf PORTB,F with comf PORTB,F, which performs a logical NOT operation and flips the pattern.
- Modify the Count program to display a pattern made up of three or more distinct LED values. (This is quite a bit trickier than 1, above. Hint: you'll need to duplicate something.)

