GPIO 子系统

linux将GPIO的控制抽象成了GPIO子系统。
GPIO子系统支持把引脚用于基本的输入输出功能,其中输入功能还支持中断检测。
GPIO子系统同样是通过sysfs文件系统控制。

查看sysfs下的GPIO子系统内容:

ls /sys/class/gpio/
export     gpiochip128  gpiochip504  gpiochip96
gpiochip0  gpiochip32   gpiochip64   unexport

GPIO子系统操作

  1. 导出GPIOx,操作export文件写入到控制的GPIOx,如果GPIO已经被别的驱动使用就会导出失败。导出GPIOx后就会在/sys/class/gpio/目录下出现gpiox的目录,x为GPIO编号。
  2. 操作gpiox下的属性文件控制GPIO
  3. 向文件unexport文件写入GPIO编号的字符串取消导入GPIO,gpiox目录消失

gpio19下的属性文件:

ls /sys/class/gpio/gpio19/
active_low  device  direction  edge  power  subsystem  uevent  value

主要关注的文件:

gpio属性文件说明
direction设置GPIO方向,输出or输入
edge设置中断触发方式,必须设置为输入
active_low设置GPIO极性,输出or输入
value设置为输入时读取该文件获取GPIO状态,设置为输出时写入”1“或”0“输出高低电平

direction取值:

取值说明
“in”输入
“out”输出

edge 取值:

取值说明
“none”无触发方式
“rising”下降沿
“falling”上升沿
“both”双边沿

active_low取值:控制极性,默认情况下为 0

取值说明
“0”输出高电平即为高电平,输出低电平即为低电平
“1”输出高电平实际输出低电平,输出低电平实际为高电平

active_low对于输入模式同样使用。

value取值:

取值说明
“0”输出,表示设置GPIO低电平;输入,表示读取到低电平
“1”输出,表示设置GPIO高电平;输入,表示读取到高电平

GPIO操作

gpio.h:

/*
 * @brief :   
 * @date :  2021-11-xx
 * @version : v1.0.0
 * @copyright(c) 2020 : OptoMedic company Co.,Ltd. All rights reserved
 * @Change Logs:   
 * @date         author         notes:  
 */
#ifndef __GPIO_H__
#define __GPIO_H__

#include "stdint.h"

enum {
    GPIO_DIR_OUTPUT = 0, 
    GPIO_DIR_INPUT,
};
typedef uint8_t gpio_dir_t;

enum {
    ACTIVE_LOW = 0,
    ACTIVE_HIGH = 1,
};
typedef uint8_t active_level_t;

/* irq */
enum {
    EDGE_NONE = 0,
    EDGE_RISING,
    EDGE_FALLING,
    EDGE_BOTH,
};
typedef uint8_t edge_t;  

int gpio_export(int gpio);
int gpio_unexport(int gpio);

int gpio_set_active(int gpio, active_level_t active);
int gpio_active_low(int gpio);
int gpio_active_high(int gpio);

int gpio_set_edge(int gpio, edge_t edge);
int gpio_edge_none(int gpio);
int gpio_edge_rising(int gpio);
int gpio_edge_falling(int gpio);
int gpio_edge_both(int gpio);

int gpio_set_direction(int gpio, gpio_dir_t dir);
int gpio_direction_input(int gpio);
int gpio_direction_output(int gpio);

void gpio_set_value(int gpio, int value);
int gpio_get_value(int gpio);

#endif /* __GPIO_H__ */

gpio.c:

#include "gpio.h"
#include <stdio.h> 
#include <stdlib.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <fcntl.h> 
#include <unistd.h> 
#include <string.h> 

/* 
gpio 编号计算
    index = GPIOn_IOx = (n-1)*32 + x
	例如蜂鸣器使用的引脚编号为:index = GPIO1_19 = (1-1)*32 +19 = 19。
	又例如GPIO4_IO20的编号为:index = GPIO4_IO20 = (4-1)*32+20=116。
*/
int gpio_export(int gpio)
{
    int fd;
    char gpio_path[100] = {0};

   /* GPIO目录路径 */
    sprintf(gpio_path, "/sys/class/gpio/gpio%d", gpio); 
    /* 判断GPIO目录是否存在 */
    if (access(gpio_path, F_OK) == 0) return 0; 

    if (0 > (fd = open("/sys/class/gpio/export", O_WRONLY))) 
    { 
        perror("open error"); 
        return -1;
    } 

    /* 导出 gpio */
    char gpio_str[10] = {0};
    sprintf(gpio_str, "%d", gpio);
    int len = strlen(gpio_str);
    if (len != write(fd, gpio_str, len))  
    {
        perror("write error"); 
        return -2;
    } 

    return 0;
}

int gpio_unexport(int gpio)
{
    int fd;
	fd = open("/sys/class/gpio/unexport", O_WRONLY);
	if(fd < 0)
		return -1;

    char gpio_str[10] = {0};
    sprintf(gpio_str, "%d", gpio);
	write(fd, gpio_str, strlen(gpio_str));
	close(fd);
    return 0;
}

static int gpio_cfg_attr(int gpio, char *attr,  char *val)
{
    int fd;
    char path[100] = {0};
    sprintf(path, "/sys/class/gpio/gpio%d/%s", gpio, attr);
    if (0 > (fd = open(path, O_WRONLY))) 
    { 
        perror("open error"); 
        return -1;
    } 
 
    int len = strlen(val); 
    if (len != write(fd, val, len)) 
    { 
        perror("write error"); 
        close(fd); 
        return -2; 
    } 

    close(fd);  
    return 0; 
}

