GPIO全称: General-Purpose Input/Output(通用输入输出),是一种软件运行期间能够动态配置和控制的通用引脚。
Tinker Board R引脚分类
Tinker Board R有四十个对外开放的GPIO口,如下图所示
控制接口主要是指上图中的/sys/class/gpio/export和/sys/class/gpio/unexport两个接口,用户态可以通过这两个接口实现对GPIO的控制。
使用实例
//进入adb需要获取root权限
D:\code\ThinkerEdgeR\adb>adb root && adb remount && adb shell
remount succeeded
//进入gpio目录
rk3399pro:/ # cd /sys/class/gpio
//查看目录下文件
rk3399pro:/sys/class/gpio # ls
export gpio10 gpio11 gpio32 gpio35 gpio36 gpio4 gpio54 gpio55 gpio56 gpiochip0 gpiochip128 gpiochip32 gpiochip511 gpiochip64 gpiochip96 unexport
//将GPIO编号10的取消GPIO控制从内核空间到用户空间的导出
rk3399pro:/sys/class/gpio # echo 10 > unexport
//再次查看目录,发现gpio10已经被移除
rk3399pro:/sys/class/gpio # ls
export gpio11 gpio32 gpio35 gpio36 gpio4 gpio54 gpio55 gpio56 gpiochip0 gpiochip128 gpiochip32 gpiochip511 gpiochip64 gpiochip96 unexport
//将gpio10从内核空间导出回用户空间
rk3399pro:/sys/class/gpio # echo 10 > export
//再次查看,发现gpio10回到了用户空间
rk3399pro:/sys/class/gpio # ls
export gpio10 gpio11 gpio32 gpio35 gpio36 gpio4 gpio54 gpio55 gpio56 gpiochip0 gpiochip128 gpiochip32 gpiochip511 gpiochip64 gpiochip96 unexport
GPIO信号,就是指GPIO本身。对应的是/sys/class/gpio/gpioN/,它拥有多个属性。我们通过对这些属性的控制,可以实现对于GPIO口的控制。
GPIO控制器,用于表示GPIO 控制实现的初始GPIO,其路径为/sys/class/gpio/gpiochipN/。比如/sys/class/gpio/gpiochip42/ 则表示实现GPIO控制器的初始化编号为42。GPIO控制器的属性为只读属性,包括base、label和ngpio等多个。
”base”属性,和gpiochipN的N代表的含义相同,表示被该组GPIO控制器实现的第一个GPIO.
”ngpio”属性,用于表示该控制器支持多少个GPIO,支持的GPIO编号为从N到N+ngpio-1
”label”属性,用于判断控制器,并不总是唯一的
每块芯片可以有N组GPIO,每组GPIO最多有32个GPIO口。所以每块芯片最多有N*32个GPIO口。
在我们的Tinker Board R中,GPIO口是如此定义的。
GPIO0_A0~GPIO0_A7 GPIO0_B0~GPIO1_B7 .... GPIO0_D0~GPIO1_D7
GPIO1_A0~GPIO1_A7 .....
GPIO2_A0~GPIO2_A7 .....
GPIO3_A0~GPIO3_A7 .....
GPIO4_A0~GPIO4_A7 .....
由此不难发现规律 A0~D7则代表一组GPIO 一共有4*8=32个
我们可以通过这种规律不难计算出管脚图中的GPIO0_A6对应的就是gpio6
$ rk3399pro:/ # cat /sys/kernel/debug/pinctrl/pinctrl/pinmux-pins
Pinmux settings per pin
Format: pin (name): mux_owner gpio_owner hog?
pin 0 (gpio0-0): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 1 (gpio0-1): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 2 (gpio0-2): pinctrl (GPIO UNCLAIMED) function npu_clk group npu-ref-clk
pin 3 (gpio0-3): (MUX UNCLAIMED) gpio0:3
pin 4 (gpio0-4): (MUX UNCLAIMED) gpio0:4
pin 5 (gpio0-5): wireless-bluetooth gpio0:5 function wireless-bluetooth group bt-irq-gpio
pin 6 (gpio0-6): (MUX UNCLAIMED) gpio0:6
pin 7 (gpio0-7): fe320000.dwmmc (GPIO UNCLAIMED) function sdmmc group sdmcc-cd
pin 8 (gpio0-8): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 9 (gpio0-9): (MUX UNCLAIMED) gpio0:9
pin 10 (gpio0-10): (MUX UNCLAIMED) gpio0:10
pin 11 (gpio0-11): (MUX UNCLAIMED) gpio0:11
pin 12 (gpio0-12): (MUX UNCLAIMED) gpio0:12
pin 13 (gpio0-13): rk_headset gpio0:13 function headphone group hp-det
...
Android系统需要通过adb登录进行操作,并且切换root权限。
使用usb type-c线连接tinker board R和电脑,启动adb命令窗口,输入如下命令:
$ adb root
$ adb remount
$ adb shell
$ rk3399pro: cd /sys/class/gpio
$ rk3399pro: ls
$ rk3399pro:echo 6 > export //6代表gpio号
$ rk3399pro:cd gpio6
$ rk3399pro:cat direction
out
$ rk3399pro:echo in > direction
$ rk3399pro:cat direction //查看gpio口状态
in
$ rk3399pro:cat value
0 // 0 代表读取到低电平 1 代表读取到高电平
$ rk3399pro:echo out > direction
$ rk3399pro:cat direction //查看gpio口状态
out
$ rk3399pro:echo 1 > value //1 代表输出高电平 0 代表输出低电平
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/stat.h>
#define GPIO_PIN 6
typedef enum {
GPIO_OUTPUT = 1,
GPIO_OUTPUT_HIGH,
GPIO_OUTPUT_LOW,
GPIO_INPUT,
} GPIO_DIRECT;
int gpio_direction(int gpio, int dir)
{
int ret = 0;
char buf[128];
sprintf(buf, "/sys/class/gpio/gpio%d/direction", gpio);
int gpiofd = open(buf, O_WRONLY);
if(gpiofd < 0) {
perror("Couldn't open IRQ file");
ret = -1;
}
if(dir == GPIO_OUTPUT && gpiofd){
if (3 != write(gpiofd, "out", 3)) {
perror("Couldn't set GPIO direction to out");
ret = -2;
}
}
else if(dir == GPIO_OUTPUT_HIGH && gpiofd){
if (4 != write(gpiofd, "high", 4)) {
perror("Couldn't set GPIO direction to out high");
ret = -3;
}
}
else if (dir == GPIO_OUTPUT_LOW && gpiofd){
if (3 != write(gpiofd, "low", 3)){
perror("Couldn't set GPIO direction to out low");
ret = -4;
}
}
else if(gpiofd) {
if(2 != write(gpiofd, "in", 2)) {
perror("Couldn't set GPIO directio to in");
ret = -5;
}
}
close(gpiofd);
return ret;
}
int gpio_set_edge(int gpio, int rising, int falling)
{
int ret = 0;
char buf[128];
sprintf(buf, "/sys/class/gpio/gpio%d/edge", gpio);
int gpiofd = open(buf, O_WRONLY);
if(gpiofd < 0) {
perror("Couldn't open IRQ file");
ret = -1;
}
if(gpiofd && rising && falling) {
if(4 != write(gpiofd, "both", 4)) {
perror("Failed to set IRQ to both falling & rising");
ret = -2;
}
} else {
if(rising && gpiofd) {
if(6 != write(gpiofd, "rising", 6)) {
perror("Failed to set IRQ to rising");
ret = -2;
}
} else if(falling && gpiofd) {
if(7 != write(gpiofd, "falling", 7)) {
perror("Failed to set IRQ to falling");
ret = -3;
}
}
}
close(gpiofd);
return ret;
}
int gpio_export(int gpio)
{
int efd;
char buf[50];
int gpiofd, ret;
/* Quick test if it has already been exported */
sprintf(buf, "/sys/class/gpio/gpio%d/value", gpio);
efd = open(buf, O_WRONLY);
if(efd != -1) {
close(efd);
return 0;
}
efd = open("/sys/class/gpio/export", O_WRONLY);
if(efd != -1) {
sprintf(buf, "%d", gpio);
ret = write(efd, buf, strlen(buf));
if(ret < 0) {
perror("Export failed");
return -2;
}
close(efd);
} else {
// If we can't open the export file, we probably
// dont have any gpio permissions
return -1;
}
return 0;
}
void gpio_unexport(int gpio)
{
int gpiofd, ret;
char buf[50];
gpiofd = open("/sys/class/gpio/unexport", O_WRONLY);
sprintf(buf, "%d", gpio);
ret = write(gpiofd, buf, strlen(buf));
close(gpiofd);
}
int gpio_getfd(int gpio)
{
char in[3] = {0, 0, 0};
char buf[50];
int gpiofd;
sprintf(buf, "/sys/class/gpio/gpio%d/value", gpio);
gpiofd = open(buf, O_RDWR);
if(gpiofd < 0) {
fprintf(stderr, "Failed to open gpio %d value\n", gpio);
perror("gpio failed");
}
return gpiofd;
}
int gpio_read(int gpio)
{
char in[3] = {0, 0, 0};
char buf[50];
int nread, gpiofd;
sprintf(buf, "/sys/class/gpio/gpio%d/value", gpio);
gpiofd = open(buf, O_RDWR);
if(gpiofd < 0) {
fprintf(stderr, "Failed to open gpio %d value\n", gpio);
perror("gpio failed");
}
do {
nread = read(gpiofd, in, 1);
} while (nread == 0);
if(nread == -1){
perror("GPIO Read failed");
return -1;
}
close(gpiofd);
return atoi(in);
}
int gpio_write(int gpio, int val)
{
char buf[50];
int nread, ret, gpiofd;
sprintf(buf, "/sys/class/gpio/gpio%d/value", gpio);
gpiofd = open(buf, O_RDWR);
if(gpiofd > 0) {
snprintf(buf, 2, "%d", val);
ret = write(gpiofd, buf, 2);
if(ret < 0) {
perror("failed to set gpio");
return 1;
}
close(gpiofd);
if(ret == 2) return 0;
}
return 1;
}
int gpio_select(int gpio)
{
char gpio_irq[64];
int ret = 0, buf, irqfd;
fd_set fds;
FD_ZERO(&fds);
snprintf(gpio_irq, sizeof(gpio_irq), "/sys/class/gpio/gpio%d/value", gpio);
irqfd = open(gpio_irq, O_RDONLY);
if(irqfd < 1) {
perror("Couldn't open the value file");
return -13;
}
// Read first since there is always an initial status
ret = read(irqfd, &buf, sizeof(buf));
while(1) {
FD_SET(irqfd, &fds);
ret = select(irqfd + 1, NULL, NULL, &fds, NULL);
if(FD_ISSET(irqfd, &fds))
{
FD_CLR(irqfd, &fds); //Remove the filedes from set
// Clear the junk data in the IRQ file
ret = read(irqfd, &buf, sizeof(buf));
return 1;
}
}
}
int main(int argc, char **argv) {
int gpio_pin = GPIO_PIN;
gpio_export(gpio_pin);
gpio_direction(gpio_pin, 1);
for(int i = 0; i < 5; i++) {
printf(">> GPIO %d ON\n", gpio_pin);
gpio_write(gpio_pin, 1);
sleep(1);
printf(">> GPIO %d OFF\n", gpio_pin);
gpio_write(gpio_pin, 0);
sleep(1);
}
return 0;
}