自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(293)
  • 收藏
  • 关注

原创 内核-mmap

ARM 架构支持一级页表映射,也就是说 MMU 根据 CPU 发来的虚拟地址可以找到第 1 个页表,从第 1 个页表里就可以知道这个虚拟地址对应的物理地址。一级页表里地址映射的最小单位是 1M。ARM 架构还支持二级页表映射,也就是说 MMU 根据 CPU 发来的虚拟地址先找到第 1 个页表,从第 1 个页表里就可以知道第 2 级页表在哪里;再取出第 2级页表,从第 2 个页表里才能确定这个虚拟地址对应的物理地址。二级页表地址映射的最小单位有 4K、 1K, Linux使用 4K。

2024-08-13 22:53:25 825 1

原创 中断的线程化处理

内核中有一个struct irq_desc结构体数组,有一个属性是struct irqaction* action,这个结构体中有一个属性irq_handler_t thread_fn,就对应的是request_threaded_irq函数中的thread_fn;内核线程被唤醒后,执行thread_fn函数可能有程序在等待thread_fn函数被执行,调用wake_threads_waitq(desc)来唤醒它。上半部用来处理紧急的事情,下半部用一个内核线程来处理,这个内核线程专用于这个中断。

2024-08-13 22:16:23 973

原创 内核工作队列

参考内核头文件structstruct0第1个宏是用来定义一个结构体,要指定它的函数。第2个宏用来定义一个结构体,也要指定它的函数。所以delayed,意思就是说要让它运行时,可以指定:某段时间之后再执行。如果要在代码中初始化0。

2024-08-12 23:26:10 969

原创 中断下半部 tasklet

当发生某一个硬件中断时,内核会调用对应的硬件中断处理函数,处理完之后,会去处理软件中断;从类似soft数组中取出action函数,对于lastlet这种软件中断,对应的函数是tasklet_action,这个函数会从某个队列/链表里取出每一个tasklet结构体,执行里面的函数。可以看到,发生 2 次硬件中断 A 时,它的上半部代码执行了 2 次,但是下半部代码只执行了一次。在第⑦步里,它会去执行中断 A 的下半部,也会去执行中断 B 的下半部。所以,同一个中断的上半部、下半部,在执行时是多对一的关系。

2024-08-12 22:31:18 889

原创 定时器的使用

