Program description

What does it do?

The two input programs demonstrate how to sense digital inputs from either the switches or phototransistors on the CHRP board.

The second INPUTA program demonstrates how to activate selected outputs within a register by using logical operations to mask the register bits.

New instructions

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

andwf 'logical AND W with file register' - performs a logical AND operation between each of the bits in W and the specified file register.
btfsc 'bit test file register, skip if clear' - checks if the specified bit of a file register is clear (0). If the bit is clear (0), the line immediately following btfss is skipped. If set (1), the line immediately following btfss is executed. (opposite of btfss)
iorwf 'inclusive-OR W with file register' - performs a logical OR operation between each of the bits in W and the specified file register

New directives

This program introduces the following new directive:

equ 'equate' - makes a label equivalent to a number or expression

Input programming activity

The previous activities showed you how to make the microcontroller activate outputs. This activity demonstrates how to receive inputs from externally connected devices. The input programs also introduce the concept of abstraction—the ability of a program to refer to I/O circuits using assigned variable names rather than their specific hardware addresses.

What you should know before starting

CHRP related information

If you examine the CHRP 2 schematic, you can see that the microcontroller's Port B pins are connected to both the octal buffer that drives the LEDs, as well as to eight pushbutton switches. You might wonder how the microcontroller can use one I/O port for both output and input circuits at the same time. It's relatively easy, as it turns out, through a combination of CHRP hardware design and the use of the previously described TRIS registers.

First, let's examine the output part of the hardware. On the schematic, notice that the LEDs are not connected directly to PORTB. Instead, the LEDs are driven through a buffer IC. The buffer presents a high-impedance load to the microcontroller I/O pins. In other words, the buffer just senses the state of the PORTB wires, and then activates the appropriate LEDs—in effect isolating the LEDs from the other PORTB circuitry.

Now, let's look at the input circuits. The PORTB pushbuttons are connected directly to the PORTB I/O pins, albeit through a series resistor. When the port pin is an input and the pushbutton is pressed, very little current flows between ground and the I/O pin so the series resistor has a negligible effect. If the pushbutton is pressed while the port pin is outputting a high signal, the series resistor limits the current flow to ground, preventing a short circuit on the output.

Port A of the microcontroller also includes some input circuits. Both phototransistors (Q1 and Q2) as well the potentiometer inputs can be sensed by the PIC. These circuits are input only in the CHRP 2 board—Port A is not shared with any output devices. The potentiometer and phototransistor Q1 share one input pin—use J9 to select which one of the two connects to bit 0 of Port A.

Microcontroller related information

Now, let's re-examine the function of the TRIS registers. The TRISA and TRISB registers control the direction of data transfer on each pin of Port A and Port B, respectively. The easy way to remember the TRIS register settings is to match the numbers 0 and 1 to the letters O and I:

0 is like O, as in Output
1 is like I, as in Input

How input circuits work

The CHRP circuit board includes a number of input circuits, such as pushbutton switches, phototransistors (light sensors), and a potentiometer (position sensor). All of these inputs operate in a similar, though slightly different way. Examine the switch input schematic diagram, below:

Switch input circuit image

The switch (S1) and pull-up resistor (R1) form a series circuit, also known as a voltage divider. The microcontroller input is connected to the output at the mid-point of the voltage divider, and senses the electrical potential across the switch. Since any microcontroller pins configured as inputs have a high impedance, they will just sense the externally applied voltage but won't conduct any appreciable current themselves. This essentially means that the effect of the series resistor (R2) between the voltage divider and the microcontroller is negligible and can be safely ignored—R2 is installed to limit the current due to ESD (electro-static discharge), protecting the microcontroller from static charges conducted into the switch circuit by a user's fingers.

Ignoring R2, the operation of the switch input circuit can be explained by the interaction of the switch and pull-up resistor R1. You can think about how this works either by Ohm's law analysis, or by thinking about it as a voltage divider. Using Ohm's Law analysis, we know that when the switch is open, no current flows through the pull-up resistor. Since no current flows, the voltage loss across the resistor is zero, and the microcontroller input pin senses the full power supply voltage connected to the resistor—5V, or a logic 1. You could say the resistor pulled-up the switch circuit voltage to 5V.

