Differences
This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
| docs:techref:hardware:port.gpio [2019/06/18 16:11] – businessphoneservice | docs:techref:hardware:port.gpio [2023/07/20 01:47] (current) – Add info box about gpiod-tools, gpio sysfs has been marked obsolete and won't work correctly djfe | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | Callture | + | ====== GPIO ====== |
| + | Read [[wp> | ||
| + | * [[docs: | ||
| + | * [[toh: | ||
| + | * [[https:// | ||
| + | |||
| + | ===== Hardware ===== | ||
| + | GPIOs are commonly used in router devices for buttons or leds. They only safely supply or sink (pull to GND) a maximum of 4mA approx., and the voltage | ||
| + | |||
| + | * **Active high**: the device is activated when the GPIO is HIGH | ||
| + | * **Active low**: the device is activated when the GPIO is LOW | ||
| + | |||
| + | In this image you can see how a GPIO is wired to buttons or leds, to work as active low or high | ||
| + | \\ \\ {{media: | ||
| + | |||
| + | **GPIOs can be used for complex tasks:** | ||
| + | | ^ kernel module ^ packaged ^ description ^ | ||
| + | | [[docs: | ||
| + | | [[docs: | ||
| + | | ::: | kmod-gpio-nxp-74hc164 | ✔ | SPI GPIO expander | | ||
| + | | ::: | kmod-gpio-mcp23s08 | ✔ | i2c/SPI GPIO expander | | ||
| + | | [[docs: | ||
| + | | [[docs: | ||
| + | | ::: | kmod-mmc-over-gpio | ✔ | [[docs: | ||
| + | | [[docs: | ||
| + | | [[docs: | ||
| + | | [[docs: | ||
| + | | [[docs: | ||
| + | | [[docs: | ||
| + | | [[docs: | ||
| + | | [[https:// | ||
| + | |||
| + | You can connect 5V digital signal sensors if you use a voltage divider to get a 3.3V range signal.You need to get acces to GND and 5V-power from the router and connect the sensor to them. | ||
| + | Sensor signal output is in the 5V range, connect it to the voltage divider! | ||
| + | \\ \\ {{: | ||
| + | |||
| + | |||
| + | ==== Pin Multiplexing ==== | ||
| + | Pin [[wp> | ||
| + | |||
| + | Many times some GPIOs cannot be controlled by the kernel because they are multiplexed, | ||
| + | |||
| + | * Example 1: on the Broadcom BCM6328 SoC, GPIO pins 25, 26, 27 and 28 are used to indicate the LAN activity with hardware controlled LEDs. The memory register for setting this multiplexing is at 0x1000009C address, 64bits wide. Let's read it in OpenWrt\\ < | ||
| + | 0x0154000000000000</ | ||
| + | |||
| + | * Example 2: on the Atheros AR7240 SoC, GPIO pins 6, 7 and 8 are used by the JTAG interface. The memory register for AR7240 GPIO pinmux is at 0x18040028 address, 32 bits wide. Let's read it:\\ < | ||
| + | 0x48002</ | ||
| + | ==== GPIO Interrupts ==== | ||
| + | GPIO interrupts are useful when a GPIO is used as input and you need to manage high signal frequencies. Without interrupts, GPIO inputs must be managed using the **polling** method. With polling you cannot manage signal inputs with high frequencies, | ||
| + | |||
| + | GPIO IRQs are also useful for detecting the edge from an input, it can be rising or falling. Some GPIO drivers also need this feature. | ||
| + | |||
| + | Not all boards have GPIO interrupts, or the GPIO kernel drivers don't provide IRQs because they aren't still implemented. As a result of this, some input drivers listed above (requiring GPIO IRQs) won't work in these boards. | ||
| + | |||
| + | Example: ar71xx target has GPIO IRQs since [[https:// | ||
| + | |||
| + | ===== Software ===== | ||
| + | <WRAP round info 100%> | ||
| + | **The GPIO SYSFS interface has been marked as obsolete in 2015. I couldn' | ||
| + | |||
| + | The new interface is the linux GPIO character device. One way to access it is [[https:// | ||
| + | |||
| + | You can install it with the [[: | ||
| + | |||
| + | <code bash> | ||
| + | opkg install gpiod-tools</ | ||
| + | |||
| + | A good article on how to use the new CLI and why we should move on: [[https:// | ||
| + | |||
| + | The biggest differences are: | ||
| + | * The new interface isn't stateless like the old one. Processes are able to take exclusive control over a gpio line now | ||
| + | * There is a character device / | ||
| + | * TODO | ||
| + | As I said: This page needs an overhaul. This info box is just to prevent people from wasting their time on a deprecated ABI. | ||
| + | </ | ||
| + | |||
| + | In linux GPIOs can be accessed through GPIO SYSFS interface: **/ | ||
| + | |||
| + | **Example**\\ | ||
| + | In this example we will use GPIO29 and use it as a switch. | ||
| + | |||
| + | With latest linux kernels you may need to first get the gpio base | ||
| + | <code bash>cat / | ||
| + | 200</ | ||
| + | and sum the base to your GPIO:\\ '' | ||
| + | |||
| + | Now first step is making GPIO available in Linux: | ||
| + | < | ||
| + | then you need to decide if it will be input or output, as we will use it as a switch so we need output | ||
| + | < | ||
| + | and last line turns GPIO on or off with 1 or 0: | ||
| + | < | ||
| + | |||
| + | ==== Utilities ==== | ||
| + | |||
| + | To control GPIOs you can use **gpioctl-sysfs**. Also with this simple script you can control GPIOs not used by buttons or leds. | ||
| + | <code bash> | ||
| + | #!/bin/sh | ||
| + | |||
| + | show_usage() | ||
| + | { | ||
| + | printf " | ||
| + | } | ||
| + | |||
| + | if [ \( $# -eq 0 \) -o \( $# -gt 3 \) ] ; then | ||
| + | show_usage | ||
| + | printf " | ||
| + | exit 255 | ||
| + | fi | ||
| + | |||
| + | GPIOBASE=`cat / | ||
| + | GPIO=`expr $1 + $GPIOBASE` | ||
| + | |||
| + | # | ||
| + | (echo $GPIO > / | ||
| + | |||
| + | if [ $# -eq 1 ] ; then | ||
| + | cat / | ||
| + | exit 0 | ||
| + | fi | ||
| + | |||
| + | if [ \( " | ||
| + | show_usage | ||
| + | printf " | ||
| + | exit 255 | ||
| + | fi | ||
| + | |||
| + | echo $2 > / | ||
| + | |||
| + | if [ $# -eq 2 ] ; then | ||
| + | cat / | ||
| + | exit 0 | ||
| + | fi | ||
| + | |||
| + | |||
| + | VAL=$3 | ||
| + | |||
| + | if [ $VAL -ne 0 ] ; then | ||
| + | VAL=1 | ||
| + | fi | ||
| + | |||
| + | echo $VAL > / | ||
| + | </ | ||
| + | Save the script | ||
| + | >'' | ||
| + | |||
| + | Example, put the GPIO14 on HIGH state: | ||
| + | >'' | ||
| + | |||
| + | Read the input value of GPIO14: | ||
| + | >'' | ||
| + | |||
| + | ===== Finding GPIO pins on the PCB ===== | ||
| + | |||
| + | Sometimes you do not know where the physical GPIO pins are on your device' | ||
| + | |||
| + | ==== script " | ||
| + | <code bash> | ||
| + | #!/bin/sh | ||
| + | |||
| + | GPIOBASE=`cat / | ||
| + | GPIOmin=`expr $1 + $GPIOBASE` | ||
| + | GPIOmax=`expr $2 + $GPIOBASE` | ||
| + | nums=`seq $GPIOmin $GPIOmax` | ||
| + | |||
| + | cd / | ||
| + | for i in $nums; do | ||
| + | echo $i > export; echo out > | ||
| + | done | ||
| + | |||
| + | while true; do | ||
| + | for i in $nums; do | ||
| + | echo 0 > gpio$i/ | ||
| + | | ||
| + | sleep 1 | ||
| + | for i in $nums; do | ||
| + | echo 1 > gpio$i/ | ||
| + | done | ||
| + | sleep 1 | ||
| + | done | ||
| + | </ | ||
| + | |||
| + | - Start with '' | ||
| + | - Press ctrl-c to stop the script, then check which GPIOs have been created: '' | ||
| + | - Restart the script and measure with a multimeter which pins " | ||
| + | - When you find one, then cut the 0-30 range from above in half; | ||
| + | - Repeat until you have identified the gpio number | ||
| + | |||
| + | ==== script " | ||
| + | <code bash> | ||
| + | #!/bin/sh | ||
| + | GPIOBASE=`cat / | ||
| + | GPIOmin=`expr $1 + $GPIOBASE` | ||
| + | GPIOmax=`expr $2 + $GPIOBASE` | ||
| + | |||
| + | cd / | ||
| + | for i in `seq $GPIOmin $GPIOmax`; do | ||
| + | echo " | ||
| + | echo $i > export; echo out > | ||
| + | echo $3 > gpio$i/ | ||
| + | echo $i > unexport | ||
| + | done | ||
| + | </ | ||
| + | |||
| + | - Measure continuosuly the voltage of the pin where you suspect there is a GPIO wired, with a multimeter or another device. | ||
| + | - Put all GPIOs on HIGH state '' | ||
| + | - Put all GPIOs on LOW state '' | ||
| + | - If the pin changes the voltage, then cut the 0-31 range from above in half '' | ||
| + | - If the pin doesn' | ||
| + | - Cut again the new range. | ||
| + | - Repeat until you have identified the gpio number | ||
| + | |||
| + | ==== script "blink all" ==== | ||
| + | |||
| + | Another script to look for GPIO connected to LED | ||
| + | <code bash> | ||
| + | #!/bin/sh | ||
| + | # Modified from https://gist.github.com/ | ||
| + | echo GPIO LED Test | ||
| + | echo | ||
| + | echo Example: $0 3s 0 1 | ||
| + | echo leave gpio range blank to test all GPIOs. | ||
| + | echo | ||
| + | wait=${1: | ||
| + | for GPIOCHIP in / | ||
| + | BASE=$(cat ${GPIOCHIP}base) | ||
| + | SIZE=$(cat ${GPIOCHIP}ngpio) | ||
| + | MAX=$(($BASE+$SIZE-1)) | ||
| + | gpio_end=${3: | ||
| + | [ -z " | ||
| + | while [ $gpio -ge $BASE -a $gpio -le $MAX -a $gpio -le $gpio_end ] ; do | ||
| + | # Save original value if needed | ||
| + | if [ -d / | ||
| + | DIRECTION=$(cat / | ||
| + | UNEXPORT=0 | ||
| + | VALUE=$(cat / | ||
| + | else | ||
| + | echo $gpio > / | ||
| + | UNEXPORT=1 | ||
| + | DIRECTION="" | ||
| + | VALUE="" | ||
| + | fi | ||
| + | if [ -d / | ||
| + | echo out > / | ||
| + | |||
| + | echo " | ||
| + | echo 0 > / | ||
| + | sleep $wait | ||
| + | |||
| + | echo " | ||
| + | echo 1 > / | ||
| + | sleep $wait | ||
| + | |||
| + | # Restore original value | ||
| + | [ ! -z " | ||
| + | [ ! -z " | ||
| + | [ " | ||
| + | else | ||
| + | echo " | ||
| + | fi | ||
| + | |||
| + | gpio=$((gpio+1)) | ||
| + | done | ||
| + | done | ||
| + | </ | ||
| + | - Run the script, the script can loop all GPIOs | ||
| + | - Look at the LED and record the number printed when LED is on or off | ||
| + | |||
| + | Note: some GPIOs may return an error because they' | ||
| + | |||
| + | ===== Finding GPIO pins (input) on the PCB ===== | ||
| + | |||
| + | ==== script "gpio in" ==== | ||
| + | |||
| + | Sometimes you do not know where the physical GPIO pins (input) are on your device' | ||
| + | |||
| + | <code bash> | ||
| + | #!/bin/sh | ||
| + | GPIOBASE=`cat / | ||
| + | GPIOmin=`expr $1 + $GPIOBASE` | ||
| + | GPIOmax=`expr $2 + $GPIOBASE` | ||
| + | |||
| + | cd / | ||
| + | for i in `seq $GPIOmin $GPIOmax`; do | ||
| + | echo $i > export; echo in > | ||
| + | done | ||
| + | nums=`seq $GPIOmin $GPIOmax` | ||
| + | while true; do | ||
| + | for i in $nums; do | ||
| + | echo read gpio$i | ||
| + | cat / | ||
| + | | ||
| + | sleep 1 | ||
| + | done | ||
| + | </ | ||
| + | |||
| + | - Start with '' | ||
| + | - Press button or change the value input. | ||
| + | - The script returns on screen: 'read gpio+number' | ||
| + | - Press ctrl-c to stop the script, then check which GPIOs have been created: '' | ||
| + | |||
| + | ==== script "gpio in" ==== | ||
| + | |||
| + | Another script which check each GPIO one-by-one | ||
| + | |||
| + | <code bash> | ||
| + | #!/bin/sh | ||
| + | # Modified from https:// | ||
| + | echo GPIO Button Test | ||
| + | echo | ||
| + | echo Example: $0 0 1 | ||
| + | echo leave gpio range blank to test all GPIOs. | ||
| + | echo | ||
| + | for GPIOCHIP in / | ||
| + | BASE=$(cat ${GPIOCHIP}base) | ||
| + | SIZE=$(cat ${GPIOCHIP}ngpio) | ||
| + | MAX=$(($BASE+$SIZE-1)) | ||
| + | gpio_end=${2: | ||
| + | [ -z " | ||
| + | while [ $gpio -ge $BASE -a $gpio -le $MAX -a $gpio -le $gpio_end ] ; do | ||
| + | # Save original value if needed | ||
| + | if [ -d / | ||
| + | DIRECTION=$(cat / | ||
| + | UNEXPORT=0 | ||
| + | else | ||
| + | echo $gpio > / | ||
| + | UNEXPORT=1 | ||
| + | DIRECTION="" | ||
| + | fi | ||
| + | if [ -d / | ||
| + | echo in > / | ||
| + | echo " | ||
| + | |||
| + | # Restore original value | ||
| + | [ ! -z " | ||
| + | [ " | ||
| + | else | ||
| + | echo " | ||
| + | fi | ||
| + | |||
| + | gpio=$((gpio+1)) | ||
| + | done | ||
| + | done | ||
| + | </ | ||
| + | |||
| + | - Start with '' | ||
| + | - Hold a button and run again, release only after the script has been finished | ||
| + | - Compare the changed GPIO value which corresponding to the button held | ||
| + | ===== Links ===== | ||
| + | * [[https:// | ||
| + | * [[http:// | ||
| + | |||
| + | ===== Devices ===== | ||
| + | The list of related devices: | ||
| + | {{tagpage> | ||