背景
在应用层操作GPIO除了驱动以外主要是通过读取改变gpio子系统的文件内容来实现gpio的读写,但是当需要监测gpio的状态改变时循环去检测文件改变是会消耗大量cpu资源的事情,此时我们可以使用epoll来检测文件的改变
代码实现
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#include <sys/epoll.h>
#include <sys/time.h>
#include <errno.h>
#include <linux/input.h>
#define MSG(args...) printf(args)
#define MSG_I(args...) printf(args)
#define MSG_W(args...) printf(args)
#define MSG_E(args...) printf(args)
static int g_epollFd = -1;
//函数声明
static int gpio_export(int pin);
static int gpio_unexport(int pin);
static int gpio_direction(int pin, int dir);
static int gpio_write(int pin, int value);
static int gpio_read(int pin);
static int gpio_export(int pin)
{
char buffer[64];
int len;
int fd;
char gpio_pin[64];
snprintf( gpio_pin, sizeof(gpio_pin), "/sys/class/gpio/gpio%d", pin );
if( access( gpio_pin, F_OK ) == 0 ){ // 如果已经申请
MSG_I("%s it already exists\n", gpio_pin );
return 0;
}
fd = open("/sys/class/gpio/export", O_WRONLY);
if (fd < 0) {
MSG("Failed to open export for writing!\n");
return(-1);
}
len = snprintf(buffer, sizeof(buffer), "%d", pin);
if (write(fd, buffer, len) < 0) {
MSG("Failed to export gpio!\n");
return -1;
}
close(fd);
return 0;
}
static int gpio_unexport(int pin)
{
char buffer[64];
int len;
int fd;
fd = open("/sys/class/gpio/unexport", O_WRONLY);
if (fd < 0) {
MSG("Failed to open unexport for writing!\n");
return -1;
}
len = snprintf(buffer, sizeof(buffer), "%d", pin);
if (write(fd, buffer, len) < 0) {
MSG("Failed to unexport gpio!");
return -1;
}
close(fd);
return 0;
}
//dir: 0-->IN, 1-->OUT
static int gpio_direction(int pin, int dir)
{
static const char dir_str[] = "in\0out";
char path[64];
int fd;
snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/direction", pin);
fd = open(path, O_WRONLY);
if (fd < 0) {
MSG("Failed to open gpio direction for writing!\n");
return -1;
}
if (write(fd, &dir_str[dir == 0 ? 0 : 3], dir == 0 ? 2 : 3) < 0) {
MSG("Failed to set direction!\n");
return -1;
}
close(fd);
return 0;
}
//value: 0-->LOW, 1-->HIGH
static int gpio_write(int pin, int value)
{
static const char values_str[] = "01";
char path[64];
int fd;
snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", pin);
fd = open(path, O_WRONLY);
if (fd < 0) {
MSG("Failed to open gpio value for writing!\n");
return -1;
}
if (write(fd, &values_str[value == 0 ? 0 : 1], 1) < 0) {
MSG("Failed to write value!\n");
return -1;
}
close(fd);
return 0;
}
static int gpio_read(int pin)
{
char path[64];
char value_str[3];
int fd;
snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", pin);
fd = open(path, O_RDONLY);
if (fd < 0) {
MSG("Failed to open gpio value for reading!\n");
return -1;
}
if (read(fd, value_str, 3) < 0) {
MSG("Failed to read value!\n");
return -1;
}
close(fd);
return (atoi(value_str));
}
// none表示引脚为输入,不是中断引脚
// rising表示引脚为中断输入,上升沿触发
// falling表示引脚为中断输入,下降沿触发
// both表示引脚为中断输入,边沿触发
// 0-->none, 1-->rising, 2-->falling, 3-->both
static int gpio_edge(int pin, int edge)
{
const char dir_str[][9] = {"none\0","rising\0","falling\0","both"};
char path[64];
int fd;
snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/edge", pin);
fd = open(path, O_WRONLY);
if (fd < 0) {
MSG("Failed to open gpio edge for writing!\n");
return -1;
}
if (write(fd, &dir_str[edge], strlen(dir_str[edge])) < 0) {
MSG("Failed to set edge!\n");
return -1;
}
close(fd);
return 0;
}
static long int getNowTime()
{
struct timeval p_time;
gettimeofday( &p_time, NULL );
return p_time.tv_sec*1000+p_time.tv_usec/1000;
}
int addFdToEpoll( int pollFd, int addFd )
{
struct epoll_event event;
event.events = EPOLLET;
event.data.fd = addFd;
return epoll_ctl( pollFd, EPOLL_CTL_ADD, addFd, &event );
}
#define CALL_KEY_NUM ( 32 )
#define VOLDOWN_KEY_NUM ( 30 )
#define VOLUP_KEY_NUM ( 98 )
typedef struct{
int fd;
bool press_key; // 按下按键
bool lift_key;
long int press_time; // 按下按键的时间
}gpio_info;
int main()
{
/* int gpio_fd1, gpio_fd2, gpio_fd3; */
gpio_info gpioCall, gpioDown,gpioUp;
int ret;
struct pollfd fds[1];
char buff[10];
unsigned char cnt = 0;
g_epollFd = epoll_create(4); //创建红黑树,返回给全局 g_efd, linux2.6之后size>0即可
if(g_epollFd <= 0)
MSG("create efd in %s err %s\n", __func__, strerror(errno));
//按键引脚初始化
gpio_export( VOLDOWN_KEY_NUM );
gpio_direction( VOLDOWN_KEY_NUM, 0);
gpio_edge( VOLDOWN_KEY_NUM,3);
gpioDown.fd = open("/sys/class/gpio/gpio30/value",O_RDONLY);
if( gpioDown.fd < 0){
MSG("Failed to open 30 value!\n");
return -1;
}
addFdToEpoll( g_epollFd, gpioDown.fd );
gpio_export(CALL_KEY_NUM);
gpio_direction(CALL_KEY_NUM, 0);
gpio_edge(CALL_KEY_NUM,3);
gpioCall.fd = open("/sys/class/gpio/gpio32/value",O_RDONLY);
if( gpioCall.fd < 0){
MSG("Failed to open 32 value!\n");
return -1;
}
addFdToEpoll( g_epollFd, gpioCall.fd );
gpio_export(VOLUP_KEY_NUM);
gpio_direction(VOLUP_KEY_NUM, 0);
gpio_edge(VOLUP_KEY_NUM,3);
gpioUp.fd = open("/sys/class/gpio/gpio98/value",O_RDONLY);
if(gpioUp.fd < 0){
MSG("Failed to open 98 value!\n");
return -1;
}
addFdToEpoll( g_epollFd, gpioUp.fd );
struct epoll_event events;
int value;
gpio_info *gpioTmp;
while(1){
ret = epoll_wait( g_epollFd, &events, 1, 500 );
if( ret < 0 ){
MSG("epoll_wait %s err %s\n", __func__, strerror(errno));
break;
}
else if( ret == 0 ){
/* MSG("epoll_wait timeout\n"); */
long int nowTime = getNowTime();
if( ( gpioCall.press_key ) &&
( !gpioCall.lift_key ) &&
( (nowTime - gpioCall.press_time) > 1000 ) ){
MSG("Press the CALL button continuously\n");
}
if( ( gpioUp.press_key ) &&
( !gpioUp.lift_key ) &&
( (nowTime - gpioUp.press_time) > 1000 ) ){
MSG("Press the VOL UP button continuously\n");
}
if( ( gpioDown.press_key ) &&
( !gpioDown.lift_key ) &&
( (nowTime - gpioDown.press_time) > 1000 ) ){
MSG("Press the VOL DOWN button continuously\n");
}
}
else{
lseek( events.data.fd, 0, SEEK_SET );
memset( buff, 0, sizeof(buff) );
ret = read( events.data.fd, buff, sizeof(buff) );
if( ret <= 0 ){
MSG_E("read error, %d, %s\n", errno, strerror(errno));
continue;
}
/* MSG("ret = %d, buff : %s\n", ret, buff ); */
/* MSG("ret = %d, buff : %s\n", ret, buff ); */
value = atoi( buff );
if( events.data.fd == gpioUp.fd){
MSG( "push vol up, %s", buff );
gpioTmp = &gpioUp;
}
else if( events.data.fd == gpioCall.fd){
MSG( "push call, %s", buff );
gpioTmp = &gpioCall;
}
else if( events.data.fd == gpioDown.fd){
MSG( "push vol down, %s", buff );
gpioTmp = &gpioDown;
}
if( value == 0 ){ // 按键按下
gpioTmp->press_key = true;
gpioTmp->press_time = getNowTime();
}else if ( gpioTmp->press_key ){ // 已经按下了
gpioTmp->lift_key = true;
}
else{
gpioTmp->press_key = false;
gpioTmp->lift_key = false;
}
// 当按下标志位和按键弹起标志位都为 1 时候,才执行回调
if( gpioTmp->press_key & gpioTmp->lift_key ){
gpioTmp->press_key = false;
gpioTmp->lift_key = false;
long int nowTime = getNowTime();
if( ( nowTime - gpioTmp->press_time ) > 1000 ){ // 长按
MSG("long pasaa!!!\n");
}else if( ( nowTime - gpioTmp->press_time ) < 10 ){ // 抖动
MSG("shake!!!\n");
}else{
MSG("sort pasaa!!!\n");
}
MSG("nowTime: %ld, press_time: %ld, %ld\n", nowTime, gpioTmp->press_time, nowTime - gpioTmp->press_time);
}
}
}
return 0;
}