Lesson GPIO

Bitmasking

Bitmasking Tutorial

Bit-masking is a technique to selectively modify individual bits without affecting other bits.

Bit SET

To set a bit, we need to use the OR operator. This is just like an OR logical gate you should've learnt in Digital Design course. To set a bit, you would OR a memory with a bit number and the bit number with which you will OR will end up getting set.

// Assume we want to set Bit#7 of a register called: REG
REG = REG | 0x80;

// Let's set bit#31:
REG = REG | 0x80000000;

// Let's show you the easier way:
// (1 << 31) means 1 gets shifted left 31 times to produce 0x80000000
REG = REG | (1 << 31);

// Simplify further:
REG |= (1 << 31);

// Set Bit#21 and Bit# 23:
REG |= (1 << 21) | (1 << 23);

Bit CLEAR

To reset or clear a bit, the logic is similar, but instead of ORing a bit, we will AND a bit. Remember that AND gate clears a bit if you AND it with 0 so we need to use a tilde (~) to come up with the correct logic:

// Assume we want to reset Bit#7 of a register called: REG
REG = REG &   0x7F;    
REG = REG & ~(0x80); // Same thing as above, but using ~ is easier

// Let's reset bit#31:
REG = REG & ~(0x80000000);

// Let's show you the easier way:
REG = REG & ~(1 << 31);

// Simplify further:
REG &= ~(1 << 31);

// Reset Bit#21 and Bit# 23:
REG &= ~( (1 << 21) | (1 << 23) );

Bit TOGGLE

// Using XOR operator to toggle 5th bit
REG ^= (1 << 5);

Bit CHECK

Suppose you want to check bit 7 of a register is set:

if(REG & (1 << 7))
{
 	DoAThing();
}
// Easier way:
while( ! (REG & (1 << 7)) );

Now let's work through another example in which we want to wait until bit#9 is 0

// One way:
while(REG & (1 << 9) != 0);

// Easier way:
while(REG & (1 << 9));

GPIO Example of LPC17xx

In this example, we will work with an imaginary circuit of a switch and an LED. For a given port, the following registers will apply:

  • GPIO selection: PINSEL register (not covered by this example)
  • GPIO direction: DIR (direction) register
  • GPIO read: IOPIN register
  • GPIO write: IOPIN register

Each bit of FIODIR1 corresponds to each external pin of PORT1. So, bit0 of FIODIR1 controls direction of physical pin P1.0 and bit31 of FIODIR2 controls physical pin P2.31. Similarly, each bit of IOPIN1 or IOPIN2 controls output high/low of physical ports P1 and P2. IOPIN not only allows you to set an output pin, but it allows you to read input values as sensed on the physical pins.

Suppose a switch is connected to GPIO Port P1.14 and an LED is connected to Port P1.15. Note that if a bit is set of FIODIR register, the pin is OUTPUT otherwise the pin is INPUT. So... 1=OUTPUT, 0=INPUT

// Set P1.14 as INPUT for the switch:
LPC_GPIO1->FIODIR &= ~(1 << 14);

// Set P1.15 as OUTPUT for the LED:
LPC_GPIO1->FIODIR |=  (1 << 15);

// Read value of the switch:
if(LPC_GPIO1->FIOPIN & (1 << 14)) 
{
    // Light up the LED:
    LPC_GPIO1->FIOPIN |= (1 << 15);
}
else 
{
    // Else turn off the LED:
    LPC_GPIO1->FIOPIN &= ~(1 << 15);
}

 

LPC also has dedicated registers to set or reset an IOPIN with hardware AND and OR logic:

if(LPC_GPIO1->FIOPIN & (1 << 14)) 
{
    LPC_GPIO1->IOSET = (1 << 15); // No need for |=
}
else 
{
    // Else turn off the LED:
    LPC_GPIO1->IOCLR = (1 << 15); // No need for &=
}

 

GPIO

Objective

