raspberry-key-led-controller

input.c文件是利用sysfs导出的树莓派gpio内核驱动,来实现的按键检测、led控制程序。 具备按键去抖动,gpio防重复写入 使用到多线程、位图、poll()监测、sysfs、lrt库->定时器技术。

作者:唐公子 日期:2018.10.12
这个代码是之前就写了,然后由于需要在csdn上面做一些技术分析,所以粘贴过来,我这篇文章会对树莓派gpio驱动的开发很有帮助和启发意义。

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#include <signal.h>
#include <assert.h>
#include <unistd.h>
#include <time.h>
#include <pthread.h>
#include <sys/time.h>


#define MSG(args ...) printf(args)

#if 0
#define KEY2 27
#define KEY3 17

#define LED1 4
#define LED2 22
#define LED3 18
#define LED4 23
#endif


#define KEY2 4
#define KEY3 17

#define LED1 27
#define LED2 2
#define LED3 18
#define LED4 3


#define LED1_LIGHT 1
#define LED1_OFF 0

#define LED2_LIGHT 0
#define LED2_OFF 1

#define LED3_LIGHT 0
#define LED3_OFF 1

#define LED4_LIGHT 0
#define LED4_OFF 1



struct gpio_tbl {
unsigned int gpio;
unsigned int dir;
unsigned int init_level;
unsigned int edge;
};

//led 配置表
struct gpio_tbl led_tbl[] = {
{LED1, 1, 0},
{LED2, 1, 0},
{LED3, 1, 0},
{LED4, 1, 0},
};

//按键 配置表
struct gpio_tbl key_tbl[] = {
{KEY2, 0, 0, 2},
{KEY3, 0, 0, 2},
};

static int fd_key2, fd_key3;
static unsigned char timer_active = 0;
static signed char timer_key2_busy = -1, timer_key3_busy = -1;
static timer_t timerid_key2, timerid_key3;
static struct sigevent sev;
static struct itimerspec its_key2, its_key3;

#define FILTER_PERIOD 20 //unit: ms
#define KEY2_PERIOD (5000 / FILTER_PERIOD) //unit: ms
#define KEY3_PERIOD (5000 / FILTER_PERIOD) //unit: ms

//static int expire_cnt2 = 0, expire_cnt3 = 0;

#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \
} while (0)

static int gpio_export(int pin)
{
char buffer[64];
int len;
int fd;

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!\r\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[%d] direction for writing!\n", pin);
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[%d] value for writing!\n", pin);
return - 1;
}

if (write(fd, &values_str[value == 0 ? 0: 1], 1) < 0) {
MSG("Failed to write value [%d]!\n", value);
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[%d] value for reading!\n", pin);
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[] = "none\0rising\0falling\0both";
char ptr;
char path[64];
int fd;

switch (edge)
{
case 0:
ptr = 0;
break;

case 1:
ptr = 5;
break;

case 2:
ptr = 12;
break;

case 3:
ptr = 20;
break;

default:
ptr = 0;
}

snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/edge", pin);
fd = open(path, O_WRONLY);

if (fd < 0) {
MSG("Failed to open gpio[%d] edge for writing!\n", pin);
return - 1;
}

if (write(fd, &dir_str[ptr], strlen(&dir_str[ptr])) < 0) {
MSG("Failed to set edge!\n");
return - 1;
}

close(fd);
return 0;
}

/**
*函数功能: 释放led key资源
*/
void sigint_hendler(int a)
{
int i;

close(fd_key2);
close(fd_key3);
//printf("----------中断信号%d------------\r\n", a);
for (i=0; i<sizeof(led_tbl) / sizeof(led_tbl[0]); i++) {
gpio_unexport(led_tbl[i].gpio);
}

for (i=0; i<sizeof(key_tbl) / sizeof(key_tbl[0]); i++) {
gpio_unexport(key_tbl[i].gpio);
}

exit(EXIT_SUCCESS);
}