When the switch is pressed, current flows through both the switch and resistor. Since the switch has effectively zero resistance, there is a zero volt loss across the switch. Ohm's Law shows that the current flow through the resistor causes all of the applied voltage to be dissipated across the resistor, and so the microcontroller ends up sensing the ground potential, 0V, or a logic 0.

Thinking about the circuit as a voltage divider, we know that the two elements in a series circuit divide the applied voltage in the ratio of their resistances. The voltage at the mid-point of the switch circuit will be proportional to the resistance of the switch, which has either infinite resistance (switch open), or zero resistance (switch closed). When the switch is open, its infinite resistance is infinitely higher than that of the pull-up resistor, causing the entire applied voltage to be measured across the switch, and producing a logic 1 (5V). When the switch is closed, its (ideally) zero resistance is infinitely smaller than that of the pull-up resistor, causing no voltage drop, and producing a logic 0 (0V).

Phototransistor input circuit image

In the light-sensing circuit, above, the switch has been replaced by a phototransistor, but otherwise the circuit acts in a similar manner to the switch input. Phototransistors change their resistance in response to light—high resistance in dim light, and low resistance in bright light. In dark conditions, the phototransistor exhibits higher resistance than the pull-up resistor, causing a high input voltage at the microcontroller's port pin. If the input voltage is high enough, the microcontroller will sense it as a logic 1. In bright conditions, the phototransistor's resistance drops, reducing the voltage at the mid-point of the voltage divider. Again, if the divider voltage is low enough, the microcontroller will sense it as a logic 0. Because of the analogue characteristics of the phototransistor (as opposed to the binary action of a switch), and the voltage threshold levels of the microcontroller's input circuits, some changes in brightness won't be enough to change the state of the input.

The potentiometer circuit behaves in an almost identical way to the phototransistor circuit, with the difference that it responds to rotational position instead of light. The wiper attached to the shaft of the potentiometer divides its internal resistor into two parts. Turning the potentiometer's wiper changes the ratio of the two resistor halves. The voltage at the wiper will be proportional to the ratio of the resistors. If the voltage is below a specific threshold, the microcontroller senses a logic 0. Above a threshold, and the microcontrolle senses a logic 1.

Internal pull-up resistors

The input circuits, above, all include a pull-up resistor to enable the operation of the input device. If you refer to the CHRP 2 schematic, you'll see pull-up resistors connected to all of the PORT A input circuits, but not the PORT B switches. The pull-ups on PORT B are omitted because the PIC microcontroller used in the CHRP circuit includes internal, software-controlled pull-up resistors for all of the PORT B input/output pins. These pull-ups are enabled in the initPorts subroutine through the OPTION_REG register, and affect only the PORT B pins set to be inputs. Using software-controlled pull-up resistors provides additional hardware flexibility by allowing PORT B to be used as both an input and output port, without having permanently attached pull-up resistors interfere with the port signals.

Referring back to the CHRP schematic for a moment, you will notice that there are resistors connected in series with the PORT B switches. These are not pull-up resistors, but are instead series current-limiting resistors necessary because the PORT B switches are connected to ground. To understand why, imagine what would happen if one of the PORT B wires is outputting a high signal, and its pushbutton was pressed, shorting the output signal to ground. To prevent this, the series current-limiting resistors provide enough of a load that short circuits are prevented.

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 Input program

The entire INPUT.ASM program is shown below. INPUT reads the state of two of the CHRP pushbuttons and outputs a unique pattern for each button on the LEDs. Start a new project in MPLAB, copy the INPUT.ASM code into the project, and build the program.


;INPUT.ASM 	v2.0	Last modified on December 1, 2008
;===============================================================================
;Description:	Input test program. Displays Port B switch state on Port B LEDs.

;Start of MPLAB and processor configuration.

	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

