2609

  • 2024-05-10
  • 回复了主题帖: 【ST NUCLEO-U5A5ZJ-Q开发板测评】8测评学习总结

    dianyuanhh 发表于 2024-5-8 16:03 怎么申请测评才能成功呀,申请了几次都没成功 给出比较详细合理的测评计划,或者比较新颖的点子 能体现产品性能和特色

  • 2024-04-12
  • 回复了主题帖: [STM32MP135F-DK]测评 ④安装SDK及HelloWorld

    不爱胡萝卜的仓鼠 发表于 2024-4-10 23:13 就是这句 source SDK/environment-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi   ... 多谢老哥 问题已解决

  • 2024-04-07
  • 回复了主题帖: 《原子嵌入式Linux驱动开发详解与实战》阅读体验5- 驱动分离和pinctrl

    驱动的分离,是的,越读越感觉到操作系统是一种思想

  • 回复了主题帖: 《原子Linux驱动开发》3-Linux设备树

    beyond_笑谈 发表于 2024-4-7 13:10 EE管理员: 那也得能争取到测评资格才算~~ 先把技能沉淀好,这样机会来了也可以直接把握住,静待

  • 回复了主题帖: 《原子Linux驱动开发》3-Linux设备树

    beyond_笑谈 发表于 2024-4-7 10:36 打算先用手上的IMX8试一下,攻城狮都比较穷,能省则省,除非EE管理员弄板子上测评 所言极是 说不定马上就来了

  • 回复了主题帖: 《原子Linux驱动开发》3-Linux设备树

    beyond_笑谈 发表于 2024-4-6 15:01 去年拜访过广州供应商的专家级linux驱动和应用工程师,全程在虚拟机上无鼠标编程 资深的专家玩终端看着都帅

  • 回复了主题帖: 《原子Linux驱动开发》3-Linux设备树

    常见泽1 发表于 2024-4-6 18:04 设备树这里感觉对我这种菜鸟还是有难度的  只能说勉强能看懂部分代码 这不是一起测u5的大佬吗 你u5都能玩的那么6 这里没问题的 只是技术的不同

  • 回复了主题帖: 《原子Linux驱动开发》3-Linux设备树

    beyond_笑谈 发表于 2024-4-6 14:58 用IMX8的开发板跟着原子linux驱动这本书操作一遍有没有必要?手上只有IMX8的开发板和这本原子驱动书籍 可以的 我用的也是st的Linux开发板 但还是建议配套开发 感觉这本书更像是书上阿尔法开发板的专属课程 因为用别的开发板遇到困难得自己在网上找方法 不是那么高效

  • 2024-04-05
  • 回复了主题帖: 《原子Linux驱动开发》6-阻塞与非阻塞、中断、异步

    总结: 至此原子嵌入式的阅读就告一段落了,从一开始看到这本清华大学出版社出版的书,又看到由原子哥主编的心动,到拿到书后对厚度的为之一振,陆陆续续读了已经有若干篇了,但对整本书的知识含量来讲我的阅读简直也是九牛一毛。 就就我简陋的领略,对有意向购买本书的吧友我有几点建议: 1.特别建议买一块Linux开发板搭配着学习,因为这本书理论和实验都有,但实验偏多,大部分都需要在具体的开发板上进行实验来感受作业传递出的思想和原理,例如设备树的修改,platform驱动框架的编写,又或者是裸机驱动点灯,编写Linux下的驱动点灯,设备树下的led点灯,gpio子系统点灯,通过体验几种不同的点灯方式,对Linux的学习起到的是醍醐灌顶的作用。 2.在开始添加虚拟机的时候Ubuntu操作系统,把储存设置大一些,我一开始跟着网上教程安装设置的30G,拉取了一下SDK又编写了几个C文件后,很快就满了,满了之后扩容很麻烦,最后操作失败只能重新安装UBuntu,内存和CPU的话视自己电脑情况定。 3.如果是新人入门Linux的话建议先去学习本书的上一册,本书一共两册,此书是第二册,第一册相对基础,第二册直接进入驱动教学,如果是新手学起来可能还是会有些力不从心。那如果具备一定基础的读者,认真完成此书所有的实验后一定可以成为大佬。 人工智能和大数据的时代,作为技术人员都有被替代的焦虑,但是熟悉并掌握操作系统的coder,是难以被淘汰的,在大厂陆续推出自家操作系统的今天,深入Linux的学习,可行性和必要性很高,最后祝各位书友学有所成,学成归来。

  • 2024-04-03
  • 回复了主题帖: 《原子Linux驱动开发》3-Linux设备树

    q1233 发表于 2024-4-3 13:37 DTS(Device Tree Source):DTS是设备树的源文件,使用一种类似于C语言的语法来描述硬件设备的信息 是的 当我真正用vscode打开开发板的源文件时 才感觉到概念的鲜活

  • 回复了主题帖: 《原子Linux驱动开发》4platform驱动框架

    q1233 发表于 2024-4-3 16:42  Linux 这样一个成熟、庞大、复杂的操作系统,代码的重用性非常重要,否则的话就会在 Linux 内核中存 ... 一针见血的

  • 发表了主题帖: 《原子Linux驱动开发》6-阻塞与非阻塞、中断、异步

    一、中断 学习单片机中的中断是非常重要的,因为中断可以让单片机在执行程序的同时响应外部事件,提高系统的实时性和效率。大家在学习单片机的时候例如stm32,了解了中断的概念、原理和种类。学习了中断的工作方式、中断向量表、中断优先级等基本知识。用开发板或仿真软件搭建了一个简单的单片机系统,然后编写了一些简单的程序来了解如何配置和使用中断。并尝试编写中断服务程序(ISR)来处理不同类型的中断请求。也通过一些实验来验证中断的工作原理,例如通过外部按键触发外部中断、定时器中断等。我也尝试了串口中断、ADC中断等不同类型的中断应用。其实有了这个基础,再来学习Linux中的中断就开朗了。 通过阅读内核源码中关于中断处理的部分,理解中断的注册、处理和响应流程。尝试编写一些简单的内核模块,包括注册中断处理程序和处理中断请求的代码。学习如何在内核中注册中断处理函数,并与设备驱动程序结合来实现中断处理。我了解到Linux 内核提供了完善的中断框架,我们只需要申请中断,然后注册中断处理函数即可,使用非常方便,不需要一系列复杂的寄存器配置。 在本书中,我们进行了一个实验:如何在 I.MX6U-ALPHA 开发板上使用中断的方式驱动 KEY0 按键,并通过定时器来实现按键消抖,最终在应用程序中读取按键值并通过终端打印出来。这个实验将帮助我们学习 Linux 内核中断的使用方法。编写一个内核模块,注册中断处理函数,并在中断处理函数中实现按键消抖逻辑。同时,配置定时器来定时检测按键状态。编译完成后,将模块加载到内核中,并检查是否加载成功。编写一个应用程序,通过读取设备节点来获取按键值,并将按键值通过终端打印出来。最后按键值正确地被读取和打印出来了。通过这个实验学习,深入理解了 Linux 内核中断处理的机制和流程。 二、阻塞与非阻塞 在Linux中,阻塞和非阻塞是用来描述I/O操作的两种模式。 这里的“IO”并不是我们学习 STM32 或者其他单片机的时候所说的“GPIO”(也就是引脚)。这里的 IO 指的是 Input/Output,也就是输入/输出,是应用程序对驱动设备的输入/输出操作。当应用程序对设备驱动进行操作的时候,如果不能获取到设备资源,那么阻塞式 IO 就会将应用程序对应的线程挂起,直到设备资源可以获取为止。对于非阻塞 IO,应用程序对应的线程不会挂 起,它要么一直轮询等待,直到设备资源可以使用,要么就直接放弃。 阻塞模式: 在阻塞模式下,当一个I/O操作发起后,程序会一直等待直到操作完成才返回结果。这意味着程序在执行I/O操作期间会被挂起,无法继续执行其他任务。在这种模式下,如果I/O操作耗时较长,程序就会被阻塞,影响整体的性能。 非阻塞模式: 在非阻塞模式下,程序在发起一个I/O操作后会立即返回,而不会等待操作完成。程序可以继续执行其他任务,然后通过轮询或者回调等机制来查询I/O操作的状态。这样可以避免程序被长时间阻塞,提高系统的并发性能。还可以通过设置文件描述符的属性来实现阻塞和非阻塞模式。使用系统调用fcntl或者ioctl可以设置文件描述符为非阻塞模式。另外,也可以使用select、poll、epoll等多路复用机制实现非阻塞I/O操作。 在上一节中断实验中imx6uirqApp 这个应用程序的 CPU 使用率竟然高达 99.6%,这仅仅是一个读取按键值的应用程序,这么高的 CPU 使用率显然是有问题的,原因就在于我们是直接在 while 循环中通过 read 函数读取按键值,因此 imx6uirqApp 这个软件会一直运行,一直读取按键值, CPU 使用率肯定就会很高。最好的方法就是在没有有效的按键事件发生的时候,imx6uirqApp 这个应用程序应该处于休眠状态,当有按键事件发生以后 imx6uirqApp 这个应用程序才运行,打印出按键值,这样就会降低 CPU 使用率,本小节我们就使用阻塞 IO 来实现此功能。一直在 while 循环中主动读取按键值,导致 CPU 被持续占用。为了降低 CPU 使用率,书中又使用阻塞 I/O 的方式来实现按需读取按键值的功能,让应用程序在没有按键事件时处于休眠状态,只有当有按键事件发生时才唤醒应用程序进行处理。 在应用程序中初始化一个等待队列,例如命名为 r_wait,用于在没有按键事件发生时让应用程序处于休眠状态。当定时器中断处理函数执行时,表示有按键按下事件发生。在处理函数中,首先判断是否是一次有效的按键事件。在判断为有效按键事件时,调用 wake_up 或 wake_up_interruptible 函数来唤醒等待队列 r_wait。应用程序的主循环中,可以在等待队列 r_wait 上进行等待,当被唤醒时再去读取按键值并处理。当没有按键事件发生时,应用程序会在等待队列上休眠,不会占用 CPU 资源,只有当有按键事件发生时才会被唤醒。通过使用 wake_up 或 wake_up_interruptible 唤醒等待队列的方式,可以实现按需唤醒应用程序处理按键事件,避免应用程序一直处于忙碌状态导致 CPU 使用率过高的问题。这种方法也符合事件驱动的编程思想,使系统更加高效和稳定。采用这种方法来改进按键事件处理逻辑,提高系统的效率和性能,同时降低 CPU 使用率,使系统更加节能和可靠。 还可以在应用程序中使用 select 函数,监听文件描述符的状态变化,当按键事件发生时再去读取按键值。在 select 函数中设置适当的超时时间,以便定时唤醒应用程序检查按键事件。应用程序的主循环中,首先调用 select 函数阻塞等待按键事件的发生,当有按键事件发生时再调用 read 函数读取按键值并打印。当没有按键事件发生时,应用程序会处于阻塞状态,不会占用 CPU 资源,只有当有按键事件发生时才会被唤醒。使用阻塞 I/O 的方式,可以有效降低应用程序的 CPU 使用率,让应用程序在需要处理事件时才会被唤醒,提高系统的效率和性能。 三、异步 异步通知是指在事件发生时通知接收方,而无需接收方主动轮询或等待事件发生。常见的异步通知方式包括信号、回调函数、消息队列等。信号是一种软件中断,用于通知进程发生了某种事件。当进程接收到信号时,会中断当前执行的程序流,转而执行信号处理函数。信号可以同步或异步发送给进程。在操作系统中,信号是一种常用的实现异步通知的方式。当某个事件发生时,可以通过发送信号的方式通知接收方进行处理。当进程接收到信号时,会立即中断当前执行的程序流,转而执行信号处理函数。这种机制实现了异步通知的效果,使得进程可以及时响应事件。使用异步通知可以避免进程不断轮询或阻塞等待事件发生,提高系统效率和响应速度。信号作为一种异步通知机制,在进程间通信和事件处理中发挥着重要作用。使用中断的时候需要设置中断处理函数,同样的,如果要在应用程序中使用信号,那么就必须设置信号所使用的信号处理函数,在应用程序中使用 signal 函数来设置指定信号的处理函数。 示例代码 Linux 信号 #define SIGHUP 1 /* 终端挂起或控制进程终止 */ #define SIGINT 2 /* 终端中断(Ctrl+C 组合键) */ #define SIGQUIT 3 /* 终端退出(Ctrl+\组合键) */ #define SIGILL 4 /* 非法指令 */ #define SIGTRAP 5 /* debug 使用,有断点指令产生 */ #define SIGABRT 6 /* 由 abort(3)发出的退出指令 */ #define SIGIOT 6 /* IOT 指令 */ #define SIGBUS 7 /* 总线错误 */ #define SIGFPE 8 /* 浮点运算错误 */ #define SIGKILL 9 /* 杀死、终止进程 */ #define SIGUSR1 10 /* 用户自定义信号 1 */ #define SIGSEGV 11 /* 段违例(无效的内存段) */ #define SIGUSR2 12 /* 用户自定义信号 2 */ #define SIGPIPE 13 /* 向非读管道写入数据 */ #define SIGALRM 14 /* 闹钟 */ #define SIGTERM 15 /* 软件终止 */ #define SIGSTKFLT 16 /* 栈异常 */ #define SIGCHLD 17 /* 子进程结束 */ #define SIGCONT 18 /* 进程继续 */ #define SIGSTOP 19 /* 停止进程的执行,只是暂停 */ #define SIGTSTP 20 /* 停止进程的运行(Ctrl+Z 组合键) */ #define SIGTTIN 21 /* 后台进程需要从终端读取数据 */ #define SIGTTOU 22 /* 后台进程需要向终端写数据 */ #define SIGURG 23 /* 有"紧急"数据 */ #define SIGXCPU 24 /* 超过 CPU 资源限制 */ #define SIGXFSZ 25 /* 文件大小超额 */ #define SIGVTALRM 26 /* 虚拟时钟信号 */ #define SIGPROF 27 /* 时钟信号描述 */ #define SIGWINCH 28 /* 窗口大小改变 */ #define SIGIO 29 /* 可以进行输入/输出操作 */ #define SIGPOLL SIGIO /* #define SIGLOS 29 */ #define SIGPWR 30 /* 断点重启 */ #define SIGSYS 31 /* 非法的系统调用 */ #define SIGUNUSED 31 /* 未使用信号 */ 实验是根据上一节阻塞实验的实现按键按下后驱动程序向应用程序发送 SIGIO 信号,应用程序接收到信号后读取并打印按键值。在驱动程序中,设置按键触发时发送 SIGIO 信号给应用程序。可以通过 fasync_helper 函数来实现异步通知的注册。在应用程序中,注册处理 SIGIO 信号的处理函数。可以使用 signal 函数来注册信号处理函数。在信号处理函数中,处理接收到的 SIGIO 信号。在信号处理函数中读取按键值并打印。应用程序的主循环中,可以继续在等待队列 r_wait 上等待,同时等待接收到 SIGIO 信号时执行相应的处理。 小结: 此次阅读解锁了诸多概念,具体还得多在虚拟机上实践才能消化,初次阅读,本文中内容如有不当还请大佬们指出。

  • 2024-04-02
  • 回复了主题帖: 颁奖:ST NUCLEO-U5A5ZJ-Q

    感谢

  • 2024-03-31
  • 回复了主题帖: 【ST B-L475E-IOT01A1蓝牙Wi-Fi开发板】5-算6轴加速度+姿态解算

    哈哈学会了可以造游戏手柄了

  • 回复了主题帖: 《原子Linux驱动开发》4platform驱动框架

    cvbfjhtyj 发表于 2024-3-31 10:01 自知则知之自知则知之这咋咋咋咋咋咋咋咋咋在咋咋咋咋咋咋咋咋咋咋咋咋咋咋咋咋咋咋咋咋咋咋咋咋咋咋咋咋咋 ... 你好

  • 发表了主题帖: 《原子Linux驱动开发》5-并发与竞争

    阅读章节并发:第8章:并发与竞争实验 1并发与竞争 跟上一节一样,原子哥这一期先给我们把思想讲明白,这样再理解起来像进程的并发竞争这种抽象的概念,理解起来会非常容易。并发就是多个“用户”同时访问同一个共享资源,比如你们公司有一台打印机,你们公司的所有人都可以使用。现在小李和小王要同时使用这一台打印机,都要打印一份文件。这两份文档肯定是各自打印出来的,不能相互影响。当两个人同时打印的话如果打印机不做处理的话可能会出现小李的文档打印了一行,然后开始打印小王的文档,这样打印出来的文档就错乱了。 可以想到,小王打印出来的文档中电话号码错误了,变成小李的了,这是绝对不允许的。如果有多人同时向打印机发送了多份文档,打印机必须保证一次只能打印一份文档,只有打印完成以 后才能打印其他的文档。 并发访问带来的问题就是竞争,学过FreeRTOS的同学应该知道临界区这个概念 我们一般在编写驱动的时候就要考虑到并发与竞争,而不是驱动都编写完了然后再处理并发与竞争。 C 语言里面简简单单的一句“a=3”,编译成汇编文件以后变成了 3 句,那么程序在执行的时候肯定是按照示例代码 47.2.1.1 中的汇编语句一条一条的执行。假设现在线程 A要向 a 变量写入 10 这个值,而线程 B 也要向 a 变量写入 20 这个值,我们理想中的执行顺序如图 所示: 按照图 所示的流程,确实可以实现线程 A 将 a 变量设置为 10,线程 B 将 a 变量设置为 20。但是实际上的执行流程可能如图 所示: 线程 A 最终将变量 a 设置为了 20,而并不是要求的 10!线程B 没有问题。这就是一个最简单的设置变量值的并发与竞争的例子,要解决这个问题就要保证示例代码 中的三行汇编指令作为一个整体运行,也就是作为一个原子存在。 2自旋锁 当一个线程要访问某个共享资源的时候首先要先获取相应的锁, 锁只能被一个线程持有,只要此线程不释放持有的锁,那么其他的线程就不能获取此锁。对于自旋锁而言,如果自旋锁正在被线程 A 持有,线程 B 想要获取自旋锁,那么线程 B 就会处于忙循环-旋转-等待状态,线程 B 不会进入休眠状态或者说去做其他的处理,而是会一直傻傻的在那里“转圈圈”的等待锁可用。比如现在有个公用电话亭,一次肯定只能进去一个人打电话,现在电话亭里面有人正在打电话,相当于获得了自旋锁。此时你到了电话亭门口,因为里面有人,所以你不能进去打电 话,相当于没有获取自旋锁,这个时候你肯定是站在原地等待,你可能因为无聊的等待而转圈圈消遣时光,反正就是哪里也不能去,要一直等到里面的人打完电话出来。终于,里面的人打 完电话出来了,相当于释放了自旋锁,这个时候你就可以使用电话亭打电话了,相当于获取到了自旋锁。 自旋锁的“自旋”也就是“原地打转”的意思,“原地打转”的目的是为了等待自旋锁可以用,可以访问共享资源。把自旋锁比作一个变量 a,变量 a=1 的时候表示共享资源可用,当 a=0的时候表示共享资源不可用。现在线程 A 要访问共享资源,发现 a=0(自旋锁被其他线程持有),那么线程 A 就会不断的查询 a 的值,直到 a=1。从这里我们可以看到自旋锁的一个缺点:那就等待自旋锁的线程会一直处于自旋状态,这样会浪费处理器时间,降低系统性能,所以自旋锁的持有时间不能太长。所以自旋锁适用于短时期的轻量级加锁,如果遇到需要长时间持有锁的场景那就需要换其他的方法了, 3信号量 大家如果有学习过 FreeRTOS的话就应该对信号量很熟悉,因为信号量是同步的一种方式。 Linux 内核也提供了信号量机制,信号量常常用于控制对共享资源的访问。举一个很常见的例子,某个停车场有 100 个停车位,这 100 个停车位大家都可以用,对于大家来说这100 个停车位就是共享资源。假设现在这个停车场正常运行,你要把车停到这个这个停车场肯定要先看一下现在停了多少车了?还有没有停车位?当前停车数量就是一个信号量,具体的停车数量就是这个信号量值,当这个值到 100 的时候说明停车场满了。停车场满的时你可以等一会看看有没有其他的车开出停车场,当有车开出停车场的时候停车数量就会减一,也就是说信号量减一,此时你就可以把车停进去了,你把车停进去以后停车数量就会加一,也就是信号量加一。这就是一个典型的使用信号量进行共享资源管理的案例,在这个案例中使用的就是计数型信号量。 相比于自旋锁,信号量可以使线程进入休眠状态,比如 A 与 B、 C 合租了一套房子,这个房子只有一个厕所,一次只能一个人使用。某一天早上 A 去上厕所了,过了一会 B 也想用厕所,因为 A 在厕所里面,所以 B 只能等到 A 用来了才能进去。 B 要么就一直在厕所门口等着,等 A 出来,这个时候就相当于自旋锁。 B 也可以告诉 A,让 A 出来以后通知他一下,然后 B 继续回房间睡觉,这个时候相当于信号量。可以看出,使用信号量会提高处理器的使用效率,毕竟不用一直傻乎乎的在那里“自旋”等待。但是,信号量的开销要比自旋锁大,因为信号量使线程进入休眠状态以后会切换线程,切换线程就会有开销。 4总结 在Linux系统中,并发是指系统能够同时处理多个任务的能力,这些任务可以是同时运行的进程或线程。Linux系统是一个多任务操作系统,可以同时运行多个进程,并发是其设计的核心特性之一。 竞争是指多个进程或线程试图同时访问共享资源(如内存、文件、网络连接等)时所发生的现象。在竞争的情况下,如果不加以处理,可能会导致数据不一致、死锁等问题。 在Linux中,处理并发和竞争的一些常见机制和工具包括: 进程和线程:Linux系统支持多进程和多线程,可以通过创建多个进程或线程来实现并发处理。 进程间通信(IPC):Linux提供了多种IPC机制,如管道、消息队列、共享内存等,用于不同进程之间的通信和数据共享。 信号量和互斥量:Linux提供了信号量和互斥量等同步机制,用于控制多个进程或线程对共享资源的访问。 文件锁:Linux提供了文件锁机制,可以通过fcntl系统调用对文件进行加锁,防止多个进程同时对同一文件进行读写操作。 读写锁:Linux提供了读写锁机制,允许多个线程同时读取共享资源,但只允许一个线程写入共享资源。 信号处理:Linux系统使用信号来处理异步事件,可以通过信号处理函数来处理并发情况下的异常情况。 通过合理地使用这些机制和工具,可以有效地处理Linux系统中的并发和竞争,确保系统的稳定性和可靠性。 本书除了本文中提到的所有思想外,对每个概率也有具体的代码说明,结合代码会使我们有更真切的理解,不得不说,原子哥可谓是费劲心思想让读者明白,不过确实也做到了,点赞  

  • 2024-03-30
  • 发表了主题帖: 《原子Linux驱动开发》4platform驱动框架

    阅读章节:第15章 platform设备驱动实验 我们在前面几章编写的设备驱动都非常的简单,都是对 IO进行最简单的读写操作。但是面对I2C、SPI、 LCD 等这些复杂外设的驱动该怎么去写呢,还能用之前的方法去写吗, Linux 系统要考虑到驱动的可重用性,因此提出了驱动的分离与分层这样的软件思路,在这个思路下诞生了我们将来最常打交道的platform 设备驱动,也叫做平台设备驱动。本章我们就来学习一下 Linux 下的驱动分离与分层,以及 platform 框架下的设备驱动该如何编写。 1驱动的分离与分层 1.1驱动的分隔与分离 对于 Linux 这样一个成熟、庞大、复杂的操作系统,代码的重用性非常重要,否则的话就会在 Linux 内核中存在大量无意义的重复代码。尤其是驱动程序,因为驱动程序占用了 Linux 内核代码量的大头,如果不对驱动程序加以管理,任由重复的代码肆意增加,那么用不了多久Linux 内核的文件数量就庞大到无法接受的地步。假如现在有三个平台 A、 B 和 C,这三个平台(这里的平台说的是 SOC)上都有 MPU6050 这个 I2C接口的六轴传感器,按照我们写裸机 I2C 驱动的时候的思路,每个平台都有一个 MPU6050的驱动,因此编写出来的最简单的驱动框架如图 Linux为了避免代码的臃肿将主机驱动和设备驱动分开了下图可以完美的表示现在的驱动架构。   这个就是驱动的分隔,也就是将主机驱动和设备驱动分隔开来,比如 I2C、SPI 等等都会采用驱动分隔的方式来简化驱动的开发。在实际的驱动开发中,一般 I2C 主机控制器驱动已经由半导体厂家编写好了,而设备驱动一般也由设备器件的厂家编写好了,我们只需要提供设备信息即可,比如 I2C 设备的话提供设备连接到了哪个 I2C 接口上,I2C 的速度是多少等等。相当于将设备信息从设备驱动中剥离开来,驱动使用标准方法去获取到设备信息(比如从设备树中获取到设备信息),然后根据获取到的设备信息来初始化设备。 这样就相当于驱动只负责驱动,设备只负责设备,想办法将两者进行匹配即可。这个就是 Linux 中的总线(bus)、驱动(driver)和设备(device)模型,也就是常说的驱动分离。总线就是驱动和设备信息的月老,负责给两者牵线搭桥。如下图所示: 是不是感觉很神奇,书中既讲了设备驱动的分离,还像我们讲述了一种优化思想,不断的优化来形成方便操控和编写维护的系统,深入浅出的行文讲解风格我愿称之为Linux教学天花板。当我们向系统注册一个驱动的时候,总线就会在右侧的设备中查找,看看有没有与之匹配的设备,如果有的话就将两者联系起来。同样的,当向系统中注册一个设备的时候,总线就会在左侧的驱动中查找看有没有与之匹配的设备,有的话也联系起来。Linux 内核中大量的驱动程序都采用总线、驱动和设备模式。而platform驱动框架就是这一模式下的产物。 1.2驱动的分层 大家应该听说过网络的 7 层模型,不同的层负责不同的内容。同样的, Linux 下的驱动往往也是分层的,分层的目的也是为了在不同的层处理不同的内容。以其他书籍或者资料常常使用到的input(输入子系统,后面会有专门的章节详细的讲解)为例,简单介绍一下驱动的分层。 input 子系统负责管理所有跟输入有关的驱动,包括键盘、鼠标、触摸等,最底层的就是设备原始驱动,负责获取输入设备的原始值,获取到的输入事件上报给 input 核心层。 input 核心层会处理各种 IO 模型,并且提供 file_operations 操作集合。我们在编写输入设备驱动的时候只需要处理好输入事件的上报即可,至于如何处理这些上报的输入事件那是上层去考虑的,我们不用管。可以看出借助分层模型可以极大的简化我们的驱动编写,对于驱动编写来说非常的友好。 2platform框架 2.1platform总线 本章前面讲了设备驱动的分离,并且引出了总线(bus)、驱动(driver)和设备(device)模型,比如 I2C、 SPI、 USB 等总线。但是在 SOC 中有些外设是没有总线这个概念的,但是又要使用总 线、驱动和设备模型该怎么办呢?为了解决此问题, Linux 提出了 platform 这个虚拟总线,相应的就有 platform_driver 和 platform_device。 在Linux内核中,platform_driver和platform_device是平台驱动程序和平台设备之间交互的重要数据结构。 platform_driver: platform_driver是一个结构体,用于表示平台驱动程序。在Linux内核中,平台驱动程序通过注册到平台总线上来与平台设备进行匹配和绑定。 platform_driver结构体包含了一些重要的字段,如驱动程序的名称、设备匹配函数、设备初始化函数、设备退出函数等。这些字段定义了平台驱动程序的行为和操作。 当平台设备与平台驱动程序匹配成功后,平台总线会调用platform_driver结构体中定义的初始化函数来初始化设备,并在设备退出时调用退出函数来释放资源。 platform_device: platform_device是一个结构体,用于表示平台设备。在Linux内核中,平台设备通过设备树描述,并在启动时由平台总线进行匹配和注册。 platform_device结构体包含了一些重要的字段,如设备名称、设备资源信息、设备节点等。这些字段描述了平台设备的属性和资源。 当平台设备与平台驱动程序匹配成功后,平台总线会创建一个platform_device结构体实例,并将其传递给匹配的平台驱动程序,使得驱动程序可以正确地管理和操作设备。 关系: platform_driver和platform_device之间的关系是通过平台总线来建立的。平台驱动程序注册到平台总线上,当设备与驱动程序匹配成功时,平台总线会创建一个platform_device实例并将其传递给匹配的驱动程序。 平台驱动程序通过platform_device结构体中的资源信息来访问和操作设备的资源,从而实现对设备的管理和控制。 platform_driver和platform_device是Linux内核中用于描述和管理平台驱动程序和平台设备之间关系的重要数据结构。它们通过平台总线实现设备的匹配和绑定,使得设备能够被正确地管理和操作。在Linux操作系统中,platform虚拟总线是一种用于管理和抽象平台设备和平台驱动程序之间关系的机制。它提供了一种统一的接口和框架,使得平台设备和平台驱动程序之间可以进行匹配和绑定,从而实现设备的正确管理和操作。 3platform驱动 在Linux内核编程中,数据结构扮演着至关重要的角色。platform_driver和platform_device等结构体的定义和使用,是实现设备驱动程序的关键。编写驱动程序时,采用模块化的设计方式能够提高代码的可维护性和可扩展性。模块初始化函数和退出函数的编写,使得驱动程序能够方便地被加载和卸载。设备树在描述和配置平台设备时起着重要的作用。正确编写设备树节点,包括设备节点和资源信息等,能够帮助内核正确识别和管理设备。 4platform框架 通过以上这些机制,Linux操作系统的platform虚拟框架实现了硬件平台相关信息的抽象和管理,使得Linux内核可以更容易地适配和支持不同的硬件平台。这种虚拟框架的设计提高了Linux的可移植性和可扩展性,使得开发人员可以更方便地开发和维护平台相关的驱动程序和设备。在Linux内核中,编写一个platform驱动程序通常需要以下步骤:定义platform_driver结构体,编写设备匹配函数,编写设备初始化函数,注册platform_driver,设备树配置,编译和加载模块。通过这些步骤可以编写一个简单的platform驱动程序,并实现设备的匹配、初始化和管理。当平台设备与驱动程序匹配成功时,驱动程序会对设备进行操作,从而实现设备的功能。

  • 2024-03-29
  • 回复了主题帖: 《原子Linux驱动开发》下载mp135开发板的SDK

    yin_wu_qing 发表于 2024-3-28 11:48 这不是发阅读心得吗?怎么在拉取SDK包,调试代码了啊 这本书讲的驱动,全是例子,想着多在板子上实践实践

  • 回复了主题帖: 《原子Linux驱动开发》2-编译下载运行点灯!

    freebsder 发表于 2024-3-28 15:43 谢谢分享,期待更多后续 谢佬鼓励

  • 2024-03-28
  • 发表了主题帖: 《原子Linux驱动开发》3-Linux设备树

    学习章节:第4章 Linux设备树 前言: 前面章节中我们多次提到“设备树”这个概念,因为时机未到,所以当时并没有详细的讲解什么是“设备树”,本章我们就来详细的谈一谈设备树。掌握设备树是 Linux 驱动开发人员必备的技能!因为在新版本的 Linux 中, ARM 相关的驱动全部采用了设备树(也有支持老式驱动的,比较少),最新出的 CPU 其驱动开发也基本都是基于设备树的,比如 ST 新出的 STM32MP157、NXP的 I.MX8系列等。我们所使用的Linux版本为 4.1.15,其支持设备树,所以正点原子I.MX6UALPHA 开发板的所有 Linux 驱动都是基于设备树的。本章我们就来了解一下设备树的起源、重点学习一下设备树语法。 什么是设备树: 在我们学习stm32的时候,我们经常会用到I2c,SPI等协议来外接一些设备,我们需要在编译器里添加对应的I2c.c和I2c.h,或者直接使用st官方的cubemx软件启用相应的协议,便会自动生成代码,虽然很是方便,但不方便管理,在Linux系统中,设备树(Device Tree)是一种描述硬件信息和设备布局的数据结构,用于在启动时动态配置硬件设备。设备树以一种与硬件无关的方式描述硬件设备和设备之间的连接关系,从而简化了内核的开发和移植工作。描述设备树的文件叫做 DTS(Device Tree Source),这个 DTS 文件采用树形结构描述板级设备,也就是开发板上的设备信息,比如CPU 数量、 内存基地址、 IIC 接口上接了哪些设备、 SPI 接口上接了哪些设备。书中用一张图形象的描绘了设备树 在图中,树的主干就是系统总线, IIC 控制器、 GPIO 控制器、 SPI 控制器等都是接到系统主线上的分支。IIC 控制器有分为 IIC1 和 IIC2 两种,其中 IIC1 上接了 FT5206 和 AT24C02 这两个 IIC 设备, IIC2 上只接了 MPU6050 这个设备。 DTS 文件的主要功能就是按照图所示的结构来描述板子上的设备信息。 DTS,DTB,DTC: DTS(Device Tree Source):DTS是设备树的源文件,使用一种类似于C语言的语法来描述硬件设备的信息、属性和连接关系。DTS文件通常以.dts为后缀,可以使用文本编辑器来编辑。DTS文件描述了硬件设备的树状结构,包括各种设备节点、设备属性和设备之间的连接关系。 DTB(Device Tree Blob):DTB是设备树的编译结果,是DTS文件经过编译后生成的二进制文件。DTB文件通常以.dtb为后缀,是Linux内核在启动时加载并解析的设备树文件。DTB文件包含了硬件设备的信息、属性和连接关系,以供内核在运行时进行设备的配置和管理。 DTC(Device Tree Compiler):DTC是设备树的编译器,用于将DTS文件编译成DTB文件。DTC工具可以在Linux系统中使用,通过命令行将DTS文件编译成DTB文件,然后将DTB文件加载到内核中。DTC还可以用于验证和检查设备树的语法正确性,以确保设备树文件能够正确地描述硬件设备和设备之间的连接关系。 一般.dtsi 文件用于描述 SOC 的内部外设信息,比如 CPU 架构、主频、外设寄存器地址范围,比如 UART、 IIC 等等。以下是 imx6ull.dtsi 描述 I.MX6ULL 这颗 SOC 内部外设情况信息 的,内容如下: #include <dt-bindings/clock/imx6ul-clock.h> #include <dt-bindings/gpio/gpio.h> #include <dt-bindings/interrupt-controller/arm-gic.h> #include "imx6ull-pinfunc.h" #include "imx6ull-pinfunc-snvs.h" #include "skeleton.dtsi" / { /* 定义设备的别名 */ aliases { can0 = &flexcan1; ... }; /* 定义 CPU 节点 */ cpus { #address-cells = <1>; #size-cells = <0>; cpu0: cpu@0 { compatible = "arm,cortex-a7"; device_type = "cpu"; ... }; }; /* 定义中断控制器节点 */ intc: interrupt-controller@00a01000 { compatible = "arm,cortex-a7-gic"; #interrupt-cells = <3>; interrupt-controller; reg = <0x00a01000 0x1000>, <0x00a02000 0x100>; }; /* 定义时钟节点 */ clocks { #address-cells = <1>; #size-cells = <0>; ckil: clock@0 { compatible = "fixed-clock"; reg = <0>; #clock-cells = <0>; clock-frequency = <32768>; clock-output-names = "ckil"; }; ... }; /* 定义 SoC 节点 */ soc { #address-cells = <1>; #size-cells = <1>; compatible = "simple-bus"; interrupt-parent = <&gpc>; ranges; busfreq { compatible = "fsl,imx_busfreq"; ... }; ... }; /* 定义 GPMI NAND 节点 */ gpmi: gpmi-nand@01806000 { compatible = "fsl,imx6ull-gpmi-nand", "fsl, imx6ul-gpminand"; #address-cells = <1>; #size-cells = <1>; reg = <0x01806000 0x2000>, <0x01808000 0x4000>; ... }; }; 设备树是采用树形结构来描述板子上的设备信息的文件,每个设备都是一个节点,叫做设备节点,每个节点都通过一些属性信息来描述节点信息,属性就是键—值对。 设备树模板: 学习设备树最重要的就是要能写出设备树文件,书中提供了一个小型设备树模板供学习,我学着写了一个简单的设备树模板 /dts-v1/; /plugin/; / { compatible = "myboard"; /* 定义设备节点 */ mydevice { compatible = "mydevice"; status = "okay"; /* 定义属性 */ myproperty = <123>; /* 定义子节点 */ subnode { compatible = "subdevice"; status = "okay"; }; }; /* 定义中断控制器节点 */ interrupt-controller@0 { compatible = "my-intc"; #interrupt-cells = <2>; interrupt-controller; }; /* 定义时钟节点 */ clocks { #address-cells = <1>; #size-cells = <0>; myclock { compatible = "myclock"; reg = <0>; }; }; /* 定义GPIO节点 */ gpio { compatible = "mygpio"; #gpio-cells = <2>; mygpio { gpio-controller; #gpio-cells = <2>; gpio-line-names = "mygpio"; }; }; }; 这段设备树代码创建了一个名为"myboard"的设备树根节点,并定义了以下内容: 设备节点 "mydevice":定义了一个名为"mydevice"的设备节点,其compatible属性为"mydevice",状态为"okay"。该设备节点包含了一个名为"myproperty"的属性,其值为123,并包含一个子节点"subnode",其compatible属性为"subdevice",状态为"okay"。 中断控制器节点:定义了一个名为"interrupt-controller@0"的中断控制器节点,其compatible属性为"my-intc",指定了中断单元的格式为2个单元。 时钟节点 "clocks":定义了一个名为"myclock"的时钟节点,其compatible属性为"myclock",并指定了寄存器地址为0。 GPIO节点 "gpio":定义了一个GPIO节点,其compatible属性为"mygpio",并指定了GPIO单元的格式为2个单元。在该节点下定义了一个名为"mygpio"的GPIO控制器,包含了GPIO线的名称为"mygpio"。 这个小型设备树没有实际的意义,做这个的目的是为了掌握设备树的语法。在实际产品开发中,我们是不需要完完全全的重写一个.dts 设备树文件,一般都是使用 SOC 厂商提供好的.dts 文件,我们只需要在上面根据自己的实际情况做相应的修改即可。以我手上的mp135为例,我们来修改一个pwm的设备树代码 修改设备树: 先进入板子的串口终端,用之前用到的Mobaxterm进入,输入 cd /sys/class/pwm ls 命令,终端没有反应,说明这个板子的系统没有支持pwm驱动,下面就通过设备树改驱动,想要改驱动可不是件容易事,我们一步一步来,首先下载板子的出场源码,然后跟我们下载板子的SDK那一期一样,从win拷贝到Ubuntu虚拟机上,创建文件夹,然后解压,之后我们便会得到 与sdk那一期不同的是,我们此次还需要对源码进行打补丁和选择应用架构等操作,具体的可以参考大佬的这一篇帖子,【STM32MP135F-DK】3-舵机驱动与pwm脉冲宽度调制功能测试 - stm32/stm8 - 电子工程世界-论坛 (eeworld.com.cn),然后我们进入文件找到linux-6.1.28源码目录,开始找寻我们的设备树文件,打开arch/arm/boot/dts所在的文件夹,这个文件夹是存放所有板子的设备树文件的文件夹。找到stm32mp131.dtsi文件,这个文件描述了系统的一些内部资源,找到timers(因为pwm由定时器产生),这些timers全部是关着的,参考书中讲到的status属性: 由此可以断定这个设备树是需要我们修改的。于是我们在stm32mp135f-dk.dts这个文件中找ctrl+f搜索timers3, 把所有的disable改为okey,这样我们就完成了一次开发板设备树的修改,之后我们需要编译设备树文件,然后把编译好的设备树文件拷贝到板子上,这个时候我们再ls一下,终端就会显示我们开启的time3的通道4口pwm已开启,至此此次修改设备树的操作就完成了。

最近访客

< 1/2 >

统计信息

已有44人来访过

  • 芯积分:175
  • 好友:2
  • 主题:15
  • 回复:46

留言

你需要登录后才可以留言 登录 | 注册


qiao--- 2024-2-1
  
查看全部