Program description

What does it do?

The Loops program demonstrates typical microcontroller loop structures using a decrement (count down) instruction. Most microcontrollers have the ability to sense a zero result, so counting down from a starting number toward zero is a common operation.

New instructions

During this activity, you will learn about this microcontroller instruction:

decfsz 'decrement file register and skip if zero' - subtracts one from the contents of a file register and skips the next instruction if the result is zero. (incfsz is the complementary increment instruction, and also skips when the result is zero.)

Loops programming activity

The Loops program demonstrates the most common finite loop structure used in PIC assembly code, and also demonstrates a method of waiting for and acting on user action.

What you should know before starting

Finite loops are used to repeat sections of program code a specific number of times. Whereas we humans would likely count events by counting up (except for rocket launches, of course), microcontroller loops typically count down to zero.

In the decision activity, you learned that the STATUS register can be used to test for a zero result. The decfsz (decrement file register and skip if zero) instruction is unique in that this one instruction combines the functions of a math instruction (by decrementing, or subtracting one) and a bit test instruction (by testing the STATUS register Zero flag, and skipping the next instruction on a zero result).

Sensing individual user actions

When you consider most of the previous programs, you will notice that they are primarily structured as infinite loops. For example, the input program repeatedly senses and updates the state of the pushbuttons. If we wanted to use a program similar to input to count button presses, the microcontroller would quickly accummulate thousands (or millions) of counts because it can execute the entire code loop in microseconds.

The loops program is structured as a finite loop as well, but the loop execution is broken up by decisions based on the state of the input pushbutton, limiting the number of times the code is allowed to loop. Each path through the loop is counted, and the count is repeated and displayed on the LEDs.

Switch bounce

As you use this program, you may notice that occasionally a switch press triggers more than one count on the LEDs. The most common cause of multiple counts is switch bounce — the mechanical connection, disconnection, and re-connection of the switch contacts. You can't sense switch bounce because it happens so fast, but the microcontroller is fast enough to register each switch closure as a separate event.

It's not used in the loops program, but one method of eliminating the effect of switch bounce utilizes a software time delay to ignore any inputs for a small amount of time after the switch changes state. A delay of 20-50ms is usually sufficient for most switch contacts to settle.

Program requirements

To use this program you will need:

An assembled CHRP 3 board, an optional power supply, a programmer and/or programming cable, and a computer with the MPLAB IDE or MPLAB X software as described in the Output activity.

Create the program

The entire LOOPS.ASM program is shown below. Create a Loops project in MPLAB, copy this code into it, and build the program.


;Loops			v3.1	January 18, 2013
;===============================================================================
;Description:	This program demonstrates a finite loop. It lights an LED each
;				time a switch is pressed. After 6 presses all LEDs turn off.

;Configure MPLAB and the microcontroller.

	include	"p16f886.inc"		;Include processor definitions

	__config _CONFIG1, _DEBUG_OFF & _LVP_OFF & _FCMEN_OFF & _IESO_OFF & _BOR_OFF & _CPD_OFF & _CP_OFF & _MCLRE_ON & _PWRTE_ON & _WDT_OFF & _INTOSCIO
	__config _CONFIG2, _WRT_OFF & _BOR40V

;Set hardware equates.

S2				equ		0		;PORTB position of pushbutton S2

;Set RAM register equates.

counter			equ	20h				;RAM storage register for the first number

;Start the program at the reset vector.

				org	00h				;Reset vector - start of program memory

				clrf	PORTA		;Clear all port outputs before configuring
				clrf	PORTB		;port TRIS registers. Clearing RA4 turns on
				clrf	PORTC		;the Run LED when TRISA is initialized.

				goto	initPorts	;Jump to initialize routine

				org	05h				;Continue program after the interrupt vector


initPorts       ;Configures PORTA and PORTB for digital I/O.

				banksel	ANSEL		;Switch register banks
				clrf	ANSEL		;Set all PORTA pins to digital
				clrf	ANSELH		;Set all PORTB pins to digital
				movlw	01010111b	;Enable Port B pull-ups, TMR0 internal
				movwf	OPTION_REG	;clock, and 256 prescaler
				banksel	TRISA		;Switch register banks
				movlw	00101111b	;Set piezo and LED pins as outputs and
				movwf	TRISA		;all other PORTA pins as inputs
				movlw	00000001b	;Set S2 as an input and all other
				movwf	TRISB		;PORTB pins as outputs
				banksel	PORTA		;Return to register bank 0

resetCount		movlw	6			;Preload Counter with number of
				movwf	counter		;loop cycles
				clrf	PORTB		;Turn off all LEDs

waitForKey		btfsc	PORTB,S2	;Check pushbutton S2 for press
				goto	waitForKey	;Keep waiting until S2 is pressed

				bsf		STATUS,C	;Set carry and turn on an LED for
				rrf		PORTB,F		;each key press

waitForRel		btfss	PORTB,S2	;Check pushbutton S2 for release
				goto	waitForRel	;Keep waiting until button released

				decfsz	Counter,F	;Subtract 1 from Counter register
				goto	waitForKey	;Repeat until counter becomes zero
		
				goto	resetCount	;Reset the count after 6 presses

				end
		

Download this program into your CHRP board and verify its operation. Pressing S2 should increment the count 6 times, then repeat.

How the program works

The initPorts section of the program is similar to that in the input program and sets up S2 as an input on PORTB while the other PORTB pins are set up as outputs.


resetCount		movlw	6			;Preload Counter with number of
				movwf	counter		;loop cycles
				clrf	PORTB		;Turn off all LEDs
		

The resetCount subroutine sets the counter variable to 6 and turns off all the PORTB LEDs.


waitForKey		btfsc	PORTB,S2	;Check pushbutton S2 for press
				goto	waitForKey	;Keep waiting until S2 is pressed

				bsf		STATUS,C	;Set carry and turn on an LED for
				rrf		PORTB,F		;each key press
		

Next, waitForKey waits until pushbutton S2 is pressed using a bit test instruction that will skip the goto when the pushbutton input goes low. Once this happens, the Carry bit is set before being rotated into the PORTB register, similar to the way in which the Chaser program worked.


waitForRel		btfss	PORTB,S2	;Check pushbutton S2 for release
				goto	waitForRel	;Keep waiting until button released

				decfsz	Counter,F	;Subtract 1 from Counter register
				goto	waitForKey	;Repeat until counter becomes zero
		
				goto	resetCount	;Reset the count after 6 presses
		

Finally, the waitForRel subroutine waits until S2 is released and goes high before continuing.

Once S2 is released, the Counter register is decremented. If the result is not zero, the microprocessor re-enters the waitForKey subroutine and gets ready for the next key press.

If all of the counts have been registered and the result of the decfsz Counter,F instruction is zero, the code continues by skipping the first goto, and returning to the resetCount subroutine, so that PORTB can be cleared and the entire process can repeat.

Test your knowledge

  1. How many counts occur as the Counter register decrements down from 6?
  2. If the waitForKey subroutine used an rlf PORTB,F instruction instead of rrf, would all counts appear on the LEDs? Why or why not?

Apply your skills

  1. Create a program that counts S2 button presses and lights the LED on RB5 after 10 presses. Use button S3 to reset the count and turn off the RB5 LED. All other LEDs (except RB0 and RB1, since they are monitoring the switch inputs) should remain off.
  2. All other code execution stops while this loops program alternates between waiting for button presses and releases. While it serves as an example to demonstrate user input and loop counting, this program would obviously not be able to accomplish any processing independent of button activity. Think about how to structure code to accomplish a task while waiting for button input, and design a flow-chart of your code idea.