自定义博客皮肤VIP专享

*博客头图:

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

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

博客底图:

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

栏目图:

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

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

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

原创 C语言结构体精讲

详细探究一下结构体在内存中的表现

2024-12-24 10:49:37 1103 2

原创 链式中断控制器驱动程序编写

接着找到硬件中断号后,根据他的domain,去调用Mapping,得到了virq,找到irq_desc里面的handle_irq,也就是Handlc,这里面的homain是GPIO的来着。我们看到得定义一个中断子节点的左边嘛,然后右边也就是clien端就是去使用了,得指定好interrupt_parent,因为是通过这个去找到父节点的irq_domain的嘛。屏蔽GIC 33号中断:调用irq_dataA的irq_chip的函数,irq_dataA由GIC驱动提供。

2025-04-15 23:10:34 579

原创 两类中断控制器处理流程_链式和层级

链式中断控制器(chained):上图中,左边的"chained intc"就是链式中断控制器。它底下的4个中断触发时,都会导致GIC的33号中断被触发。处理中断时,需要:是谁触发了GIC 33号中断?这需要读取"chained intc"中的寄存器。层级中断控制器(hierarchy):上图中,右边边的"hierarchy intc"就是层级中断控制器。它底下的4个中断,跟GIC中的4个中断一一对应。处理GIC 100~103号中断时,不需要读取"hierarchy intc"的寄存器来。

2025-04-15 22:11:16 386

原创 GIC驱动程序分析

这边就是提供irq_chip了,那么到这里,我们就彻底讲完啦,其实GIC也没有想象的那么复杂,逻辑性很强,嘿嘿,完结,撒花(doge.)所以,GIC用gic_chip_data来表示,gic_chip_data中重要的成员是:irq_chip、irq_domain。内核有一个__irqchip_of_table数组,里面有多个of_device_id,表示多种GIC。在内核里,使用gic_chip_data结构体表示GIC,gic_chip_data里有什么?

2025-04-14 21:43:45 341

原创 GIC驱动程序对中断的处理流程

我们先来看看老版本的CPU是怎么去处理中断的:这种的话,基本上就是一开始就确定你有多少个中断,就为你分配好了多少个irq_desc,这样子每一个硬件中断,也就是hwirq跟虚拟中断virq就是确定的了,这个时候只需要去使用宏定义去标定这俩个的关系就可以了,但是这样子是有问题的,一旦硬件中断多起来,那么我们得一个个去定义嘛,工作量有点过于庞大了,所以在新的处理中断的方法中:我们使用了动态分配的方法。

2025-04-14 20:50:13 746

原创 设备树里的中断控制器详解

在硬件上,“中断控制器”只有GIC这一个,但是我们在软件上也可以把上图中的“GPIO”称为“中断控制器”。很多芯片有多个GPIO模块,比如GPIO1、GPIO2等等。所以软件上的“中断控制器”就有很多个:GIC、GPIO1、GPIO2等等。GPIO1连接到GIC,GPIO2连接到GIC,所以GPIO1的父亲是GIC,GPIO2的父亲是GIC。假设GPIO1有32个中断源,但是它把其中的16个汇聚起来向GIC发出一个中断,把另外16个汇聚起来向GIC发出另一个中断。

2025-04-14 13:31:12 293

原创 中断的硬件框架

它使软件能够屏蔽,启用和禁用来自各个中断源的中断,以(在硬件中)对各个中断源进行优先级排序和生成软件触发中断。中断控制器会向CPU发出中断信号,CPU可以读取中断控制器的寄存器,判断当前处理的是哪个中断。说实话,这点还挺坑的。我们从这里可以发现,f103的话,专门为GPIO提供了一个外部中断控制器EXTI,通过EXTI向NVIC发送中断信息,而其他外设,例如串口这些,是可以直接给NVIC发送中断信息的。多个中断源汇聚到NVIC,NVIC的职责就是从多个中断源中取出优先级最高的中断,向CPU发出中断信号。

2025-04-13 22:19:24 456

原创 interrupt子系统中的数据结构

