【LINUX】i.MX6学习笔记(3) 花式点灯大全 —— 裸机点灯(汇编、C),linux内核点灯,资源分离点灯,设备树点灯,pinctrl点灯,platform点灯 的异同

0. 引言

最近一直在做点灯实验,做了各种各样的点灯,感觉其实也是一个循序渐进的过程,通过点灯这么一个小小的工程,一步步的套进linux的各种框架中。
所以萌生了一个比较各个工程的点灯区别和联系的想法。 这个学习步骤应该是各家教程都公用的一个流程。
原意是想把相关代码和流程全部附上来。后来发现有些繁琐,而且没有意义,因为就是把《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.4.pdf》教程上的一些流程搬了上来,干脆就不写这些具体的了,只记录一下自己学习这几部分的整理的一个异同点吧。
需要源码的可以去这本书里找。链接放在文章开头了,0积分下载。

主要就是要分清,“设备” 和 “驱动” 是两个东西。要分清楚。
另外还有“总线”的概念。

设备是资源。
驱动是使用资源的方法。
总线是匹配两者的机制。

重点看:2.4 、2.7

吐槽下:
NXP这个引脚命名真的绕,习惯了PA0,PA1这种的,看NXP这种绕来绕去晕,还有寄存器名字。
NXP自己的图形配置软件也是用的PA0/PA1这种,为啥datasheet引脚命名要这么绕。

1. 点灯基础知识

1.1 硬件电路

本文硬件使用的是正点原子 IMX6UL Alpha 阿尔法开发板。
在这里插入图片描述
使用的引脚是GPIO_3,对应datasheet上的GPIO1_IO03
在这里插入图片描述
当输出低电平时,LED亮。
当输出高电平时,LED灭。

1.2 涉及IMX6ULL的寄存器

配置引脚主要是配置:

  1. 时钟使能(为了省电,平时不用的外设时钟都是关的)
  2. 功能选择(配置为GPIO功能,而不是uart、spi等功能)
  3. 方向配置(输入还是输出)
  4. 设置值(高电平还是低电平)

涉及到的寄存器有:
分两类,配置IO的寄存器和配置GPIO的寄存器。
配置IO的寄存器是MUX选择功能和PAD选择驱动能力等,
配置GPIO的寄存器一共有八个:DR、 GDIR、 PSR、 ICR1、 ICR2、 EDGE_SEL、 IMR 和 ISR。
另外还有打开时钟的寄存器CCM_CCGR1.

  1. 配置时钟,CCM_CCGR1。
  2. 配置 IO,IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 ( 20E_0068h) 。
    进行这个引脚的功能选择ALT。虽然这个引脚名为GPIO1_IO03,但是实际可以选为很多功能。
  3. 配置IO,IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 (20E_02F4h)。
    进行上下拉,速率,驱动能力,摆压率 等功能的选择。
  4. 配置GPIO,DR获取GPIO的输入电平值 和 控制 输出电平值。
  5. 配置GPIO,GDIR控制方向(输入输出)。
  6. 配置GPIO,PSR获取GPIO的输入电平值。
  7. 配置GPIO ,ICR1和ICR2都是配置中断的。
  8. 配置GPIO ,IMR 是配置中断屏蔽的。
  9. 配置GPIO,ISR是中断状态寄存器

2. 点灯的进阶之路

先放结论:

版本变化
裸核汇编点灯直接汇编配置寄存器 裸机程序
裸核C语言点灯直接C语言配置寄存器 裸机程序
linux内核点灯
(老字符设备接口)
在linux内核框架下 直接C语言配置寄存器
linux内核点灯
(新字符设备接口)
在linux内核框架下 直接C语言配置寄存器,使用更灵活的接口注册驱动
资源分离点灯
(左右分离)
实现了 设备 和 驱动的分离
设备树点灯实现了利用单独文件来描述设备,把设备从内核源码中剥离
platform点灯2.6之后引入的一种 用来匹配 设备和驱动的机制
设备树platform点灯实现了利用单独文件来描述设备,把设备从内核源码中剥离,然后利用platform总线自动匹配
pinctrl & GPIO子系统原厂官方给的操作GPIO的接口

2.1 裸核汇编点灯

对应《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.4.pdf》的 第八章。
这个没什么说的,直接找到要操作的几个寄存器,查找寄存器说明,直接把需要的值配置到对应的寄存器里即可。

2.2 裸核C点灯

对应《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.4.pdf》的 第十章。
和汇编点灯很像,要注意的就是前面要写汇编的启动代码,准备C语言运行环境(堆栈),然后再跳转C。

2.3 linux内核点灯(老字符设备接口)

对应《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.4.pdf》的 第四十一章。
在驱动文件中直接定义所需要的寄存器地址,然后ioremap过来使用。
在驱动文件中,通过register_chrdev注册驱动。

这种情况下,可以在/proc/devices下看到这个驱动,但是不会生成设备节点,还需要mknod来生成设备节点。
register_chrdev 是比较老的注册函数,需要指明主设备号。

2.4 linux内核点灯(新字符设备接口)

对应《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.4.pdf》的 第四十二章。
和1.3类似,只是注册设备的接口变了。
这一章要好好理解一下
同样的,定义需要的寄存器地址,然后ioremap过来。
在注册驱动时,使用register_chrdev_region、alloc_chrdev_region、cdev_init、cdev_add、class_create、device_create 接口来注册。比上一节单纯使用register_chrdev多了不少接口。

