Linux驱动.之I2C,iic驱动层(二)
一、 Linux下IIC驱动架构
本篇只分析,一个整体框架。
1、首先说说,单片机,的i2c硬件接口图,一个i2c接口,通过sda和scl总线,外接了多个设备device,通过单片机,来控制i2c的信号发生,
控制器件,所以,我们需要写起始信号,停止信号,应对信号,数据发送函数等等。
2、Linux定义了系统的IIC驱动体系结构,在Linux系统中,IIC驱动由3部分组成,IIC核心层、IIC总线层 ,IIC设备驱动层。这3部分相互协作,形成了非常通用、可适应性很强的IIC框架。
3、linux与单片机
linux与单片机的对比,原理是一样的,在编写单片机信号控制的代码(始信号,停止信号,应对信号),其实就是对应linux 的IIC总线层,linux对它进行了抽象成(i2c adapter的algorithm),对外提供统一接口,提供给驱动层调用。
单片机 app.c,应用程序
unsigned char AT24C_WriteStr(unsigned char devaddr, unsigned char romaddr, unsigned char *s, unsigned char num)
{// 1、发送起始信号,器件地址,探测器件是否存在IIC_Start()//2、发送要写入数据的地址//3、发送数据IIC_SendByte()//4、发送停止信号,写完了
}
驱动层,i2c_drive.c
void IIC_Start(void)
void IIC_Stop(void)
void IIC_SendByte(unsigned char dat)
void IIC_ACK(void)
单片机外接了很多设备,就需操作多个设备,在单片机编程中,往往会封装一个write_data ,read_data 函数,放在应用层app文件,那里面就包含了时序控制函数,具体参考C语言模拟i2c篇。封装后,给具体的i2c device设备用。那对应到linux,不就是设备驱动层。驱动层仿照一套字符设备的方法,提供probe函数,注册class类,device类,device类里就是封装了open,read,write等file_opration结构体,给应用层调用。app应用层,就可以把一个个i2c设备,当做·文件来处理,打开各个i2c外接设备,传输读写数据。
linux的核心层,不就是,linux内核,提供一套注册方法,描述了很多东西,将设备驱动绑定到总线bus的adapter的方法,并注册到内核。
上图完整的描述了linux i2c驱动架构,虽然I2C硬件体系结构比较简单,但是i2c体系结构在linux中的实现却相当复杂。
那么我们如何编写特定i2c接口器件的驱动程序?就是说上述架构中的那些部分需要我们完成,而哪些是linux内核已经完善的或者是芯片提供商已经提供的?
1、架构层次分类
第一层:提供i2c adapter的硬件驱动,探测、初始化i2c adapter(如申请i2c的io地址和中断号),驱动soc控制的i2c adapter在硬件上产生信号(start、stop、ack)以及处理i2c中断。覆盖图中的硬件实现层
第二层:提供i2c adapter的algorithm,用具体适配器的xxx_xferf()函数来填充i2c_algorithm的master_xfer函数指针,并把赋值后的i2c_algorithm再赋值给i2c_adapter的algo指针。覆盖图中的访问抽象层、i2c核心层
第三层:实现i2c设备驱动中的i2c_driver接口,用具体的i2c device设备的attach_adapter()、detach_adapter()方法赋值给i2c_driver的成员函数指针。实现设备device与总线(或者叫adapter)的挂接。覆盖图中的driver驱动层
第四层:实现i2c设备所对应的具体device的驱动,i2c_driver只是实现设备与总线的挂接,而挂接在总线上的设备则是千差万别的,所以要实现具体设备device的write()、read()、ioctl()等方法,赋值给file_operations,然后注册字符设备(多数是字符设备)。覆盖图中的driver驱动层
第一层和第二层又叫i2c总线驱动(bus),第三 ,第四 属于i2c设备驱动层(device driver)。
在linux驱动架构中,几乎不需要驱动开发人员再添加bus,因为linux内核几乎集成所有总线bus,如usb、pci、i2c等等。并且总线bus中的(与特定硬件相关的代码)已由芯片提供商编写完成,现在与硬件相关的adapter,是放在芯片厂商各自dts中,老的内核版本是,放在内核中,例如三星的s3c-2440平台i2c总线bus为/drivers/i2c/buses/i2c-s3c2410.c。第三第四层与特定device相干的就需要驱动工程师来实现了。
二、Linux下I2C驱动体系结构三部分详细分析
1、 IIC核心
IIC 核心提供了IIC总线驱动和设备驱动的注册、注销方法,IIC通信方法(即“algorithm”,笔者认为直译为“运算方法”并不合适,为免引起误解, 下文将直接使用“algorithm”)上层的、与具体适配器无关的代码以及探测设备、检测设备地址的上层代码等。
在我们的Linux驱动的i2c文件夹下有algos,busses,chips三个文件夹,另外还有i2c-core.c和i2c-dev.c两个文件。
i2c-core.c文件实现了I2Ccore框架,是Linux内核用来维护和管理的I2C的核心部分,其中维护了两个静态的List,分别记录系统中的I2Cdriver结构和I2Cadapter结构。I2Ccore提供接口函数,允许一个I2Cadatper,I2Cdriver和I2Cclient初始化时在I2Ccore中进行注册,以及退出时进行注销。同时还提供了I2C总线读写访问的一般接口,主要供在I2C设备驱动中,函数调用,进一步分装给app层用。
2、 IIC总线驱动
IIC总线驱动是对IIC硬件体系结构中适配器端的实现,适配器可由CPU控制,甚至直接集成在CPU内部。总线驱动的职责,是为系统中每个I2C总线增加相应的读写方法。但是总线驱动本身并不会进行任何的通讯,它只是存在那里,等待设备驱动调用其函数。
IIC总线驱动主要包含了IIC适配器数据结构i2c_adapter、IIC适配器的algorithm数据结构i2c_algorithm和控制IIC适配器产生通信信号的函数。经由IIC总线驱动的代码,我们可以控制IIC适配器以主控方式产生开始位、停止位、读写周期,以及以从设备方式被读写、产生ACK等。
Busses文件夹下的i2c-mpc.c文件实现了PowerPC下I2C总线适配器驱动,定义描述了具体的I2C总线适配器的i2c_adapter数据结构,实现比较底层的对I2C总线访问的具体方法,比如设置gpio的寄存器,中断。I2Cadapter 构造一个对I2Ccore层接口的数据结构,并通过接口函数向I2Ccore注册一个控制器。I2Cadapter主要实现对I2C总线访问的算法,iic_xfer() 函数就是I2Cadapter底层对I2C总线读写方法的实现。同时I2Cadpter 中还实现了对I2C控制器中断的处理函数。
3、 IIC设备驱动
IIC设备驱动是对IIC硬件体系结构中设备端的实现,设备一般挂接在受CPU控制的IIC适配器上,通过IIC适配器与CPU交换数据。设备驱动则是与挂在I2C总线上的具体的设备通讯的驱动。通过I2C总线驱动提供的函数,设备驱动可以忽略不同总线控制器的差异,不考虑其实现细节地与硬件设备通讯。这样就无论换成什么soc,比如海思,三星,nxp,高通,都只需要写一个驱动函数就可以了,顶多修改下,设备名称,或者不用改。其实就是我前面讲的,这个驱动层,会去调用封装好的,adapter,时序·控制函数,transfer,数据传输函数,因为接口是linux内核统一了,所以驱动代码是无需更改的,i2c core层,提供的统一接口,所以满足了设备驱动,总线分离分层的思想,终于明白了分离的思想,就是分开了,soc换了,驱动不用改。
IIC设备驱动主要包含了数据结构i2c_driver和i2c_client,我们需要根据具体设备实现其中的成员函数。
i2c-dev.c文件中实现了I2Cdriver,提供了一个通用的I2C设备的驱动程序,实现了字符类型设备的访问接口,实现了对用户应用层的接口,提供用户程序访问I2C设备的接口,包括实现open,release,read,write以及最重要的ioctl等标准文件操作的接口函数。我们可以通过open函数打开 I2C的设备文件,通过ioctl函数设定要访问从设备的地址,然后就可以通过 read和write函数完成对I2C设备的读写操作。注册到内核,在class,device,/dev下就有i2c节点了。open(/dev/i2c2),就可以了,读写数据。
/* AP3216C操作函数file_operations */
static const struct file_operations ap3216c_ops = {.owner = THIS_MODULE,.open = ap3216c_open,.read = ap3216c_read,.write= ap3216c_write,.release = ap3216c_release,
};static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{/* 1、构建设备号 */if (ap3216cdev.major) {ap3216cdev.devid = MKDEV(ap3216cdev.major, 0);register_chrdev_region(ap3216cdev.devid, AP3216C_CNT, AP3216C_NAME);} else {alloc_chrdev_region(&ap3216cdev.devid, 0, AP3216C_CNT, AP3216C_NAME);ap3216cdev.major = MAJOR(ap3216cdev.devid);}/* 2、注册设备 */cdev_init(&ap3216cdev.cdev, &ap3216c_ops);cdev_add(&ap3216cdev.cdev, ap3216cdev.devid, AP3216C_CNT);/* 3、创建类 */ap3216cdev.class = class_create(THIS_MODULE, AP3216C_NAME);if (IS_ERR(ap3216cdev.class)) {return PTR_ERR(ap3216cdev.class);}/* 4、创建设备 */ap3216cdev.device = device_create(ap3216cdev.class, NULL, ap3216cdev.devid, NULL, AP3216C_NAME);if (IS_ERR(ap3216cdev.device)) {return PTR_ERR(ap3216cdev.device);}ap3216cdev.private_data = client;return 0;
}static struct i2c_driver ap3216c_driver = {.probe = ap3216c_probe,.remove = ap3216c_remove,.driver = {.owner = THIS_MODULE,.name = "ap3216c",.of_match_table = ap3216c_of_match, },.id_table = ap3216c_id,
};static int __init ap3216c_init(void)
{int ret = 0;ret = i2c_add_driver(&ap3216c_driver);return ret;
}module_init(ap3216c_init);
最后,可以在linux内核看到这些,这里没用i2c,只是例子
drvie结构体,包含probe函数,和设备描述client,probe是字符设备那一套,注册一个类clase,设备device,到内核,最后内核起来后,映射到/dev/i2c1,这样就可以app,写应用程序,来操作字符设备那一套,读写数据,字符设备那一套,读写,就是单片机封装的,读写数据的app,给应用层使用
通过I2Cdriver提供的通用方法可以访问任何一个I2C的设备,但是其中实现的read,write及ioctl等功能完全是基于一般设备的实现,所有的操作数据都是基于字节流,没有明确的格式和意义。为了更方便和有效地使用I2C设备,我们可以为一个具体的I2C设备开发特定的I2C设备驱动程序,在驱动中完成对特定的数据格式的解释以及实现一些专用的功能。
三、具体结构体分析
因为IIC设备种类太多,如果每一个IIC设备写一个驱动程序,那么显得内核非常大。不符合软件工程代码复用,所以对其层次话:
这里简单的将IIC设备驱动分为设备层、总线层。理解这两个层次的重点是理解4个数据结构,这4个数据结构是i2c_driver、i2c_client、i2c_algorithm、i2c_adapter。i2c_driver、i2c_client属于设备层;i2c_algorithm、i2c_adapter属于总线型。如下图:
设备层关系到实际的IIC设备,总线层包括CPU中的IIC总线控制器和控制总线通信的方法。值得注意的是:一个系统中可能有很多个总线层,也就是包含多个总线控制器;也可能有多个设备层,包含不同的IIC设备,如下图
由IIC总线规范可知,IIC总线由两条物理线路组成,这两条物理线路是SDA和SCL。只要连接到SDA和SCL总线上的设备都可以叫做IIC设备。
1、 i2c_client
一个IIC设备由i2c_client数据结构进行描述:
struct i2c_client
{ unsigned short flags; //标志位 unsigned short addr; //设备的地址,低7位为芯片地址 char name[I2C_NAME_SIZE]; //设备的名称,最大为20个字节 struct i2c_adapter *adapter; //依附的适配器i2c_adapter,适配器指明所属的总线 struct i2c_driver *driver; //指向设备对应的驱动程序 struct device dev; //设备结构体 int irq; //设备申请的中断号 struct list_head list; //连接到总线上的所有设备 struct list_head detected; //已经被发现的设备链表 struct completion released; //是否已经释放的完成量
};
设备结构体i2c_client中addr的低8位表示设备地址
IIC设备还有一些重要的注意事项:
1、i2c_client数据结构是描述IIC设备的“模板”,驱动程序的设备结构中应包含该结构;
2、adapter指向设备连接的总线适配器,系统可能有多个总线适配器。内核中静态指针数组adapters记录所有已经注册的总线适配器设备;
3、driver是指向设备驱动程序,这个驱动程序是在系统检测到设备存在时赋值的;
2、IIC设备驱动 i2c_driver
struct i2c_driver
{ int id; //驱动标识ID unsigned int class; //驱动的类型 int (*attach_adapter)(struct i2c_adapter *); //当检测到适配器时调用的函数 int (*detach_adapter)(struct i2c_adapter*); //卸载适配器时调用的函数 int (*detach_client)(struct i2c_client *) __deprecated; //卸载设备时调用的函数 //以下是一种新类型驱动需要的函数,这些函数支持IIC设备动态插入和拔出。如果不想支持只实现上面3个。要不实现上面3个。要么实现下面5个。不能同时定义 int (*probe)(struct i2c_client *,const struct i2c_device_id *); //新类型设备探测函数 int (*remove)(struct i2c_client *); //新类型设备的移除函数 void (*shutdown)(struct i2c_client *); //关闭IIC设备 int (*suspend)(struct i2c_client *,pm_messge_t mesg); //挂起IIC设备 int (*resume)(struct i2c_client *); //恢复IIC设备 int (*command)(struct i2c_client *client,unsigned int cmd,void *arg); //使用命令使设备完成特殊的功能。类ioctl()函数 struct devcie_driver driver; //设备驱动结构体 const struct i2c_device_id *id_table; //设备ID表 int (*detect)(struct i2c_client *,int kind,struct i2c_board_info *); //自动探测设备的回调函数 const struct i2c_client_address_data *address_data; //设备所在的地址范围 struct list_head clients; //指向驱动支持的设备
};
结构体i2c_driver和i2c_client的关系较为简单,其中i2c_driver表示一个IIC设备驱动,i2c_client表示一个IIC设备。关系如下图:
3、 i2c_adapter
IIC总线适配器就是一个IIC总线控制器,在物理上连接若干个IIC设备。IIC总线适配器本质上是一个物理设备,其主要功能是完成IIC总线控制器相关的数据通信:
struct i2c_adapter
{ struct module *owner; //模块计数 unsigned int id; //alogorithm的类型,定义于i2c_id.h中 unsigned int class; //允许探测的驱动类型 const struct i2c_algorithm *algo; //指向适配器的驱动程序 void *algo_data; //指向适配器的私有数据,根据不同的情况使用方法不同 int (*client_register)(struct i2c_client *); //设备client注册时调用 int (*client_unregister(struct i2c_client *); //设备client注销时调用 u8 level; struct mutex bus_lock; //对总线进行操作时,将获得总线锁 struct mutex clist_lock ; //链表操作的互斥锁 int timeout; //超时 int retries; //重试次数 struct device dev; //指向 适配器的设备结构体 int nr ; struct list_head clients; //连接总线上的设备的链表 char name[48]; //适配器名称 struct completion dev_released; //用于同步的完成量
};
4、i2c_algorithm
每一个适配器对应一个驱动程序,该驱动程序描述了适配器与设备之间的通信方法:
struct i2c_algorithm
{ int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msg, int num); /int (*smbus_xfer)(struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data);u32 (*functionality)(struct i2c_adapter *); };
IIC设备驱动程序大致可以分为设备层和总线层。设备层包括一个重要的数据结构,i2c_client。总线层包括两个重要的数据结构,分别是i2c_adapter和i2c_algorithm。一个i2c_algorithm结构表示适配器对应的传输数据方法。
IIC设备驱动程序的步骤:
5、各结构体的作用与它们之间的关系
1、 i2c_adapter与i2c_algorithm
i2c_adapter对应与物理上的一个适配器,而i2c_algorithm对应一套通信方法,一个i2c适配器需要i2c_algorithm中提供的(i2c_algorithm中的又是更下层与硬件相关的代码提供)通信函数来控制适配器上产生特定的访问周期。缺少i2c_algorithm的i2c_adapter什么也做不了,因此i2c_adapter中包含其使用i2c_algorithm的指针。i2c_algorithm中的关键函数master_xfer()用于产生i2c访问周期需要的start stop ack信号,以i2c_msg(即i2c消息)为单位发送和接收通信数据。
i2c_msg也非常关键,调用驱动中的发送接收函数需要填充该结构体
struct i2c_msg { __u16 addr; /* slave address */ __u16 flags; __u16 len; /* msg length */ __u8 *buf; /* pointer to msg data */
};
2、i2c_driver和i2c_client
i2c_driver对应一套驱动方法,其主要函数是attach_adapter()和detach_client()
i2c_client对应真实的i2c物理设备device,每个i2c设备都需要一个i2c_client来描述
i2c_driver与i2c_client的关系是一对多。一个i2c_driver上可以支持多个同等类型的i2c_client.
3、 i2c_adapter和i2c_client
i2c_adapter和i2c_client的关系与i2c硬件体系中适配器和设备的关系一致,即i2c_client依附于i2c_adapter,由于一个适配器上可以连接多个i2c设备,所以i2c_adapter中包含依附于它的i2c_client的链表。
总结:从i2c驱动架构图中可以看出,linux内核对i2c架构抽象了一个叫核心层core的中间件,它分离了设备驱动device driver和硬件控制的实现细节(如操作i2c的寄存器),core层不但为上面的设备驱动提供封装后的内核注册函数,而且还为小面的硬件事件提供注册接口(也就是i2c总线注册接口),可以说core层起到了承上启下的作用。
5、设备树dts的作用。
以前是真没理解,为啥要有设备树,看这个图,恍然大悟,以前写单片机,要设置i2c控制,然后,有不同的单片机,51,nxp,stm32,太多了,每个soc的i2c设置都不一样,在内核里,每个芯片厂商,都会提交自己的芯片,设置i2c控制的,导致内核好大啊,越来越多,完蛋,就发明了dts,剥离出来,每个芯片商,自己写自己的dts,描述i2c寄存器,控制器的文件,发布时,不提交到内核里,自己发给客户,
现在才明白,并不是减少了代码,只是,不提交到内核社区,,这些描述soc相关的adapter,device跟板子有关的,自己维护就好了。内核只提供标注的接口。
相关文章:

