目录
GPIO文件
指令操作GPIO
程序操作GPIO
程序说明
程序代码
2_GPIO_4.c
启动交叉编译工具
编译
拷贝到开发板
测试
GPIO文件
在/sys/class/gpio目录下,存放了GPIO的文件。
gpiochipX:当前SoC所包含的GPIO控制器,STM32MP157一共包含了12个GPIO控制器。
GPIO组 | GPIO控制器 |
---|---|
GPIOA | gpiochip0 |
GPIOB | gpiochip16 |
GPIOC | gpiochip32 |
GPIOD | gpiochip48 |
GPIOE | gpiochip64 |
GPIOF | gpiochip80 |
GPIOG | gpiochip96 |
GPIOH | gpiochip112 |
GPIOI | gpiochip128 |
GPIOJ | gpiochip144 |
GPIOK | gpiochip160 |
GPIOZ | gpiochip176 |
每个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 risingchar 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-PA15if (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){ //导出 gpioperror("导出引脚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