现在有一个共享的中断,网卡跟开关共享同一个gpio中断,然后当触发中断的时候,GIC会发信号给CPU,告诉他已经发生了中断了,然后CPU会去读取GIC里面的寄存器,查看是发生了什么中断,然后发现是GPIO中断后,就会去查看GPIO各个引脚的寄存器,查看是哪个引脚发生了中断,发现是B号中断来着,接着会去调取产生这个引脚中断的各种外设的中断判断函数,看看是哪一个发生了中断,然后去执行他的处理函数。对于共享中断,比如GPIO中断B,它的中断来源可能有多个,每个中断源对应一个中断处理函数。

2025-04-10 17:39:03 926

原创 interrupt子系统详谈

a. 中断的处理可以分为上半部,下半部b. 中断上半部,用来处理紧急的事,它是在关中断的状态下执行的c. 中断下半部,用来处理耗时的、不那么紧急的事,它是在开中断的状态下执行的d. 中断下半部执行时,有可能会被多次打断,有可能会再次发生同一个中断e. 中断上半部执行完后,触发中断下半部的处理f. 中断上半部、下半部的执行过程中,不能休眠:中断休眠的话,以后谁来调度进程啊?

2025-04-10 17:17:05 1417

原创 GPIO子系统与Pinctrl子系统的交互

要使用pinA来控制LED,首先要通过Pinctrl子系统把它设置为GPIO功能,然后才能设置它为输出引脚、设置它的输出值。所以在设备树文件里,应该添加Pinctrl的内容:但是很多芯片,并不要求在设备树中把把引脚复用为GPIO功能。比如STM32MP157,在它的设备树工具即使把引脚配置为GPIO功能,它也不会在设备树中出现。原因在于:GPIO走了后门。现实的芯片中,并没有Pinctrl这样的硬件,它的功能大部分是在GPIO模块中实现的。

2025-04-09 17:01:06 277

原创 虚拟GPIO子系统

那么到这里,其实一开始小编有一个疑惑,这里只是写了server端的,那么clien端呢,其实clien端在我们去调用API函数的时候,会自动的去解析里面的数据,但是clien端和server端肯定是有一个匹配的过程的,就跟Pinctrl子系统一样,只不过这里没有展开去细讲,后面看看找不找的到,再回来细讲吧,下一篇,我们来讲讲Pinctrl子系统和GPIO子系统的联系,完结,撒花(doge.)当解析设备节点中的GPIO信息时,需要用到上面的属性。这里我们都是去简单的处理了,最后就是注册了。

2025-04-09 16:05:05 255

原创 GPIO子系统中的数据结构

也就是每一个gpio子系统对应一个gpio_device结构体,但是我们在构造的时候,只需要去配置gpio_chip结构体,也就是gpio子系统的操作方法,另一个gpio_desc,其实也就类似于之前pinctrl子系统时候的描述。看这个设备树clien端,他这里面gpio1也就表示第一个gpio controller,这里面10也就代表着那个chip结构体数组里面的第11个元素,也就是10嘛,因为是从0开始的。在gpio_device中有一个gpio_desc数组,每一引脚有一项gpio_desc。

2025-04-09 10:50:36 1061

原创 详解GPIO子系统

要操作GPIO引脚,先把所用引脚配置为GPIO功能,这通过Pinctrl子系统来实现。然后就可以根据设置引脚方向(输入还是输出)、读值──获得电平状态,写值──输出高低电平。以前我们通过寄存器来操作GPIO引脚,即使LED驱动程序,对于不同的板子它的代码也完全不同。当BSP工程师实现了GPIO子系统后,我们就可以:a. 在设备树里指定GPIO引脚b. 在驱动代码中:使用GPIO子系统的标准函数获得GPIO、设置GPIO方向、读取/设置GPIO值。这样的驱动代码,将是单板无关的。

2025-04-08 16:19:54 1120

原创 C与指针的神秘面纱

首先,我们定义了一个swap函数,其没有返回值,传入的参数是俩个Int型的指针变量,所有我们传参赋值时,需要传的是地址,在看其内部,其实跟普通函数交换无不同,只不过里面的ab是指针变量来着,所有我们需要对其解引用,去间接操作目标地址中存储的那个变量,其实,从这里我们看出,其实ab俩个变量从始至终都未曾进来swap函数,我们之所以能操作地址,是因为变量在定义的时候,cpu就给该变量分配了一个确定的地址,是不会改变的,这样子,我们就解释清楚了为什么能通过访问地址空间去改变变量的值。

