A40i使用笔记:按键输入扫描检测(设备树方式)

一、前言

按键输入检测方式由硬件决定了软件如何开发,一开始我设计的硬件没有考虑到这点,也正是因为这,迫使我不得不学习设备树的使用,因为我的硬件按键输入检测是按键按下经过电阻到地的方式,而不是上拉的模式,所以必须设置GPIO的引脚为上拉模式,但是普通的引脚驱动只能设置输入和输出模式,再有就是高低电平,如果想要设置上拉模式,必须使用设备树的方式,所以这边文章的主要目的也是介绍和分享一下如何使用设备树配置GPIO引脚的模式,和简单的按键输入应用介绍。全部源码下载在这里

二、环境

宿主机:window10,Ubuntu16.04

目标及:a40i,linux3.10

三、正文

本文采用设备树的方式,因为普通的GPIO操作无法设置上拉模式,我的硬件原理图如下所示

硬件未采用上拉电阻,所以需要软件中设置引脚上拉,只能使用设备树的方式,设备树配置如下

	keyscan {
	   compatible = "key,scan";
	   gpios = <&pio PB 8 0 1 1 1>,<&pio PB 10 0 1 1 1>,<&pio PB 12 0 1 1 1>,<&pio PH 2 0 1 1 1>,<&pio PH 5 0 1 1 1>,<&pio PB 2 0 1 1 1>;
	};

之后使用platform_driver去调用设备树资源,代码如下

struct of_device_id ids[] = {
    {.compatible = "key,scan"},
    {},
};
/* 1. 定义platform_driver */
static struct platform_driver chip_demo_gpio_driver = {
    .probe      = myprobe,
    .remove     = myremove,
    .driver     = {
        .name   = "mydrv",
        .of_match_table = ids,
    },
};
/* 2. 在入口函数注册platform_driver */
static int __init keyscan_init(void)
{
	int err;
	err = platform_driver_register(&chip_demo_gpio_driver); 
	return err;
}

之后就是将设备树中的引脚配置出来,代码如下

	struct device_node *nd = pdev->dev.of_node;
	struct gpio_config config;
	unsigned long conf;
	char pin_name[6][32];
	printk("gpio count:%d\n", of_gpio_named_count(nd, "gpios"));
	for (int i = 0; i < 6; i++)
    {
		key_gpio[i] = of_get_named_gpio_flags(nd, "gpios", i, (enum of_gpio_flags *)&config);
		printk("gpio name%d:%s\n", i+1,sunxi_gpio_to_name(config.gpio, pin_name[i]));
		if (!gpio_is_valid(key_gpio[i]))
			printk("gpio isn't valid\n");
		if (gpio_request(key_gpio[i], pdev->name) < 0)//申请注册gpio
			printk("gpio request failed %d\n", key_gpio[i]);
		gpio_direction_input(key_gpio[i]);//设置io输入模式
		if(config.pull!=GPIO_PULL_DEFAULT){//判断设备树配置输入模式与默认是否相同,不同则配置设备树模式
			conf=SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_PUD,config.pull);
			pin_config_set(SUNXI_PINCTRL,pin_name[i],conf);
		}
		if(config.drv_level!=GPIO_DRVLVL_DEFAULT){//判断设备树配置驱动能力等级与默认是否相同,不同则配置设备树模式
			conf=SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DRV,config.drv_level);
			pin_config_set(SUNXI_PINCTRL,pin_name[i],conf);
		}
		if(config.data!=GPIO_DATA_DEFAULT){//判断设备树配置有效电平与默认是否相同,不同则配置设备树模式
			conf=SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DAT,config.data);
			pin_config_set(SUNXI_PINCTRL,pin_name[i],conf);
		}
	}

在其中注册了一个注册file_operations

其中只写了一个read函数,read代码和消抖功能如下所示

static ssize_t keyscan_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	char status[6];//第一次获取键值
	char after_status[6];//延时后第二次获取键值
	char result[6];//返回的数据结果,1有按键按下,0无按键按下
	for (int i = 0; i < 6; i++)
		status[i]=gpio_get_value(key_gpio[i]);//读取gpio电平
	mdelay(10);//延时10ms,消抖
	for (int i = 0; i < 6; i++)
		after_status[i]=gpio_get_value(key_gpio[i]);//读取gpio电平	
	for (int i = 0; i < 6; i++){
		if(status[i]==0 && after_status[i]==0)//判断有按键按下,硬件导通下拉电阻到地,所以按下按键数值为0
			result[i]=1;
		else//有抖动或者按键未按下,默认为按键未按下
			result[i]=0;
	}
	if (copy_to_user(buf,&result, sizeof(result))) 
	    return -EFAULT; //拷贝失败
	return 0;
}

驱动写完了之后,测试驱动程序如下所示:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <poll.h>
#include <unistd.h>
#include <time.h> 
#include <sys/time.h> 

char *keyscan_PATH = "/dev/kbq_keyscan0";

int main(int argc, char **argv)
{
	int fd;
	char status[6];

	/* 打开文件 */
	fd = open(keyscan_PATH, O_RDWR);
	if (fd == -1){
		printf("can not open file %s\n", keyscan_PATH);
		return -1;
	}

	while(1){
		usleep(10000);//延时100ms
		read(fd, &status, sizeof(status));//读取
		printf("key1=%d,key2=%d,key3=%d,key4=%d,key5=%d,key6=%d\r\n",status[0],status[1],status[2],status[3],status[4],status[5]);
	}
	close(fd);
	return 0;
}

测试效果如下所示:

四、结语

学习

猜你喜欢

转载自blog.csdn.net/qq_37603131/article/details/121433572