精华内容
下载资源
问答
  • 下载linux版的vscode ,并且安装 我用的板子是nxp的imx6ull,下载nxp的的arm交叉编译工具链和他们的内核(也可以下载官方版本的内核和编译工具链),解压并设置路径,我的路径如下: 内核路径/home/mayunzhi/linux/Linux-...
  • 为以后的 Linux 驱动开发做准备,通过本篇大家可以掌握在 Ubuntu 下进 行 ARM 开发的方法。 第三篇: Uboot、 Linux 和根文件系统移植 本篇讲解如何将 Uboot、 Linux 和根文件系统移植到我们的开发板上,为后面的...
  • 《领航者ZYNQ之Linux驱动开发指南_V1.3.pdf》非常好的正点原子zynq的linux驱动资料,值得拥有,希望对你的工作学习有所帮助。
  • I.MX6U嵌入式Linux驱动开发指南V1.5,原子出品,很详细,很不错的资料,希望对你的学习工作有所帮助。
  • 【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0.pdf 官方下载完整版,2019.10.26发布完整版
  • 正点原子IMX6ULL linux驱动开发文档V1.2, 1600页版本的开发手册,包含裸机开发、系统移植、linux设备驱动开发等等
  • 1、修改文档中的部分错误 2、修改 B1 章 开发板 FTP 服务器移植与搭建 3、修改 30.4.5 小节,添加在 uboot 更新 EMMC 的 u-boot.imx。 4、修改 30.4.8 小节,删除 uboot 中更新 NAND ...驱动的配置路径
  • 正点原子团队基于IMX6U Alpha/Mini开发板推出的教程。 v1.4版本。 很详细,适合作为初学者入门,以及相关问题来查询使用。 本着原子自身开源分享的精神的,我设置的所需下载积分是0。(可能系统会自动改成1或者其他...
  • linux驱动开发面试题及答案 是一些比较常见的linux面试题 及 其对应答案
  • 《嵌入式Linux驱动开发教程》完整的源代码下载。没有密码,解压直接编译即可以使用。
  • 《I.MX6U嵌入式Linux驱动开发指南V1.1.pdf》很不错的资料,原子出品,手把手教程,希望对你的学习工作有所帮助。
  • Linux设备驱动程序(第三版)、嵌入式Linux应用开发完全手册
  • 宋宝华的最新著作, linux驱动开发详解 基于linux4.0内核 源代码。 linux驱动开发详解 的第一版和第二版源代码都大部分都不能在最新的linux kernel上直接编译。这份代码和最新的kernel代码是吻合的,可以不加任何...
  • linux驱动程序开发是当前非常热门的一个领域,大多数给予linux操作系统的嵌入式系统都需要编写驱动程序。笔者编写了这本书,希望借助本书能使驱动程序的开发更容易被开发人员所理解
  • 结合本作者博客 Linux简单设备驱动(1):使用IO内存操作GPIO–LED 的源代码
  • 驱动开发另附书的代码 供arm驱动开发学习用 包括入门
  • 为什么要学习 Linux 环境下的编程 Linux 是一个开放、灵活、跨平台的操作系统,上至庞大的...可以说,上世纪70年代学习的 Unix 知识和技巧,在今天仍然大有用武之地,这与 Windows 平台的开发形成了鲜明的对比。程序

    转载于:http://www.cnblogs.com/xmphoenix/archive/2012/03/27/2420044.html

    为什么要学习 Linux 环境下的编程

    Linux 是一个开放、灵活、跨平台的操作系统,上至庞大的数据中心,下至可放于掌心中的嵌入式设备,无处没有 Linux 的身影。更为重要的是, Linux 是一个与 Unix 既一脉相承又与时俱进的系统。可以说,上世纪70年代学习的 Unix 知识和技巧,在今天仍然大有用武之地,这与 Windows 平台的开发形成了鲜明的对比。程序员不用担心今天微软出一个 .net,明天又出一个 F#,使得自己过去学习的成果付之东流。

    成为一名精通 Linux 程序设计的高级程序员一直是不少朋友孜孜以求的目标。根据中华英才网统计数据,北京地区 Linux 程序员月薪平均为 Windows 程序员的 1.8 倍、Java 程序员的 2.6 倍, Linux 程序员年终奖金平均为 Windows 程序员的 2.9 倍。同时数据显示,随着工作经验的增长, Linux 程序员与 Windows 程序员的收入差距还有扩大的趋势。

    上个星期,水煮鱼与一位 Linux 项目经理聊天过程中,这位 Linux 项目经理告诉水煮鱼,他们项目的利润非常高,急需具备一定 Linux 编程知识的程序员。他说:“其实我们对程序员的编程技巧要求也并非很高,这是可以在工作中培训和提高的,关键是很多程序员连基本的 Linux 编程思想都不了解,我怎么聘用他们呢?我们去大学招聘的时候,给本科应届生开出 8000 元的月薪,但是就是很难招到人。我拿一些经典书籍中一些经典例子来考他们,他们基本上都是一问三不知。其实,如果他们能回答上一半的问题,我还是很愿意考虑是否聘用他们的。而对于项目相关部分的专业知识,我们有专业的内部培训,并不担心这个问题,关键是看应聘者是否具备 Linux 编程的基本思想。”

    水煮鱼认为,这位项目经理朋友提到的问题还是很有代表性的。其实很多程序员朋友,只要能掌握这些书中的基础知识,是很容易脱颖而出的。事实上,项目经理他们也都很忙,并没有空去自己设计高难度的面试题目,而是直接采用经典书籍中的经典例子。

    选择合适的学习书籍

    Linux驱动学习的最大困惑在于书籍的缺乏,市面上最常见的书为 《linux_device_driver 3rd Edition》,这是一本很经典的书,无奈Linux的东东还是过于庞大,这本侧重于实战的书籍也只能停留在基本的接口介绍上,更深入的东东只能靠我们自己摸索了。但万事总有一个开头,没有对Linux驱动整体框架的把握是很难做一个优秀的驱动开发者的。除了这本Jonathan Corbet, Greg Kroah-Hartman, Alessandro Rubini合著的经典大作外,另一本理论实践并重的书就是 《Linux Kernel Development,2nd Edition》 由著名的内核专家Robert Love所著,通过Robert Love的娓娓道来,相信你会感到自己功力的不断提升,但学习驱动,最本质的东西还是操作系统的一些基本的理论了,《Understanding The Linux Kernel, 3rd ed 2005》 更加关注这一点,作为一个注重理论的经典之作,则是Linux驱动研发人员内功的根基。

    但很遗憾的是,以上几本书都更侧重于编程者内功的修养,对于初学者而言,往往有过于深奥之感,关乎国内的书,也似乎只懂得copy些代码做些粗浅的讲解,花拳绣腿的态势又过于明显。

    水煮鱼认为,要学好 Linux 环境下的编程,关键是要看对、选对、学会正确的书籍。可以说,如果你选对了 Linux编程的经典书籍,配合你在程序设计工作中的刻苦钻研,成为一名精通 Linux 程序设计的高级程序员并非一件可望不可及的事情。但如果各位程序员朋友没有选对正确的书籍,则你的职业生涯之路就可能面临坎坷。

    今天,水煮鱼向各位朋友推荐的这些书,有的是资深老前辈们当初向水煮鱼的推荐,还有的是 IBM 的内部培训指定参考书,它们都很值得各位朋友抽空认真一读。


    1.《UNIX环境高级编程》

    《UNIX环境高级编程》(第2版),史蒂文斯著,推荐指数:★★★★★

    在这里插入图片描述
    《UNIX环境高级编程》是 Unix/ Linux 程序员案头必备的一本书籍。可以说,Linux 程序员如果没有读过这本书,就好像基督教徒没有读过圣经一样,是很难让人理解的。这本书概括了 Linux 编程所需的一切理论框架、主要系统函数、多进程编程、乃至 Linux 网络通信。对于初学者,如果你能将《操作系统》这门课程结合着这本书来学习,试着用 Linux 程序实践《操作系统》这门书中讲的进程间通信、进程调度、进程同步等内容,相信这将是一个一举两得的事情。哦,忘了告诉大家,上次 CNN 采访 Google 总裁佩林的时候,水煮鱼看到佩林的书架上就有这本书的英文版,可见它真的是一本全球开发者必备的一本书。


    2.《深入理解 Linux 内核》

    《深入理解 Linux 内核》(第三版) ,博韦等著,推荐指数:★★★★
    在这里插入图片描述
    学习 Linux ,就要学习 Linux 的精华。而 Linux 的精华,则在于 Linux 的内核。《深入理解 Linux 内核》就是一本辅助学习 Linux 内核的经典书籍。有的初学者,在没有人指导的情况下,就钻入 Linux Kernel代码的海洋中埋头苦学,结果学了半天仍然是一头雾水。当然了,在大师指导下学习就不一样了。本书以最新的 Linux 2.6 版架构为基础,分门别类地向初学者介绍了 Linux 内核的架构、编程思想、以及功能模块。相信你在本书的指导下学习,对于你读懂 Linux 操作系统的精华部分,会取得事半功倍的效果。事实上,不少知名公司招聘的题目里面,很多就出自这本书,可见它真的是论述 Linux 内核的经典书籍。


    3.《Linux 设备驱动程序》

    《Linux 设备驱动程序》,科波特著,推荐指数:★★★
    在这里插入图片描述
    程序开发,高薪在 Linux ; Linux 程序员,高薪在驱动开发。可以说,水煮鱼见过的 Linux 驱动程序员,薪水在万元以下的,一个都没有。普遍观点认为, Linux 驱动开发很难学,这并不正确。初学者认为 Linux 驱动开发很难学,关键在于其没有选对正确的入门书籍。《 Linux 设备驱动程序》这本书,过去已经有多位 Linux 项目经理向水煮鱼推荐过,你要想学MOD编程,想挑战高薪职位,这本书读一读是很有必要的。当然,这本书不是一本针对初学者的书籍,初学者应首先学习前面提到的《UNIX环境高级编程》再看这本书,你才能真正的学懂学好 Linux 驱动开发。


    4.《嵌入式 Linux 应用开发完全手册》

    《嵌入式 Linux 应用开发完全手册》,韦东山著,推荐指数:★★★
    在这里插入图片描述

    要说 Linux 应用最广泛的地方,那一定是无处不在的嵌入式设备了,《嵌入式 Linux 应用开发完全手册》就是一本教会您怎样开发 Linux 嵌入式系统软件的一本好书。水煮鱼与本书作者韦东山老师曾有过一面之缘,当时韦老师正在写作这本书。这本书综合了常见的嵌入式开发经验技巧,以及常见的嵌入式系统应用,系统移植,调试及异常处理等,内容非常丰富,是中文 Linux 领域难得的一本好书。嵌入式处理器种类繁多,韦老师着重讲了目前国内最常用的ARM系统,实用性很强,是国内嵌入式程序员不容错过的一本工具书。


    5.《人月神话》

    《人月神话》(32周年中文纪念版),布鲁克斯著,推荐指数:★★★★★
    在这里插入图片描述

    水煮鱼上面为大家推荐了几本 Linux 编程经典书籍,但要问编程的最终目的是什么,那一定还是要应用到实际项目中。做项目,那就一定离不开《人月神话》这本书。《人月神话》的作者布鲁克斯,是水煮鱼所在公司的超级大牛人,也是图灵奖的获得者。去年,他到中国访问的时候曾经表示,《人月神话》这本书,浓缩了项目实践中的正反两方面经验,是项目经理和系统分析师必读的一本书。水煮鱼认为,各位年轻的朋友,最终还是会要管项目的,与其你临时再学习,还不如早点接触这方面的知识比较好。可以说,Linux 的项目,对开发者的要求都很高,大家不要去跟 Windows 程序员学一些不好的习惯,特别是项目实施方面的一些不好的习惯,而应该深入体会大师的书籍。目前,《人月神话》的32周年纪念版已经上市了,有志于成为项目经理和产品经理的人,不应该错过这本做项目的圣经。

    总结

    古人云:“生而知之者,上也;学而知之者,次也;困而学之,又其次也;困而不学,民斯为下矣。”水煮鱼认为,正如古人所言,对于成功的 Linux 程序员,勤奋苦读的结果,将为您的 Linux 学习之路锦上添花。对于还没有入门的投资者,尽快熟读一两本入门的 Linux 书籍,对于你少走弯路是很重要的。对于已经进入项目,但还不能熟练掌握 Linux 开发的程序员,越早补习自己缺乏的东西,您将越早受益。对于基础薄弱,又拒绝学习的程序员,或许“民斯为下矣”就将是您惨淡的结局。

    展开全文
  • linux 内核及驱动开发经典书籍: Advanced Programming in the UNIX Environment, 3rd Edition Linux Device Drivers 3rd Linux.Kernel.Development.3rd.Edition Linux设备驱动开发详解-基于最新的Linux4.0内核_PDF_...
  • Linux驱动开发入门与实战(第2版 ) 实例源代码 + 教学PPT linux驱动开发
  • linux设备驱动之PCIE驱动开发 PCIE驱动开发(内含Makefile,直接编译即可使用) http://download.csdn.net/download/u010872301/10116259
  • 早期的ubuntu版本上没有好的可用的IDE,那个时候嵌入式linux驱动开发人员多使用vim进行编码;对于没有图像界面的linux服务器,开发人员更是只能选择vim这一编辑工具。但是,接触过IDE的人会意识到vim的不方便:需要...

    背景

    早期的ubuntu版本上没有好的可用的IDE,那个时候嵌入式linux驱动开发人员多使用vim进行编码;而对于没有图像界面的linux服务器,开发人员更是只能选择vim这一编辑工具。但是,接触过IDE的人会感觉到vim的不方便:需要记一些指令,而且没有代码自动补全,没有代码提示,没有括号自动补全,没有回车后自动缩进等等。

    后来ubuntu上有了gedit这个好了许多的编辑工具,使开发人员有了在windows上编辑文本文档的感觉,但它仍然缺乏IDE拥有的代码自动补全等功能。

    虽然真正的linux开发大神们都是号称使用vim或者gedit加上插件(如代码自动补全插件,关键词高亮插件,括号自动补全插件)的方式来编程的,但在选择更为丰富的现在,选择一款界面漂亮、支持各种友好操作(如代码自动补全)的IDE也是一个不错的选择,它能大大提升嵌入式linux驱动人员的编码体验。而对于没有图像界面的linux服务器,我们也可以选择先在本地用IDE交叉编译好了,然后再上传到服务器上。

    本文主要介绍visual studio code(vs code)这款IDE在嵌入式linux驱动开发中的使用。其他可能也能用于嵌入式linux驱动开发的IDE,还有eclipse。

    本文所有操作是建立在你的开发环境已经搭建好了的前提下的。如果你的开发环境没有搭建好,可以参考我另一篇博客《ubuntu20.04.1 64位搭建嵌入式linux开发环境》

    下面总结下VS CODE能帮我们做什么:

    1. 提供函数名的自动补全以及参数的提示
    2. 提供已存在变量名的自动补全
    3. 换行自动对齐
    4. 输入左括号时自动补齐右括号
    5. 可以用于阅读linux源码,因为他有很方便的查找功能:菜单栏中Edit->Find以及Edit->Find in Files;Ctrl+左键也可以尝试查找函数和变量定义的位置(列出来很多地方让你自己看,准确率挺高)。
    6. 可以自定义快捷键,比如我不想用键盘上的上下左右来进行光标移动,可以将他们用更方便的快捷键替代(如Alt+w,a,s,d)。

    目前发现的VS CODE不能做的事情:

    暂时没有,我需要的功能他都有提供。

    原本以为VS CODE无法自动补全结构体名和结构体变量名,也无法自动补全结构体的成员名。后来发现是没有正确设置"compilerPath"导致的。见“设置vs code工程的头文件查找路径及编译器路径”一节中最后部分。

    另外IDE本身也有点问题:当定义一个结构体变量时,输入struct xxx aaa;时,结构体名xxx不会自动补全;但是当你不输入struct,而是直接输入xxx时又会自动补全。

    我猜测这是IDE认为你输入struct后可能会定义一个结构体,此时确实是不需要代码补全的。代码补全只有在定义结构体变量的时候才需要。不过我们也不用担心,我们可以通过按键Ctrl+Space强制弹出代码提示。在一些自动补全突然不好使的情况下,这种强制的方法挺好用的。

    系统版本

    我是通过在win10系统上安装vmware workstation pro 15,然后在上面安装ubuntu20.04.1 64位虚拟机来建立开发环境的。

    之所以选择20.04.1这么高的版本,是因为我平时还会做opencv的开发,较高版本的opencv需要较高版本的ubuntu来支持,否则会有各种麻烦的问题。选择这种高版本的ubuntu的话,我就不用建立各种版本的虚拟机,只需要一个虚拟机就能搞定所有开发。arm linux开发板厂商的提供给我们的开发环境总是低版本的vmware加低版本的ubuntu(虽然也是64位),高版本的开发环境的搭建需要我们自己去探索。我的这番尝试也证实了高版本的ubuntu也是能胜任开发环境的。

    vs code的安装和使用

    1.安装

    Visual Studio Code是微软开发的一款可以用于Linux平台的免费IDE。我们可以在vs code的官网上下载,也可以在ubuntu software软件中心下载。我是从后者下载的。安装完之后,点击ubuntu界面的左上角的activity,在搜索框中输入visual studio code,打开vs code并将其添加到favorite中,方便以后使用。

    2.新建工程

    vs code是基于文件夹进行工程管理的。

    如果你只需要用到一个文件夹,那么直接在你的目录中(比如Home)创建一个新文件夹,然后点击vs code菜单栏的File->Open Folder,打开你刚刚新建的文件夹就完成了工程的建立,接下来你就可以往里面添加文件了:如下图,点击左侧工程栏,新建的文件夹(我这里是HELLODRIVER)的右侧的红圈部分。这里我添加了helloWorld.c文件和Makefile文件,用于编写之后的驱动程序。注意第一次使用vs code时,此时它会要求你安装C/C++ for Visual Studio Code。
    图1 往工程中添加文件

    如果你需要同时管理多个文件夹,vs code的workspace机制会帮助到你。此时,除了要新建一个文件夹外,还需要点击菜单栏File->Add Folder to Workspace添加你要管理的第二个文件夹到你的workspace中。接着保存你的workspace并为其命名:File->Save Workspace As。这样,你就得到如下图所示的界面(我的workspace名为HELLODRIVER,下面有两个文件夹:helloDriver和iTop4412_Kernel_3.0)。
    以后使用的时候,你就只需要打开这个工作空间了。

    图2 workspace管理多文件夹

    3.设置vs code工程的头文件查找路径及编译器路径

    我们使用vs code,是希望能使用“代码自动补全”这一功能。要想实现这一点,IDE自然要知道包含那些函数、变量声明的头文件所在的路径。

    vs code的每一个工程都需要我们自己设置头文件的查找路径,我们也可以保存一份配置文件,新建工程时把它复制过来再改改。

    vs code配置头文件查找路径以及宏定义的文件叫做c_cpp_properties.json,打开它的方法如下:在菜单栏View->Command Palette,然后在出现的搜索框中输入“edit”,注意搜索框中本身有一个“>”,这样搜索框里的内容就是“>edit”,之后在搜索提示列表中选择 c/c++:Edit Configurations(JSON),这样就打开了c_cpp_properties.json这个配置文件。

    图3 打开头文件路径配置文件的方法
    c_cpp_properties.json文件内容如下:

    {
        "configurations": [
            {
                "name": "Linux",
                "includePath": [
                    "${workspaceFolder}/**",
                    "/home/liyu/iTop4412_Kernel_3.0/include",
                    "/home/liyu/iTop4412_Kernel_3.0/arch/arm/include",
                    "/home/liyu/iTop4412_Kernel_3.0/arch/arm/mach-exynos/include"                
                ],
                "defines": [],
                "compilerPath": "/usr/local/arm/gcc-4.6.2-glibc-2.13-linaro-multilib-2011.12/fsl-linaro-toolchain/bin/arm-none-linux-gnueabi-gcc",
                "cStandard": "c11",
                "cppStandard": "c++17",
                "intelliSenseMode": "gcc-x64"
            }
        ],
        "version": 4
    }
    

    如上面代码块所示,我们需要在“includePath”中填写需要包含的头文件路径,用于让IDE知道如何补全代码。"${workspaceFolder}/**"这一条是默认就有的。我们还需要添加linux源码的通用头文件包含路径:"/home/liyu/iTop4412_Kernel_3.0/include"以及具体平台的头文件包含路径:"/home/liyu/iTop4412_Kernel_3.0/arch/arm/include"和 "/home/liyu/iTop4412_Kernel_3.0/arch/arm/mach-exynos/include"

    除此之外,我们还需要在 "compilerPath"中填写我们使用的交叉编译工具中的具体编译器的路径,如"compilerPath": "/usr/local/arm/gcc-4.6.2-glibc-2.13-linaro-multilib-2011.12/fsl-linaro-toolchain/bin/arm-none-linux-gnueabi-gcc"。把鼠标放在 "compilerPath"上,会提示:Full path of the compiler being used, e.g. /usr/bin/gcc, to enable more accurate IntelliSense. 可见虽然我们直接用他进行编译工作,但是他直接影响到我们的代码补全(IntelliSense),实测具体表现为:结构体名不会自动补全,成员名也不会自动补全。另外,如果不设置正确的编译器路径,当需要使用一些标准库时,如stdlib.h,IDE定位到的标准库就不是你的交叉编译工具中的标准库,而是你的PC上自带的标准库了,这会带来一些错误。

    至于宏定义“defines”,我目前还没用到,以后可能会用到。

    4.Intelli Sense Engine Fallback设置为ENABLE

    有时候IDE的自动补全功能会失效(包括函数名和变量名等),我猜测这种情况会在找不到头文件时出现。

    为了解决这个问题,选择菜单栏File->Preferences->Settings,在弹出的窗口中选择User->Extensions->C/C++,然后再上方的搜索栏中输入“Intelli Sense Engine Fallback”,将其设置为ENABLE即可。

    他的英文描述我不是太懂,感觉意思是当包含头文件出现错误时,IDE仍能正常智能解析和提示(自动补全)。

    编辑驱动文件和Makefile

    “新建工程”一节中提到的helloWorld.c和Makefile内容如下:

    //helloWorld.c
    #include <linux/init.h>
    #include <linux/module.h>
    
    MODULE_LICENSE("DUAL BSD/GPL");
    MODULE_AUTHOR("LIYU");
    
    static int hello_init(void)
    {
        printk(KERN_EMERG "hello world enter!");
        return 0;
    }
    
    static int hello_exit(void)
    {
        printk(KERN_EMERG "hello world exit!");
        return 0;
    }
    
    module_init(hello_init);
    module_exit(hello_exit);
    
    
    //Makefile
    obj-m += helloWorld.o
    KDIR := /home/liyu/iTop4412_Kernel_3.0
    PWD ?= $(shell pwd)
    all:
    	make -C $(KDIR) M=$(PWD) modules
    clean:
    	rm -rf *.o
    	rm -rf *.ko
    	rm -rf *.mod.c
    	rm -rf *.symvers
    	rm -rf *.order
    

    在敲上述代码的时候,头文件名是自动补全的,MODULE_LICENSE这些也是可以自动补全的,还能看到函数允许填写的参数;括号自动补全、回车自动缩进也都是有的,可谓友好极了。这里就不解释代码了,毕竟我们的重心不在这里,只要证明可行就行了。

    需要注意的是helloWorld.c文件里会有报错,比如包含头文件时,说找不到一些头文件里面包含的头文件:cannot open source file "asm/rwsem.h" (dependency of "linux/module.h")C/C++(1696)。这是正常的,并且不影响我们使用Makefile构建驱动模块。因为我们仅仅使用IDE进行编辑工作,并不用它来进行编译。

    最后,我们点击vs code下方的terminal或者菜单栏Terminal->New Terminal来使用内置的终端,在里面输入make,出现下面的结果表示编译成功。此时你的目录下也会多出来很多文件,当然也包含你的.ko文件。

    在这里插入图片描述
    将该.ko文件拷贝到开发板,运行insmod helloWorld.ko,系统打印出hello world enter!,证明驱动模块编译成功。

    展开全文
  • Linux驱动开发入门与实战.part2,对于学习Linux编程的同学会有很大的帮助。
  • linux驱动开发架构

    万次阅读 多人点赞 2019-05-30 14:38:37
    最近开始开发驱动,现总结通用驱动开发模型如下 驱动整体模型: 添加一个设备,多数需要用户空间下发指令等操作。那么有两个问题: kernel如何控制设备 用户空间如何和kernel中的驱动交互 问题1: kernel中有各种...

    驱动模型

    最近开始开发驱动,现总结通用驱动开发模型如下
    驱动整体模型:
    在这里插入图片描述
    添加一个设备,多数需要用户空间下发指令等操作。那么有两个问题:

    1. kernel如何控制设备
    2. 用户空间如何和kernel中的驱动交互

    问题1:
    kernel中有各种总线,设备挂载在总线上,驱动通过kernel总线提供的接口初始化控制设备。
    问题2:
    kernel中提供文件设备驱动,在驱动中增加一个文件设备,如字符设备、proc、sys等文件设备。

    基于以上两个问题,驱动包含两部分
    在这里插入图片描述

    开发设备驱动

    系统端驱动开发步骤

    1、阅读设备相关的规格书、demo
    2、确定设备挂载总线、文件交互设备
    3、参照demo,编写驱动代码,代码包含两部分:设备树添加结点、逻辑代码

    注意: 设备树结点中的字段可以是标准的(内核已有代码解析),也可以包含自定义的(设备驱动逻辑代码解析)。

    设备端基于单片机驱动开发

    设备端硬件形态有两种:
    1、设备端有一个独立的小系统,通过一个单片机运行
    2、设备端仅由电子原件和电路图实现

    形态1:
    一般情况下,该部分代码,设备厂商已提供,无需系统端负责开发。系统端通过总线和设备进行交互。

    形态2:
    设备端上电后,驱动实现初始化,控制设备端寄存器,配置设备以满足对设备功能、数据的需求

    应用场景

    以上描述的驱动开发架构模式,常用于应用级驱动开发。一些控制器类型,如i2c控制器、spi总线控制器等kernel中框架层的,一般不需要文件系统设备的存在,只需要开发设备驱动即可;为debug方便,一般也会搭配文件系统设备,方便命令行查看设备状态。

    在开发中,可以根据实际需求决定。

    DEMO

    需求描述

    给一个i2c编写驱动,i2c提供一些接口给userspace使用

    添加设备树结点
    // SoC上的i2c控制器的地址
    i2c@138B0000 {
    	#address-cells = <1>;
        #size-cells = <0>;
        samsung,i2c-sda-delay = <100>;
        samsung,i2c-max-bus-freq = <20000>;
        pinctrl-0 =<&i2c5_bus>;
        pinctrl-names="default";
        // 这个一定要okay,其实是对"./arch/arm/boot/dts/exynos4.dtsi +387"处的status = "disabled"的重写,
        // 相同的节点的不同属性信息都会被合并,相同节点的相同的属性会被重写
        status="okay";
        // 设备子节点,/表示板子,它的子节点node1表示SoC上的某个控制器,
        // 控制器中的子节点node2表示挂接在这个控制器上的设备(们)。68即是设备地址。
        // 父结点是一个i2c总线,在此处定义i2c设备,设备i2c客户端自动和总线关联;
        // 否则,多个总线,设备i2c客户端驱动如何和总线关联?(待学习了解)
        mpu6050@68{
        	// 这个属性就是我们和驱动匹配的钥匙,一个字符都不能错
            compatible="invensense,mpu6050";
            // 这个属性是从设备的地址,我们可以通过查阅手册"MPU-6050_DataSheet_V3_4"得到
            reg=<0x68>;
        };
    };
    
    驱动实现
    //mpu6050_common.h
    #define MPU6050_MAGIC 'K'
    
    union mpu6050_data
    {
        struct {
            short x;
            short y;
            short z;
        }accel;
        struct {
            short x;
            short y;
            short z;
        }gyro;
        unsigned short temp;
    };
    
    #define GET_ACCEL _IOR(MPU6050_MAGIC, 0, union mpu6050_data)
    #define GET_GYRO  _IOR(MPU6050_MAGIC, 1, union mpu6050_data) 
    #define GET_TEMP  _IOR(MPU6050_MAGIC, 2, union mpu6050_data)
    
    //mpu6050_drv.h
    
    #define SMPLRT_DIV      0x19    //陀螺仪采样率,典型值:0x07(125Hz)
    #define CONFIG          0x1A    //低通滤波频率,典型值:0x06(5Hz)
    #define GYRO_CONFIG     0x1B    //陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)
    #define ACCEL_CONFIG        0x1C    //加速计自检、测量范围及高通滤波,典型值:0x18(不自检,2G,5Hz)
    #define ACCEL_XOUT_H        0x3B
    #define ACCEL_XOUT_L        0x3C
    #define ACCEL_YOUT_H        0x3D
    #define ACCEL_YOUT_L        0x3E
    #define ACCEL_ZOUT_H        0x3F
    #define ACCEL_ZOUT_L        0x40
    #define TEMP_OUT_H      0x41
    #define TEMP_OUT_L      0x42
    #define GYRO_XOUT_H     0x43
    #define GYRO_XOUT_L     0x44
    #define GYRO_YOUT_H     0x45
    #define GYRO_YOUT_L     0x46
    #define GYRO_ZOUT_H     0x47    //陀螺仪z轴角速度数据寄存器(高位)
    #define GYRO_ZOUT_L     0x48    //陀螺仪z轴角速度数据寄存器(低位)
    #define PWR_MGMT_1      0x6B    //电源管理,典型值:0x00(正常启用)
    #define WHO_AM_I        0x75    //IIC地址寄存器(默认数值0x68,只读)
    #define SlaveAddress        0x68    //MPU6050-I2C地址寄存器
    #define W_FLG           0
    #define R_FLG           1
    
    //mpu6050.c
    struct mpu6050_pri {
        struct cdev dev;
        struct i2c_client *client;
    };
    struct mpu6050_pri dev;
    static void mpu6050_write_byte(struct i2c_client *client,const unsigned char reg,const unsigned char val)
    { 
        char txbuf[2] = {reg,val};
        struct i2c_msg msg[2] = {
            [0] = {
                .addr = client->addr,
                .flags= W_FLG,
                .len = sizeof(txbuf),
                .buf = txbuf,
            },
        };
        i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
    }
    static char mpu6050_read_byte(struct i2c_client *client,const unsigned char reg)
    {
        char txbuf[1] = {reg};
        char rxbuf[1] = {0};
        struct i2c_msg msg[2] = {
            [0] = {
                .addr = client->addr,
                .flags = W_FLG,
                .len = sizeof(txbuf),
                .buf = txbuf,
            },
            [1] = {
                .addr = client->addr,
                .flags = I2C_M_RD,
                .len = sizeof(rxbuf),
                .buf = rxbuf,
            },
        };
    
        i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
        return rxbuf[0];
    }
    static int dev_open(struct inode *ip, struct file *fp)
    {
        return 0;
    }
    static int dev_release(struct inode *ip, struct file *fp)
    {
        return 0;
    }
    static long dev_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
    {
        int res = 0;
        union mpu6050_data data = {{0}};
        switch(cmd){
        case GET_ACCEL:
            data.accel.x = mpu6050_read_byte(dev.client,ACCEL_XOUT_L);
            data.accel.x|= mpu6050_read_byte(dev.client,ACCEL_XOUT_H)<<8;
            data.accel.y = mpu6050_read_byte(dev.client,ACCEL_YOUT_L);
            data.accel.y|= mpu6050_read_byte(dev.client,ACCEL_YOUT_H)<<8;
            data.accel.z = mpu6050_read_byte(dev.client,ACCEL_ZOUT_L);
            data.accel.z|= mpu6050_read_byte(dev.client,ACCEL_ZOUT_H)<<8;
            break;
        case GET_GYRO:
            data.gyro.x = mpu6050_read_byte(dev.client,GYRO_XOUT_L);
            data.gyro.x|= mpu6050_read_byte(dev.client,GYRO_XOUT_H)<<8;
            data.gyro.y = mpu6050_read_byte(dev.client,GYRO_YOUT_L);
            data.gyro.y|= mpu6050_read_byte(dev.client,GYRO_YOUT_H)<<8;
            data.gyro.z = mpu6050_read_byte(dev.client,GYRO_ZOUT_L);
            data.gyro.z|= mpu6050_read_byte(dev.client,GYRO_ZOUT_H)<<8;
            printk("gyro:x %d, y:%d, z:%d\n",data.gyro.x,data.gyro.y,data.gyro.z);
            break;
        case GET_TEMP:
            data.temp = mpu6050_read_byte(dev.client,TEMP_OUT_L);
            data.temp|= mpu6050_read_byte(dev.client,TEMP_OUT_H)<<8;
            printk("temp: %d\n",data.temp);
            break;
        default:
            printk(KERN_INFO "invalid cmd");
            break;
        }
        printk("acc:x %d, y:%d, z:%d\n",data.accel.x,data.accel.y,data.accel.z);
        res = copy_to_user((void *)arg,&data,sizeof(data));
        return sizeof(data);
    }
    
    // 初始化文件系统设备操作接口
    struct file_operations fops = {
        .open = dev_open,
        .release = dev_release,
        .unlocked_ioctl = dev_ioctl, 
    };
    
    #define DEV_CNT 1
    #define DEV_MI 0
    #define DEV_MAME "mpu6050"
    
    struct class *cls;
    dev_t dev_no ;
    
    static void mpu6050_init(struct i2c_client *client)
    {
        mpu6050_write_byte(client, PWR_MGMT_1, 0x00);
        mpu6050_write_byte(client, SMPLRT_DIV, 0x07);
        mpu6050_write_byte(client, CONFIG, 0x06);
        mpu6050_write_byte(client, GYRO_CONFIG, 0x18);
        mpu6050_write_byte(client, ACCEL_CONFIG, 0x0);
    }
    static int mpu6050_probe(struct i2c_client * client, const struct i2c_device_id * id)
    {
        dev.client = client;
        printk(KERN_INFO "xj_match ok\n");
        // 初始化设备文件系统
        cdev_init(&dev.dev,&fops);    
        alloc_chrdev_region(&dev_no,DEV_MI,DEV_CNT,DEV_MAME);    
        cdev_add(&dev.dev,dev_no,DEV_CNT);
        
        // 设备初始化
        mpu6050_init(client);
    
        /*自动创建设备文件*/
        cls = class_create(THIS_MODULE,DEV_MAME);
        device_create(cls,NULL,dev_no,NULL,"%s%d",DEV_MAME,DEV_MI);
        
        printk(KERN_INFO "probe\n");
        
        return 0;
    }
    
    static int mpu6050_remove(struct i2c_client * client)
    {
        device_destroy(cls,dev_no);
        class_destroy(cls);
        unregister_chrdev_region(dev_no,DEV_CNT);
        return 0;
    }
    
    struct of_device_id mpu6050_dt_match[] = {
        {.compatible = "invensense,mpu6050"},
        {},
    };
    
    // 设备驱动注册到总线
    struct i2c_device_id mpu6050_dev_match[] = {};
    struct i2c_driver mpu6050_driver = {
        .probe = mpu6050_probe,
        .remove = mpu6050_remove,
        .driver = {
            .owner = THIS_MODULE,
            .name = "mpu6050drv",
            .of_match_table = of_match_ptr(mpu6050_dt_match), 
        },
        .id_table = mpu6050_dev_match,
    };
    module_i2c_driver(mpu6050_driver);
    MODULE_LICENSE("GPL");
    

    在代码实现中把文件系统设备实现和设备驱动实现混合在一起,个人加以分开实现,利于代码重用和设备驱动替换和多方案切换。

    验证

    通过上面的驱动, 我们可以在应用层操作设备文件从mpu6050寄存器中读取原始数据, 应用层如下

    int main(int argc, char * const argv[])
    {
        int fd = open(argv[1],O_RDWR);
        if(-1== fd){
            perror("open");
            return -1;
        }
        union mpu6050_data data = {{0}};
        while(1){
            ioctl(fd,GET_ACCEL,&data);
            printf("acc:x %d, y:%d, z:%d\n",data.accel.x,data.accel.y,data.accel.z);
            ioctl(fd,GET_GYRO,&data);
            printf("gyro:x %d, y:%d, z:%d\n",data.gyro.x,data.gyro.y,data.gyro.z);
            ioctl(fd,GET_TEMP,&data);
            printf("temp: %d\n",data.temp);
            sleep(1);
        }
        return 0;
    }
    

    最终可以获取传感器的原始数据如下
    在这里插入图片描述
    说明: 以上demo是借鉴 https://www.cnblogs.com/xiaojiang1025/p/6500540.html 的,在实际开发中个人也有实现,代码不在写该篇博客的电脑中,就借用了大神代码。

    展开全文
  • linux驱动开发手册

    2010-12-20 15:38:38
    linux驱动开发常用的函数调用接口和经常使用的概念。是学习linux驱动必不可少的参考资料。
  • Linux驱动开发入门与实践源代码
  • 正点原子IMX6ULL linux驱动开发文档V1.1, 1600页版本的开发手册,包含裸机开发、系统移植、linux设备驱动开发等等
  • 这是一个linux开发的入门资料,适合于初学者。希望能对大家有帮助
  • linux驱动程序开发实例2 中源代码,包含每个书中每个章节中设计的驱动开发源码,可以直接拷贝到机器上运行,方便阅读和学习

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 220,522
精华内容 88,208
关键字:

linux驱动开发

linux 订阅