To be able to General Purpose Input Output (GPIO), to generate digital output signals and to read input signals. Digital outputs can be used as control signals to other hardware, to transmit information, to signal another computer/controller, to activate a switch or, with sufficient current, to turn on or off LEDs or to make a buzzer sound.

Below will be a discussion on using GPIO to drive an LED.

Although the interface may seem simple, you do need to consider hardware design and know some of the fundamental of electricity. There are a couple of goals for us:

  • No hardware damage if faulty firmware is written.
  • Circuit should prevent excess amount of current to avoid processor damage.

Required Background

You should know the following:

  • bit-masking in C
  • wire-wrapping or use of a breadboard
  • Fundamentals of electricity such as Ohm's law (V = IR) and how diodes work.

GPIO

   

Figure 1. Internal Design of a GPIO

GPIO stands for "General Purpose Input Output". Each pin can at least be used as an output or input. In an output  configuration, the pin voltage is either 0v or 3.3v. In input mode, we can read whether the voltage is 0v or 3.3v.

You can locate a GPIO that you wish to use for a switch or an LED by first starting with the schematic of the board. The schematic will show which pins are "available" because some of the microcontroller pins may be used internally by your development board. After you locate a free pin, such as P2.0, then you can look-up the microcontroller user manual to locate the memory that you can manipulate.

Hardware Registers Coding

The hardware registers map to physical pins. If we want to attach our switch and the LED to our microcontroller's PORT0, then here are the relevant registers and their functionality :

LPC17xx Port0 Registers
LPC_GPIO0->FIODIR Direction of the port pins, 1 = output
LPC_GPIO0->FIOPIN Read: Sensed inputs of the port pins, 1 = HIGH
Write: Control voltage level of the pin, 1 = 3.3v
LPC_GPIO0->FIOSET Write only: Any bits written 1 are OR'd with FIOPIN
LPC_GPIO0->FIOCLR Write only: Any bits written 1 are AND'd with FIOPIN

Switch

We will interface our switch to PORT0.2, or port zero's 3rd pin (counting from 0).

Note that the "inline" resistor is used such that if your GPIO is mis-configured as an OUTPUT pin, hardware damage will not occur from badly written software.

 

Figure 2. Button Switch Circuit Schematic

/* Make direction of PORT0.2 as input */
LPC_GPIO0->FIODIR &= ~(1 << 2);

/* Now, simply read the 32-bit FIOPIN registers, which corresponds to
 * 32 physical pins of PORT0.  We use AND logic to test if JUST the
 * pin number 2 is set
 */
if (LPC_GPIO0->FIOPIN & (1 << 2)) 
{
    // Switch is logical HIGH
}
else 
{
    // Switch is logical LOW
}

LED

We will interface our LED to PORT0.3, or port zero's 4th pin (counting from 0).

Given below are two configurations of an LED. Usually, the "sink" current is higher than "source", hence the active-low configuration is used more often.

 

 

Figure 3. Active High LED circuit schematic

 

 

Figure 4. Active low LED circuit schematic

 

/* Make direction of PORT0.3 as OUTPUT */
LPC_GPIO0->FIODIR |= (1 << 3);

/* Setting bit 3 to 1 of IOPIN will turn ON LED
 * and resetting to 0 will turn OFF LED.
 */
LPC_GPIO0->FIOPIN |= (1 << 3);

/* Faster, better way to set bit 3 (no OR logic needed) */
LPC_GPIO0->FIOSET = (1 << 3);

/* Likewise, reset to 0 */
LPC_GPIO0->FIOCLR = (1 << 3);

Lab Assignment: GPIO

Objective

To grow your skills in the following:

  1. Manipulating a microcontroller's registers in order to access and control physical pins
  2. Use implemented driver to sense input signals and control LEDs.

Assignment

Test your knowledge by doing the following:

Part 1. Implement the GPIO_0 Driver