2025-04-03 17:12:29 914

原创 详解Pinctrl子系统

无论是哪种芯片,都有类似下图的结构:想要pinA、B用于,需要设置IOMUX让它们连接到GPIO模块;要想让pinA、B用于I2C,需要设置IOMUX让它们连接到I2C模块,这里GPIO、I2C应该是并列的关系,它们能够使用之前,需要设置复用关系IOMUX,有时还要配置引脚,比如上拉、下拉、开漏等等。

2025-04-03 16:52:15 649

原创 虚拟pinctrl驱动

最后一个也就是最重要的,也就是去把对应的组设置成指定的功能:但是因为没有硬件寄存器去给我们操作,所以我们去打印信息就行了,毕竟只是虚拟的嘛,所以没有必要去搞的那么复杂。那么我们就只剩下怎么去从设备树中去注册一个Mapping结构体了,client驱动只需要去写一个简单的platform_driver就可以了。先来看第一个函数,这个是用来查看一共有多少个功能的:这里面全是静态的,是比较简单的,其实还有一种动态的从设备树里面去获取的方法。一样的,我们只需要去返回pins结构体数组里面的名字就行。

2025-03-30 21:51:35 316

原创 linux网络编程

当调用socket函数时,如果protocol参数设置为0,则会选择默认的协议。默认的协议通常是与给定的域和套接字类型最匹配的协议。但是,有些域和套接字类型支持多种协议。在这种情况下,可以使用protocol参数来选择特定的协议。例如,在IPv4中,SOCK_STREAM套接字类型支持TCP和SCTP协议,SOCK_DGRAM套接字类型支持UDP和DCCP协议。如果我们要使用SCTP协议而不是TCP协议来创建一个SOCK_STREAM套接字,则可以将protocol参数设置为IPPROTO_SCTP。

2025-03-28 14:26:13 776

原创 详谈多线程2

有关多线程的创建流程如图 9.14所示,首先需要创建线程,一旦线程创 建完成后,线程与线程之间会发生竞争执行,抢占时间片来执行线程逻辑。在 创建线程时候,可以通过创建线程的第四个参数传入参数,在线程退出时亦可 传出参数被线程回收函数所回收,获取到传出的参数。

2025-03-27 14:49:35 1305

原创 详谈多线程

我们先来讲讲为什么要引入多线程:在编写代码时,是否会遇到以下的场景会感觉到难以下手?要做2件事,一件需要阻塞等待,另一件需要实时进行。例如播放器:一边 在屏幕上播放视频,一边在等待用户的按键操作。如果使用单线程的话,程序必 须一会查询有无按键,一会播放视频。查询按键太久,就会导致视频播放卡顿;视频播放太久,就无法及时响应用户的操作。并且查询按键和播放视频的代码混杂在一起,代码丑陋。如果使用多线程,线程1单独处理按键,线程2单独处理播放,可以完美解决上述问题。

2025-03-27 13:43:03 663

原创 内核中设备树的转换

他填充的逻辑就是先找出有几个子节点,然后再遍历子节点里面的hoggrp-1这些,最后再遍历里面fsl,pins,这样子就完成了存储,但是细节还得看视频,文档的话,只是讲了中心思想和流程。首先呢,我们得先知道我们驱动常用的设备树有几个部分,一共有3个部分,一个部分是厂家提供的,这个也是等会我们最先开始去讲的,剩下的俩个部分都是需要我们去写的,也就是。就是这个结构体,这里面也是很有细节的,因为有个soc,就表示是芯片,也就是厂家提供的,接着我们来看这里面的每个成员是表示什么。

2025-03-26 14:04:24 565

原创 内核中的互斥量

但是在代码上,并没有要求“谁获得mutex,就必须由谁释放mutex”,只是在使用惯例上是“谁获得mutex,就必须由谁释放mutex”。如果mutex的值加1后还是小于等于0,就表示有人在等待mutex,需要去wait_list把它取出唤醒,这需要用到slowpath的函数:__mutex_unlock_slowpath。大部分情况下,加1后mutex的值都是1,表示无人等待mutex,所以通过fastpath函数直接增加mutex的count值为1就可以了。

2025-03-24 23:41:43 480

原创 自旋锁和信号量在内核中的实现

