Linux嵌入式驱动开发07——GPIO驱动过程记录(飞凌开发板)

全系列传送门

Linux嵌入式驱动开发01——第一个驱动Hello World(附源码)

Linux嵌入式驱动开发02——驱动编译到内核

Linux嵌入式驱动开发03——杂项设备驱动(附源码)

Linux嵌入式驱动开发04——应用层和内核层数据传输

Linux嵌入式驱动开发05——物理地址到虚拟地址映射

Linux嵌入式驱动开发06——第一个相对完整的驱动实践编写

Linux嵌入式驱动开发07——GPIO驱动过程记录(飞凌开发板)

Linux嵌入式驱动开发08——字符设备(步步为营)

Linux嵌入式驱动开发09——平台总线详解及实战

Linux嵌入式驱动开发10——设备树开发详解

Linux嵌入式驱动开发11——平台总线模型修改为设备树实例

Linux嵌入式驱动开发12——pinctl和gpio子系统实践操作

Linux嵌入式驱动开发13——ioctl接口(gpio控制使用)

Linux嵌入式驱动开发14——中断的原理以及按键中断的实现(tasklet中断下文)

Linux嵌入式驱动开发15——等待队列和工作队列

Linux嵌入式驱动开发16——按键消抖实验(内核定时器)

Linux嵌入式驱动开发17——输入子系统

Linux嵌入式驱动开发18——I2C通信

1. 在/arch/arm/boot/dts/imx6q-pinfunc.h查找

/arch/arm/boot/dts

在这里插入图片描述
打开头文件

imx6q-pinfunc.h

查找EIM_A17
在这里插入图片描述

#define MX6QDL_PAD_EIM_A16__GPIO2_IO22              0x0f4 0x408 0x000 0x5 0x0

2. 在设备树配置文件中添加设备节点定义以及其引脚定义

然后,我们要在设备树中添加引脚定义,打开imx6qdl-sabresd.dtsi

imx6qdl-sabresd.dtsi

添加下面的两部分代码

	gpio_user: gpios{
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_user>;
		compatible = "gpio-user";
		gpio0{
			label = "D01";
			gpios = <&gpio3 15 1>;
			default-direction = "out";
		};
		gpio1{
			label = "D02";
			gpios = <&gpio2 21 1>;
			default-direction = "out";
		};
	};

在这里插入图片描述
保证这些引脚没有被定义,把复用去掉

		pinctrl_user: usergrp {
			fsl,pins = <
				MX6QDL_PAD_EIM_DA15__GPIO3_IO15 0x0b0b10
				MX6QDL_PAD_EIM_A17__GPIO2_IO21	0x0b0b10
			>;
		};

在这里插入图片描述

3. 修改设备树文件添加配置

arch/arm/boot/dts/imx6q-c-sabresd.dts

添加代码

&gpio_user {
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_user>;
        fsl,user;
        status = "okay";
};

在这里插入图片描述

4. drivers/gpio目录下添加gpio驱动

drivers/gpio目录下
在这里插入图片描述

添加gpio驱动gpio-user.c,名字需要与节点定义里的驱动名字保持相同,客户也可以自己编写驱动。

新建driver/gpio/gpio-user.c

Linux嵌入式飞凌开发板GPIO驱动模块modules_gpio_test

#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/ioctl.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/fs.h>

#define GPIO_U_IOCTL_BASE 'x'
#define GPIOC_OPS	_IOWR(GPIO_U_IOCTL_BASE,0,int)

#define MAX_GPIO_NR 32

struct gpio_user_data{
	const char *label;
	bool input;
	unsigned gpio;
	unsigned dft;
};
static struct gpio_misc{
	struct miscdevice misc;
	struct gpio_user_data *data;
	int gpio_count;
} *gpio_misc;



static int gpio_user_open(struct inode *inodp, struct file *filp)
{
	if(!gpio_misc)
				return -ENODEV;
		return 0;
}

