Blog:
NXP iMX8 嵌入式Linux下Libgpiod应用示例

2020年7月1日星期三

简介

NXP iMX8是NXP去年底发布的基于Cortex-A72/A53和Coretex-M4异构多核架构的ARM处理器,本文就基于嵌入式Linux演示GPIO相关应用示例。

本文所演示的ARM平台来自于Toradex 基于NXP iMX8QM ARM处理器的Apalis iMX8QM ARM嵌入式平台。

准备

Apalis iMX8QM ARM核心版配合Apalis Evaluation Board载板,连接调试串口UART1(载板X29)到开发主机方便调试。

Apalis iMX8 Cortex-A核心安装Toradex Ycoto Linux Console image V3.04版本,详细信息请参考这里

Apalis Evaluation Board GPIO相关硬件连接

X2 MXM_3 <-> X34 LED1
X2 MXM_5 <-> X34 SW5
X2 MXM_11 <-> X34 SW1

GPIO 命令行测试

嵌入式Linux系统下之前呗广泛应用的GPIO工具为sysfs GPIO接口(/sys/class/gpio),但是目前这个项目已经处于deprecated状态,经Linux Kernel Community确定其替代者就是GPIO字符设备API Libgpiod。因此,尽管本文测试使用的Toradex Ycoto Linux Console image V3.04版本依然支持sysfs GPIO接口,但是已经不建议使用,如果需要相关说明,可以参考这里。本文接下来的测试都是基于Libgpiod来进行。

GPIO 数字编码

ARM SOC定义GPIO管脚通常为一串字母数字的组合,之前sysfs API操作时候需要将不同SOC GPIO的字母数字命名转换为纯数字编码,对于不同的SOC转换规则的详细说明请见这里,而本文使用的libgpiod API则直接可以按照GPIO命名进行操作,更为直观和便利。

首先根据 Apalis iMX8 datasheet 4.4章节的表格,找出所需要GPIO管脚的字母数字组合命名,比如本文涉及的三个GPIO管脚;在接下来的Libgpiod API中都需要使用对应的GPIO命名中的<controller>和<gpio>这两个参数来操作对应的GPIO管脚

GPIO字符数次串命名规则:LSIO.GPIO<controller>.IO<gpio>

X2 MXM_3 -> LSIO.GPIO0.IO09
X2 MXM_5 -> LSIO.GPIO0.IO12
X2 MXM_11 -> LSIO.GPIO4.IO01

在具体调试GPIO之前,还需要确认在Linux kernel device tree中,这个管脚被定义为GPIO且并没有被其他驱动功能占用,否则在具体操作GPIO的时候会出现冲突导致异常,关于device tree的说明本文不赘述,可以参考这里。本文所涉及的三个GPIO管脚默认即配置为GPIO,因此无需修改。

调用Libgpiod API在命令行下进行GPIO测试

Apalis iMX8 上电开机进入嵌入式Linux系统,下面操作在调试串口下进行

罗列GPIO banks,也就是对应的上面提到的controller。可以看到有8个banks,每个banks包含32 lines,也就是32 gpio。当然,实际上结合具体的SOC和模块定义,不一定每个banks和lines都有对应的GPIO管脚,有效的肯呢个只是一部分。

root@apalis-imx8:~# gpiodetect 
gpiochip0 [5d080000.gpio] (32 lines)
gpiochip1 [5d090000.gpio] (32 lines)
gpiochip2 [5d0a0000.gpio] (32 lines)
gpiochip3 [5d0b0000.gpio] (32 lines)
gpiochip4 [5d0c0000.gpio] (32 lines)
gpiochip5 [5d0d0000.gpio] (32 lines)
gpiochip6 [5d0e0000.gpio] (32 lines)
gpiochip7 [5d0f0000.gpio] (32 lines)

查看某个bank具体GPIO lines的情况,可以查看系统中当前哪些GPIO被占用了。