所以,对于不支持preempt的单CPU系统,spin_lock是空函数,不需要做其他事情。假设程序A要访问临界资源,可能会有中断也来访问临界资源,可能会有程序B也来访问临界资源,那么使用spin_lock_irq()来保护临界资源:先禁止中断防止中断来抢,再禁止preempt防止其他进程来抢。从以上代码可知,在UP系统中spin_lock()就退化为preempt_disable(),如果用的内核不支持preempt,那么spin_lock()什么事都不用做。owner就相当于电子叫号牌,现在谁在吃饭。

2025-03-24 14:03:42 686

原创 ModBus寄存器封装——使用面向对象思想

可以看出,这个串口句柄的结构体里面是继承着modbus结构体里面的端口这一部分的,其实老实说,这俩个其实是完全不一样的东西来着,应该是先有的结构体句柄来着,然后他的里面是需要串口端口的函数方法来着,最后再统一给Modbus结构体句柄,然后再包含给iotp结构体。这个真的是大有说法的,这个是去寻找一个空白句柄队列,然后去注册,也就是modbus的结构体句柄是这么干的,而之前那个串口句柄的注册的话,确实先去寻找是否已经注册过了,确保没有重复注册,我感觉这个跟生命周期有关,因为modbus的句柄是动态的。

2025-03-24 09:17:50 1079

原创 详解Linux中的锁

spin_lock_irq()/spin_unlock_irq()会禁止/使能中断,另一套函数是spin_lock_irqsave()/spin_unlock_irqrestore(),spin_lock_irqsave()会先保存当前中断状态(使能还是禁止),再禁止中断;这些函数改名为spin_lock_softirq也许更恰当,请记住:spin_lock_bh会禁止Softirq,而不仅仅是禁止“中断下半部”(timer、tasklet里等都是Softirq,中断下半部只是Softirq的一种)。

2025-03-23 22:29:47 942 1

原创 详解同步与互斥

在上面的第2个失败例子里,问题在于对valid变量的修改被打断了。假设CPU0上进程A、CPU1上进程B同时运行到上图中读出valid的地方,它们同时发现valid都是1,减减后都等于0,在第07行判断条件都不成立,所以在第14行都可以返回0,都可以成功打开驱动。第5行的atomic_dec_and_test,这是一个原子操作,在ARMv6以下的CPU架构中,这个函数是在关中断的情况下执行的,它确实是“:编写驱动程序时,要有系统的概念,程序A调用驱动程序时,它可能被程序B打断,程序B也去调用这个驱动程序。

2025-03-23 21:49:30 694 1

原创 详解内联汇编

有一些输出操作数在汇编代码中早早就被写入了新值A,在这之后,汇编代码才去读取某个输入操作数,这个输出操作数就被称为earlyclobber(早早就被改了)。所以,如果汇编代码中某个输出操作数是earlyclobber的,它的constraint就要加上“&”,这就是告诉编译器:给我分配一个单独的寄存器,别为了省事跟输入操作数用同一个寄存器。但是在C函数中写汇编代码,可以不用另外新建一个汇编文件,比较方便。第8行更新了%0的值后,第9行修改%1的值,由于%0、%1是同一个寄存器,所以%0的值也被修改了。

2025-03-23 21:13:11 980 1

原创 OTA远程升级

首先呢,我们知道,程序运行,其实就是一个全局指针一直在指向不同的地址,然后去执行他指向的地址里面的内容,就完成了程序的运行,那么就存在有一个内核指针,他在不同的启动方式下是指向着不同的区域的,我们来看看stm32的三种启动模式。可以看到这里面有4个分区,bootloder就是启动文件来着,APPA就是应用程序区来着,APPB就是备份来着,最后一个就是参数来着。刚刚的话,我们已经把整体的框架学会了,那么下面,我们来看看完整的代码应该怎么去写吧。那么到这里,我们就已经清楚stm32是怎么去启动的了。

2025-03-19 15:47:10 444

原创 关于主控调试的一些方法

spi的写也挺简单的,不过还是需要注意的是,因为用户输入的是16进制的数据来着,所以输入的俩个数据其实才是真实的一个数据来着,所以需要对这些数据进行合并,然后计算出实际需要发送的字节,最后拉低片选,调用spi的发送函数因为spi是全双工的嘛,所以你在读之前,还是需要去随便发一些数据的,也就是你想要接收的数据的字节数。