Using the following class template

  1. Implement ALL class methods.
  2. All methods must function work as expected of their method name.
#ifndef LABGPIO_H
#define LABGPIO_H

class LabGPIO_0
{
private:
    /**
     * port, pin and any other variables should be placed here.
     * NOTE: that the state of the pin should not be recorded here. The true
     *      source of that information is embedded in the hardware registers
     */
public:
    /**
     * You should not modify any hardware registers at this point
     * You should store the port and pin using the constructor.
     *
     * @param {uint8_t} pin  - pin number between 0 and 32
     */
	LabGPIO_0(uint8_t pin);
    /**
     * Should alter the hardware registers to set the pin as an input
     */
	void setAsInput();
    /**
     * Should alter the hardware registers to set the pin as an input
     */
	void setAsOutput();
    /**
     * Should alter the set the direction output or input depending on the input.
     *
     * @param {bool} output - true => output, false => set pin to input
     */
    void setDirection(bool output);
    /**
     * Should alter the hardware registers to set the pin as high
     */
    void setHigh();
    /**
     * Should alter the hardware registers to set the pin as low
     */
    void setLow();
    /**
     * Should alter the hardware registers to set the pin as low
     *
     * @param {bool} high - true => pin high, false => pin low
     */
    void set(bool high);
    /**
     * Should return the state of the pin (input or output, doesn't matter)
     *
     * @return {bool} level of pin high => true, low => false
     */
	bool getLevel();
	~LabGPIO_0();
};

#endif

Code Block 1: LabGPIO_0.hpp template

Part 2. Use Driver for an application

The application is to use an internal AND external switch to control an on board and external LED, respectively.

void vControlLED( void * pvParameters )
{
    /* Get Parameter */
    uint32_t param = (uint32_t)(pvParameters);
    /* Define Constants Here */

    /* Define Local Variables and Objects */
    
    /* Initialization Code */

    while(1)
    {
        /* Insert Loop Code */
    }
    /* Only necessary if above loop has a condition */
    xTaskDelete(NULL);
}

void vReadSwitch( void * pvParameters )
{
    /* Get Parameter */
    uint32_t param = (uint32_t)(pvParameters);
    /* Define Constants Here */

    /* Define Local Variables and Objects */
    
    /* Initialization Code */

    while(1)
    {
        /* Insert Loop Code */
    }
    /* Only necessary if above loop has a condition */
    xTaskDelete(NULL);
}

Code Block 2: FreeRTOS Task Templates

Requirements:

You MUST NOT use any pre-existing library such as a GPIO class for this assignment. 

You MAY USE LPC17xx.h it is not a library but a list of registers mapped to the appropriate locations. 

  1. You MUST use the two task templates above, you MUST pass a parameter to these tasks to inform them which switch or led they are reading/controlling.
  2. You will need to create a GPIO_1 library as well.
  3. The task above must do the following:
    1. vReadSwitch:  Will read from the internal OR external GPIO, depending on the input parameter, connected to the switches. If a button is RELEASED (not when pressed, but when it released), this task will set a global variable.
    2. vControlLED: Will loop and toggle the state of your designated LED, specified by the input parameter, and clear that global variable flag.
  4. In total, you will need to create 4 tasks, two vReadSwitch tasks, one for reading external switch and another for reading the internal switch two vControlLED tasks, one for toggling the internal and another for toggling the external LED.
  5. Pass in a parameter to change the behavior of each task. See code block 2.

Upload only relevant .cpp files into canvas. A good example is: main.cpp, LabGPIO.hpp, LabGPIO.cpp. See Canvas for rubric and grade breakdown.

Extra Credit

Add a flashy easter egg feature to your assignment, with your new found LED and switch powers! The extra credit is subject to the instructor's, ISA's and TA's discretion about what is worth the extra credit.

Consider using additional switches and/or LEDs.

Extra Credit

Be able to handle multiple ports (port 1 and port 2) with a single class.