CI20 GPIO LED Blink Tutorial
Blinking an LED using a GPIO pin is the "Hello World" of embedded systems, let's do it on a CI20 Creator board!
Small disclaimer: I ran this sequence of commands on my CI20 with no problems or damage, but always be careful of possible short circuits and similar electrical issues. If you manage to damage your board, you're on your own...
- 1 Blink an LED on a GPIO pin from CI20's command shell
- 2 What is a General-Purpose Input/Output (GPIO) pin?
- 3 Controlling GPIOs from the shell (Linux' sysfs interface for GPIOs)
- 4 Example: blinking LED
- 5 Example: Using the onboard LED and button
- 6 Example 2: Using the onboard LEDs of Ci20 V2 boards
Goals of this tutorial are:
- explain the basics of GPIO control in Linux
- blink a LED connected to a GPIO pin on the expansion header
All of this can be done using the sysfs interface from the command line, no programming is involved.
You will need a CI20 board, a working LED and a current limiting resistor for it.
If you just want to try it without explanation, see the section below
What is a General-Purpose Input/Output (GPIO) pin?
A GPIO is a software controlled digital signal, a GPIO pin is a pin on an integrated circuit whose function and behaviour can be controlled by the user (see the other wiki here).
The two main operation mode of a GPIO are input and output:
- in "input" mode, you can read "low" and "high" values
- in "output" mode, you can write "low" and "high" values
You can think of it as a binary sensor (input) or controlled switch (output). Other uses (e.g. as IRQ lines) are outside the scope of this tutorial.
Controlling GPIOs from the shell (Linux' sysfs interface for GPIOs)
The GPIOs on the CI20 board can be accessed using the special sysfs filesystem at the "/sysfs/" mount point, specifically in the "/sys/class/gpio/" directory. The main concept is that reading from and writing to special files in this directory will enable you to control the CI20's GPIOs operation mode. A full documentation on GPIO control implementation in the Linux kernel can be found in the Linux kernel source documentation, specifically in "Documentation/gpio/".
Some links to the online git repository at kernel.org:
What's in "/sys/class/gpio/"
There are three kinds of things in "/sys/class/gpio/":
- control interface "export" and "unexport"
- exported GPIOs directories (nodes)
- GPIO controllers (gpiochip*) directories
Writing a GPIO number to the "export" file will enable access to the GPIO and create the corresponding directory. You can do it simply using the "echo" shell command, so the following command:
echo 123 > export
will result in a "/sys/class/gpio/gpio123" directory.
Writing a GPIO number to the "unexport" file will disable access to the GPIO and remove the corresponding directory. The following command:
echo 123 > unexport
will remove "/sys/class/gpio/gpio123".
Usually a large number of GPIOs is available in embedded platforms, the control of these is split between multiple GPIO controllers. Each controller presents a separate directory in "/sys/class/gpio", with the form "/sys/class/gpio/gpiochip*", e.g. "/sys/class/gpio/gpiochip0". Among the content of gpiochip* directories, we are interested in three files:
- base: the first GPIO number managed by this particual GPIO controller
- label: controller label
- ngpio: how many GPIOs are managed by this controller
You can check these stats for every GPIO controller on the CI20 with the following shell command:
for f in `ls -d /sys/class/gpio/gpiochip*`; do echo $f `cat $f/label $f/base $f/ngpio` ; done
It shows the list of controllers and for each controller its label, base and ngpio.
How to know what GPIO number to use
In the CI20_Hardware page there is a description of the expansion headers that shows both "schematic" names and "sysfs" numbers for GPIOs, just hover the mouse on the desired pin to see what is the associated GPIO's number in sysfs.
See for example the schematics of the Primary expansion header
The following is an explanation of how the mechanism works for who is interested.
The GPIO names on the CI20's schematics (e.g. PD26, PE14, ...) are not the same as shown in the sysfs interface: how can we work out what GPIO number to use? Let's have a look at the gpiochip* stats, running again the command:
root@ci20:/home/ci20/src# for f in `ls -d /sys/class/gpio/gpiochip*`; do echo $f `cat $f/label $f/base $f/ngpio` ; done /sys/class/gpio/gpiochip0 GPIO A 0 32 /sys/class/gpio/gpiochip128 GPIO E 128 32 /sys/class/gpio/gpiochip160 GPIO F 160 32 /sys/class/gpio/gpiochip32 GPIO B 32 32 /sys/class/gpio/gpiochip64 GPIO C 64 32 /sys/class/gpio/gpiochip96 GPIO D 96 32
As you can see, there are 6 controllers, labeled from "GPIO A" to "GPIO F", each controlling 32 GPIOs (ngpio == 32). Assuming we want to control GPIO PD28, what GPIO number do I need to use? PD28 means 28th GPIO of block "D", controller "/sys/class/gpio/gpiochip96" is labeled as "GPIO D", its base is 96, thus: 96 (first GPIO in block D) + 28 (our GPIO of interest in block D) = 124 (GPIO number to be used in sysfs)
Connect LED's cathode to a ground pin on CI20's primary expansion header (e.g. pin 25) Connect LED's anode to a resistor (used to limit current thus preventing LED to burn up, for a quick test everything from 100 Ohm to 1k Ohm should work) Connect resistor's other end to chosen GPIO pin, in this case GPIO PD28 means pin 7 on primary expansion header
Open a shell / command prompt on the board Acquire admin privileges running "su" and inserting root password (default is "ci20")
Just run the following commands (text after "#" are comments and can be omitted):
cd /sys/class/gpio/ echo 124 > export #enable access to the GPIO cd gpio124/ #enter new directory cat direction #check urrent direction echo out > direction #Set GPIO as output cat direction #Check new GPIO direction cat value #Check current value echo 1 > value #Set value as high as a test, LED should lit-up cat value #Check that value changed #Endless loop, 1 sec on plus 1 sec off, press CTRL+C to end while ( true ); do echo 1 > value; cat value; sleep 1; echo 0 > value; cat value; sleep 1; done;
At this point, the LED should blink on/off with a period of 2 seconds. Congratulation, you completed the first tutorial on GPIO control on the CI20 :-)
After you finish experimenting, you can press "Ctrl+C" to stop the blinking, write "0" to disable the output and "unexport" the GPIO (or simply power down the board).
A simple script
This is a simple script you can use to do the same things, with the addition that you can change the value of the "GPIO" variable to change the affected GPIO:
#!/bin/bash GPIO=124 GPIODIR=/sys/class/gpio/gpio$GPIO echo "Configuring GPIO $GPIO" #check if the gpio is already exported if [ ! -e "$GPIODIR" ] then echo "Exporting GPIO" echo $GPIO > /sys/class/gpio/export else echo "GPIO already exported" fi echo "Current direction: `cat $GPIODIR/direction`" echo "Set GPIO as output" echo out > $GPIODIR/direction echo "New GPIO direction: `cat $GPIODIR/direction`" echo "Current value: `cat $GPIODIR/value`" echo "Set value as high" echo 1 > $GPIODIR/value echo "New value: `cat $GPIODIR/value`" #Endless loop echo "Start blinking, 1 sec on plus 1 sec off, press CTRL+C to end" while ( true ); do echo 1 > $GPIODIR/value; cat $GPIODIR/value; sleep 1; echo 0 > $GPIODIR/value; cat $GPIODIR/value; sleep 1; done;
On revision B boards, the onboard LED (PF15) is GPIO 175 and the onboard button (PD17) is GPIO 113. The following script will use the button's value to update the the LED's value.
Once the script is running, the onboard LED should turn blue when the button is not pressed and red when the button is pressed.
#!/bin/sh LED=175 BTN=113 GPIODIR=/sys/class/gpio if [ ! -e $GPIODIR/gpio$LED ] then echo $LED > $GPIODIR/export fi if [ ! -e $GPIODIR/gpio$BTN ] then echo $BTN > $GPIODIR/export fi echo in > $GPIODIR/gpio$BTN/direction echo out > $GPIODIR/gpio$LED/direction while ( true ); do cat $GPIODIR/gpio$BTN/value > $GPIODIR/gpio$LED/value; done;
Example 2: Using the onboard LEDs of Ci20 V2 boards
Revision v2 of the Ci20 adds 4 general purpose LEDs to the design. These are made available via the LED class driver, with default triggers as follows
- LED0 - None
- LED1 - NAND Flash access
- LED2 - CPU1 activity
- LED3 - CPU0 activity
To control these LEDs yourself, you must first replace the default trigger with "None" to disconnect the LED from the kernels activity:
echo none > /sys/class/leds/ledX/trigger
Then the LED may be turned on by writing a non-zero value into the brightness file:
echo 1 > /sys/class/leds/ledX/brightness
And to turn it off again:
echo 0 > /sys/class/leds/ledX/brightness