register_chrdev_region 和 上一节的 register_chrdev 很像,都是静态申请设备号。
alloc_chrdev_region 是动态申请。
那为什么,换个动态申请就解决的事,要搞这么多新的接口出来呢?每个接口是做什么的呢?

因为register_chrdev 接口其实内部已经把cdev_init、cdev_add这些集成了。
新的接口 register_chrdev_region 和 alloc_chrdev_region 其实相当于把 register_chrdev又细分了。
register_chrdev_region + cdev_init + cdev_add ,就等于之前的 register_chrdev。
我们对比看一下这次实验和上一个实验的代码区别。
在这里插入图片描述

我们再看下register_chrdev的实现,可以看出,他内部集成了 cdev_add 等操作,所以很简洁,同样,也很不灵活。

int __register_chrdev(unsigned int major, unsigned int baseminor,
		      unsigned int count, const char *name,
		      const struct file_operations *fops)
{
	struct char_device_struct *cd;
	struct cdev *cdev;
	int err = -ENOMEM;

	cd = __register_chrdev_region(major, baseminor, count, name);
	if (IS_ERR(cd))
		return PTR_ERR(cd);

	cdev = cdev_alloc();
	if (!cdev)
		goto out2;

	cdev->owner = fops->owner;
	cdev->ops = fops;
	kobject_set_name(&cdev->kobj, "%s", name);

	err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);
	if (err)
		goto out;

	cd->cdev = cdev;

	return major ? 0 : cd->major;
out:
	kobject_put(&cdev->kobj);
out2:
	kfree(__unregister_chrdev_region(cd->major, baseminor, count));
	return err;
}

2.5 资源分离点灯(左右分离)

这一部分在书中没有单独涉及,在韦东山的课程中有提过,作为设备树之前的课程。
之前写驱动,都是在驱动里定好要用的寄存器,直接ioremap过来用,其实这个耦合度很高。一个好的驱动,要适应各种各样的开发板和硬件方法,肯定不是这样的用的。
就涉及到了设备资源 和 驱动的分离。
最早的时候,是把这些用到的资源(eg:寄存器,中断 等)都放在另外一个.c中描述,驱动中获取资源,只负责资源的使用,实现左右的分离。
如果修改硬件,则去修改或添加这些.c里的资源即可。
后来板子太多,另加的.c太多,lonus发火了,全面引入设备树,把这些资源描述代码统统从内核中剥离开,作为一个配置文件加载,这样保持linux代码的优雅。

2.6 设备树点灯(左右分离)

对应《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.4.pdf》的 第四十四章。
这一章就是把LED使用到的资源(寄存器)都写在设备树文件中,内核是通过设备树来获取要资源,进而进行驱动。
使用的接口有,of_find_node_by_path,of_find_property,of_property_read_string,of_property_read_u32_array 等。
使用起来类似:
dtsled.nd = of_find_node_by_path("/alphaled");
通过一个明确的结点名称,去找到设备相关的资源来使用。
匹配的机制就是去找一个固定的结点名称。
这里我们引入了,除了 驱动 和 设备 之外的第三个概念:匹配。
怎么样把驱动和设备很好的匹配起来?
Linux官方给出的答案是platform总线。

2.7 platform点灯(Linux内核框架)

对应《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.4.pdf》的 第五十四章。
我理解paltform总线的提出,就是为了解决一些驱动和设备进行匹配的问题,platform是针对那些映射在内存空间的设备而提出的虚拟总线。

这一章也很重要,是最常用的一类设备驱动模板。
注意,platform设备 和 我们学过的字符设备、块设备、网络设备,不是同一种分类标准。
一个设备,可以是字符设备,同时也是platform设备。
这一节就是引入了总线匹配的机制,
设备资源描述好之后,使用 platform_device_register注册,
驱动使用 platform_driver_register 注册,
都挂在platform虚拟总线上,匹配的工作交给总线。如果匹配上了,则运行驱动的probe函数,类似之前的init函数。

2.8 设备树的platform点灯

这个和前面引入设备树的原因类似,设备不再使用单独的 .c文件来描述,设备也不再使用platform_device_register注册,而是通过写在设备树文件中,系统启动时,自动地去根据设备树文件去注册这些设备,进而进行驱动的匹配。

2.9 pinctrl点灯

对应《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.4.pdf》的 第四十五章。
这一部分和驱动的整体框架关系不大,主要是针对IO操作的。
因为IO的操作其实很固定,所以linux官方帮忙写了一个现成的IO操作框架,然后各个芯片厂家都会去根据自己的芯片来适配这个框架,我们直接用即可。这个就是pinctrl和gpio的作用。
文章开头我们去整理了驱动IMX6ULL的GPIO所需要的的寄存器,其实可以跳过这一步,我们查看datasheet也不会比厂家的驱动工程师更熟,由他们完成的驱动,更加完善一些。

3. 总结

这篇文章没有写实验细节,只是个人把这堆实验做完一轮的一些理解和感触。我会尽量地把相关的东西都先列进来,这样有个大概的印象,不然看到这么多的LED实验也会有些困惑。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值