荔枝派nano(f1c100s)中断按键驱动

硬件环境:
1、荔枝派nano(f1c100s)
软件环境:
1、Linux 4.15

文章目录

  • 一、GPIO子系统
  • 二、程序大致流程
  • 三、按键原理图
  • 四、完整的驱动程序
  • 五、测试结果
  • 六、总结

一、GPIO子系统

GPIO子系统是操作系统内核提供的一个软件接口,用于控制和管理硬件上的通用输入输出引脚;可以理解GPIO子系统的诞生方便了驱动工程师,为什么?因为这样驱动工程师在编写相关GPIO驱动程序时就可以使用GPIO子系统提供的API来完成对GPIO的配置。又有一个问题:GPIO子系统怎么知道我用的是什么芯片什么板子,它凭什么能帮我完成对具体芯片的GPIO的配置?没错,GPIO子系统是一个软件的概念,它对外的接口是统一的,它背后的具体实现是由每个不同的芯片厂家的BSP工程师来完成的。 所以是BSP工程师完成了对寄存器的操作等其它事情。所以芯片厂家对GPIO子系统实现了很强的支持,则你用起来就会很舒服,但并不是所有芯片厂家都这样。

二、程序大致流程

1、从设备树获得 GPIO;
2、从 GPIO 获得中断号;
3、申请中断;
4、实现中断函数;

三、按键原理图

荔枝派nano(f1c100s)中断按键驱动_第1张图片

四、完整的驱动程序

设备树如下,gpios中添加了三组GPIO信息

suniv.dtsi

suniv-f1c100s-licheepi-nano.dts
荔枝派nano(f1c100s)中断按键驱动_第2张图片

完整的驱动程序如下:

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


struct gpio_key{
	int gpio;		//gpio编号
	struct gpio_desc *gpiod;
	int flag;		//flag
	int irq;		//中断号
};

static struct gpio_key *gpio_keys_f1c100s;

static int major = 0;			//主设备号
static struct class *key_class;	//
static int g_key = 0;			//全局变量,记录中断时间状态

static DECLARE_WAIT_QUEUE_HEAD(gpio_key_wait);	//申请等待队列

static ssize_t key_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	int err;
	
	//将当前进程放入等待队列gpio_key_wait中,并且释放CPU进入睡眠状态
	wait_event_interruptible(gpio_key_wait, g_key);
	err = copy_to_user(buf, &g_key, 4);
	g_key = 0;
	
	return 4;
}

static struct file_operations key_drv = {
	.owner	 = THIS_MODULE,
	.read    = key_drv_read,
};

static irqreturn_t gpio_key_isr(int irq, void *dev_id)
{
	struct gpio_key *gpio_key = dev_id;
	int val;
	val = gpiod_get_value(gpio_key->gpiod);
	
	printk("key %d %d\n", gpio_key->gpio, val);
	g_key = (gpio_key->gpio << 8) | val;
	wake_up_interruptible(&gpio_key_wait);	//唤醒休眠的进程,即调用read函数的进程
	
	return IRQ_HANDLED;
}

static int f1c100s_key_probe(struct platform_device *pdev)
{	
	struct device_node *node = pdev->dev.of_node;
	int err;
	int i;
	int count;
	enum of_gpio_flags flag;

	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	
	count = of_gpio_count(node);	//获取GPIO数量
	if(!count)
	{
		printk("%s %s line %d, there isn't any gpio available\n", __FILE__, __FUNCTION__, __LINE__);
		return -1;
	}
	
	gpio_keys_f1c100s = kzalloc(sizeof(struct gpio_key) * count, GFP_KERNEL);
	if(gpio_keys_f1c100s == NULL)
	{
		printk("%s %s line %d, kzalloc fail\n", __FILE__, __FUNCTION__, __LINE__);
		return -1;
	}
	
	for(i = 0; i < count; i++)
	{

		gpio_keys_f1c100s[i].gpio = of_get_gpio_flags(node, i, &flag);
		if (gpio_keys_f1c100s[i].gpio < 0)
		{
			printk("%s %s line %d, of_get_gpio_flags fail\n", __FILE__, __FUNCTION__, __LINE__);
			return -1;
		}
		
		gpio_keys_f1c100s[i].gpiod = gpio_to_desc(gpio_keys_f1c100s[i].gpio);
		gpio_keys_f1c100s[i].flag = flag & OF_GPIO_ACTIVE_LOW;
		gpio_keys_f1c100s[i].irq  = gpio_to_irq(gpio_keys_f1c100s[i].gpio);		//获取中断号
	}
	
	for(i = 0; i < count; i++)
	{

		err = request_irq(gpio_keys_f1c100s[i].irq, gpio_key_isr, IRQF_TRIGGER_RISING, "f1c100s_gpio_key", &gpio_keys_f1c100s[i]);	//申请中断,IRQF_TRIGGER_RISING上升沿触发
	}
	
	major = register_chrdev(0, "f1c100s_key", &key_drv);

	key_class = class_create(THIS_MODULE, "f1c100s_key_class");
	if (IS_ERR(key_class)) {
		unregister_chrdev(major, "f1c100s_key");
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		return PTR_ERR(key_class);
	}
	
	device_create(key_class, NULL, MKDEV(major, 0), NULL, "keys-key");
	
    return 0;
}

static int f1c100s_key_remove(struct platform_device *pdev)
{
	struct device_node *node = pdev->dev.of_node;
	int count;
	int i;
	
	device_destroy(key_class, MKDEV(major, 0));
	class_destroy(key_class);
	unregister_chrdev(major, "f1c100s_key");
	
	count = of_gpio_count(node);
	for (i = 0; i < count; i++)
	{
		free_irq(gpio_keys_f1c100s[i].irq, &gpio_keys_f1c100s[i]);
	}
	kfree(gpio_keys_f1c100s);
   
    return 0;
}

static const struct of_device_id f1c100s_key_table[] = {
    { .compatible = "f1c100s,keysdrv" },
    { },
};

static struct platform_driver f1c100s_key_driver = {
    .probe      = f1c100s_key_probe,
    .remove     = f1c100s_key_remove,
    .driver     = {
        .name   = "f1c100s_keys",
        .of_match_table = f1c100s_key_table,
    },
};

static int __init f1c100s_key_init(void)
{
	int err;
	
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	err = platform_driver_register(&f1c100s_key_driver);

	return err;
}

static void __exit f1c100s_key_exit(void)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	platform_driver_unregister(&f1c100s_key_driver);
}

module_init(f1c100s_key_init);
module_exit(f1c100s_key_exit);

MODULE_LICENSE("GPL");

完整的测试应用程序如下:

#include 
#include 
#include 
#include 
#include 
#include 

/*
 * ./button_test /dev/keys-key
 *
 */
int main(int argc, char **argv)
{
	int fd;
	int val;
	
	/* 1. 判断参数 */
	if (argc != 2) 
	{
		printf("Usage: %s \n", argv[0]);
		return -1;
	}

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

	/* 3. 写文件 */
	while(1)
	{
		read(fd, &val, 4);
		printf("get button : %d\n", val);
	}
	close(fd);
	
	return 0;
}

五、测试结果

内核打印
荔枝派nano(f1c100s)中断按键驱动_第3张图片

六、总结

以上出现的专业术语或名词解释或个人理解有不妥,欢迎指出!

你可能感兴趣的:(荔枝派nano,-,f1c100s,linux)