这表示内核每秒中会发生 100 次系统滴答中断(tick),这就像人类的心跳一样,这是 Linux 系统的心跳。所谓定时器,就是闹钟,时间到后你就要做某些事。核心在于:在 GPIO 中断中并不立刻记录按键值,而是修改定时器超时时间,10ms 后再处理。按下或松开一个按键,它的 GPIO 电平会反复变化,最后才稳定。如果不处理抖动的话,用户只操作一次按键,中断程序可能会上报多个数据。显然第 1 种方法太耗时,违背“中断要尽快处理”的原则,你的系统会很卡。在内核中使用定时器很简单,涉及这些函数(参考内核源码。

2024-08-11 23:00:19 922

原创 阻塞与非阻塞

从驱动代码也可以看出来,当 APP 打开某个驱动时,在内核中会有一个struct file 结构体对应这个驱动,这个结构体中有 f_flags,就是打开文件时的标记位;也可以清除这个位表示阻塞。使用 poll 时,可以设置超时时间为 0,这样即使没有数据它也会立刻返回,这就是非阻塞方式。APP 调用open函数时,传入O_NONBLOCK,就表示要使用非阻塞方式;使用 poll 时,如果传入的超时时间不为 0,这种访问方法也是阻塞的。先输出-1,因为没有中断(此时是非阻塞),10次之后是阻塞的。

2024-08-11 18:05:51 410

原创 异步通知机制

写信号处理函数in val;注册信号处理函数// 3注册信号处理函数打开文件把APP的pid告诉驱动程序;读取驱动程序flag并设置FASYNC位为1。

2024-08-11 17:41:55 1023

原创 POLL机制的内核代码详解

即 table->pt->qproc = __pollwait, __pollwait 将在驱动的 poll 函数里用到。当执行完①之后,在⑥或⑦处, pt->_qproc 被设置为 NULL,所以第二次调用驱动程序的 poll 时,不会再次把线程放入某个队列里。是一个宏,它定义于 include/linux/syscalls.h,展开后就有 sys_poll 函数。沿着②③④⑤,你可以看到:驱动程序里的poll_wait会调用__pollwait 函数把线程放入某个队列。,他们对应的内核函数都是。

2024-08-11 12:25:05 804

原创 POLL机制

POLL方式:妈妈要干很多活,但是可以陪小孩睡一会,定个闹钟要浪费点时间,但是可以继续干活。使用休眠-唤醒的方式等待某个事件发生时,有一个缺点: 等待的时间可能很久。使用 poll 机制时,驱动程序的核心就是提供对应的 drv_poll 函数。⑦ 线程从休眠中被唤醒,继续执行 for 循环,再次调用 drv_poll: drv_poll 返回数据状态;什么都不做,5秒超时,调用两次drv_poll函数(打印两次)。⑥ 在休眠过程中,一直没有按下了按键,超时时间到:内核把这个线程唤醒;后,很有可能会休眠。

2024-08-11 12:17:50 989

原创 休眠-唤醒

值得注意的是,上面 2个红线部分都属于 APP1 的“上下文”,或者这样说:红线所涉及的代码,都是 APP1调用的。但是按键的中断服务程序,不属于 APP1的“上下文”,这是突如其来的,当中断发生时, APP1正在休眠呢。当按下按键,驱动程序中的中断服务程序被调用,它会记录数据,并唤醒APP1。中剩下的代码,把数据复制回用户空间,返回用户空间。在 APP1的“上下文”,也就是在 APP1的执行过程中,它是可以休眠的。驱动中有数据时,图中红线就是APP1 的执行过程,设计用户态、内核态。

2024-08-10 23:39:04 616

原创 修改设备树及上机操作

哎呀,好像不对,需要的是,按下是1,松开是0,重新在驱动文件中设置。我们只需要指定GPIO信息,表示使用哪个GPIO即可。这里没问题,如果不是/boot分区,重新挂载。把编译出来的设备树文件拷贝到/boot目录下。安装驱动,按按键测试,按下是0,松开是1。使用前面编译出来的驱动。测试,按下1,松开0。

2024-08-10 22:20:16 492

原创 编写使用中断的按键驱动程序

而这些基础知识是更复杂的驱动程序的基础要素,以后的复杂驱动程序也就是对硬件操作的封装不同罢了,但是要用到的基础编程是一样的。还是建议从头写按键驱动,特别是如何使用中断。查看原理图确定按键使用的引脚,再在设备树中添加节点,在节点中指定中断信息。对于GPIO 按键,我们并不需要去写驱动程序,使用内核自带的驱动程序。休眠-唤醒、POLL机制、异步通知、定时器、中断的线程化处理。就可以了,然后你需要做的只是修改设备树指定引脚及按键值。

2024-08-10 16:46:08 182

原创 在设备树中指定中断_在代码中获得中断

对于SPI设备节点, SPI总线驱动在处理设备树里的 SPI 子节点时,也会处理其中的中断信息。假设GPIO1有32 个中断源,但是它把其中的16个汇聚起来向GIC发出一个中断,把另外16个汇聚起来向GIC发出另一个中断。如果中断控制器有级联关系,下级的中断控制器还需要表明它的“interrupt-parent”是谁,用了interrupt-parent ” 中的哪一个“ interrupts”。一个外设,它的中断信号接到哪个“中断控制器”的哪个“中断引脚”,这个中断的触发方式是怎样的?

2024-08-10 14:33:32 801

原创 Linux 中断系统中的重要数据结构

比如GPIO控制器里有第1号中断,UART模块里也有第1号中断,这两个“第1号中断”是不一样的,它们属于不同的“域”──irq_domain。假设irq_desc[A].handle_irq是XXX_gpio_irq_handler(XXX 指厂家),这个函数需要读取芯片的 GPIO控制器,细分发生的是哪一个GPIO中断(假设是 B),再去调用irq_desc[B].handle_irq。对于共享中断,比如GPIO中断B,它的中断来源可能有多个,每个中断源对应一个中断处理函数。

2024-08-08 22:49:19 814

原创 Linux 系统对中断处理的演进

很耗时的中断处理,应该放到线程里去可以使用 work,work queue在中断上半部调用 schedule_work 函数,触发 work 的处理既然是在线程中运行,那对应的函数可以休眠。

2024-08-06 22:42:07 941

原创 Linux 系统对中断的处理

在 Linux 中:资源分配的单位是进程,调度的单位是线程, 也就是说,在一个进程里,可能有多个线程,这些线程共用打开的文件句柄、全局变量等等。而这些线程,之间是互相独立的,“同时运行”,也就是说:每一个线程,都有自己的栈。可以,但是开销太大:读按键的程序,要把按键通知播放音乐的程序,进程间通信的效率没那么高。从上图可知, CPU内部的寄存器很重要,如果要暂停一个程序,中断一个程序,就需要把这些寄存器的值保存下来:这就称为保存现场。这样,按键的读取及 GUI 显示、音乐的播放,可以分开来,不必混杂在一起。

2024-08-05 22:45:15 1000

原创 异常与中断的概念及处理流程

但是表中的各个异常向量的偏移地址,是固定的:复位向量偏移地址是 0,中断是 0x18。在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU 暂停在当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行。当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断,CPU 再次暂停当前中断程序,转而去处理新的中断程序,处理完成后以此进行返回。在向量表里,一般都是放置一条跳转指令,发生该异常时, CPU 就会执行向量表中的跳转指令,去调用更复杂的函数。

2024-08-05 22:07:19 1190

原创 GPIO 子系统LED驱动程序上机操作

就可以在 GUI 界面中选择引脚,配置它的功能,这就可以自动生成 Pinctrl 的子节点信息。100ASK_IMX6ULL 使用的 LED 原理图如下,可知引脚是。然后把设备树拷贝到开发板的/boot目录下,更新设备树,然后reboot,然后insmod就行了。原来的这个led被用来作为cpu的指示灯;要在设备树文件中搜索出来,然后disable一下。确定引脚并生成设备树节点,NXP 公司对于 IMX6ULL 芯片,有设备树生成工具,安装。对应的设备树代码生成,把自动生成的设备树信息,放到内核源码。

2024-08-02 23:08:40 302

原创 基于 GPIO 子系统的 LED 驱动程序

的地位跟其他模块,比如 I2C、 UART 的地方是一样的,要使用某个引脚,需要先把引脚配置为 GPIO 功能,这要使用 Pinctrl 子系统,只需要在设备树里指定就可以。下面,保存该厂家的文档,如果连文档都没有,那只能参考内核源码中的设备树文件,在内核源码目录。有些芯片提供了设备树生成工具,在GUI 界面中选择引脚功能和配置信息,就可以自动生成。本身需要确定引脚,这也需要在设备树里指定。核心代码是如下,它从该设备(对应设备树中的设备节点)获取名为。,对应的,驱动代码中要注册一个。

2024-08-01 23:08:51 467

原创 GPIO 子系统重要概念

几乎在所有的ARM 芯片中,GPIO 都分为几组,每组若干个引脚,所以在使用GPIO 子系统之前,我们就要确定它是哪组的,组里的哪一个的。的基地址是128,ngpio 是个数,GPIOH是第H 组GPIO ,有时候是一串别的字符串,根据字符在设备树中查找对应的GPIO 组。,这通常都由芯片厂家设置好,我们要做的是找到它的名字,比如gpio1,然后指定要用它里面的那个引脚,比如 。以前我们通过寄存器来操作 GPIO 引脚,即使LED 驱动程序,对于不同的板子它的代码也完全不同。

2024-07-30 22:26:51 1053

原创 GPIO && Pinctrl 子系统

大多数的芯片,没有单独的 IOMUX 模块,引脚的复用、配置等等,就是在GPIO 模块内部实现的。Pinctrl 系统的客户,那就是使用 Pinctrl 系统的设备,使用引脚的设备。现在的芯片动辄几百个引脚,在使用到 GPIO 功能时,让你一个引脚一个引脚去找对应的寄存器,这要疯掉。所以为了更方便驱动工程师的开发,要把引脚的复用、配置抽出来,做成 Pinctrl 子系统,给 GPIO、I2C 等模块使用。比如默认状态下, UART 设备是工作的,那么所用的引脚就要复用为 UART 功能。

2024-07-29 23:21:15 831

原创 查询方式的按键驱动程序_编写框架

对于 LED,APP 调用 open 函数导致驱动程序的 led_open 函数被调用。安装驱动程序后并不意味着会使用对应的硬件,而 APP 要使用对应的硬件,必须先调用 open 函数。APP 继续调用 write 函数传入数值,在驱动程序的 led_write 函数根据该数值去设置 GPIO的数据寄存器,从而控制 GPIO 的输出电平。按键没被按下时,上图中左边的 GPIO 电平为高,右边的 GPIO 电平为低。按键被按下后,上图中左边的 GPIO 电平为低,右边的 GPIO 电平为高。

2024-07-28 22:34:12 287

原创 APP 怎么读取按键值

APP 得到 poll/select 函数的返回结果后,如果确认是有数据的,则再调用 read 函数,这会导致驱动中的 read 函数被调用,这时驱动程序中含有数据,会直接返回数据。APP 调用 fcntl 函数,把驱动程序的 flag 改为 FASYNC,这会导致驱动程序的 fasync 函数被调用,它只是简单记录进程 PID。当用户按下按键时,GPIO 中断被触发,导致驱动程序之前注册的中断服务程序被执行。当用户按下按键时,GPIO 中断被触发,导致驱动程序之前注册的中断服务程序被执行。

2024-07-28 21:38:56 1063

原创 LED 模板驱动程序的改造:设备树

核心永远是结构体。上述的三种方法,只是指定的硬件资源的方式不一样。

2024-07-28 16:03:04 846

原创 内核里操作设备树的常用函数

处理 DTBof_fdt.h // dtb 文件的相关操作函数, 我们一般用不到,// 因为 dtb 文件在内核中已经被转换为 device_node 树(它更易于使用)处理of.h // 提供设备树的一般处理函数// 比如 of_property_read_u32(读取某个属性的 u32 值),// of_get_child_count(获取某个 device_node 的子节点数of_address.h // 地址相关的函数,

2024-07-27 22:26:58 919

原创 内核对设备树的处理

如果 of_match_table 中含有 name 值,就跟 dev 的 name 属性比较,若一致则成功,否则返回失败。而设备树中建议不再使用 devcie_type 和 name 属性,所以基本上只使用设备节点的compatible 属性来寻找匹配的 platform_driver。接下来比较。

2024-07-27 21:48:46 771

原创 设备树语法基础知识

再来,同一款芯片的板子,它们所用的外设资源都不一样的,比如A板子用到GPIOA,B板用到GPIOB。我们需要编写设备树文件(dts:device tree source),它需要编译为dtb(device tree blob)文件,内核使用的是dtb文件。一旦你需要更换LED 所用到的 GPIO 引脚,需要修改驱动程序源代码,重新编译,重新加载驱动。回想,要操作硬件就需要去操作复杂的寄存器,如果设备树可以操作寄存器,那么它就是“驱动”,它就一样很复杂。的意思就是“机器描述”,学到内核启动流程时才涉及。

2024-07-27 15:07:09 943

原创 LED 模板驱动程序的改造:总线设备驱动模型

并且,使用时要先加载 a.ko。

2024-07-20 16:19:42 276

原创 驱动进化之路:总线设备驱动模型

最先比较:platform_device. driver_override 和 platform_driver.driver.name。然后比较:platform_device. name 和 platform_driver.id_table[i].name。使用哪个引脚,怎么操作引脚,都写死在代码中。代码稍微复杂,但是易于扩展。,将资源与驱动分离开来。冗余代码太多,修改引脚时设备端的代码需要重新编译。文件,把它传给内核,无需重新编译内核/驱动。无冗余代码,修改引脚时只需要修改。修改引脚时,需要重新编译。

2024-07-16 16:46:07 742

原创 驱动设计的思想:面向对象/分层/分离

比如 board_A.c 使用芯片 chipY,那就可以写出:chipY_gpio.c,它实现芯片 Y 的 GPIO操作,适用于芯片 Y 的所有 GPIO 引脚。以面向对象的思想,在 board_A_led.c 中实现 led_resouce 结构体,它定义“资源”──要用哪一个引脚。在 chipY_gpio.c 中仍是实现 led_operations 结构体,它要写得更完善,支持所有 GPIO。使用时,我们只需要在 board_A_led.c 中指定使用哪一个引脚即可。你要去修改上面结构体中的。

2024-07-15 22:29:58 354

原创 LED 驱动程序框架

【代码】LED 驱动程序框架。

2024-07-15 14:48:56 271

原创 最简单的LED驱动程序

板子没有操作 led 的效果图。函数,驱动层提供自己的。

2024-07-09 23:29:16 543

原创 IMX6UL/6ULL的GPIO操作方法

要用CCM_CCGRy 寄存器中的2位来决定该组GPIO是否使能。哪组GPIO用哪个CCM_CCGR寄存器来设置,可以通过上面的红色框部分知道。:General-purpose input/output, 通用的输入输出口。:Clock Controller Module(时钟控制模块):IOMUX Controller , IO 复用控制器。通过查看手册可以得出;

2024-07-09 23:14:57 427

原创 普适的 GPIO 引脚操作方法

General-purpose input/output,通用的输入输出口。三个寄存器对应的是同一个物理寄存器;原则:不能影响到其他位。

2024-07-06 14:36:55 300

原创 硬件知识 LED 原理图

编写ARM程序时,也应该会有一个像hello一样的程序入门,这个程序就是LED。

2024-07-04 22:43:15 282

原创 hello 驱动程序

内核版本:4.x先不解释概念,让hello 驱动程序能跑起来再说。

2024-07-04 22:04:28 492

原创 驱动与应用层的交互(输入系统应用编程)

常见的输入设备有键盘、鼠标、遥控杆、书写板、触摸屏等等,用户可以通过这些输入设备与Linux 系统进行数据交换。问题来了?输入设备种类繁多,能否统一它们的接口?既在驱动层面统一,也在应用程序层面统一?可以的。Linux系统为了统一管理这些输入设备,实现了一套能兼容所有输入设备的框架:输入系统。驱动开发人员基于这套框架开发出程序,应用开发人员就可以使用统一的API去使用设备。

2024-06-30 18:20:50 1117

原创 交叉编译freetype

然后你会看到头文件目录,库目录(LIBRARY_PATH),你需要做的是在头文件目录确定有没有这个文件,或者是自己指定头文件目录。在程序中如果是这样包含的头文件:#include 如果你编译的是一个库,需要把得到的头文件,库文件放入工具链的。运行时不需要头文件,所以头文件不用放到板子上。等目录了,里面有可以执行的程序,库,头文件。,在交叉编译开源软件时,如果它里面有。系统目录:就是交叉编译工具链里的某个。系统目录:就是交叉编译工具链里的某个。这种尖角号里的头文件,去哪里找?

2024-06-29 12:11:38 1345

原创 Makefile实例

除了编译参数-Werror,还可以加上-I参数,指定头文件路径,-Iinclude表示当前的inclue文件夹下。,前者表示当前目录,后者表示编译器指定的路径和GCC路径。工作中,我们希望把警告也当成错误来处理。此时就可以把c.c文件里的。

2024-06-26 23:20:45 186

原创 Makefile 函数

patsubst 函数是从var变量中取出每一个值,如果这个符合patten格式,把它替换成 replacement 格式。$(wildcard pattern) # pattern定义了文件名的格式, wildcard取出其中存在的文件。这个函数 wildcard 会以 pattern 这个格式,去寻找存在的文件,返回存在文件的名字。实例:(在该目录下创建三个文件:a.c b.c c.c)同时还可以利用其函数的特点来判断文件是否存在。

2024-06-26 22:39:15 265

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除