Menu Close

基于模版实现 linux 下 GPIO 操作 LED 灯

此文章介绍了如何基于模板使用 GPIO 操作点亮并熄灭开发板上的 LED 灯,以及如何使用按键控制。

如果想查看官方的资料和例子,可以访问官网

相关参考文章:SOC 教学教案

 

LED 输出

如果想在 Shell 中操作 GPIO,我们需要先进入到 gpio 目录中。

cd /sys/class/gpio

我们使用 ls 指令查看内容会发现有一个 gpiochip。应该记住我们的 zynq GPIO EMIO 的前 54 个 GPIO pin 供给 MIO 用,54 后的 GPIO pin 供给 EMIO 用。我们也应该知道 Zynq 的第一个 pin 引脚从 906 开始,并且结束在 1023。因为我们要使用 EMIO,我们将在显示的 gpiochip906 pin 引脚上再加上 54,也就是 960。

%title插图%num

接下来我们将演示如何输出一个 GPIO pin 引脚,读取该引脚的 direction 和 value 值,并且把 direction 设为 output 输出以及写入数值 1。

 

输出引脚的命令如下。注意您可以使用 960 至 967 之间的任何引脚因为他们都分别连接到开发板上的 8 个 LED 灯。

echo 960 > /sys/class/gpio/export

如果想查看所有可能的选项,可以使用以下命令:

ls /sys/class/gpio/gpio960/

%title插图%num

把 direction 输入/输出值改为输出 out 的命令如下:

echo out > /sys/class/gpio/gpio960/direction

设置点亮/熄灭 LED 的 value 值的命令如下。因为我们要点亮灯,所以我们将其设为 1。

echo 1 > /sys/class/gpio/gpio960/value

如果想查看 direction 或 value 的值,可以使用 cat 命令。

cat /sys/class/gpio/gpio960/direction
cat /sys/class/gpio/gpio960/value

当然,如果您已经使用 cd 在 /sys/class/gpio/gpio960/ 目录中,只输入 cat direction 和 cat value 即可。

%title插图%num

可以看到 direction 被设为了 out,并且 value 被设为了 1。看开发板会发现有一个 LED 灯亮了。

 

我们现在可以结合以上知识编写一个简单的 LED 应用。

应用代码:ledinputapp

应用代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main()
{
    int valuefd, exportfd, directionfd;

    printf("GPIO test running...\n");

    // The GPIO has to be exported to be able to see it
    // in sysfs
    while (1){
    exportfd = open("/sys/class/gpio/export", O_WRONLY);
    if (exportfd < 0)
    {
        printf("Cannot open GPIO to export it\n");
        exit(1);
    }

    // Determine Entered GPIO
    char lednum[2];
    printf("Enter the light you want to turn on/off (1-8): ");
    scanf("%s", &lednum);

    int gpiocount = 959 + atoi(lednum);    // atoi converts string to int

    //printf("int value of gpiocount is: %d\n", gpiocount);

    char writegpio[4];
    sprintf(writegpio, "%d", gpiocount);    // sprintf converts int to string

    write(exportfd, writegpio, 4);
    close(exportfd);

    printf("GPIO exported successfully\n");

    // Update the direction of the GPIO to be an output

    char defaddress[23] = "/sys/class/gpio/gpio";    // create address base
    strcat(defaddress, writegpio);    // creates address for the specified GPIO

    char directionaddress[33] = "";
    strcat(directionaddress, defaddress);    // increase address size for address

    char direction[] = "/direction";
    strcat(directionaddress, direction);    // create direction address

    directionfd = open(directionaddress, O_RDWR);
    if (directionfd < 0)
    {
        printf("Cannot open GPIO direction it\n");
        exit(1);
    }

    write(directionfd, "out", 4);    // set direction as out
    close(directionfd);

    printf("GPIO direction set as output successfully\n");

    // Get the GPIO value ready to be turned on/off

    char valueaddress[29] = "";
    strcat(valueaddress, defaddress);    // increase address size for value

    char value[] = "/value";
    strcat(valueaddress, value);    // create value address

    valuefd = open(valueaddress, O_RDWR);
    if (valuefd < 0)
    {
        printf("Cannot open GPIO value\n");
        exit(1);
    }

    printf("GPIO value opened\n");
    // toggle the GPIO

    char ledvalue;
    printf("Turn on or off? (0/1): ");
    scanf("%d", &ledvalue);

    if (ledvalue == 1){
        write(valuefd,"1", 2);
        printf("value set to 1\n");
    }
    else if (ledvalue == 0){
        write(valuefd,"0", 2);
        printf("value set to 0\n");
    }
    else{
        printf("invalid value\n");
        }
    }
    return 0;
}

我在应用中多次使用 strcat 合并字符串让程序使每一个用户指定的 GPIO 都可以输出相应的引脚,并对其进行操作。查看更详细的解释请看代码注释。

编写后,可以使用 PetaLinux 编译并使用 RCP 传送到开发板上。

编译命令如下。注意根据您实际的文件名和路径修改指令。

arm-linux-gnueabihf-gcc input_app.c -o input_test_app.o