Linux驱动.之I2C,iic驱动层(二)
一、 Linux下IIC驱动架构 本篇只分析,一个整体框架。 1、首先说说,单片机,的i2c硬件接口图,一个i2c接口,通过sda和scl总线,外接了多个设备device,通过单片机,来控制i2c的信号发生&…...

【STM32】USART串口和I2C通信
个人主页~ USART串口和I2C通信 USART串口一、串口1、简介2、电路要求3、参数及时序 二、USART外设1、USART结构2、波特率发生器 三、数据包1、HEX数据包HEX数据包接收 2、文本数据包文本数据包接收 I2C通信一、简介二、通信协议1、硬件电路2、I2C时序基本单元 三、I2C外设1、简…...

【Material-UI】按钮组:垂直按钮组详解
文章目录 一、按钮组概述1. 组件介绍2. 基本用法 二、垂直按钮组的应用场景1. 导航菜单2. 表单操作3. 选项切换 三、按钮组的样式定制1. 变体(Variants)2. 颜色(Colors) 四、垂直按钮组的优势1. 空间利用2. 可读性与易用性3. 视觉…...

DDR5 的优势与应用
DDR5 是新一代 DRAM 内存,具有一系列强大的功能,可提升可靠性、可用性和可维护性 (RAS),降低能耗并显著提高性能。请查看下方表格,了解 DDR4 和 DDR5 之间的一些主要特性差异。 DDR5 的优势 特性/选项 DDR4DDR5DDR5 优势数据速率…...

