input.c文件是利用sysfs导出的树莓派gpio内核驱动,来实现的按键检测、led控制程序。 具备按键去抖动,gpio防重复写入 使用到多线程、位图、poll()监测、sysfs、lrt库->定时器技术。
邮箱:[email protected] 作者:唐公子 日期:2018.10.12
这个代码是之前就写了,然后由于需要在csdn上面做一些技术分析,所以粘贴过来,我这篇文章会对树莓派gpio驱动的开发很有帮助和启发意义。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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;
}
email:[email protected] 画笔