本文基于 RockPI 4A 单板 Linux 4.4 内核介绍 RK3399 Linux GPIO 功能。
GPIO(General Purpose Input/Output Port) :通用输入输出端口。
除作为一般的输入/输出功能外,还可以配置为中断和模拟 PWM、I2C 等接口功能。
RK3399 GPIO 属性如下:
1、一共有5组 GPIO(GPIO0~4) ,每组 GPIO 为一个 Bank ,共32个引脚。每个 Bank 包括4个 Group (GPIOA(0~7) ~ D(0~7)) 。不是所有 Bank 都有 GPIOA~D 的编号, RK3399 共122个 GPIO 引脚。
2、所有 GPIO 都可被配置为 CA55或CA53 的中断功能,且 GPIO0 和 GPIO1 可用于系统低功耗唤醒模式。
3、在上电复位后,所有 GPIO 默认输入状态。
4、软件可配置 GPIO 驱动强度。
RK3399 引脚在系统中显示如下:
RK3399 GPIO 引脚号计算方式:
例:
以 ROCKPI 4A 单板 WIFI 模块电源( GPIO0_B2 )为例,介绍 DTS 中 GPIO 配置。
在系统启动后,可以查看 GPIO ,命令如下:
注:
如果debugfs没有挂载,使用下面命令挂载
Linux 内核 GPIO 主要实现文件:
GPIO 子系统有两套 API :
1、基于描述符(descriptor-based)
前缀为: gpiod_ 。
参考: Documentation/gpio/consumer.txt
2、老版本接口(legacy)
前缀为: gpio_ 。
参考: Documentation/gpio/gpio-legacy.txt
3、常用API
GPIO 还有很多接口,就不一一列举了。
RK3399 GPIO 控制器驱动实现文件: drivers/pinctrl/pinctrl-rockchip.c ,涉及主要函数:
所有 GPIO 子系统的 API 最终都会调到 SOC 的 GPIO 控制器驱动函数。
ROCKPi 4A 单板有个40个引脚的扩展口,引用 radxa 图片,见下图。
1、进入测试目录
2、导出GPIO
在使用 GPIO2_A7 前,需要导出该引脚。方法:配置 export 后,会出现 gpio71 节点。
测试时,注意不要使用在程序中已经申请过或配置为其它功能的 GPIO 引脚。
3、配置GPIO方向
设置 GPIO2_A7 的输入/输出方向。
in :表示输入。
out :表示输出。
active_low :用于中断配置中高电平或低电平有效。
edge :用于中断配置中上升沿或下降沿有效。
4、配置GPIO输出值
在 GPIO 为输入时,通过 value 查询 GPIO 的输入电平(高或低电平)。
在 GPIO 为输出时,通过 value 配置 GPIO 的输出电平(高或低电平)。
5、查看GPIO
查看已经导出的 GPIO71 。
6、取消导出
使用完 GPIO2_A7 后,需要进行释放。方法:配置 unexport 后, gpio71 节点会消失。
方法/步骤1
安装SD Linux系统
如图所示,先后将Arduino Software 1.5.3 (Arduino IDE)和SD-Card Linux Image下载到本机,Arduino IDE在后面查找GPIO与Arduino IO 之间的映射关系时需要用到。
如图所示,将SDCard1.0.4.tar.bz2解压后出现一个“image-full-galileo”的文件夹。
在MicroSD使用前需先将其以Fat32进行格式化,然后将“image-full-galileo”文件夹下地所有文件直接拷贝到microSD卡的根目录下。
进入Galileo
将MicroSD插到Galileo中,在路由器页面的已连接设备列表中会看到设备名称为“clanton”有线连接设备,找到其IP地址,然后中
Terminal(Unix和Linux,Windows可用Putty)中通过ssh进入Galileo,“ssh
root@192.168.199.121”。
有意思的是,这个在MicroSD中运行的Linux系统开启了ssh服务,并且root账号没有设置密码,可以直接进入。如上图所示,d出一对话框后输入 “yes”回车即可进入Galileo,出现下图中的 “root@clanton”说明这一步成功完成了
到这里,可能会有疑问了,Galileo板载也是有一个 *** 作系统的,microSD卡中也有一个Linux,如何保证现在进入的就是microSD卡中的系
统呢?在Terminal中输入“cat/proc/version”即可查看Linux系统版本,显示为“3.8.7-yocto-standard”,这就是前面下载的为Galileo定制的Linux *** 作系统,Yocto。
找到那个属于你的GPIO
下面就要开始这篇文章中的核心部分,也是最难的一部。找Linux GPIO 与 Arduino IO之间的映射关系!
如右图所示,在“/sys/class/gpio/”中有多大60多个GPIO,如何找出右侧GPIO与左侧Arduino IDE中对应的IO呢。
首先将0-13IO口全部设为“INPUT”输入模式
voidsetup(){//putyoursetupcodehere,torunonce:
pinMode(0,INPUT)pinMode(1,INPUT)pinMode(2,INPUT)pinMode(3,INPUT)
pinMode(4,INPUT)pinMode(5,INPUT)pinMode(6,INPUT)pinMode(7,INPUT)
pinMode(8,INPUT)pinMode(9,INPUT)pinMode(10,INPUT)
pinMode(11,INPUT)pinMode(12,INPUT)pinMode(13,INPUT)} voidloop(){
//putyourmaincodehere,torunrepeatedly: }
如图所示,左侧"pinMode(13,OUTPUT)"将13引脚变为输出模式,右侧gpio7变成out模式,因此gpio7对应的就是Arduino IO 13(pin13)
按照这种方法依次找出Arduino IO与GPIO之间如下的对应关系
GPIODigitalI/Ogpio11pin0
gpio12pin1 gpio13pin2 gpio14pin3 gpio6pin4 gpio0pin5 gpio1pin6
gpio38pin7 gpio40pin8 gpio4pin9 gpio10pin10 gpio5pin11 gpio15pin12
gpio7pin13
下面就需要来对上面找到的gpio对应关系进行验证了。“echo "out"
>/sys/class/gpio/gpio*/direction”为将gpio变为输出模式,“echo "1"
>/sys/class/gpio/gpio*/value”为将gpio输出高电平。然后就有了下面这段python程序,这段程序依次将
pin13,pin12,pin11,pin10四个引脚的LED点亮然后关闭,但由于python程序的执行效率问题,应该所有LED同时点亮有了延时
成为流水灯,如下图所示效果。这段程序在Linux系统的任意文件夹内均可。
importos,timewhileTrue:os.system('echo"out">/sys/class/gpio/gpio7/direction')
os.system('echo"1">/sys/class/gpio/gpio7/value')
os.system('echo"out">/sys/class/gpio/gpio15/direction')
os.system('echo"1">/sys/class/gpio/gpio15/value')
os.system('echo"out">/sys/class/gpio/gpio5/direction')
os.system('echo"1">/sys/class/gpio/gpio5/value')
os.system('echo"out">/sys/class/gpio/gpio10/direction')
os.system('echo"1">/sys/class/gpio/gpio10/value') time.sleep(0.2)
os.system('echo"0">/sys/class/gpio/gpio5/value')
os.system('echo"0">/sys/class/gpio/gpio15/value')
os.system('echo"0">/sys/class/gpio/gpio7/value')
os.system('echo"0">/sys/class/gpio/gpio10/value') time.sleep(0.2)
通过sysfs方式控制GPIO,先访问/sys/class/gpio目录,向export文件写入GPIO编号,使得该GPIO的 *** 作接口从内核空间暴露到用户空间,GPIO的 *** 作接口包括direction和value等,direction控制GPIO方向,而value可控制GPIO输出或获得GPIO输入。文件IO方式 *** 作GPIO,使用到了4个函数open、close、read、write。
首先,看看系统中有没有“/sys/class/gpio”这个文件夹。如果没有请在编译内核的时候加入 Device Drivers->GPIO Support ->/sys/class/gpio/… (sysfs interface)。
/sys/class/gpio 的使用说明:
gpio_operation 通过/sys/文件接口 *** 作IO端口 GPIO到文件系统的映射
◇ 控制GPIO的目录位于/sys/class/gpio
◇ /sys/class/gpio/export文件用于通知系统需要导出控制的GPIO引脚编号
◇ /sys/class/gpio/unexport 用于通知系统取消导出
◇ /sys/class/gpio/gpiochipX目录保存系统中GPIO寄存器的信息,包括每个寄存器控制引脚的起始编号base,寄存器名称,引脚总数 导出一个引脚的 *** 作步骤
◇ 首先计算此引脚编号,引脚编号 = 控制引脚的寄存器基数 + 控制引脚寄存器位数
◇ 向/sys/class/gpio/export写入此编号,比如12号引脚,在shell中可以通过以下命令实现,命令成功后生成/sys/class/gpio/gpio12目录,如果没有出现相应的目录,说明此引脚不可导出
◇ direction文件,定义输入输入方向,可以通过下面命令定义为输出。direction接受的参数:in, out, high, low。high/low同时设置方向为输出,并将value设置为相应的1/0
◇ value文件是端口的数值,为1或0
几个例子:
1. 导出
/sys/class/gpio# echo 44 >export
2. 设置方向
/sys/class/gpio/gpio44# echo out >direction
3. 查看方向
/sys/class/gpio/gpio44# cat direction
4. 设置输出
/sys/class/gpio/gpio44# echo 1 >value
5. 查看输出值
/sys/class/gpio/gpio44# cat value
6. 取消导出
/sys/class/gpio# echo 44 >unexport
文件读写例程:
#include stdlib.h
#include stdio.h
#include string.h
#include unistd.h
#include fcntl.h //define O_WRONLY and O_RDONLY
//芯片复位引脚: P1_16
#define SYSFS_GPIO_EXPORT "/sys/class/gpio/export"
#define SYSFS_GPIO_RST_PIN_VAL "48"
#define SYSFS_GPIO_RST_DIR "/sys/class/gpio/gpio48/direction"
#define SYSFS_GPIO_RST_DIR_VAL "OUT"
#define SYSFS_GPIO_RST_VAL "/sys/class/gpio/gpio48/value"
#define SYSFS_GPIO_RST_VAL_H"1"
#define SYSFS_GPIO_RST_VAL_L"0"
int main()
{
int fd
//打开端口/sys/class/gpio# echo 48 >export
fd = open(SYSFS_GPIO_EXPORT, O_WRONLY)
if(fd == -1)
{
printf("ERR: Radio hard reset pin open error.\n")
return EXIT_FAILURE
}
write(fd, SYSFS_GPIO_RST_PIN_VAL ,sizeof(SYSFS_GPIO_RST_PIN_VAL))
close(fd)
//设置端口方向/sys/class/gpio/gpio48# echo out >direction
fd = open(SYSFS_GPIO_RST_DIR, O_WRONLY)
if(fd == -1)
{
printf("ERR: Radio hard reset pin direction open error.\n")
return EXIT_FAILURE
}
write(fd, SYSFS_GPIO_RST_DIR_VAL, sizeof(SYSFS_GPIO_RST_DIR_VAL))
close(fd)
//输出复位信号: 拉高>100ns
fd = open(SYSFS_GPIO_RST_VAL, O_RDWR)
if(fd == -1)
{
printf("ERR: Radio hard reset pin value open error.\n")
return EXIT_FAILURE
}
while(1)
{
write(fd, SYSFS_GPIO_RST_VAL_H, sizeof(SYSFS_GPIO_RST_VAL_H))
usleep(1000000)
write(fd, SYSFS_GPIO_RST_VAL_L, sizeof(SYSFS_GPIO_RST_VAL_L))
usleep(1000000)
}
close(fd)
printf("INFO: Radio hard reset pin value open error.\n")
return 0
}
另外参考网上一个网友的程序,这里做了验证,并实现中断检测函数。如下:
#include stdlib.h
#include stdio.h
#include string.h
#include unistd.h
#include fcntl.h
#include poll.h
#define MSG(args...) printf(args)
//函数声明
static int gpio_export(int pin)
static int gpio_unexport(int pin)
static int gpio_direction(int pin, int dir)
static int gpio_write(int pin, int value)
static int gpio_read(int pin)
static int gpio_export(int pin)
{
char buffer[64]
int len
int fd
fd = open("/sys/class/gpio/export", O_WRONLY)
if (fd <0) {
MSG("Failed to open export for writing!\n")
return(-1)
}
len = snprintf(buffer, sizeof(buffer), "%d", pin)
if (write(fd, buffer, len) <0) {
MSG("Failed to export gpio!")
return -1
}
close(fd)
return 0
}
static int gpio_unexport(int pin)
{
char buffer[64]
int len
int fd
fd = open("/sys/class/gpio/unexport", O_WRONLY)
if (fd <0) {
MSG("Failed to open unexport for writing!\n")
return -1
}
len = snprintf(buffer, sizeof(buffer), "%d", pin)
if (write(fd, buffer, len) <0) {
MSG("Failed to unexport gpio!")
return -1
}
close(fd)
return 0
}
//dir: 0-->IN, 1-->OUT
static int gpio_direction(int pin, int dir)
{
static const char dir_str[] = "in\0out"
char path[64]
int fd
snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/direction", pin)
fd = open(path, O_WRONLY)
if (fd <0) {
MSG("Failed to open gpio direction for writing!\n")
return -1
}
if (write(fd, &dir_str[dir == 0 ? 0 : 3], dir == 0 ? 2 : 3) <0) {
MSG("Failed to set direction!\n")
return -1
}
close(fd)
return 0
}
//value: 0-->LOW, 1-->HIGH
static int gpio_write(int pin, int value)
{
static const char values_str[] = "01"
char path[64]
int fd
snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", pin)
fd = open(path, O_WRONLY)
if (fd <0) {
MSG("Failed to open gpio value for writing!\n")
return -1
}
if (write(fd, &values_str[value == 0 ? 0 : 1], 1) <0) {
MSG("Failed to write value!\n")
return -1
}
close(fd)
return 0
}
static int gpio_read(int pin)
{
char path[64]
char value_str[3]
int fd
snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", pin)
fd = open(path, O_RDONLY)
if (fd <0) {
MSG("Failed to open gpio value for reading!\n")
return -1
}
if (read(fd, value_str, 3) <0) {
MSG("Failed to read value!\n")
return -1
}
close(fd)
return (atoi(value_str))
}
// none表示引脚为输入,不是中断引脚
// rising表示引脚为中断输入,上升沿触发
// falling表示引脚为中断输入,下降沿触发
// both表示引脚为中断输入,边沿触发
// 0-->none, 1-->rising, 2-->falling, 3-->both
static int gpio_edge(int pin, int edge)
{
const char dir_str[] = "none\0rising\0falling\0both"
char ptr
char path[64]
int fd
switch(edge){
case 0:
ptr = 0
break
case 1:
ptr = 5
break
case 2:
ptr = 12
break
case 3:
ptr = 20
break
default:
ptr = 0
}
snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/edge", pin)
fd = open(path, O_WRONLY)
if (fd <0) {
MSG("Failed to open gpio edge for writing!\n")
return -1
}
if (write(fd, &dir_str[ptr], strlen(&dir_str[ptr])) <0) {
MSG("Failed to set edge!\n")
return -1
}
close(fd)
return 0
}
//GPIO1_17
int main()
{
int gpio_fd, ret
struct pollfd fds[1]
char buff[10]
unsigned char cnt = 0
//LED引脚初始化
gpio_export(115)
gpio_direction(115, 1)
gpio_write(115, 0)
//按键引脚初始化
gpio_export(49)
gpio_direction(49, 0)
gpio_edge(49,1)
gpio_fd = open("/sys/class/gpio/gpio49/value",O_RDONLY)
if(gpio_fd <0){
MSG("Failed to open value!\n")
return -1
}
fds[0].fd = gpio_fd
fds[0].events = POLLPRI
ret = read(gpio_fd,buff,10)
if( ret == -1 )
MSG("read\n")
while(1){
ret = poll(fds,1,0)
if( ret == -1 )
MSG("poll\n")
if( fds[0].revents &POLLPRI){
ret = lseek(gpio_fd,0,SEEK_SET)
if( ret == -1 )
MSG("lseek\n")
ret = read(gpio_fd,buff,10)
if( ret == -1 )
MSG("read\n")
gpio_write(115, cnt++%2)
}
usleep(100000)
}
return 0
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)