void
exit_handler(void)
{
int i;


sigint_hendler(0);

#if 0
//printf("----------中断信号%d------------\r\n", a);
for (i=0; i<sizeof(led_tbl) / sizeof(led_tbl[0]); i++) {
gpio_unexport(led_tbl[i].gpio);
}

for (i=0; i<sizeof(key_tbl) / sizeof(key_tbl[0]); i++) {
gpio_unexport(key_tbl[i].gpio);
}
#endif

exit(EXIT_SUCCESS);
}


/* 定时器回调函数 for key2*/
void key2_timer_handler()
{
int ret = -1;
char buff[2];
static unsigned short low_cnt = 0, high_cnt = 0, shake_win = 0;

//printf("timer expire\r\n");
//读取key2按键值
ret = lseek(fd_key2, 0, SEEK_SET);

if (ret == -1)
MSG("lseek\n");

ret = read(fd_key2, buff, 2);

if (ret == -1) {
MSG("read\n");
}
else {
if (0x5a == timer_key2_busy) {
low_cnt = 0;
high_cnt = 0;
shake_win = 5;
timer_key2_busy = 0xa5;
}

if (buff[0] == '1') {
low_cnt++;

if (high_cnt >= shake_win) {
high_cnt -= shake_win;

(shake_win > 1) ? (shake_win--) : shake_win;
}
}
else if (buff[0] == '0'){

if (low_cnt >= shake_win) {
low_cnt -= shake_win;

(shake_win > 1) ? (shake_win--) : shake_win;
}

high_cnt++;
}
}

if ((high_cnt > low_cnt) && (high_cnt > KEY2_PERIOD * 8 / 10)) {
printf("------------key2按下,网关数据复位,仅保留缴费信息\r\n");
printf("--------------删除key2滤波定时器\r\n");
timer_delete(timerid_key2);

timer_key2_busy = -1;
timer_active &= ~0x02;
}
else if (low_cnt > KEY2_PERIOD * 1 / 10) {
printf("------------key2弹起\r\n");
printf("--------------删除key2滤波定时器\r\n");
timer_delete(timerid_key2);


timer_key2_busy = -1;
timer_active &= ~0x02;
}

//exit(EXIT_SUCCESS);
return;
}

/* 定时器回调函数 for key3
*/
void key3_timer_handler()
{
int ret = -1;
char buff[2];
static unsigned short low_cnt = 0, high_cnt = 0, shake_win = 0;

//printf("timer expire\r\n");
//读取key2按键值
ret = lseek(fd_key3, 0, SEEK_SET);

if (ret == -1)
MSG("lseek\n");

ret = read(fd_key3, buff, 2);

if (ret == -1) {
MSG("read\n");
}
else {
if (0x5a == timer_key3_busy) {
low_cnt = 0;
high_cnt = 0;
shake_win = 5;
timer_key3_busy = 0xa5;
}

if (buff[0] == '0') {
low_cnt++;

if (high_cnt >= shake_win) {
high_cnt -= shake_win;

(shake_win > 1) ? (shake_win--) : shake_win;
}
}
else if (buff[0] == '1'){

if (low_cnt >= shake_win) {
low_cnt -= shake_win;

(shake_win > 1) ? (shake_win--) : shake_win;
}

high_cnt++;
}
}

if ((high_cnt > low_cnt) && (high_cnt > KEY3_PERIOD * 8 / 10)) {
printf("------------key3按下,管理员密码重置\r\n");
printf("--------------删除key3滤波定时器\r\n");
timer_delete(timerid_key3);

timer_key3_busy = -1;
timer_active &= ~0x04;
}
else if (low_cnt > KEY3_PERIOD * 1 / 10) {
printf("------------key3弹起\r\n");
printf("--------------删除key3滤波定时器\r\n");
timer_delete(timerid_key3);

timer_key3_busy = -1;
timer_active &= ~0x04;
}

//exit(EXIT_SUCCESS);
return;
}

static void timer_handler()
{
if (timer_active & 0x02) {
key2_timer_handler();
}

if (timer_active & 0x04) {
key3_timer_handler();
}
}

typedef struct __led_status{
unsigned char gateway_type; //1,主网关 0,辅网关
unsigned char online; //1,连接到服务器 0,未连接到服务器 2,tcpip数据流过
unsigned char dhcp; //1,dhcp开 0,dhcp关
unsigned char ziege_all_online; //1,zigbee节点全部在线 0,有zigbee节点掉线
}LED_STATUS;