STM32 - 笔记
1 STM32的串口通信 【keysking的STM32教程】 第8集 STM32的串口通信_哔哩哔哩_bilibili 波特律动 串口助手...

基于QT实现的简易WPS(已开源)
一、开发工具及开源地址: 开发工具:QTCreator ,QT 5 开源地址: GitHub - Whale-xh/WPS_official: Simple WPS based on QTSimple WPS based on QT. Contribute to Whale-xh/WPS_official development by creating an acc…...
Flask-WTF 表单处理详细教程(第六阶段)
目录 Flask-WTF 表单处理详细教程1. 安装 Flask-WTF2. 创建 Flask 应用3. 创建表单类表单字段解释: 4. 渲染表单渲染模板CSRF 保护 5. 表单验证6. 处理错误7. 完整示例8. 结论 Flask-WTF 表单处理详细教程 Flask-WTF 是 Flask 框架的一个扩展,简化了 We…...

C语言 | Leetcode C语言题解之第330题按要求补齐数组
题目: 题解: int minPatches(int* nums, int numsSize, int n) {int patches 0;long long x 1;int index 0;while (x < n) {if (index < numsSize && nums[index] < x) {x nums[index];index;} else {x << 1;patches;}}retu…...

无人机之测绘行业篇
无人机倾斜摄影技术凭借快速高效、机动灵活、成本低等优势,正慢慢颠覆传统测绘的作业方式,已成为测绘行业的“新宠”,将倾斜摄影技术应用到无人机上,实际上就是在做一个三维模型,而建立起来的这个模型更加真实…...
Java编程:每日挑战
目录 题目1.一个抽象类并不需要其中所有的方法都是抽象的。( )2.下面有关java hashmap的说法错误的是?A. HashMap 的实例有两个参数影响其性能:“初始容量” 和 “加载因子”。B. HashMap 的实现不是同步的,意味着它不…...

【自动驾驶】ubuntu server安装桌面版
目录 安装桌面版当锁屏界面使用root用户登录错误时 这里环境一开始是ubuntu20.04服务器版本 安装桌面版 sudo apt-get update sudo apt-get upgrade apt-get install -y ubuntu-desktop # 如果你不想安装一些附加的程序,可用以下命令 sudo apt install --no-instal…...

前端模块化-手写mini-vite
前言 本文总结了一些关于 Vite 的工作原理,以及一些实现细节。 本节对应的 demo 可以在这里找到。 什么是 Vite Vite 是一个基于浏览器原生 ES imports 的开发服务器。利用浏览器去解析 imports,在服务器端按需编译返回,完全跳过了打包这个…...

SpringBoot中fastjson扩展: 自定义序列化和反序列化方法实战
❃博主首页 : 「码到三十五」 ,同名公众号 :「码到三十五」,wx号 : 「liwu0213」 ☠博主专栏 : <mysql高手> <elasticsearch高手> <源码解读> <java核心> <面试攻关> ♝博主的话 :…...

【QT】鼠标按键事件 - QMouseEvent QKeyEvent
qt 事件 事件1. 事件概念2. 事件的处理3. 按键事件(1)单个按键(2)组合按键 4. 鼠标事件(1)鼠标单击事件(2)鼠标释放事件(3)鼠标双击事件(4&#x…...
纯手工在内网部署一个Docker私有仓库
纯手工在内网部署一个Docker私有仓库 下载Docker仓库的镜像上传仓库的镜像导入仓库的镜像启动仓库镜像配置客户端的Docker上传镜像到本地仓库从本地仓库拉取镜像 下载Docker仓库的镜像 这个镜像不太好找,有需要的可以从下面的地址中下载。 通过百度网盘分享的文件…...
农林经济管理学报
《农林经济管理学报》是由江西省教育厅主管、江西农业大学主办、北京大学中国农业政策研究中心和中国人民大学农业与农村发展学院学术支持的农林经管类学术双月刊,以主要刊载农林经济政策与理论,反映农林经济管理前沿动态和研究成果,开展学术…...

【初阶数据结构题目】16.用队列实现栈
用队列实现栈 点击链接答题 思路: 出栈:找不为空的队列,将size-1个数据导入到另一个队列中。 入栈:往不为空队列里面插入数据 取栈顶元素: 例如: 两个队列: Q1:1 2 3Q2:…...
使用 OpenAI Whisper v2 模型进行中英文混合语音识别
https://huggingface.co/openai/whisper-large-v2 使用 OpenAI Whisper 模型进行中英文混合语音识别 在本篇博客中,我们将详细介绍如何使用 OpenAI 的 Whisper 模型进行中英文混合语音识别,并设置 Hugging Face 的缓存路径。 简介 Whisper 是 OpenAI 提供的一个强大的自动…...
代码随想录算法训练营day37|动态规划part05
完全背包问题; 第一题:518. Coin Change II class Solution {public int change(int amount, int[] coins) {//递推表达式int[] dp new int[amount 1];//初始化dp数组,表示金额为0时只有一种情况,也就是什么都不装dp[0] 1;fo…...
Git 如何提交代码
一. 简介 前面几篇文章简单学习了 git常用命令,文章如下: Git使用过程中涉及的几个区域-CSDN博客 Git常用命令的使用-CSDN博客 本文学习一下 如何使用 git命令,将本地代码提交到远程仓库。 二. 使用 git命令将本地代码提交到远程仓库中 …...

idea大量爆红问题解决
问题描述 在学习和工作中,idea是程序员不可缺少的一个工具,但是突然在有些时候就会出现大量爆红的问题,发现无法跳转,无论是关机重启或者是替换root都无法解决 就是如上所展示的问题,但是程序依然可以启动。 问题解决…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互
引擎版本: 3.8.1 语言: JavaScript/TypeScript、C、Java 环境:Window 参考:Java原生反射机制 您好,我是鹤九日! 回顾 在上篇文章中:CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果
云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...
Fabric V2.5 通用溯源系统——增加图片上传与下载功能
fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...

Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...