Lecture: 8
Objective: How to generate a varity of output bit sequences using delays. How to recognize input patters using delay structures.

GPIO

In order to master control of the GPIO pins you will need three concepts, delay, manipulation of input and manipulation of output.

A common input device - buttons and switches

Before jumping into code to check buttons and switches, let's take a moment to examine the language (nomenclature) used to describe switches and buttons.

Switch nomenclature

Several switch configurations are shown in the figure below. Each of these configurations has its abreviatied name given in the form #P#T, where #P is the number of poles (single/1 or double/2) and #T is the number of throws (single/1, double/2, or 3).


The arrow in the figures above is a conductor that can be moved between the throws (circles on the right). In the case of a single throw switch ("ST" or those with only one circle on the right) the conductor either makes contact with the opposing conductor or is in an open circuit configuration. The default configuration of a single throw switch is referred to as either normally open (N.O.) or. normally closed (N.C.). The distinction between these two types of switches is the state of the connection between the two terminals of the switch when the button is not depressed. The terminal in a N.O. switch are not connected (open) when the button is not pressed. Hence they are normally open. Similiarilty the terminals of a N.C. switch are connected until the button is pressed. The dotted line in the figure represents the actuating mechanism that moves all the poles simultanously and in a cordinated fashion.

There are two ways that you can connect buttons to your circuit, active high and active low as shown in the circuit diagram below. The switches are shown in the open position which indicates that they are N.O.


The configuration determines what logic level is output when the button is pressed according to the following table.
Active low When the button is pressed, pin RA0 is attached to ground. This will cause Port A bit 0 to read a logic 0. When the button is not pressed, pin RA0 is weakly pulled-up to 3.3V through the resistor. This will cause Port A bit 0 to read a logic 1. When the button is pressed (active) the Port pin will read a logic 0 (low). Note, the resistor is necessary to prevent a short-circuit between 3.3V and GND when the button is pressed. A value of 10kΩ is typical.
Active high When the button is pressed, pin RA1 is attached to 3.3. This will cause Port A bit 1 to read a logic 1. When the button is not pressed, pin RA1 is weakly pulled-down to 0V through the resistor. This will cause Port A bit 1 to read a logic 0. When the button is pressed (active) the Port pin will read a logic 1 (high).
Note, when reading a value from a GPIO pin you need to reference the pins associated PORT register. So for example, to read the value on the active low pin you would reference "PORTAbits.RA0".

Digging deeper

In order to completly understand the operation of a switch and their associated resistors, you need to dig a little deeper and understand that is going on inside the chip. The following figure shows an active low switch connected to an I/O pin of the PIC. The pull-up resistor has a pretty typical value of 1kΩ Most chips these days have inputs that are connected to the gate a MOSFET. This MOSFET gate can be modeled as a pair of resistors (with very high resistances), one attached to 3.3V and the other to ground as shown. The analog voltage seen between these resistors is converted into a digital voltage by a "chip voltage detection circuit".


Now, let's ask ourselves what voltage the "chip voltage detection circuit" sees when the button is in its normally open configuration as shown. In this case, the 1kΩ and 1GΩ resistors tied to 3.3V are in parallel. You can use the parallel resistor equation to calculate the equivlent resistance as follows:
		    1
	R_eq = -----------
		 1	 1
		---  + 	---
		1kΩ     1GΩ
which is very close to 1kΩ because 1GΩ is so much larger than 1kΩ. Hence we will call the parallel combination 1kΩ Combining these two parallel resistors simplifies the circuit to a series circuit with a 1kΩ resistor (connected to 3.3V) connected to a 1GΩ resistor connected to ground. The voltage at the "chip voltage detection circuit" is:
		  1GΩ
	3.3V * 	---------
		1kΩ + 1GΩ 
Which is esentially 3.3V. So, when the button is in a normally open configuration, the chip senses 3.3V and registers a logic 1. On the other hand, when the button is in its closed position, the GPIO pin is attached to ground causing the "chip voltage detection circuit" to reach 0v, registering logic 0.

Wait for button press