2025-03-11 20:35:32 798

原创 关于数据类型转换

这个就是我们一开始提到的把整数转换成浮点数,但是值得注意的是,这里是高位在前面来着,我们可以看到,这里有一个联合体,联合体嘛,共地址,地址是以字节为单位的嘛,所有地址+3就是最高位的字节了,最后再把联合体里面的value赋值给输出型指针就可以啦 3210嘛。这个就比较有意思了,这个其实就是8bit的数据和16比特的数据之间的转换来着,这里就涉及到高位字节和低位字节的问题,其实我们在接收协议数据的时候,也是需要去考虑这个问题的。那么到这里,我们的数据类型转换也就讲的差不多啦,完结,撒花(doge.)

2025-03-07 13:24:32 466 1

原创 Makefile

Makefile.build文件的话,就是先包含各个子目录的Makefile文件,然后因为有普通文件和目录文件,所有要分开处理,Makefile中定义的普通文件,也就是.o文件,我们就根据正常的规则去进行处理,目录文件的话,就要去执行make -c命令,跳转到指定的目录去执行相应的makefile文件,然后就是为普通文件生产相应的可依赖文件.d文件,子目录生成built-in.o文件,最后把俩个文件去链接就可以啦。Makefile笔记 韦东山通用Makefile解析 - 明明1109 - 博客园。

2025-03-05 15:23:28 321

原创 linux中串口的配置

剩下的就是把结构体里面的变量一个个去初始化配置好,然后去调用驱动API函数去初始化硬件串口了。虽然我感觉其实第一个结构体也能配置了,但是奇偶校验不行。这个其实就是把波特率,奇偶校验给配置好。

2025-02-25 11:25:43 646 1

原创 MODBUS数据封装——寄存器(2)

这个就只是把值打印出来,用来检验了,那么到这里,其实我们就已经搞明白怎么把寄存器和传感器数据绑定,然后怎么去接收主机通过Modbus发送的命令,然后解析,最后打包好modbus数据,其实就只有地址那些在,然后调用刚刚跟寄存器绑定好的函数,去把数据给填充进去,最后通过modbus发回去给主机。可以知道,其实他就是在死循环里面去监听主设备发送的指令,然后去解析,最后调用刚刚绑定的寄存器对应的那些函数,去进行modbus数据的发送,所以现在,我们只需要去弄懂他死循环里面的函数就可以了。

2025-02-19 09:11:04 710 1

原创 MODBUS数据封装——寄存器

这些就是把接收modbus结构体以及响应modbus结构体给准备好先,然后顺便重新去定义一个指针指向刚刚传进来的队列里面的数据段,当然,接收和响应modbus里面的数据段也是需要去定义一个指针去指向的,方便操作。这个就是把刚刚的队列里面的信息给提取出来,然后赋值给我们准备好的接收modbus数据的结构体,这里还有一个很有意思的地方,那就是去构建错误码,我们来岔开话题去看看。那么到这里,接收modbus命令的结构体就已经赋值好了,那么下面,就是去构建应答结构体了,这里面可还是有点学问的。

2025-02-18 19:12:20 610 1

原创 MODBUS代码实战

可以看到,这里面其实就是5个函数指针外加最后一个其实是串口号来着,因为小编是在Linux操作系统上面去使用串口的,所以需要去操作文件,我们只需要去传入串口号就可以,这个等后面小编再详细写一写吧,所以也就难免会有去操作串口的设备文件的操作啦,我们知道在linux中,要去操作一个文件,就需要先去open,然后使用他的文件描述符去进行操作,比如读写里面的内容,所以我们可以去定义几个抽象的函数指针,这样子又何尝不是类似于c++中的多态呢。

2025-02-15 14:41:37 956 1

原创 MODBUS浅谈

那么到这里呢,其实我们的modbus协议就已经讲完啦,是不是很简单呢,那么在下一篇中,我们来看看他能玩出什么样的花出来,来感受一下c语言的优美,这些都是下期预告,我们下期见哦,完结,撒花(doge.)

2025-02-14 23:34:27 457 1

C语言结构体精讲,结构体在内存中的访问

C语言结构体精讲,结构体在内存中的访问

2024-12-23

空空如也

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

TA关注的人

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