static int gpio_user_release(struct inode *inodp, struct file *filp)
{
	if(!gpio_misc)
				return -ENODEV;
		return 0;
}

static long gpio_user_ioctl(struct file *filp, unsigned int cmd,unsigned long arg)
{
		int no, offset;
		unsigned long val;
		unsigned long __user *p = (void __user *)arg;
		struct gpio_user_data *data;
		unsigned long get_value;
		if(!gpio_misc)
				return -ENODEV;
		data = gpio_misc->data;

		if(_IOC_TYPE(cmd) != GPIO_U_IOCTL_BASE)
				return -EINVAL;
		switch(_IOC_NR(cmd)){
				case 0:
						if(get_user(val,p))
						return -EFAULT;
						
						if(data[val].input){
							val = gpio_get_value(data[val].gpio);
							put_user(val,p);
						} else {
							gpio_set_value(data[val].gpio,val >> 31);
						}

						break;

				default:
						return -ENOTTY;
		}
		return 0;
}

static const struct file_operations gpio_user_fops = {
	.owner = THIS_MODULE,
	.open = gpio_user_open,
	.release = gpio_user_release,
	.unlocked_ioctl = gpio_user_ioctl,
};

static void gpio_user_init_default(void)
{
		int i,ret;
		struct gpio_user_data *data;
		data = gpio_misc->data;

		for(i = 0;i < gpio_misc->gpio_count;i++){
				if(!gpio_is_valid(data[i].gpio)){
						continue;
				}
				ret = gpio_request(data[i].gpio,data[i].label);
				if(ret < 0){
						continue;
				}
				if(data[i].input)
				{
						gpio_direction_input(data[i].gpio);
				}
				else
				{
					gpio_direction_output(data[i].gpio,data[i].dft);
				}
		}
}
static void gpio_user_free_default(void)
{
		int i;
		struct gpio_user_data *data;

		data = gpio_misc->data;

		for(i = 0;i < gpio_misc->gpio_count;i++){
				if(!gpio_is_valid(data[i].gpio)){
						continue;
				}
				gpio_free(data[i].gpio);
		}
}
static int gpio_user_probe(struct platform_device *pdev)
{
	int index;
	struct device_node *node = pdev->dev.of_node, *child;
	gpio_misc = devm_kzalloc(&pdev->dev,sizeof(*gpio_misc),GFP_KERNEL);
	if(!gpio_misc){
		return -ENOMEM;
	}

	gpio_misc->gpio_count = of_get_available_child_count(node);
	if(!gpio_misc->gpio_count){
		return -ENODEV;
	}
	if(gpio_misc->gpio_count > MAX_GPIO_NR){
		gpio_misc->gpio_count = MAX_GPIO_NR;
	}

	gpio_misc->data = devm_kzalloc(&pdev->dev,sizeof(struct gpio_user_data) * gpio_misc->gpio_count,GFP_KERNEL);
	if(!gpio_misc->data){
		return -ENOMEM;
	}

	index = 0;
	for_each_available_child_of_node(node,child){
			const char *input;
			struct gpio_user_data *data = &gpio_misc->data[index++];		
			data->label = of_get_property(child,"label",NULL) ? : child->name;
			input = of_get_property(child,"default-direction",NULL) ? : "in";
			if(strcmp(input,"in") == 0)
				data->input = true;
			data->gpio = of_get_gpio_flags(child,0,&data->dft);
	}
	gpio_user_init_default();
	gpio_misc->misc.name = "gpio";
	gpio_misc->misc.minor = MISC_DYNAMIC_MINOR;
	gpio_misc->misc.fops = &gpio_user_fops;
	return misc_register(&gpio_misc->misc);
}
static int gpio_user_remove(struct platform_device *pdev)
{
	gpio_user_free_default();
	misc_deregister(&gpio_misc->misc);
	return 0;
}

static const struct of_device_id of_gpio_user_id_table[] = {
	{ .compatible = "gpio-user",},
	{},
};