In order to synchronize the actions of the microcontroller to a human, we will frequently program the microcontroller to wait for a button press.
In the following example you should assume that the push button is normally open and wired active high. The port pin associated with the button has been aliased, via #define BUTTON PORTAbits.RA0 to BUTTON. Thus when the push button is pressed, BUTTON will equal logic 1 and when the push button is not pressed, BUTTON will read a logic 0.
Second, we will connect an LED to an output pin on the microcontroller in a active high configuration. The port pin associated with the LED has been aliased, via #define LED LATAbits.LATA1 to LED. Assigning LED to 0 will turn the LED off, assigning a 1 will turn the LED on.
Finally, we need to recognize that the microcontroller is executing the instructions many millions of times faster than a user can press or release the button. With these assumptions in place lets look at a practical delay loop that you will see many times in our course.

1.    LED = 0;			// turn off the LED
2.    for(;;) {
3.    	while(BUTTON == 0);	// wait for a button press
4.    	while(BUTTON == 1);	// wait for a button release
5.	LED ^= 1;		// toggle the LED
6.    }


Let's walk through the program from top to bottom. To facilitate this discussion, I have added line numbers in the far left; please understand that these are not actually part of the program.
Line 1 turns the LED off. While not strictly part of the problem statement, its often a good idea to put indicators like Leeds into a know initial state at the start of a program.
Lines 2-6 form an infinite loop. Meaning that our computer will forever be checking the state of the button and turning the LED on and off.
Line 3 is a while-loop that executes its (empty) body while the button is not pressed. There are two important points to make here. First, the microcontoller may get stuck executing this while-loop many millions of times before the button is pressed. This is because the PIC is running at 64MHz, meaning that its executing 64 million (assembly) instructions per second. Hence, it can do a lot of checks every second. Second, while our program is executing this while-loop it is doing absolutely nothing except checking that button. This is the quintessential delay loop, synchronizing the execution of our program to the user's input by waiting and doing nothing until the user performs some task. You rarely see these types of statements in other programming classes because variables in those classes only change under your programs control, but in this class we will have variables that change based on actions performed outside your program.
Line 4 is a while-loop that executes its (empty) body while the button is pressed. Once again, the PIC is executing statements millions of times faster than the user can release the button. Hence, our program needs to wait until the button has been released before continuing.
Line 5 toggle the state of the LED after the button has been pressed and then released.

A common output device - LEDs

LEDs are polarized devices that, when current is passed through them, produce light. Polarized, means that the terminals of the device are not interchangable. In order to produce light, current must pass from the anode to the cathode. When looking at a LED there are several ways to distinguish between the anode and cathode as shown in the following image from Henry's Bench.


Note, if you try to pass current from the cathode to the anode, no current will flow and the LED will not produce light. There are two ways that you can connect LEDs to your circuit, active high and active low shown in the circuit diagram below. Note that both RA0 and RA1 are configured as outputs with the following instructions.
 
    TRISAbits.TRISA0 = 0;	// clearing TRIS bit makes RA0 an output
    TRISAbits.TRISA1 = 0;	// clearing TRIS bit makes RA1 an output



Active high When you set Port A bit 1 to logic 1, the pin RA1 goes to 3.3V. This forward-bias the voltage across the diode (LED) causing current to flow through the LED and illuminating it.
When you clear Port A bit 1 to logic 0, the pin RA1 goes to 0V. This creates no voltage drop across the diode (LED). Thus no current flows through the LED and it does not illuminate.
Thus, the LED illuminates (is active) when the Port bit is logic 1 (high).
Active low When you clear Port A bit 0 to logic 0, the pin RA0 goes to 0V. This forward-bias the voltage across the diode (LED) causing current to flow through the LED and illuminating it.
When you set Port A bit 1 to logic 1, the pin RA1 goes to 3.3V. This creates no voltage drop across the diode (LED). Thus no current flows through the LED and it does not illuminate.
Thus, the LED illuminates (is active) when the Port bit is logic 0 (low).
In order to turn the LEDs on or off you need to write the output latch associated with the pin. The nomenclature for the latch associated with GPIO pin Rx# is "LATxbits.LATx#" Setting a latch bit will cause the associated GPIO pin to go to 3.3V, clearing a latch bit will cause the associated GPIO pin to go to 0V. Let's see if you can put all this together to answer the questions below.
	LATAbits.LATA0 = 0;	// What does this command do?
	LATAbits.LATA0 = 1;	// What does this command do?
	LATAbits.LATA1 = 0;	// What does this command do?
	LATAbits.LATA1 = 1;	// What does this command do?