RCP 命令如下。注意根据实际情况更改,注意使用开发板的 IP 地址。通常开发板的用户和密码都是 “root”。

rcp /home/ubuntu/petalinux_app_dev_tests/GPIO_tests/input_test_app.o root@192.168.0.88:/mnt/sd1

注意在运行该命令之前要先在开发板系统中挂载上 SD 卡。在我的情况下,在 /mnt 目录下创建了 sd1 文件夹,并把 SD 卡的第一分区挂了上去。

%title插图%num

如果您的 RCP 命令给出了以下错误信息,是因为主机密钥出了问题。执行它提供的命令即可修复。之后再运行 RCP 命令。

%title插图%num

成功传送后可以在开发板中运行。

%title插图%num

找到文件并打开即可运行,运行过程中可以指定开发板上 8 个 LED 的任意一个,并使其点亮或熄灭。

因为程序跑在一个无限循环中,按下 [Ctrl] + c 即可退出。

 

按键输入

如果我们看 LED 旁边可以扳动的开关的话,我们可以先使用它们测试输入。

可以先使用 cat 命令查看当前的 direction 值是输入还是输出。如果是 out,您会发现扳动开关对 LED 没有任何影响。

如果我们将其设为 in,会发现扳动开关会点亮并熄灭 LED。在 LED 灭着的时候查看 value 值会发现它是 0,在亮着的时候会发现是 1。

%title插图%num

下面我们可以看一下 LED 旁边的 7 个按钮。

如果我们仔细看内核编译的 TOP.v 文件的话,会看到以下两行命令。

assign led = GPIO_EMIO_tri_io[7:0];
assign key_but = GPIO_EMIO_tri_io[14:8];

表示 960 后面的 0 到 7 个分配给了 LED,8 到 14 个分配给了按键。这也就表明 968 至 974 给按键使用。排序从左到右,从上到下。

%title插图%num

跟 LED 的 GPIO 一样,我们输出一个可以用的引脚。我们选择使用第一个按键,也就是左上角的那一个。相对应的引脚是 968。

在 gpio 目录下输入命令。

echo 968 > /sys/class/gpio/export

使用 ls 可以看到成功添加了引脚,接下来我们将使用 cd 进入到该目录中。

使用 cat 命令查看 direction 可以看到已经被设为了输入。如果被设为输出您可以将此设为输入。

设为输入后,可以看到他的 value 值为 0,因为它没有被按。

在按着按钮的同时再次运行 cat 命令查看 value,会发现是 1。

%title插图%num

我们可以根据以上信息为按键和 LED 编写一个应用。按下一个按键就随之点亮一个LED。

应用代码:buttonapp

代码如下。

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main()
{
    int valuefd, exportfd, directionfd, ledfd;

    printf("GPIO test running...\n");

    // The GPIO has to be exported to be able to see it
    // in sysfs

    while (1){

        for (int a = 960; a <= 974; a++){

            exportfd = open("/sys/class/gpio/export", O_WRONLY);
            if (exportfd < 0)
            {
                printf("Cannot open GPIO to export it\n");
                exit(1);
            }

            char pin[4];
            sprintf(pin, "%d", a);
            write(exportfd, pin, 4);
            close(exportfd);

        }

        for (int i = 968; i <= 974; i++){
            exportfd = open("/sys/class/gpio/export", O_WRONLY);
            if (exportfd < 0)
            {
                printf("Cannot open GPIO to export it\n");
                exit(1);
            }

            char pin[4];
            sprintf(pin, "%d", i);
            write(exportfd, pin, 4);
            close(exportfd);

            char address[30] = "/sys/class/gpio/gpio";
            strcat(address, pin);
            strcat(address, "/value");
            valuefd = open(address, O_RDWR);
            if (valuefd < 0)
            {
                printf("Cannot open GPIO value\n");
                exit(1);
            }

            lseek(valuefd, 0, SEEK_SET);
            char readvalue[2];
            read(valuefd, readvalue, 2);
            int intvalue = atoi(readvalue);


            // For the LED
            char pin2[4];
            sprintf(pin2, "%d", i - 7);
            char address2[30] = "/sys/class/gpio/gpio";
            strcat(address2, pin2);
            strcat(address2, "/value");

            char address3[34] = "/sys/class/gpio/gpio";
            strcat(address3, pin2);
            strcat(address3, "/direction");

            directionfd = open(address3, O_RDWR);
            if (directionfd < 0)
            {
                printf("Cannot open GPIO direction it\n");
                exit(1);
            }

            write(directionfd, "out", 4);
            close(directionfd);

            ledfd = open(address2, O_RDWR);

            if (ledfd < 0)
            {
                printf("Cannot open led GPIO value\n");
                exit(1);
            }

            if(intvalue == 1){
                char test1[2];
                char test2[2];

                lseek(ledfd, 0, SEEK_SET);
                write(ledfd, "1", 2);
            }

            close(valuefd);
            close(ledfd);
        }

    }
    return 0;
}

因为是个无限循环,运行起来后按下 [Ctrl] + c 即可。

Posted in SOC, 教材与教案

发表评论

相关链接