NPN transitors as switches and memory-based GPIO control

This project is based on the following sections in Molloy - Exploring Raspberry Pi (hereafter referred to as the Book):

  • LED: pp. 128 - 129
  • Transistors: pp. 132 - 138
  • Fig. 6-2(b) on p. 223
  • Memory-based GPIO control: pp. 245 - 252

The circuit

A S8050 NPN transistor was used in a circuit similar to Fig. 6-2(b). The base resistor value was determined by using the equation on p. 135 of the Book For this curcuit, . is approximately 5V (the output of pin 2) divided by 220 (LED has little resistance; see p. 129 of the Book), which comes out as 0.023A. This is well within the power rating of the pins (see here). From p. 2 of S8050's dataset, . Using the 4th figure on p. 3 of S8050's dataset, is approximately 0.8V. Thus, . Thus, two 1K resistors and one 220 were used.

Controlling the LED

V4 of the Pi no longer supports sysfs described in the Book.

Using pinctrl

# Turning the LED on, by setting GPIO 17 to output high 
pinctrl set 17 op dh

# Turning the LED off, by setting GPIO 17 to be an output zero/low
pinctrl set 17 op dl

By accessing the registers directly.

The following code is based on Listing 6-9 in the Book, but with the following changes: - The rpi 4's registers are used. - The LED is made to blink before the program terminates.

Useful details can be found in the comments.

// compile using: gcc LEDflash.c -o LEDflash
// execute using: sudo ./LEDflash

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/mman.h>
#include <stdint.h>   // for uint32_t - 32-bit unsigned integer
#include <unistd.h>   // getuid
#include <string.h>   // strerror

#define GPIO_BASE    0xFE200000   // The returned value of `sudo cat /proc/iomem | grep gpio -i`
#define GPSET0       0x1c         // BCM2711 datasheet p. 66; BCM2835 datasheet p. 90
#define GPCLR0       0x28         // BCM2711 datasheet p. 66; BCM2835 datasheet p. 90
#define GPLVL0       0x34         // GPLEV0 on BCM2711 p. 66; BCM2835 p. 90
static volatile uint32_t* gpio;   // pointer to the gpio (*int)

int main() {
    int fd, x;
    printf("Start of GPIO memory-manipulation test program.\n");
    if (getuid() != 0) {
       printf("You must run this program as root. Exiting.\n");
       return -EPERM;
    }
    if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) {
       printf("Unable to open /dev/mem: %s\n", strerror(errno));
       return -EBUSY;
    }
    // get a pointer that points to the peripheral base for the GPIOs
    // All accesses are assumed to be 32-bit (BCM2711 p. 66)
    gpio = (uint32_t *) mmap(0, getpagesize(), PROT_READ | PROT_WRITE,
       MAP_SHARED, fd, GPIO_BASE);
    // On error, the value MAP_FAILED (that is, (void *) -1) is returned, and errno is set to indicate the error.
    if (gpio == (void *) -1) {
       printf("Memory mapping failed: %s\n", strerror(errno));
       return -EBUSY;
    }
    // at this point gpio points to the GPIO peripheral base address
    // set up the LED GPIO FSEL17 mode = 001 at addr GPFSEL1 (0004)
    // From the GPFSEL1 Register Table on p. 67 of BCM2711, 001 means the pin is an output
    // gpio points to uint32. Thus, moving one step is equivalent to 
    // moving 4 bytes, which is the offset for GPFSEL1
    // writing NOT 7 (i.e., ~111) clears bits 21, 22 and 23.
    *(gpio + 1) = (*(gpio + 1) & ~(7 << 21) | (1 << 21));

    // turn the LED on using the bit 17 on the GPSET0 register
    for (int idx = 0; idx < 10; idx++) {
        // Each move of the gpio pointer is equivalent to jumping 4 bytes. Thus divide the offset by 4.
        *(gpio + (GPSET0 / 4)) = 1 << 17;
        usleep(500000);          // cannot use sleep as it is non-blocking
        *(gpio + (GPCLR0 / 4)) = 1 << 17;  // turn the LED off
        usleep(500000);          // cannot use sleep as it is non-blocking
    }

    close(fd);
    return 0;
}

Using wiringPi

Download a pre-built .deb file from https://github.com/WiringPi/WiringPi/releases and install using sudo apt install $path_to_deb_file.

// Compile: gcc $path_to_src_file -o LEDflash2 -l wiringPi

#include <wiringPi.h> // Include WiringPi library!
#include <unistd.h>   // usleep

int main(void)
{
    int pin_num = 17;

    // uses BCM numbering of the GPIOs and directly accesses the GPIO registers.
    wiringPiSetupGpio();

    // pin mode ..(INPUT, OUTPUT, PWM_OUTPUT, GPIO_CLOCK)
    pinMode(pin_num, OUTPUT);

    // pull up/down mode (PUD_OFF, PUD_UP, PUD_DOWN) => down
    pullUpDnControl(pin_num, PUD_OFF);

    for (int idx = 0; idx < 10; idx++) {
        digitalWrite(pin_num, HIGH);
        usleep(500000);          // cannot use sleep as it is non-blocking
        digitalWrite(pin_num, LOW);
        usleep(500000);          // cannot use sleep as it is non-blocking
    }
}