目录
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 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