树莓派led key开发

raspberry-key-led-controller

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] 画笔

你可能感兴趣的:(树莓派led key开发)