LED_STATUS led_status;


void * led_thread(void *arg)
{
static LED_STATUS prev_state= {0xff, 0xff, 0xff, 0xff};

while(1)
{
printf("------led thread \r\n");

switch(led_status.gateway_type) {
case 0:
printf("--------辅助网关\r\n");
if (led_status.gateway_type != prev_state.gateway_type) {
gpio_write(LED1, LED1_OFF);
prev_state.gateway_type = led_status.gateway_type;
}
break;
case 1:
printf("--------主网关\r\n");
if (led_status.gateway_type != prev_state.gateway_type) {
gpio_write(LED1, LED1_LIGHT);
prev_state.gateway_type = led_status.gateway_type;
}
break;
default:
led_status.gateway_type = 0;
break;
}

switch (led_status.online)
{
case 0:
printf("--------未连接到服务器\r\n");
if (led_status.online != prev_state.online) {
gpio_write(LED2, LED2_OFF);
prev_state.online = led_status.online;
}
break;
case 1:
printf("--------已连接到服务器\r\n");
if (led_status.online != prev_state.online) {
gpio_write(LED2, LED2_LIGHT);
prev_state.online = led_status.online;
}
break;
case 2:
printf("--------有tcpip数据流\r\n");
//flash
break;
default:
led_status.online = 0;
break;
}

switch(led_status.dhcp) {
case 0:
printf("--------dhcp关\r\n");
if (led_status.dhcp != prev_state.dhcp) {
gpio_write(LED3, LED3_OFF);
prev_state.dhcp = led_status.dhcp;
}
break;
case 1:
printf("--------dhcp开\r\n");
if (led_status.dhcp != prev_state.dhcp) {
gpio_write(LED3, LED3_LIGHT);
prev_state.dhcp = led_status.dhcp;
}
break;
default:
led_status.dhcp = 0;
break;
}

switch (led_status.ziege_all_online)
{
case 0:
printf("--------zigbee节点掉线\r\n");
if (led_status.ziege_all_online != prev_state.ziege_all_online) {
gpio_write(LED4, LED4_OFF);
prev_state.ziege_all_online = led_status.ziege_all_online;
}
break;
case 1:
printf("--------zigbee节点全部在线\r\n");
if (led_status.ziege_all_online != prev_state.ziege_all_online) {
gpio_write(LED4, LED4_LIGHT);
prev_state.ziege_all_online = led_status.ziege_all_online;
}
break;
default:
led_status.ziege_all_online = 0;
break;
}
printf("\r\n");

usleep(1000000);
}

return NULL;
}


