1、概述
本文主要讲述在应用层控制gpio,内核的设备树修改在此不做说明。
2、原理
1.设备树添加对应IO;
2.在应用层设置对应IO的配置;
3.打开对应IO的文件,写入或者读取对应IO目录下的“value”的文件;
3、代码
.h文件
#ifndef _LED_H_
#define _LED_H_
#define FALSE -1
#define TRUE 0
enum
{
LED0=0,
LED1,
LED2,
LED3,
LED4,
LED5,
LED6,
LED7,
};
struct gpio_t
{
unsigned char gpio;
char number[10];
char direction[100];
char gpio_path[100];
int fd;
};
extern struct gpio_t led_control[8];
extern struct gpio_t swtich_obtain[8];
static int gpio_config(struct gpio_t *gpio,int index,const char *attr, const char *val);
int gpio_init(struct gpio_t *gpio,int index);
int gpio_set(struct gpio_t *gpio,int index);
int gpio_reset(struct gpio_t *gpio,int index);
int gpio_read(struct gpio_t *gpio,int index);
void LED_init(void);
#endif
.c文件
#include "led.h"
struct gpio_t led_control[8]=
{
{LED0,"503","out",""},
{LED1,"504","out",""},
{LED2,"505","out",""},
{LED3,"506","out",""},
{LED4,"507","out",""},
{LED5,"508","out",""},
{LED6,"509","out",""},
{LED7,"510","out",""},
};
/**
* 函数名称 static int gpio_config(struct gpio_t *gpio,int index,const char *attr, const char *val)
* 函数功能 目标引脚功能配置
* 输入参数 gpio: 引脚功能定义
* index:目标引脚名称
* attr:配置文件(未调用gpio_init,直接配置引脚会失败)
* val: 配置功能
* 函数返回值 0: 函数执行成功
* -1/fd:函数执行失败
*/
static int gpio_config(struct gpio_t *gpio,int index,const char *attr, const char *val)
{
char file_path[100];
int len;
int fd;
char result[100] = "";
sprintf(file_path, "%s/%s", gpio[index].gpio_path, attr);
if (0 > (fd = open(file_path, O_WRONLY)))
{
strcat(result, gpio[index].gpio_path);
strcat(result, " gpio config open ");
strcat(result, "failed");
LOG_INFO(LOG_DEBUG, "%s", result);
return fd;
}
len = strlen(val);
if (len != write(fd, val, len))
{
strcat(result, gpio[index].gpio_path);
strcat(result, " gpio config write ");
strcat(result, "failed");
LOG_INFO(LOG_DEBUG, "%s", result);
close(fd);
return -1;
}
close(fd); /* 关闭文件 */
return 0;
}
/**
* 函数名称 int gpio_init(int index)
* 函数功能 目标引脚功能初始化(第一次初始化时,未存在配置对应引脚文件情况需要优化!(第一次使用失败,再次初始化一次))
* 输入参数 gpio:引脚功能定义
* index:目标引脚名称
* 函数返回值 0 :函数执行成功
* -1:函数执行失败
*/
int gpio_init(struct gpio_t *gpio,int index)
{
/* 判断指定编号的GPIO是否导出 */
sprintf(gpio[index].gpio_path, "/sys/class/gpio/gpio%s", gpio[index].number);
if (access(gpio[index].gpio_path, F_OK)) /* 如果目录不存在 则需要导出 */
{
int fd;
int len;
char result[100] = "";
if (0 > (fd = open("/sys/class/gpio/export", O_WRONLY)))
{
strcat(result, gpio[index].gpio_path);
strcat(result, " gpio init open ");
strcat(result, "failed");
LOG_INFO(LOG_DEBUG, "%s", result);
return -1;
}
len = strlen(gpio[index].number);
if (len != write(fd, gpio[index].number, len)) /* 导出gpio文件 */
{
strcat(result, gpio[index].gpio_path);
strcat(result, " gpio init write ");
strcat(result, "failed");
LOG_INFO(LOG_DEBUG, "%s", result);
close(fd);
return -1;
}
close(fd); /* 关闭文件 */
}
if(strcmp(gpio[index].direction, "out")==0)
{
/* 配置为输出模式 */
if (gpio_config(gpio,index,"direction", gpio[index].direction))
return -1;
/* 极性设置 */
if (gpio_config(gpio,index,"active_low", "0"))
return -1;
}
else if(strcmp(gpio[index].direction, "in")==0)
{
/* 配置为输入模式 */
if (gpio_config(gpio,index,"direction", "in"))
return -1;
/* 极性设置 */
if (gpio_config(gpio,index,"active_low", "0"))
return -1;
/* 配置为非中断方式 */
if (gpio_config(gpio,index,"edge", "none"))
return -1;
}
else
{
return -1;
}
/* 退出程序 */
return 0;
}
/**
* 函数名称 int gpio_set(int gpio,int index)
* 函数功能 设置目标引脚高电平
* 输入参数 gpio:引脚功能定义
* index:目标引脚名称
* 函数返回值 0 :函数执行成功
* -1:函数执行失败
*/
int gpio_set(struct gpio_t *gpio,int index)
{
char result[100] = "";
/* 控制GPIO输出高电平 */
if (gpio_config(gpio,index,"value", "1")==0)
{
return 0;
}
else
{
strcat(result, gpio[index].gpio_path);
strcat(result, " gpio set ");
strcat(result, "failed");
LOG_INFO(LOG_DEBUG, "%s", result);
return -1;
}
}
/**
* 函数名称 int gpio_reset(int index)
* 函数功能 设置目标引脚低电平
* 输入参数 gpio:引脚功能定义
* index:目标引脚名称
* 函数返回值 0 :函数执行成功
* -1:函数执行失败
*/
int gpio_reset(struct gpio_t *gpio,int index)
{
char result[100] = "";
/* 控制GPIO输出低电平 */
if (gpio_config(gpio,index,"value", "0")==0)
{
return 0;
}
else
{
strcat(result, gpio[index].gpio_path);
strcat(result, " gpio reset ");
strcat(result, "failed");
LOG_INFO(LOG_DEBUG, "%s", result);
return -1;
}
}
/**
* 函数名称 int gpio_read(int index)
* 函数功能 读取目标引脚高低电平状态
* 输入参数 gpio:引脚功能定义
* index:目标引脚名称
* 函数返回值 value:0: 低电平
* 1: 高电平
* -1:读取电平失败
*/
int gpio_read(struct gpio_t *gpio,int index)
{
char file_path[100];
char val;
int fd;
char result[100] = "";
/* 读取GPIO电平状态 */
sprintf(file_path, "/sys/class/gpio/gpio%s/%s", gpio[index].number, "value");
if (0 > (fd = open(file_path, O_RDONLY)))
{
LOG_INFO(LOG_DEBUG, "%s", "open error");
return -1;
}
if (0 > read(fd, &val, 1))
{
LOG_INFO(LOG_DEBUG, "%s", "read error");
close(fd);
return -1;
}
close(fd);
if((unsigned char)val==0x31)
{
return 1;
}
else if((unsigned char)val==0x30)
{
return 0;
}
else
{
strcat(result, gpio[index].gpio_path);
strcat(result, " gpio read ");
strcat(result, "failed");
LOG_INFO(LOG_DEBUG, "%s", result);
return -1;
}
}
/**
* 函数名称 void LED_init(void)
* 函数功能 led灯io初始化
* 输入参数 无
* 函数返回值 无
*/
void LED_init(void)
{
int i=0;
/* 初始化LED */
for(i=0;i<8;i++)
{
gpio_init(led_control,i);
gpio_set(led_control,i);/* 引脚拉高,led灯灭 */
}
}
main.c文件
/* 系统相关串口,IO功能头文件 */
#include<stdio.h> /* 标准输入输出定义 */
#include<stdlib.h> /* 标准函数库定义 */
#include<unistd.h> /* Unix 标准函数定义 */
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h> /* 文件控制定义 */
#include<termios.h> /* PPSIX 终端控制定义 */
#include<string.h>
#include <sys/time.h>
#include <ctype.h>
#include "led.h"
int main()
{
unsigned char i=0U;
LED_init();
for(i=0U;i<LED7;i++)
{
gpio_set(led_control,i);
}
}
4、总结
上述方法可以控制IO,进行IO功能的写入与读取,但使用此方法控制IO写时序,会存在控制周期长,例如adc采集频率不够,建议对调用IO有时间要求的宝子们,研究一下mmap功能或者直接控制寄存器的方法去控制IO。
如果宝子们对上述代码有什么疑惑,可以评论区留言,我看到必回。如果文章反响不错,之后我也会继续写下去。