打印
[技术问答]

在用户应用中使用GPIO

[复制链接]
3804|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
eefas|  楼主 | 2024-4-25 21:04 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

开发板中提供了两组共四个直连到GPIO口上的轻触按钮和发光二极管,可以方便地进行GPIO功能测试:

这篇文章主要是在下面文章基础上进行的:
《新唐NUC980使用记录:访问以太网(LAN8720A) & 启用SSH》

基础说明

默认情况下NUC980官方的内核是配置启用了sysfs文件系统GPIO支持的:

所以可以通过读写 /sys/class/gpio/ 目录下指定GPIO口编号的文件来操作GPIO口。GPIO口编号换算如下:
PB13 = 32 x 1(PA) + 13 = 45
PF10 = 32 x 5(PA/PB/PC/PD/PE) + 10 = 170
PE10 = 32 x 4(PA/PB/PC/PD) + 10 = 138
PE12 = 32 x 4(PA/PB/PC/PD) + 12 = 140

需要注意的是默认情况下PE10和PE12是被设置成USB相关功能的,需要修改内核进行调整:

在终端中操作

基于上面内容我们就可以直接在终端中操作GPIO口了:

# 导出以使用GPIO45echo 45 > /sys/class/gpio/export# 导出后将在 /sys/class/gpio/ 目录下出现 gpio45 目录,读写其中的文件即可操作该GPIO口# 将GPIO45设置为输出模式echo out > /sys/class/gpio/gpio45/direction# 将GPIO45设置为输出高电平echo 1 > /sys/class/gpio/gpio45/value# 将GPIO45设置为输出低电平echo 0 > /sys/class/gpio/gpio45/value# ====================# 导出以使用GPIO140echo 140 > /sys/class/gpio/export# 将GPIO140设置为输入模式echo in > /sys/class/gpio/gpio140/direction# 打印GPIO140端口电平cat /sys/class/gpio/gpio140/value# ====================# 取消使用GPIO45echo 45 > /sys/class/gpio/unexport# 取消使用GPIO140echo 140 > /sys/class/gpio/unexport


上面演示中按钮按下和松开时可以读取到不同的电平值。

使用程序操作

除了在终端中使用,也可以通过程序进行操作:

cd ~/nuc980-sdk/mkdir -p apps/gpiocd apps/gpio/gedit main.c

在 main.c 文件中写入下面代码:

#include <stdlib.h>#include <unistd.h>int main(void){    int i;    system("echo 45 > /sys/class/gpio/export");    system("echo out > /sys/class/gpio/gpio45/direction");    for (i = 0; i < 8; i++)    {        system("echo 0 > /sys/class/gpio/gpio45/value");        sleep(1);        system("echo 1 > /sys/class/gpio/gpio45/value");        sleep(1);    }    system("echo 45 > /sys/class/gpio/unexport");    return 0;}

编译生成程序并拷贝到开发板中:

export PATH=$PATH:/home/nx/nuc980-sdk/buildroot-2022.02.3/output/host/binarm-linux-gcc main.c# 开发板启用了SSH的话可以使用SCP命令将程序通过网络拷贝到开发板中scp a.out root@192.168.31.142:/root/

在开发板上运行程序:

/root/a.out

上面代码也可以使用下面这种传统的文件操作方式:

#include <string.h>#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#define OPEN_FILE(fd, file, flag) \    (fd) = open((file), (flag)); \    if ((fd) < 0) { printf("open %s failed!\n", (file)); return -1; }#define WRITE_FILE(fd, str) \    if(write((fd), (str), strlen(str)) != strlen(str)) \    { printf("write %s failed!\n", (str)); return -1; }int main(void){    int i, fd;    if(access("/sys/class/gpio/gpio45/", F_OK))    {        OPEN_FILE(fd, "/sys/class/gpio/export", O_WRONLY);        WRITE_FILE(fd, "45");        close(fd);    }    OPEN_FILE(fd, "/sys/class/gpio/gpio45/direction", O_WRONLY);    WRITE_FILE(fd, "out");    close(fd);        OPEN_FILE(fd, "/sys/class/gpio/gpio45/value", O_RDWR);    for (i = 0; i < 8; i++)    {        lseek(fd, 0, SEEK_SET); // 移动文件操作指针到文件开头        WRITE_FILE(fd, "0");        sleep(1);        lseek(fd, 0, SEEK_SET); // 移动文件操作指针到文件开头        WRITE_FILE(fd, "1");        sleep(1);    }    close(fd);    OPEN_FILE(fd, "/sys/class/gpio/unexport", O_WRONLY);    WRITE_FILE(fd, "45");    close(fd);    return 0;}

