【STM32MP157应用编程】2.GPIO输入、输出、中断

目录

GPIO文件

指令操作GPIO

程序操作GPIO

程序说明

程序代码

2_GPIO_4.c

启动交叉编译工具

编译

拷贝到开发板

测试

GPIO文件

在/sys/class/gpio目录下,存放了GPIO的文件。

 

 gpiochipX:当前SoC所包含的GPIO控制器,STM32MP157一共包含了12个GPIO控制器。

GPIO组GPIO控制器
GPIOAgpiochip0
GPIOBgpiochip16
GPIOCgpiochip32
GPIODgpiochip48
GPIOEgpiochip64
GPIOFgpiochip80
GPIOGgpiochip96
GPIOHgpiochip112
GPIOIgpiochip128
GPIOJgpiochip144
GPIOKgpiochip160
GPIOZgpiochip176

 每个gpiochipX中都有base、label、ngpio这三个重要的文件。

 

       base:该控制器所管理的这组GPIO引脚中最小的编号。每一个GPIO引脚都会有一个对应的编号,Linux下通过这个编号来操控对应的GPIO引脚。

cat base

 label:该组GPIO对应的标签,也就是名字。

cat label

 ngpio:该控制器所管理的GPIO引脚的数量(引脚编号范围:base ~ base+ngpio-1)。

cat ngpio

 

引脚编号的计算:如PC13。

              |-> GPIOC组 -> gpiochip32 -> 32 ->|

PC13 ->|                                                         |-> 32+13=45

              |-> IO13 -> 13号引脚 -> 13 --------->|

        export:用于将指定编号的GPIO引脚导出。在使用GPIO引脚之前,需要将其导出,导出成功之后才能使用它。export文件是只写文件,不能读取,将一个指定的引脚编号写入到export文件中即可将对应的GPIO引脚导出。

echo 0 > export

 

       gpio0为导出的引脚编号为0的目录,用于管理、控制该GPIO引脚。

       unexport:将导出的GPIO引脚删除,也就是取消导出的GPIO。当使用完GPIO引脚之后,需要将导出的引脚删除,同样该文件也是只写文件、不可读。

echo 0 > unexport

 

       注意:不是所有GPIO引脚都可以成功导出,如果对应的GPIO已经在内核中被使用了,那便无法成功导出。

指令操作GPIO

       direction:配置GPIO引脚为输入或输出模式。该文件可读、可写,读表示查看GPIO当前是输入还是输出模式,写表示将GPIO配置为输入或输出模式;读取或写入操作可取的值为“out”(输出模式)和“in”(输入模式)。

       将引脚的模式设置为输出模式。

cat direction
echo "out" > direction
cat direction

 

        value:在GPIO配置为输出模式下,向value文件写入“0”控制GPIO引脚输出低电平,写入“1”控制GPIO引脚输出高电平。在输入模式下,读取value文件获取GPIO引脚当前的输入电平状态。

       获取引脚的输入电平。

echo "in" > direction
cat value
cat value

 控制GPIO引脚输出高电平。

echo "out" > direction
echo "1" > value

 active_low:这个属性文件用于控制极性,可读可写,默认情况下为0。

设置正常极性。

echo "0" > active_low
echo "out" > direction
echo "1" > value	#输出高电平
echo "0" > value 	#输出低电平

 

 设置反向极性。

echo "1" > active_low
echo "out" > direction
echo "1" > value	#输出低电平
echo "0" > value	#输出高电平

 

       edge:控制中断的触发模式,该文件可读可写。在配置GPIO引脚的中断触发模式之前,需将其设置为输入模式。

       配置为非中断触发。

echo "none" > edge

 配置为上升沿触发。

echo "rising" > edge

 

 配置为下降沿触发。

echo "falling" > edge

 

 配置为边沿触发。

echo "both" > edge

 

程序操作GPIO

程序说明

 ./xxx --YinJiao 参数1 --MoShi ShuChu --DianPing 参数2

引脚输出电平。

参数1:引脚。

                PA0~PZ15。

参数2:电平。

                0:低电平。

                1:高电平。 

./xxx --YinJiao 参数1 --MoShi ShuRu

引脚获取输入的电平。

参数1:引脚。

                PA0~PZ15。

./xxx --YinJiao 参数1 --MoShi ZhongDuan --ChuFa_MoShi 参数2

引脚中断。

参数1:引脚。

                PA0~PZ15。