root@apalis-imx8:~# gpioinfo 0
gpiochip0 - 32 lines:
      line   0:      unnamed       unused   input  active-high 
      line   1:      unnamed       unused   input  active-high 
      line   2:      unnamed       unused   input  active-high 
      line   3:      unnamed       unused   input  active-high 
      line   4:      unnamed       unused   input  active-high 
      line   5:      unnamed       unused   input  active-high 
      line   6:      unnamed       unused   input  active-high 
      line   7:      unnamed       unused   input  active-high 
      line   8:      unnamed       unused  output  active-high 
      line   9:      unnamed       unused  output  active-high 
      line  10:      unnamed       unused   input  active-high 
      line  11:      unnamed       unused   input  active-high 
      line  12:      unnamed       unused   input  active-high 
      line  13:      unnamed       unused   input  active-high 
      line  14:      unnamed       unused   input  active-high 
      line  15:      unnamed       unused   input  active-high 
      line  16:      unnamed       unused   input  active-high 
      line  17:      unnamed       unused   input  active-high 
      line  18:      unnamed       unused   input  active-high 
      line  19:      unnamed       unused   input  active-high 
      line  20:      unnamed       unused   input  active-high 
      line  21:      unnamed       unused   input  active-high 
      line  22:      unnamed       unused   input  active-high 
      line  23:      unnamed       unused   input  active-high 
      line  24:      unnamed       unused   input  active-high 
      line  25:      unnamed       unused   input  active-high 
      line  26:      unnamed       unused   input  active-high 
      line  27:      unnamed       unused   input  active-high 
      line  28:      unnamed       unused   input  active-high 
      line  29:      unnamed       unused   input  active-high 
      line  30:      unnamed      "reset"  output   active-low [used]
      line  31:      unnamed "usb3503 connect" output active-high [used]

测试X2 MXM_3 管脚GPIO output操作,gpioset命令的作用就是设置对应GPIO管脚为输出状态,并输出为设置的高或者低电平。

设置GPIO输出为高电平驱动LED点亮

root@apalis-imx8:~# gpioset 0 9=1

设置GPIO输出为低电平驱动LED熄灭                                                                                                                     

root@apalis-imx8:~# gpioset 0 9=0

通过 gpioinfo查看bank0 可以发现line 9是output状态

root@apalis-imx8:~# gpioinfo 0
gpiochip0 - 32 lines:
      ……
      line   9:      unnamed       unused  output  active-high
      ……

测试X2 MXM_11 管脚GPIO input操作,gpioget命令的作用就是设置对应GPIO管脚为输入状态,并读取当前的GPIO电平。

SW1 拨至低电平位置(靠近载板边缘),读取连接的X2 MXM_11GPIO电平

root@apalis-imx8:~# gpioget 4 1
0

SW1 拨至高电平位置(远离载板边缘),读取连接的X2 MXM_11GPIO电平                                                                                                                     

root@apalis-imx8:~# gpioget 4 1
1

通过 gpioinfo查看bank4 可以发现line 1是input状态

root@apalis-imx8:~# gpioinfo 4
gpiochip4 - 32 lines:
      ……
      line   1:      unnamed       unused  input  active-high
      ……

