Program description

What does it do?

The chaser program sweeps an LED back and forth.

New instructions

During this activity, you will learn about these microcontroller instructions:

call 'call a subroutine' - save the next program instruction address on the Stack and continue running the program from a label. Like goto, but call is used with return.
return 'return from subroutine' - returns to the address saved on the Stack, continuing the program from the instruction after a call.
bcf 'bit clear file register' - clears a single bit in a file register (RAM).
bsf 'bit set file register' - sets a single bit in a file register.
rlf 'rotate left file register' - shift every bit in a file register (RAM location) one bit to the left.
rrf 'rotate right file register' - shift every bit in a file register one bit to the right.

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

Chaser programming activity

Chaser uses decision structures to choose between two subroutines, sweeping an illuminated LED back and forth in the process. It also illustrates subroutine re-use, by using call and return instructions instead of goto.

What you should know before starting

Microcontroller related information

The PIC16F876A microcontroller includes an 8-level hardware Stack which is attached directly to its processing unit (see the simplified PIC16F876 block diagram). This Stack is a LIFO (last-in, first-out) buffer that stores up to eight program addresses.

Specific instructions automatically cause program memory addresses to be written to, or read from, the Stack, providing the microcontroller with the ability to store and remember the address of instructions that will be executed following another subroutine or event (such as an interrupt). Unlike more advanced microprocessors, only the PIC's hardware can control the stack—no program instructions can be used to access the stack.

In this program, a call instruction will store the address of the instruction directly after itself in memory on to the top of the Stack before jumping to the called subroutine. At the end of the called subroutine, a return instruction will read the top address from the Stack, thereby continuing program execution from the instruction following the call.

The advantage of a call instruction is that it allows a program subroutine to be called from more than one place in the program code, and the return instruction will always return program control to the specific subroutine that initiated the call.

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 CHASER.ASM program is shown below. Create a Chaser project in MPLAB, copy this code into it, and build the program.


;CHASER.ASM 	v1.2	Last modified on July 28, 2007
;===============================================================================
;Description:	LED light chaser program.

;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 any 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

Main		bsf	PORTB,0		;Turn on one LED only

Chase_Left	call	Time_Delay	;Delay so humans can see the light
		bcf	STATUS,C	;Clear C before rotating
		rlf	PORTB,F		;Shift PORTB contents to the left
		btfss	PORTB,7		;Has the 1 moved to the far left?
		goto	Chase_Left	;If not, keep moving left
					;Otherwise, switch direction

Chase_Right	call	Time_Delay	;Delay so humans can see the light
		bcf	STATUS,C	;Clear C before rotating
		rrf	PORTB,F		;Shift PORTB contents to the right
		btfss	PORTB,0		;Has the 1 moved to the far right?
		goto	Chase_Right	;If not, keep moving right
		goto	Chase_Left	;Otherwise, switch direction

Time_Delay	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
		return			;Return when done

	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

The start of the program and port initialization follow the same pattern as in the Count program. The bsf PORTB,0 instruction beside the Main label lights up one PORTB LED by setting just the bit in position 0 of the PORTB register. Whereas the combination of movlw and movwf instructions let us change all eight PORTB bits in Output, bsf changes only the one, specified bit.

		
Main		bsf	PORTB,0		;Turn on one LED only

Chase_Left	call	Time_Delay	;Delay so humans can see the light
		bcf	STATUS,C	;Clear C before rotating
		rlf	PORTB,F		;Shift PORTB contents to the left
		btfss	PORTB,7		;Has the 1 moved to the far left?
		goto	Chase_Left	;If not, keep moving left
					;Otherwise, switch direction
		

The first line of Chase_Left calls the Time_Delay subroutine. Like goto, a call instruction changes the flow of the program. Unlike goto, call stores its return address on the stack so that a later return instruction (in the called subroutine) can continue running the program from the line directly after the call. This important difference between call and goto allows more than one part of our program to call the Time_Delay subroutine—in each case returning to the subroutine it was called from.

The Time_Delay subroutine is essentially the same as the delay code in the Count program, with the addition of a return instruction at its end. After TMR0 reaches zero, return causes the microcontroller to return to the instruction directly below call Time_Delay.

After returning from the time delay, the bcf STATUS,C instruction clears the C (Carry) bit in the Status register. This is important for the upcoming rotate operation that will be used to move the LED pattern. During a rotate operation, the contents of a file register are rotated through C and back into the opposite side of the register. Clearing C before the rotate ensures that it won't accidentally introduce a second lit LED into our display pattern.

Next, rlf PORTB,F shifts the contents of Port B (the light pattern in this program) one position to the left. The left-most bit (bit 7) in the register rotates into Carry, and Carry rotates back into bit 0 of the register—which, again, is the reason we had to clear Carry before rotating.

Then the decision-making btfss PORTB,7 instruction checks to see if the one in the pattern we loaded into the PORTB register has arrived at the far left bit position of the register (the bit 7 position) by checking if the bit is set. If bit 7 is clear, the program repeats the time delay before rotating Port B again. After seven rotates, bit 7 will finally be set, and the program execution will jump over the goto Chase_Left instruction and into the Chase Right subroutine.

Chase_Right performs the exact same function as Chase_Left, albeit in the opposite direction, and both subroutines are able to use the same Time_Delay code through the call instruction.

Test your knowledge

  1. The Stack is attached to, and controlled by, the microcontroller. Can your program perform nested calls—a call to a subroutine calls another subroutine—and how many levels deep?
  2. What do you think will happen if a return instruction is encountered before a call? (Try this in the MPLAB simulator)

Apply your skills

  1. Rotate instructions perform the software equivalent of shift registers and state machines. Modify the Chaser program to continuously cycle a pattern through PORTB. How many states does this pattern have? Can you think of a simple way to expand the number of states in the pattern?