参数2:中断的触发模式。

                none:非中断。

                rising:上升沿触发。

                falling:下降沿触发。

                both:边沿触发。

程序代码

2_GPIO_4.c

/*
        GPIO输入、输出、中断综合
*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <poll.h>

//      ./xxx --YinJiao PA0 --MoShi ShuChu --DianPing 0
//      ./xxx --YinJiao PA0 --MoShi ShuRu
//      ./xxx --YinJiao PA0 --MoShi ZhongDuan --ChuFa_MoShi rising

char GPIO_Path[100];

int main(int argc, char *argv[])
{
    //校验传参
    if (argc != 5 && argc != 7)
    {
        printf("%s文件的参数错误。\n", argv[0]);
        return -1;
    }

    //引脚
    char YinJiaoZu = 0;       //引脚组
    int YinJiao_BianHao = -1; //引脚编号
    char num_str[3];
    int num = 0;
    if (!strcmp(argv[1], "--YinJiao"))
    {
        //argv[2] PA0-PA15
        if (strlen(argv[2]) == 3)
        {
            YinJiaoZu = argv[2][1];
            if (YinJiaoZu == 'Z')
            {
                YinJiao_BianHao = (12 - 1) * 16;
                if (isdigit(argv[2][2])) //isdigit()判断字符是否是数字
                {
                    num_str[0] = argv[2][2];
                    num_str[1] = '\0';
                    sscanf(num_str, "%d", &num); // 将提取的数字转化为整型
                    YinJiao_BianHao = YinJiao_BianHao + num;
                }
            }
            else if (YinJiaoZu >= 'A' && YinJiaoZu <= 'K')
            {
                YinJiao_BianHao = (YinJiaoZu - 65) * 16;
                if (isdigit(argv[2][2])) //isdigit()判断字符是否是数字
                {
                    num_str[0] = argv[2][2];
                    num_str[1] = '\0';
                    sscanf(num_str, "%d", &num); // 将提取的数字转化为整型
                    YinJiao_BianHao = YinJiao_BianHao + num;
                }
            }
            else
            {
                printf("%s参数错误。\n", argv[2]);
                return -1;
            }
        }
        else if (strlen(argv[2]) == 4)
        {
            YinJiaoZu = argv[2][1];
            if (YinJiaoZu == 'Z')
            {
                YinJiao_BianHao = (12 - 1) * 16;
                if (isdigit(argv[2][2]) && isdigit(argv[2][3])) //isdigit()判断字符是否是数字
                {
                    num_str[0] = argv[2][2];
                    num_str[1] = argv[2][3];
                    num_str[2] = '\0';
                    sscanf(num_str, "%d", &num); // 将提取的数字转化为整型
                    YinJiao_BianHao = YinJiao_BianHao + num;
                }
            }
            else if (YinJiaoZu >= 'A' && YinJiaoZu <= 'K')
            {
                YinJiao_BianHao = (YinJiaoZu - 65) * 16;
                if (isdigit(argv[2][2]) && isdigit(argv[2][3])) //isdigit()判断字符是否是数字
                {
                    num_str[0] = argv[2][2];
                    num_str[1] = argv[2][3];
                    num_str[2] = '\0';
                    sscanf(num_str, "%d", &num); // 将提取的数字转化为整型
                    YinJiao_BianHao = YinJiao_BianHao + num;
                }
            }
            else
            {
                printf("%s参数错误。\n", argv[2]);
                return -1;
            }
        }
        else
        {
            printf("%s参数错误。\n", argv[2]);
            return -1;
        }

        //判断引脚GPIO是否导出
        sprintf(GPIO_Path, "/sys/class/gpio/gpio%d", YinJiao_BianHao);
        if (access(GPIO_Path, F_OK))
        { //如果目录不存在 则需要导出
            int fd;
            char YinJiao_BianHao_str[5];
            if (0 > (fd = open("/sys/class/gpio/export", O_WRONLY)))
            {
                perror("打开export错误!\n");
                return -1;
            }

            sprintf(YinJiao_BianHao_str, "%d", YinJiao_BianHao);
            if (write(fd, YinJiao_BianHao_str, strlen(YinJiao_BianHao_str)) == -1)
            { //导出 gpio
                perror("导出引脚GPIO错误!\n");
                close(fd);
                exit(-1);
            }
            close(fd); //关闭文件
        }
    }
    else
    {
        printf("%s选项错误。\n", argv[1]);
        return -1;
    }

    //模式
    int fd_direction;
    int fd_active_low;
    int fd_edge;
    int fd_value;
    char direction_path[100];  //direction路径
    char active_low_path[100]; //active_low路径
    char edge_path[100];       //edge路径
    char value_path[100];      //value路径
    char value;
    if (!strcmp(argv[3], "--MoShi"))
    {
        if (!strcmp(argv[4], "ShuChu")) //输出
        {
            //校验电平
            if (strcmp(argv[5], "--DianPing"))
            {
                printf("%s选项错误。\n", argv[5]);
                return -1;
            }
            if (strcmp(argv[6], "0") && strcmp(argv[6], "1"))
            {
                printf("%s参数错误。\n", argv[6]);
                return -1;
            }

            //配置输出模式
            sprintf(direction_path, "%s/%s", GPIO_Path, "direction");
            if (0 > (fd_direction = open(direction_path, O_WRONLY)))
            {
                perror("打开direction错误!\n");
                close(fd_direction);
                return -1;
            }
            if (strlen("out") != write(fd_direction, "out", strlen("out")))
            {
                perror("设置模式错误!\n");
                close(fd_direction);
                return -1;
            }

            //配置极性
            sprintf(active_low_path, "%s/%s", GPIO_Path, "active_low");
            if (0 > (fd_active_low = open(active_low_path, O_WRONLY)))
            {
                perror("打开active_low错误!\n");
                close(fd_active_low);
                return -1;
            }
            if (strlen("0") != write(fd_active_low, "0", strlen("0")))
            {
                perror("设置极性错误!\n");
                close(fd_active_low);
                return -1;
            }

            //配置为非中断方式
            sprintf(edge_path, "%s/%s", GPIO_Path, "edge");
            if (0 > (fd_edge = open(edge_path, O_WRONLY)))
            {
                perror("打开edge错误!\n");
                close(fd_edge);
                return -1;
            }
            if (strlen("none") != write(fd_edge, "none", strlen("none")))
            {
                perror("设置非中断方式错误!\n");
                close(fd_edge);
                return -1;
            }

            //输出电平
            sprintf(value_path, "%s/%s", GPIO_Path, "value");
            if (0 > (fd_value = open(value_path, O_WRONLY)))
            {
                perror("打开value错误!\n");
                close(fd_value);
                return -1;
            }
            if (strlen(argv[6]) != write(fd_value, argv[6], strlen(argv[6])))
            {
                perror("输出电平错误!\n");
                close(fd_value);
                return -1;
            }
            printf("输出的电平:%s\n", argv[6]);
            close(fd_value);
        }
        else if (!strcmp(argv[4], "ShuRu")) //输入
        {
            //配置输入模式
            sprintf(direction_path, "%s/%s", GPIO_Path, "direction");
            if (0 > (fd_direction = open(direction_path, O_WRONLY)))
            {
                perror("打开direction错误!\n");
                close(fd_direction);
                return -1;
            }
            if (strlen("in") != write(fd_direction, "in", strlen("in")))
            {
                perror("设置模式错误!\n");
                close(fd_direction);
                return -1;
            }

            //配置极性
            sprintf(active_low_path, "%s/%s", GPIO_Path, "active_low");
            if (0 > (fd_active_low = open(active_low_path, O_WRONLY)))
            {
                perror("打开active_low错误!\n");
                close(fd_active_low);
                return -1;
            }
            if (strlen("0") != write(fd_active_low, "0", strlen("0")))
            {
                perror("设置极性错误!\n");
                close(fd_active_low);
                return -1;
            }

            //配置为非中断方式
            sprintf(edge_path, "%s/%s", GPIO_Path, "edge");
            if (0 > (fd_edge = open(edge_path, O_WRONLY)))
            {
                perror("打开edge错误!\n");
                close(fd_edge);
                return -1;
            }
            if (strlen("none") != write(fd_edge, "none", strlen("none")))
            {
                perror("设置非中断方式错误!\n");
                close(fd_edge);
                return -1;
            }

            //读取电平状态
            sprintf(value_path, "%s/%s", GPIO_Path, "value");
            if (0 > (fd_value = open(value_path, O_RDONLY)))
            {
                perror("打开value错误!\n");
                close(fd_value);
                return -1;
            }
            if (0 > read(fd_value, &value, 1))
            {
                perror("读取value错误!\n");
                close(fd_value);
                return -1;
            }
            printf("输入的电平:%c\n", value);
            close(fd_value);
        }
        else if (!strcmp(argv[4], "ZhongDuan")) //中断
        {
            //校验触发模式
            if (strcmp(argv[5], "--ChuFa_MoShi"))
            {
                printf("%s选项错误。\n", argv[5]);
                return -1;
            }
            if (strcmp(argv[6], "rising") && strcmp(argv[6], "falling") && strcmp(argv[6], "both"))
            {
                printf("%s参数错误。\n", argv[6]);
                return -1;
            }

            //配置输入模式
            sprintf(direction_path, "%s/%s", GPIO_Path, "direction");
            if (0 > (fd_direction = open(direction_path, O_WRONLY)))
            {
                perror("打开direction错误!\n");
                close(fd_direction);
                return -1;
            }
            if (strlen("in") != write(fd_direction, "in", strlen("in")))
            {
                perror("设置模式错误!\n");
                close(fd_direction);
                return -1;
            }

            //配置极性
            sprintf(active_low_path, "%s/%s", GPIO_Path, "active_low");
            if (0 > (fd_active_low = open(active_low_path, O_WRONLY)))
            {
                perror("打开active_low错误!\n");
                close(fd_active_low);
                return -1;
            }
            if (strlen("0") != write(fd_active_low, "0", strlen("0")))
            {
                perror("设置极性错误!\n");
                close(fd_active_low);
                return -1;
            }

            //配置触发方式
            sprintf(edge_path, "%s/%s", GPIO_Path, "edge");
            if (0 > (fd_edge = open(edge_path, O_WRONLY)))
            {
                perror("打开edge错误!\n");
                close(fd_edge);
                return -1;
            }
            if (strlen(argv[6]) != write(fd_edge, argv[6], strlen(argv[6])))
            {
                perror("设置中断方式错误!\n");
                close(fd_edge);
                return -1;
            }

            //中断触发
            struct pollfd pfd;
            int ret;
            sprintf(value_path, "%s/%s", GPIO_Path, "value");
            if (0 > (pfd.fd = open(value_path, O_RDONLY)))
            {
                perror("打开value错误!\n");
                close(pfd.fd);
                return -1;
            }
            pfd.events = POLLPRI; //events:等待的事件。POLLPRI:调用方对高优先级数据事件感兴趣,并且poll()函数将等到发生此类事件。
            read(pfd.fd, &value, 1);
            while (1)
            {
                ret = poll(&pfd, 1, -1);
                if (ret < 0)
                {
                    perror("轮询错误!\n");
                    return -1;
                }
                else if (ret == 0)
                {
                    printf("轮询超时!\n");
                    continue;
                }
                if (pfd.revents & POLLPRI) //校验高优先级可读
                {
                    //文件读取位置移到头部
                    if (lseek(pfd.fd, 0, SEEK_SET) < 0)
                    {
                        perror("文件读取位置移到头部错误!\n");
                        return -1;
                    }
                    if (read(pfd.fd, &value, 1) < 0)
                    {
                        perror("读取value错误!\n");
                        return -1;
                    }
                    printf("GPIO中断触发,value=%c\n", value);
                }
            }
        }
    }
    else
    {
        printf("%s选项错误。\n", argv[3]);
        return -1;
    }
    return 0;
}

启动交叉编译工具

source /opt/st/stm32mp1/3.1-snapshot/environment-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi

编译

${CC} -o 2_GPIO_4 2_GPIO_4.c

拷贝到开发板

scp 2_GPIO_4 root@10.3.22.219:/home/root/Linux_C_YingYong_BianCheng/JiaoCheng/2_GPIO/

 

测试

使用PE4引脚进行测试,PE4引脚处于悬空状态,电平状态不确定。

PE4输出高电平。

./2_GPIO_4 --YinJiao PE4 --MoShi ShuChu --DianPing 1

 

 PE4输出低电平。

./2_GPIO_4 --YinJiao PE4 --MoShi ShuChu --DianPing 0

 

 PE4获取输入的电平。

./2_GPIO_4 --YinJiao PE4 --MoShi ShuRu

 

 PE4上升沿中断。

./2_GPIO_4 --YinJiao PE4 --MoShi ZhongDuan --ChuFa_MoShi rising

 PE4下降沿中断。

./2_GPIO_4 --YinJiao PE4 --MoShi ZhongDuan --ChuFa_MoShi falling

 

 PE4边沿中断。

./2_GPIO_4 --YinJiao PE4 --MoShi ZhongDuan --ChuFa_MoShi both

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

因心,三人水

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值