;End of MPLAB and processor configuration.

;Start of hardware equates

S2		equ	0		;Port B position of switch S2
S3		equ	1		;Port B position of switch S3

;End of hardware equates

	org	00h			;Start of program memory

		clrf	PORTA		;Turn off all port outputs
		clrf	PORTB
		clrf	PORTC
		goto	initPorts	;Jump to initialize subroutine

	org	05h

initPorts	;Set Ports B and C to support CHRP digital circuitry.

		banksel	ANSELH		;Switch register banks
		movlw	01010111b	;Enable Port B pull-ups, TMR0 internal
		movwf	OPTION_REG	;clock, and 256 prescaler
		clrf	ANSELH		;Set all PORTB pins to digital
		movlw	00000011b	;Set S2 and S3 as inputs and
		movwf	TRISB		;all other PORTB pins as outputs
		banksel	TRISC		;Switch register banks
		movlw	10110000b	;Setup serial input and output pins,
		movwf	TRISC		;and set motor outputs
		banksel	PORTB		;Return to PORTB register bank

checkS2		btfsc	PORTB,S2	;Check if S2 is pressed
		goto	checkS3		;If S2 is not pressed, check S3
		movlw	10000000b	;If pressed, copy this pattern to
		movwf	PORTB		;the LEDs

checkS3		btfsc	PORTB,S3	;Check if S3 is pressed
		goto	checkS2		;If S3 is not pressed, check S2
		movlw	01100000b	;If pressed, copy this pattern to
		movwf	PORTB		;the LEDs
		goto	checkS2		;Check pushbuttons again

	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

		
S2		equ	0		;Port B position of switch S2
S3		equ	1		;Port B position of switch S3
		

This program begins by using the equate directive to assign bit addresses to the S2 and S3 labels. The equate statements allow us to refer to the bits by name, like this 'btfsc PORTB,S2', instead of 'btfsc PORTB,0'. Equate directives can really improve the readability of assembly code programs, especially if logical and useful label names are used. In addition to naming bits, equate directives can also assign a name, or label, to constants, register addresses, and expressions. At build time, the assembler simply substitutes the equivalent value or expression for the equated label.

Using equ directives provides some important benefits to assembly code programmers by abstracting the hardware. If the circuit design changes (perhaps in a future revision of the circuit board), the program code can be updated by simply changing the label reference once at the top of the program, rather than at every location in which the reference appears in the program. In addition, the label names have more meaning than bit numbers because they relate to the circuit board—you can look at the board and see where S2 is, but you have to refer to the schematic to find out that it's really PORTB,0. And, also not inconsequentially, humans prefer names over numbers. This web page is hosted at www.siriusmicro.com, and also at an equivalent numeric IP address such as 67.220.228.5, but I'll bet you didn't type in the IP address to get to here in your browser!

		
initPorts	;Set Ports B and C to support CHRP digital circuitry.

		banksel	ANSELH		;Switch register banks
		movlw	01010111b	;Enable Port B pull-ups, TMR0 internal
		movwf	OPTION_REG	;clock, and 256 prescaler
		clrf	ANSELH		;Set all PORTB pins to digital
		movlw	00000011b	;Set S2 and S3 as inputs and
		movwf	TRISB		;all other PORTB pins as outputs
		...
		

Following the equates, the initialize code in the input program is almost exactly like that in the previous output programs. In addition to controlling the timer, the OPTION_REG register also enables or disables the internal pull-up resistors. The pull-ups are enabled as a group for all Port B pins, but only affect those pins set to be inputs in TRISB.

		
checkS2		btfsc	PORTB,S2	;Check if S2 is pressed
		goto	checkS3		;If S2 is not pressed, check S3
		movlw	10000000b	;If pressed, copy this pattern to
		movwf	PORTB		;the LEDs
		

The checkS2 and checkS3 subroutines both use btfsc instructions to check the button states. If no buttons are pressed, the associated port pins will be high, causing the code to go to the other check subroutine. If the appropriate button is pressed the port pin will be low, and the bit test instruction will cause the processor to skip over the goto and set the LEDs with a unique pattern for that button.

