linux应用开发 —— GPIO控制
GPIO 子系统linux将GPIO的控制抽象成了GPIO子系统。GPIO子系统支持把引脚用于基本的输入输出功能,其中输入功能还支持中断检测。GPIO子系统同样是通过sysfs文件系统控制。查看sysfs下的GPIO子系统内容:ls /sys/class/gpio/exportgpiochip128gpiochip504gpiochip96gpiochip0gpiochip32gpiochip64
·
GPIO 子系统
linux将GPIO的控制抽象成了GPIO子系统。
GPIO子系统支持把引脚用于基本的输入输出功能,其中输入功能还支持中断检测。
GPIO子系统同样是通过sysfs文件系统控制。
查看sysfs下的GPIO子系统内容:
ls /sys/class/gpio/
export gpiochip128 gpiochip504 gpiochip96
gpiochip0 gpiochip32 gpiochip64 unexport
GPIO子系统操作
- 导出GPIOx,操作export文件写入到控制的GPIOx,如果GPIO已经被别的驱动使用就会导出失败。导出GPIOx后就会在/sys/class/gpio/目录下出现gpiox的目录,x为GPIO编号。
- 操作gpiox下的属性文件控制GPIO
- 向文件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是否触发中断。
更多推荐
已为社区贡献1条内容
所有评论(0)