自定义博客皮肤VIP专享

*博客头图:

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

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

博客底图:

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

栏目图:

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

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

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

原创 C语言结构体精讲

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

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

原创 梯形路径规划详解

则加速阶段走过的路程S1=(vn*vn-v0*v0)/2a,减速阶段走过的路程S3=(vn*vn-vt*vt)/2a,S1为加速阶段走过的总路程,若S1<0,代表没有加速阶段只有减速阶段,若S2>S,代表只有加速阶段没有减速阶段。v0为初速度,vn为匀速运行速度,vt为末速度,加速和减速阶段加速度为a,总路程为S。则匀速阶段走过的路程S2=S-S1-S2。

2025-05-11 12:38:50 152

原创 V4L2应用程序开发-- 控制流程

使用摄像头时,我们可以调整很多参数,比如:对于视频流本身:设置格式:比如V4L2_PIX_FMT_YUYV、V4L2_PIX_FMT_MJPEG、V4L2_PIX_FMT_RGB565设置分辨率:1024*768等对于控制部分:调节亮度调节对比度调节色度。

2025-05-08 21:34:08 780

原创 V4L2应用程序开发--数据采集流程

所以需要申请若干个buffer,驱动程序把数据放入buffer,APP从buffer得到数据。这里是一个循环:使用poll/select监测buffer,然后从"完成链表"中取出buffer,处理后再放入"空闲链表"ioctl VIDIOC_REQBUFS:申请buffer,APP可以申请很多个buffer,但是驱动程序不一定能申请到。处理:前面使用mmap映射了每个buffer的地址,处理时就可以直接使用地址来访问buffer。ioctl VIDIOC_QBUF:把buffer放入"空闲链表"

2025-05-08 21:33:33 904

原创 SPI设备树处理过程

上一篇我们讲了SPI的总线驱动模型,这一节呢,我们来讲讲SPI设备树是怎么处理的:我们再回头来看看spi_device结构体:各个成员含义如下:max_speed_hz:该设备能支持的SPI时钟最大值chip_select:是这个spi_master下的第几个设备在spi_master中有一个cs_gpios数组,里面存放有下面各个spi设备的片选引脚spi_device的片选引脚就是:cs_gpios[spi_device.chip_select]cs_gpio:这是可选项,也可以把spi_device的

2025-04-23 11:04:17 1048

原创 SPI总线设备驱动模型

左边是spi_driver,使用C文件实现,里面有id_table表示能支持哪些SPI设备,有probe函数。在设备树里描述SPI控制器的硬件信息,在设备树子节点里描述挂在下面的SPI设备的信息。可以来自设备树:比如由SPI控制器驱动程序解析设备树后创建、注册spi_device。Linux中使用spi_master结构体描述SPI控制器,里面最重要的成员就是。右边是spi_device,用来描述SPI设备,比如它的片选引脚、频率。SPI子系统中涉及2类硬件:SPI控制器、SPI设备。

2025-04-23 10:47:32 811

原创 UART驱动情景分析_write

方法1:直接使能 tx empty中断,一开始tx buffer为空,在中断里填入数据。方法2:写部分数据到tx fifo,使能中断,剩下的数据再中断里继续发送。UART有Tx FIFO,可以往里面写入若干个数据,然后使能中断。STM32MP157发送串口数据时,有两种方法:DMA、中断。数据最终存入uart_state->xmit的buffer里。使用硬件驱动中uart_ops->start_tx开始发送。具体的发送方法有2种:通过DMA,或通过中断。我们来分析第二种方式:通过中断来发送数据。

2025-04-18 15:41:38 184

原创 UART驱动情景分析_read

APP被唤醒后,从行规程buffer中读入数据,返回。读大概就讲完了,完结,撒花(doge.)UART接收到数据,产生中断。行规程处理后存入buffer。中断程序从硬件上读入数据。

2025-04-18 10:13:54 216

原创 UART驱动情景分析_open