Create the INPUTA program

A variation of the input program, INPUTA.ASM, is shown below. INPUTA uses Port A for input, and requires the phototransistors to be installed in the CHRP board. When each phototransistor senses light, four LEDs corresponding to that phototransistor are lit. Start a new project in MPLAB, copy the INPUTA.ASM code into the project, and build the program.

		
;INPUTA.ASM 	v2.0	Last modified on December 1, 2008
;===============================================================================
;Description:	Displays Port A phototransistor input state on Port B LEDs.

;Start of MPLAB and processor configuration.

	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

;End of MPLAB and processor configuration.

;Start of hardware equates

Q1		equ	0		;Port A bit position of phototransistor Q1
Q2		equ	1		;Port A bit position of phototransistor Q2
LED10		equ	7		;Port A bit position of LED10

;End of hardware equates

	org	00h			;Start of program memory

		clrf	PORTA		;Turn off all port outputs
		clrf	PORTB
		clrf	PORTC
		goto	initPorts	;Jump to initialize subroutine

	org	05h

initPorts	;Set Ports B and C to support CHRP digital circuitry.

		banksel	ANSEL		;Switch register banks
		movlw	01010111b	;Enable Port B pull-ups, TMR0 internal
		movwf	OPTION_REG	;clock, and 256 prescaler
		clrf	ANSEL		;Set all PORTA pins to digital I/O
		clrf	ANSELH		;Set all PORTB pins to digital I/O and
		banksel	TRISA		;Switch register banks
		movlw	01101111b	;Setup LED and beeper outputs, and
		movwf	TRISA		;make all other PORTA pins inputs
		clrf	TRISB		;Make all PORTB pins outputs
		movlw	10110000b	;Setup serial input and output pins,
		movwf	TRISC		;and set motor outputs
		banksel	PORTB		;Return to PORTB register bank
		bsf	PORTA,LED10	;Turn LED10 on

checkQ1		btfsc	PORTA,Q1		;Check if Q1 sees light
		goto	q1Off		;If not, turn off Q1 pattern
		movlw	00001111b	;If Q1 sees light, copy this pattern
		iorwf	PORTB,f		;to the LEDs
		goto	checkQ2		;Check of Q2 sees light

q1Off		movlw	11110000b	;Turn off LEDs displaying Q1 state
		andwf	PORTB,f

checkQ2		btfsc	PORTA,Q2		;Check if Q2 sees light
		goto	q2Off		;If not, turn off Q2 pattern
		movlw	11110000b	;If Q2 sees light, copy this pattern
		iorwf	PORTB,f		;to the LEDs
		goto	checkQ1		;Check Q1 again

q2Off		movlw	00001111b	;Turn off LEDs displaying Q2 state
		andwf	PORTB,f
		goto	checkQ1		;Do it again


	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


Q1		equ	0		;Port A position of phototransistor Q1
Q2		equ	1		;Port A position of phototransistor Q2
		

As in the Input program, equate statements are used to assign bit addresses to the Q1 and Q2 phototransistors. Notice that the actual phototransistor bit positions are the same as they were for pushbuttons S2 and S3 in the Input program, but in the InputA program these bits refer to PORT A bit addresses rather than PORT B bit addresses. MPLAB doesn't inherently know which port you are referring to from the equate—it merely substitutes the equated bit address into the later btfsc PORTA instructions.


initPorts	;Set Ports B and C to support CHRP digital circuitry.

		banksel	ANSEL		;Switch register banks
		movlw	01010111b	;Enable Port B pull-ups, TMR0 internal
		movwf	OPTION_REG	;clock, and 256 prescaler
		clrf	ANSEL		;Set all PORTA pins to digital I/O
		clrf	ANSELH		;Set all PORTB pins to digital I/O and
		banksel	TRISA		;Switch register banks
		movlw	01101111b	;Setup LED and beeper outputs, and
		movwf	TRISA		;make all other PORTA pins inputs
		