MODULE_DEVICE_TABLE(of,of_gpio_user_id_table);

static struct platform_driver gpio_user_driver = {
	.probe = gpio_user_probe,
	.remove = gpio_user_remove,
	.driver = {
		.owner = THIS_MODULE,
		.name = "gpio-user",
		.of_match_table = of_gpio_user_id_table,
	},
};
module_platform_driver(gpio_user_driver);
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:gpio-user");

同时修改Kconfig和Makefile文件。

修改dirver/gpio目录下的Kconfig和Makefile文件:

driver/gpio/Makefile添加:

obj-$(CONFIG_GPIO_USER_INTF) += gpio-user.o

在这里插入图片描述

在driver/gpio/Kconfig添加:

config GPIO_USER_INTF
		tristate "gpio user interface"
		---help---
		this driver for all gpio control

在这里插入图片描述

5. 通过make menuconfig来进行配置

在目录linux-4.1.15下,首先进行的操作

加载环境变量

 . /opt/fsl-imx-x11/4.1.15-2.1.0/environment-setup-cortexa9hf-neon-poky-linux-gnueabi

然后,

清除之前的编译

make distclean

复制arch/arm/config/imx_v7_defconfig ->.config

arch/arm/config/imx_v7_defconfig

到目录linux-4.1.15下,修改名字为

.config

make menuconfig

然后运行

make menuconfig

在这里插入图片描述
修改完成后,保存,然后打开 .config 文件,可以看到我们的gpio已经加载了过来
在这里插入图片描述
然后

复制 .config 到 arch/arm/config/imx_v7_defconfig

cp  .config arch/arm/config/imx_v7_defconfig

最后

执行编译

make distclean
make imx_v7_defconfig
make zImage -j16
make dtbs
make modules -j16

编译结束后

查看验证

在这里插入图片描述
在这里插入图片描述

验证

复制我们的测试程序到开发板

我们的测试程序,测试的是DO1,也就是EIM_AD15,out是输出,0是选择了第一个DO1对应的EIM_AD15,1的话就是选择DO2对应的EIM_A17,最后的0或者1就是输出的高低电平
在这里插入图片描述

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
学习Linux嵌入式驱动开发可以按照以下步骤进行: 1. 掌握Linux基础知识:了解Linux操作系统的基本原理、命令行操作和文件系统等知识。可以通过阅读相关书籍或在线课程来学习。 2. 学习C编程语言:Linux驱动开发主要使用C语言,因此需要掌握C语言的基本语法、指针操作、内存管理等知识。可以通过阅读相关教程或参加编程培训来学习。 3. 了解Linux内核结构和设备模型:深入了解Linux内核的架构和设备模型,包括设备树(Device Tree)、驱动模型(Driver Model)、字符设备、块设备等。可以通过阅读Linux内核源码、内核文档以及相关书籍来学习。 4. 阅读和分析现有驱动代码:选择一些开源的嵌入式驱动代码,如GPIO驱动、I2C驱动等,仔细阅读和分析其代码结构、注册和初始化过程、设备操作函数等,理解其工作原理和设计思路。 5. 实践项目:尝试自己实现一个简单的嵌入式驱动,可以选择一些常用的外设,如LED、按键、传感器等,并编写相应的设备驱动代码。 6. 调试和测试:学习使用调试工具,如GDB、 printk等,掌握嵌入式驱动的调试和测试方法,能够定位和解决常见的驱动问题。 7. 深入研究特定领域的驱动开发:根据自己的兴趣和需求,深入研究特定领域的驱动开发,如网络设备驱动、视频设备驱动等。 此外,可以参考一些优秀的书籍和在线资源,如《Linux设备驱动开发详解》、Linux内核文档、Linux内核源码等,以加深对Linux嵌入式驱动开发的理解和应用。同时,参与开源社区、论坛或者相关的培训课程也是提高技能和获取实践经验的好途径。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值