说实话,确实是绕,tty_operration tty_port_operations都是usart_core.c里面的,只有最后一个uart_ops才是涉及到硬件的。调用tty_driver->ops->open // ops是struct tty_operations指针类型, 就是uart_open。// ops是struct tty_port_operations指针类型。调用uart_port->ops->startup // ops是struct uart_ops指针类型。

2025-04-18 10:12:21 268

原创 UART驱动情景分析_注册

这里就是从设备树中获取资源,然后设置那个port结构体嘛,但这里可以知道的是,他操作函数imx_pops也是在这里注册的,最后把uart_port注册进uart_driver就行啦。那么到这里,就把TTY框架的三层联系起来了,也就是把注册关系给搞明白啦,那么注册过程也就完结啦,完结,撒花(doge.)

2025-04-17 21:09:47 692

原创 字符设备驱动程序的另一种注册方法

我们先来看看我们之前是怎么去写的:编写驱动程序的套路:确定主设备号,也可以让内核分配定义自己的file_operations结构体实现对应的drv_open/drv_read/drv_write等函数,填入file_operations结构体把file_operations结构体告诉内核:register_chrdev谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数有入口函数就应该有出口函数:卸载驱动程序时,出口函数调用unregister_chrdev。

2025-04-17 21:09:15 246

原创 TTY驱动程序框架

为了方便起见,操作系统提供了一个编辑缓冲区和一些基本的编辑命令(退格,清除单个单词,清除行,重新打印),这些命令在行规范(line discipline)内默认启用。大多数交互程序(编辑器,邮件客户端,shell,及所有依赖curses或readline的程序)均以原始模式运行,并自行处理所有的行编辑命令。PC会通过UART发送"l"出去,然后TTY框架会接收到数据,调用串口的驱动程序,然后把数据写入行规程中的buf,然后就是回显了,行规程调用串口的驱动程序,最后把数据发送回PC端,然后显示。

2025-04-17 17:18:00 184

原创 TTY体系中设备节点的差别

它们有什么差别?它们有什么差别?各类设备节点的差别:

2025-04-17 17:10:19 226

原创 层级中断控制器驱动程序编写

这里去注册gpio的domain的时候需要他父亲的domain,所以需要先从这个节点的资源中取出父亲的节点,然后得到父亲的域,最后就把那些mapping,ops之类的全部存入到gpio的domain里面。跟链式相比,我们在中断节点中不明确使用GIC中的哪个中断,而是给了一个属性,upper_hwirq_base,这个表示使用的中断的基地址是啥,然后使用的话,是一样的。替换irq_desc[virq].irq_data,里面有irq_chip改为irq_chipB,即GPIO的irq_chip。

2025-04-16 11:59:01 743

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

接着找到硬件中断号后,根据他的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 1172

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

链式中断控制器(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 759

原创 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 616

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

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

2025-04-14 20:50:13 955

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

在硬件上,“中断控制器”只有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 387

原创 中断的硬件框架

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

2025-04-13 22:19:24 526

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

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

2025-04-10 17:39:03 947

原创 interrupt子系统详谈

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

2025-04-10 17:17:05 1440

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

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

2025-04-09 17:01:06 286

原创 虚拟GPIO子系统

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

2025-04-09 16:05:05 269

原创 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 1078

原创 详解GPIO子系统

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

2025-04-08 16:19:54 1153

原创 C与指针的神秘面纱

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

2025-04-03 17:12:29 924

原创 详解Pinctrl子系统

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

2025-04-03 16:52:15 693

原创 虚拟pinctrl驱动

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

2025-03-30 21:51:35 329

原创 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 792

原创 详谈多线程2

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

2025-03-27 14:49:35 1321

原创 详谈多线程

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

2025-03-27 13:43:03 671

原创 内核中设备树的转换

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

2025-03-26 14:04:24 570

原创 内核中的互斥量

但是在代码上,并没有要求“谁获得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 492

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

所以,对于不支持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 695

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

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

2025-03-24 09:17:50 1092

原创 详解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 1023 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 716 1

原创 详解内联汇编

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

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

原创 OTA远程升级

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

2025-03-19 15:47:10 563

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

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

2024-12-23

空空如也

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

TA关注的人

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