int gpio_set_direction(int gpio, gpio_dir_t dir)
{
    char dirs[10] = {0};
    if (dir == GPIO_DIR_OUTPUT)
    {
        sprintf(dirs, "%s", "out");
    }
    else
    {
        sprintf(dirs, "%s", "in");
    }
    return gpio_cfg_attr(gpio, "direction", dirs);
}

int gpio_direction_output(int gpio)
{
    return gpio_set_direction(gpio, GPIO_DIR_OUTPUT);
}

int gpio_direction_input(int gpio)
{
    return gpio_set_direction(gpio, GPIO_DIR_INPUT); 
}

int gpio_set_active(int gpio, active_level_t active)
{
    char active_str[10] = {0};
    
    if (active == ACTIVE_LOW)
    {
        gpio_cfg_attr(gpio, "active_low", "0");
    }
    else
    {
        gpio_cfg_attr(gpio, "active_low", "1");
    }
}

int gpio_active_low(int gpio)
{
    return gpio_cfg_attr(gpio, "active_low", "0");
}

int gpio_active_high(int gpio)
{
    return gpio_cfg_attr(gpio, "active_low", "1");
}

int gpio_set_edge(int gpio, edge_t edge)
{
    int ret;
    edge_t edge_index[4] = {EDGE_NONE, EDGE_RISING, EDGE_FALLING, EDGE_BOTH};
    char str_edge_index[4][10] = {{"none"}, {"rising"}, {"falling"}, {"both"}};
    for (int i = 0; i < 4; i++)
    {
        if (edge == edge_index[i])
        {
            ret = gpio_cfg_attr(gpio, "edge", str_edge_index[i]);
            break;
        }
    }
    return ret;
}

int gpio_edge_none(int gpio)
{
    return gpio_set_edge(gpio, EDGE_NONE);
}

int gpio_edge_rising(int gpio)
{
    return gpio_set_edge(gpio, EDGE_RISING);
}

int gpio_edge_falling(int gpio)
{
    return gpio_set_edge(gpio, EDGE_FALLING);
}

int gpio_edge_both(int gpio)
{
    return gpio_set_edge(gpio, EDGE_BOTH);
}


void gpio_set_value(int gpio, int value)
{
    char val[10] = {0};
    if (value == 0)
    {
        sprintf(val, "%d", 0);
    }
    else
    {
        sprintf(val, "%d", 1);
    }

    gpio_cfg_attr(gpio, "value", val);
}

int gpio_get_value(int gpio)
{
    char gpio_path[100] = {0};
    char vals[5] = {0};
    int fd;

    sprintf(gpio_path, "/sys/class/gpio/gpio%d/value", gpio);
    if (0 > (fd = open(gpio_path, O_WRONLY))) 
    { 
        perror("open error"); 
        return -1;
    } 

    if (0 > read(fd, vals, sizeof(vals))) 
    { 
        perror("read error"); 
        close(fd); 
        return -1;
    } 

    int value = atoi(vals);
    
    return value;
}

使用控制GPIO蜂鸣器

GPIO编号的计算

index = GPIOn_IOx = (n-1)*32 + x

例如蜂鸣器使用的引脚编号为:index = GPIO1_19 = (1-1)*32 +19 = 19。

又例如GPIO4_IO20的编号为:index = GPIO4_IO20 = (4-1)*32+20=116

beep.h:

#ifndef __BEEP_H__
#define __BEEP_H__

int beep_init(void);
int beep_on(void);
int beep_off(void);
int beep_deinit(void);

#endif /* __BEEP_H__ */

beep.c:

#include "gpio.h"
#include "beep.h"


#define BEEP_GPIO_INDEX 	19


int beep_init(void)
{
    int ret;
    ret = gpio_export(BEEP_GPIO_INDEX);
    ret = gpio_direction_output(BEEP_GPIO_INDEX);
    return ret;
}

int beep_on(void)
{
    gpio_set_value(BEEP_GPIO_INDEX, 1);
    return 0;
}

int beep_off(void)
{
    gpio_set_value(BEEP_GPIO_INDEX, 0);
    return 0;
}

int beep_deinit(void)
{
    gpio_set_value(BEEP_GPIO_INDEX, 0);
    return gpio_unexport(BEEP_GPIO_INDEX);
}

#include "stdio.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <poll.h>
#include <stdint.h>
#include <sys/select.h>

#include "beep.h"

void sleep_ms(unsigned int ms)
{
    struct timeval delay;
	delay.tv_sec = 0;
	delay.tv_usec = ms * 1000; 
	select(0, NULL, NULL, NULL, &delay);
}

int main(int argc, char **argv)
{
    beep_init();

    int cnt = 0;
    while (1)
    {
        beep_on();
        sleep_ms(500);
        beep_off();
        sleep_ms(500);
        if (++cnt >= 40)
            break;
    }

    beep_deinit();

    return 0;
}

中断

对于使用中断可以使用poll多路复用IO监测中断是否触发,poll事件有POLLIN、POLLOUT、POLLERR、POLLPRI 等,其中 POLLIN 和 POLLOUT 表示普通优先级数据可读、可写。中断就是一种高优先级事件,需要使用 POLLPRI ,当中断触发时表示有高优先级数据可被读取。
此外还可以使用信号方式监测GPIO是否触发中断。


代码:https://gitee.com/guangjieMVP/gj_linux_app

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