测试X2 MXM_5 管脚GPIO 中断操作,gpiomon命令的作用就是设置对应的GPIO为输入状态,并监测对应GPIO管脚的事件(。

设置X2 MXM_5 管脚中断监测

root@apalis-imx8:~# gpiomon 0 12

按下SW5按键

event:  RISING EDGE offset: 12 timestamp: [1609840088.602076625]

松开SW5按键

event: FALLING EDGE offset: 12 timestamp: [1609840089.268163875]

通过 gpioinfo查看bank0 可以发现line 12是input状态

root@apalis-imx8:~# gpioinfo 0
gpiochip0 - 32 lines:
      ……
      line   12:      unnamed       unused  input  active-high

GPIO C代码示例程序测试

首先先在开发Linux 主机安装SDK,SDK为按照这里说明通过Ycoto project/OpenEmbedded 环境编译得到,安装过程中根据提示可以自行更改SDK安装目录

$ chmod +x tdx-xwayland-glibc-x86_64-Qt5-Wayland-Image-aarch64-toolchain-2.6.4.sh
$ ./tdx-xwayland-glibc-x86_64-Qt5-Wayland-Image-aarch64-toolchain-2.6.4.sh 
TDX Wayland with XWayland SDK installer version 2.6.4
=====================================================
Enter target directory for SDK (default: /opt/tdx-xwayland/2.6.4): <SDK_install_dir>
You are about to install the SDK to "<SDK_install_dir>". Proceed[Y/n]? Y

C 代码示例1 - 测试X2 MXM_3 管脚输出循环驱动LED亮和灭,时间间隔为1s

源代码参考如下

https://github.com/simonqin09/Apalis_iMX8_Libgpiod/blob/master/gpio-toggle/gpio-toggle.c

主要说明如下

include libgpiod 头文件

#include <gpiod.h>

定义GPIO chip和line 结构体

struct gpiod_chip *output_chip;
struct gpiod_line *output_line;

配置GPIO为输出

output_chip = gpiod_chip_open_by_name(chip);
output_line = gpiod_chip_get_line(output_chip, offset);
gpiod_line_request_output(output_line, "gpio-toggle", GPIOD_LINE_ACTIVE_STATE_HIGH);

切换GPIO输出状态

int line_value = 0; OR ine_value = 1;
gpiod_line_set_value(output_line, line_value);

编译,本示例使用通过命令行编译方式,详细说明请参考这里

export SDK 环境变量

$ source <SDK_install_dir>/environment-setup-aarch64-tdx-linux

编译,因为需要gpiod库,因此要再link选项加入 -lgpiod 选项,否则会出现 ”undefined reference to …” 错误

$ cd <work_dir>
$ ${CC} -Wall -lgpiod -lpthread gpio-toggle.c -o gpio-toggle

部署

$ scp gpio-toggle root@<apalis_imx8_ipaddress>:/home/root

测试,在Apalis iMX8上面运行刚编译部署好的 gpio-toggle 二进制程序,载板上面的LED1 LED灯会1s间隔亮和灭

root@apalis-imx8:~# cd /home/root/
root@apalis-imx8:~# ./gpio-toggle 
Usage by bank/pin number:
      gpio-toggle OUTPUT-BANK-NUMBER OUTPUT-GPIO-NUMBER
root@apalis-imx8:~# ./gpio-toggle 0 9
LED turns ON
LED turns OFF
LED turns ON
LED turns OFF

C 代码示例2 - 测试X2 MXM_5 管脚通过按键输入,捕获上升沿事件后切换X2 MXM_3输出状态驱动LED亮和灭

源代码参考如下

https://github.com/simonqin09/Apalis_iMX8_Libgpiod/blob/master/gpiotest/gpiotest.c

主要说明如下

include libgpiod 头文件

#include <gpiod.h>

定义GPIO chip和line 以及event结构体

struct gpiod_chip *input_chip;
struct gpiod_line *input_line;
struct gpiod_line_event event;

配置GPIO为输入,中断事件为上升沿

input_chip = gpiod_chip_open_by_name(chip);
input_line = gpiod_chip_get_line(input_chip, offset);
gpiod_line_request_rising_edge_events(input_line, "gpiotest");

GPIO输入事件响应

while (1) {
gpiod_line_event_wait(input_line, NULL);

if (gpiod_line_event_read(input_line, &event) != 0)
continue;
/* this should always be a rising event in our example */
if (event.event_type != GPIOD_LINE_EVENT_RISING_EDGE)
continue;
……
}

.编译,本示例使用通过Eclipse IDE工具进行编译,详细Eclipse IDE 下载安装以及SDK配置说明请参考这里

export SDK 环境变量

$ source <SDK_install_dir>/environment-setup-aarch64-tdx-linux

在同一个终端中启动Eclopse IDE

$ cd <Eclipse_install_dir>
$ ./eclipse

在Eclipse IDE下参考上面的说明文档创建新的C/cross compile工程,并按照说明文档配置好SDK。同样因为linker需要gpiod相关库,因此要在下面位置增加相关库

菜单:

Project --> Properties

选项卡:

C/C++ Build --> Settings

选项:

Cross GCC Linker --> Libraries

Libraries (-l) 栏目下面增加:

gpiod

Eclipse 点击build进行编译,根据选择是Debug还是Release,编译后的二进制文件会在对应的目录,本文使用Debug

部署

$ cd <Eclipse_workspace>/gpiotest/Debug
$ scp gpiotest root@<apalis_imx8_ipaddress>:/home/root

.测试,在Apalis iMX8上面运行刚编译部署好的 gpiotest 二进制程序,载板上面的LED1 LED灯初始状态为灭,随着按下SW5按键交替亮和灭

root@apalis-imx8:~# cd /home/root/
root@apalis-imx8:~# ./gpiotest
Usage by bank/pin number:
      gpio-test INPUT-BANK-NUMBER INPUT-GPIO-NUMBER OUTPUT-BANK-NUMBER OUTPUT-GPIO-NUMBER
root@apalis-imx8:~# ./gpiotest 0 12 0 9
LED initial status is OFF
button pressed 1 times
LED turns ON
button pressed 2 times
LED turns OFF
button pressed 3 times
LED turns ON

总结

本文基于NXP iMX8示例了嵌入式Linux使用Libgpiod API来驱动GPIO,通过示例可见其直观性和便利性均大大优于传统的 sysfs API,因此会被 Linux Kernel Community确定为 sysfs API的替代者。

作者: 秦海,技术销售工程师,韬睿(上海)

评论

Please login to leave a comment!
Have a Question?