/**
* gcc input.c -lrt -lpthread
*/
int main()
{
pthread_t pid;
int arg = 123;
pthread_create(&pid, NULL, led_thread, &arg);


//#if 1
int ret;

//int a = sysconf(_SC_ATEXIT_MAX);
//printf("ATEXIT_MAX = %ld\n", a);
ret = atexit(exit_handler);
if (ret != 0) {
fprintf(stderr, "cannot set exit function\n");
exit(EXIT_FAILURE);
}

//#else
struct sigaction s;
memset(&s, 0, sizeof(s));
s.sa_handler = sigint_hendler;
sigfillset(&s.sa_mask);
//s.sa_flags |= SA_RESTART;
assert(sigaction(SIGINT, &s, NULL) != 1);
//#endif

/* 注册key2定时器信号回调函数 */
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = SIGUSR1;
sev.sigev_value.sival_ptr = &timerid_key2;
signal(SIGUSR1, timer_handler);

/* 注册key3定时器信号回调函数 */
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = SIGUSR1;
sev.sigev_value.sival_ptr = &timerid_key3;
signal(SIGUSR1, timer_handler);

struct pollfd fds[2];
char buff[64];
unsigned char cnt = 0;

//打开LED资源
int i;
for (i=0; i<sizeof(led_tbl) / sizeof(led_tbl[0]); i++) {
gpio_export(led_tbl[i].gpio);
gpio_direction(led_tbl[i].gpio, led_tbl[i].dir);
gpio_write(led_tbl[i].gpio, led_tbl[i].init_level);
}

//打开按键资源
for (i=0; i<sizeof(key_tbl) / sizeof(key_tbl[0]); i++) {
gpio_export(key_tbl[i].gpio);
gpio_direction(key_tbl[i].gpio, key_tbl[i].dir);
gpio_edge(key_tbl[i].gpio, key_tbl[i].edge);
}

//打开按键2
sprintf(buff, "/sys/class/gpio/gpio%d/value", KEY2);
fd_key2 = open(buff, O_RDONLY);
if (fd_key2 < 0) {
MSG("Failed to open value!\n");
return - 1;
}

//打开按键3
sprintf(buff, "/sys/class/gpio/gpio%d/value", KEY3);
fd_key3 = open(buff, O_RDONLY);
if (fd_key2 < 0) {
close(fd_key2);
MSG("Failed to open value!\n");
return - 1;
}

fds[0].fd = fd_key2;
fds[0].events = POLLPRI;
fds[1].fd = fd_key3;
fds[1].events = POLLPRI;

ret = read(fd_key2, buff, 10);

if (ret == -1)
MSG("read\n");

ret = read(fd_key3, buff, 10);

if (ret == -1)
MSG("read\n");


while (1) {
ret = poll(fds, 2, -1);

//MSG("------------------------------\r\n");
if (ret == -1) {
//MSG("poll\n");
continue;
}

if (fds[0].revents & POLLPRI) {
//MSG("--------key2 input event occured--------\r\n");

ret = lseek(fd_key2, 0, SEEK_SET);

if (ret == -1)
MSG("lseek\n");

ret = read(fd_key2, buff, 10);

if (ret == -1) {
//MSG("read key2 fail\n");
}
else {
//MSG("read key2 ok\n");
}

if (-1 == timer_key2_busy)
{
timer_key2_busy = 0x5a;
timer_active |= 0x02;

/* 创建定时器 */
if (timer_create(CLOCK_MONOTONIC, &sev, &timerid_key2) == -1)
{
errExit("timer_create");
}
else {
printf("-------------创建key2滤波定时器\r\n");
}

/* 设置定时器时间参数超时1s */
memset(&its_key2, 0, sizeof(struct itimerspec));
its_key2.it_value.tv_sec = 0;
its_key2.it_value.tv_nsec = FILTER_PERIOD * 1000 * 1000;
its_key2.it_interval.tv_sec = its_key2.it_value.tv_sec;
its_key2.it_interval.tv_nsec = its_key2.it_value.tv_nsec;

/* 启动定时器 */
if (timer_settime(timerid_key2, 0, &its_key2, NULL) == -1)
{
errExit("timer_settime");
}
}
}
else {
//MSG("--------------end----------------\r\n");
}

if (fds[1].revents & POLLPRI) {
//MSG("--------key3 input event occured--------\r\n");

ret = lseek(fd_key3, 0, SEEK_SET);

if (ret == -1)
MSG("lseek\n");

ret = read(fd_key3, buff, 10);

if (ret == -1) {
//MSG("read key2 fail\n");
}
else {
//MSG("read key2 ok\n");
}

if (-1 == timer_key3_busy)
{
timer_key3_busy = 0x5a;
timer_active |= 0x04;

/* 创建定时器 */
if (timer_create(CLOCK_MONOTONIC, &sev, &timerid_key3) == -1)
{
errExit("timer_create");
}
else {
printf("-------------创建key3滤波定时器\r\n");
}

/* 设置定时器时间参数超时1s */
memset(&its_key3, 0, sizeof(struct itimerspec));
its_key3.it_value.tv_sec = 0;
its_key3.it_value.tv_nsec = FILTER_PERIOD * 1000 * 1000;
its_key3.it_interval.tv_sec = its_key3.it_value.tv_sec;
its_key3.it_interval.tv_nsec = its_key3.it_value.tv_nsec;

/* 启动定时器 */
if (timer_settime(timerid_key3, 0, &its_key3, NULL) == -1)
{
errExit("timer_settime");
}
}
}
else {
//MSG("--------------end----------------\r\n");
}
}
close(fd_key2);
close(fd_key3);

pthread_join(pid, NULL);

return 0;
}