上面的C语言程序都是输出控制LED的,也可以用程序来读取按键输入电平:

#include <string.h>#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#define OPEN_FILE(fd, file, flag) \    (fd) = open((file), (flag)); \    if ((fd) < 0) { printf("open %s failed!\n", (file)); return -1; }#define WRITE_FILE(fd, str) \    if(write((fd), (str), strlen(str)) != strlen(str)) \    { printf("write %s failed!\n", (str)); return -1; }#define READ_FILE(fd, bufptr, size) \    if (read((fd), bufptr, size) != size) \    { printf("read failed!\n"); return -1; }    int main(void){    int i, fd;    char value[2] = {0};    if(access("/sys/class/gpio/gpio140/", F_OK))    {        OPEN_FILE(fd, "/sys/class/gpio/export", O_WRONLY);        WRITE_FILE(fd, "140");        close(fd);    }    OPEN_FILE(fd, "/sys/class/gpio/gpio140/direction", O_WRONLY);    WRITE_FILE(fd, "in");    close(fd);    OPEN_FILE(fd, "/sys/class/gpio/gpio140/value", O_RDWR);    for (i = 0; i < 8; i++)    {        lseek(fd, 0, SEEK_SET); // 移动文件操作指针到文件开头        READ_FILE(fd, value, 1);        printf("value is %s\n", value);        sleep(2);    }    close(fd);    OPEN_FILE(fd, "/sys/class/gpio/unexport", O_WRONLY);    WRITE_FILE(fd, "140");    close(fd);    return 0;}


在程序运行过程中按下按钮可以看到输出的值改变。

上面对于输入值获取操作属于轮询方式,也可以使用中断方式来获取(开发板上两个按键的引脚都是有外部中断功能的):

#include <string.h>#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include <poll.h> #define OPEN_FILE(fd, file, flag) \    (fd) = open((file), (flag)); \    if ((fd) < 0) { printf("open %s failed!\n", (file)); return -1; }#define WRITE_FILE(fd, str) \    if(write((fd), (str), strlen(str)) != strlen(str)) \    { printf("write %s failed!\n", (str)); return -1; }#define READ_FILE(fd, bufptr, size) \    if (read((fd), bufptr, size) != size) \    { printf("read failed!\n"); return -1; }    int main(void){    int i, fd, ret;    char value[2] = {0};    struct pollfd fds[1];    nfds_t nfds = 1;        if(access("/sys/class/gpio/gpio140/", F_OK))    {        OPEN_FILE(fd, "/sys/class/gpio/export", O_WRONLY);        WRITE_FILE(fd, "140");        close(fd);    }    OPEN_FILE(fd, "/sys/class/gpio/gpio140/direction", O_WRONLY);    WRITE_FILE(fd, "in");    close(fd);    OPEN_FILE(fd, "/sys/class/gpio/gpio140/edge", O_WRONLY); // edge文件用于设置外部中断触发方式    // none 无; rising 上升沿触发; falling 下降沿触发(实际测试有点问题); both 双边触发    WRITE_FILE(fd, "rising"); // 使能下降沿中断    close(fd);    OPEN_FILE(fd, "/sys/class/gpio/gpio140/value", O_RDWR | O_NONBLOCK);    READ_FILE(fd, value, 1); // 先读取一次,以免触发第一次不期望的中断    fds[0].fd = fd;    fds[0].events = POLLPRI;    for (i = 0; i < 8; i++)    {        ret = poll(fds, nfds, 5000);        // ret = poll(fds, nfds, -1); // timeout=-1 无限等待        if (ret > 0)        {            // 这里返回的fds[0].revents其实是 POLLERR | POLLPRI            if (fds[0].revents & POLLPRI)            {                lseek(fd, 0, SEEK_SET); // 移动文件操作指针到文件开头                READ_FILE(fd, value, 1);                printf("value is %s\n", value);            }        }        else if (ret == 0)        {            printf("timeout!\n");        }        else        {            printf("poll error!\n");        }    }    close(fd);    OPEN_FILE(fd, "/sys/class/gpio/unexport", O_WRONLY);    WRITE_FILE(fd, "140");    close(fd);    return 0;}

总结

GPIO是最基础的外设,使用频率非常高,同时在用户应用中使用GPIO也是比较简单的,基础的使用参考上面这些内容就差不多了。


使用特权

评论回复
沙发
黑心单片机| | 2024-4-28 15:30 | 只看该作者
代码太乱了,没法看

使用特权

评论回复
板凳
Henryko| | 2024-5-8 21:29 | 只看该作者
确实代码格式乱了

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

82

主题

2855

帖子

2

粉丝