The resistor in the circuit diagrams is called a current-limiting resistor and its job is to limit the amount of current flowing through the LED. You can calculate a value for this resistor by consulting the datasheet for the LED and looking up Vf and Imax. Vf is the forward voltage drop you should expect across the LED when it's illuminated. Imax is the maximum continous current. For a red LED, typicall values would be Vf = 2V and Imax = 20mA. To be conservative, I run LEDs at about 30% of their maximum current. Try calulating the resistance value that would produce this much current through the resistor.

Delay routine

An inherent problem exists in all our interactions with micontrollers, they operate on time scales of microseconds, while we operate on a time scale of seconds. In order to breach this difference we need to slow down the microcontroller or speed-up our preception. In order to accomplish the second, we need to resort to artificial aides like oscilloscopes. The first is accomplished by having the microcontroller delay for periods of time. In the following section you can assume to have a function called "delay" which takes as an argument an uint8_t and produces a delay of consisting of that many "delay units". Its function prototype looks like: void delay(uint8_t delayUnits); While the exact duration of a delay unit is unimportant in our exercise, the fact that the microcontroller does nothing during the execution of the delay function is. We are intentionally telling the microcontroller to do nothing, and this can prove to be very useful when we have precise control over how long the microcontroller does nothing. To see how, let's proceed with the following examples.

Generating output patterns

In order to program a microcontroller to control and communicate with the outside world, you will need to be able to program precise sequences of logic activity on GPIO pins. In the following exercises, the expression "LED" has been aliased via a #define statement to a GPIO pin and that pin has been configured as an output. The LED attached to the GPIO pin is wired so that it functions active high.
Static
LED Active High
Task Illuminate LED
Code
	LED = 1;
Dynamic
LED Active High
Task Toggle illumination level of LED
Code
	LED ^= 1;
Duration
LED Active High
Task Illuminate LED for a 100 delay units.
Code
	LED = 1;
	delay(100);
	LED = 0;
Count
LED Active High
Task Toggle LED illumination N times, hold each for 100 delay units.
Code
	LED = 0;
	for (i=0; i < N; i++) {
		delay(100);
		LED ^= 1;
	}
Fixed Frequency, fixed duty cycle
LED Active High
Task Toggle LED at 50% duty cycle with a period of 100 delay units.
Code
	LED = 0;
	for (;;) {
	    delay(50);
	    LED = 1;
	    delay(50);
	    LED = 0;
	}
Fixed frequency, variable duty cycle
LED Active High
Task Toggle LED at D% duty cycle with a period of 100 delay units.
Code
	LED = 0;
	for (;;) {
	    delay(D);
	    LED = 1;
	    delay(100-D);
	    LED = 0;
	}
Sequence
LED Active High
Task Generate a LED illumination sequence. Each element in the sequence has two characteristics, the LED illumination state and the duration of this state.
Code
	uint8_t sequence[N][2] = { {ON, 100}, {OFF, 20}, ...};
	LED = 0;
	for (i=0; i < N; i++) {
	    LED = sequence[i][1];
	    delay(sequence[i][0]);
	} // end for

Test your understanding

You can find the solutions embedded in the "source code" for this web page by right mouse clicking on this web page and selecting "view source". The solutions are in HTML comments.
  1. Let's apply these concepts by configureing our PIC with the active high LED connected to RA1 and the active low push button connected to RA0. Let's write a program that illuminates the LED when the button is pressed. Remember that when working with input pins that are functionally multiplex with analog to digital converter channels you must configure inputs to be digital by clearing the associated analog select bit. We do not need to configure the analog select bit when a pin is configured to be an output.
  2. Write a code snippet that reads an active-low button on RA5 and illuminates an active-low LED connected to RA3 only when the button is pressed.
  3. Write a code snippet that reads an active-high button on RA5 and illuminates an active-low LED connected to RA3 only when the button is pressed.
  4. Write a code snippet that creates a 50% duty cycle waveform with a period of D delay units. Do not use the divide operator and instead use shifts.
  5. Write a code snippet that creates a 12.5% duty cycle waveform with a period of D delay units. Do not use the divide operator and instead use shifts.
  6. Write a code snippet that lights the LED whenver the active high push button is pressed an even number of times.
  7. Write a code snippet that generates a increases the duty cycle of the LED by 12.5% everytime the button is pressed. The duty cycle should increase starting from 0% to 100% and then back to 0% to increase again. The period of the waveform should be D delay units (D is assumed to have type uint8_t. If you create variable, include their type definitions.