The initPorts subroutine now has to configure PORT A I/O as well. (Refer to the CHRP 2 schematic to determine the correct number and positions of input, output or multi-purpose pins.) The ANSEL and TRISA registers control the PORT A pins.


checkQ1		btfsc	PORTA,Q1		;Check if Q1 sees light
		goto	q1Off		;If not, turn off Q1 pattern
		movlw	00001111b	;If Q1 sees light, copy this pattern
		iorwf	PORTB,f		;to the LEDs
		goto	checkQ2		;Check of Q2 sees light

q1Off		movlw	11110000b	;Turn off LEDs displaying Q1 state
		andwf	PORTB,f
		

In a manner similar to the Input program, the checkQ1 and checkQ2 subroutines each check the state of their associated phototransistors to determine which LEDs should be illuminated. The similarity ends at the iorwf instruction, however. Unlike Input, in which pressing one button lights the LEDs until the other button is pressed, we now want the LEDs to go on only when the phototransistor is actually seeing light. Logical operations can be used to make this work.

Logical operations

In InputA, half of the LEDs will illuminate when one of the phototransistors sees light. To avoid overwriting the state of the other half of the LEDs, we can use logical gate instructions to modify only the four bits corresponding to one set of LEDs. We do this by using an AND operation to clear all bits and turn off the LEDs, and an OR operation to turn on the LEDs.

To see how this works, take a look at the following logic gate truth tables. Imagine that the A input is under our control (through the program), and the B input is the value already in the PORTB RAM register. Divide each truth table into a top half (in which A is 0), and a bottom half (in which A is 1).

AND Gate
A B X
0 0 0
0 1 0
1 0 0
1 1 1
OR Gate
A B X
0 0 0
0 1 1
1 0 1
1 1 1

If you compare the AND gate's X output while A is 0, and again while A is 1, you should see the pattern that the AND gate provides. When A=0, X=0, but when A=1, X=B. In and AND operation, we can use the A input to either clear the register (A=0), or to leave the register value unchanged (A=1).

Likewise, for the OR truth table, we can use the A input to either leave the register unchanged (A=0), or set the register (A=1).

Now, let's look at the actual gate operation that takes place in the program. Assume that PORT B is still clear, as it would be after power-up. The checkQ1 subroutine has determined that four LEDs should go on. We can use an OR operation to accomplish this:


  OR constant (A)     00001111
  PORT B (B)          00000000
                      ========
  New Result  (X)     00001111
  

Any bits in the register ORed with a 0 will remain unchanged. Those bits ORed with a 1 will be set.


   AND constant (A)   11110000
   PORT B (B)         11111111
                      ========
   Result (X)         11110000
  

Any bits in the register ANDed with a 1 will remain unchanged. Those bits ANDed with a 0 will be cleared.

By using logical operations, such as AND and OR, we can change as many (or as few) bits in a register as we need to, without modifying any of the other bits that need to remain unchanged.

Test your knowledge

  1. Explain the purpose of the equate statements in these programs, as well as two advantages of using equates instead of statically-coded references.
  2. Explain the difference between the btfsc and btfss instructions.
  3. How many clock cycles does it take to execute a goto instruction? Why is this different from other instructions, like movlw, or bsf?
  4. When a PORT B switch is pressed, will the microcontroller input be low, or high? Why?
  5. When a light sensor connected to PORT A sees light, will its I/O pin be low, or high? Why?

Apply your skills

  1. Assemble the Input program. What do you think will happen when both buttons are pressed? Verify this with the simulator or on your CHRP circuit.
  2. Modify the Input program to be a push-on, push-off light switch. Use S2 to turn one or more lights on, and S3 to turn the same lights off.
  3. Make a copy of the program in which the three switches S2, S3, and S4 each illuminate a different light pattern.
  4. Modify the InputA program to use the CHRP board's potentiometer as its input, and measure the input threshold voltages—the potential at which the input pin switches from 0 to 1, and back from 1 to 0 using a multimeter.