Linux下的I2C驱动框架以及代码实现

参考资料:

1、Linux IIC 驱动分析 — 框架分析 - 知乎 (zhihu.com)

2、《Linux驱动开发指南》第十一章

3、《正点原子 I.MX6U嵌入式Linux驱动开发指南 V1.6》

4、《Linux设备驱动开发详解》

代码版本:Linux4.1.15

阅读本文需要先有一定的I2C基础以及Linux驱动基础。

I2C协议视频教程:DAY3-1 IIC总线概述_哔哩哔哩_bilibili

IIC硬件抽象出的软件结构

IIC接口的设备以一些传感器为多,如常见的AT24C02,I2C在硬件上比较简单,总线上只有两根数据线,数据线SDA和时钟线SCL,

但是到了Linux驱动就变得复杂一些,在Linux系统中,I2C驱动由3部分组成:I2C核心、I2C总线驱动、I2C设备驱动,按照Linux软硬件分离的思想,当硬件发生变化时,上层的控制逻辑能保持不变。

如下图所示,Linux驱动将硬件设备抽象成了一系列的结构体:

I2C硬件抽象出的软件结构体——知乎

其中:

非红色部分:实际的I2C设备的硬件挂载情况

红色部分:Linux抽象出来的软件结构

结构体的表示如下:

1、i2c_adatper:描述一个物理的 I2C 控制器  
2、i2c_algorithm:描述一个物理的I2C控制器的一组IIC控制器操作的集合,如特定SOC的IIC模块产生通信波形方法。
3、i2c_bus_type:描述IIC总线的结构体,根据Linux的设备、驱动、总线的思想,Linux设备应该通过Linux的I2C总线挂载上去,再通过固定的driver进行匹配。

4、i2c_client:描述一个挂接到 I2C 总线上的具体物理设备,也是我们需要驱动的传感器
5、i2c_driver:用于描述一个 I2C 设备的驱动

......

【Tip】在《Linux驱动开发指南》中,有另外的理解:适配器指的是主机,客户端指的是从机!这个解释会更加浅显易懂。在主机想要对从机进行读写操作时,需要先注册到总线上去,从机也是一样,需要注册到总线上,I2C系统也是遵从了platform设备模型的。

I2C系统的结构:I2C核心、I2C总线驱动、I2C设备驱动

整个I2C系统的结构图如下:

IIC子系统结构图
I2C子系统结构图——《Linux驱动开发》

 架构是驱动的核心,I2C已经搭建了很完善的驱动了,Linux系统对I2C进行分层,分为硬件层、内核层、用户层。

用户层:通过标准的open、read、write、close调用IIC设备操作。

内核层:向上提供用户调用接口,核心为i2c_core,提供了总线、驱动、通信方式的注册和钩子函数的设置,起到一个承上启下的作用,driver、device、控制驱动程序我们都比较熟悉,那么什么是I2C子系统的核心(接口)呢?

硬件层:具体的硬件设备以及控制器。

前面我们提到了,在Linux系统中,I2C驱动由3部分组成:I2C核心、I2C总线驱动、I2C设备驱动。我们先简要介绍下三部分各是什么。

(1)I2C设备驱动【从机】:也叫客户驱动,是对I2C硬件体系结构中设备端的实现,设备一般挂接在受CPU控制的I2C适配器上,通过I2C适配器与CPU进行数据交互。I2C设备驱动主要包含数据结构:i2c_driver和i2c_client。

(2)I2C总线驱动【主机】:是对I2C适配器端的实现,适配器可以由CPU控制(甚至直接集成在CPU内部),I2C适配器的数据结构主要是i2c_adapter和、i2c_algorithm、以及控制I2C适配器产生通信信号的函数。

 (3)I2C核心:提供了I2C总线驱动和设备驱动的注册、注销、通信方法(Algorithm)上层与具体适配器无关的代码以及其他上层代码。

I2C体系结构——Linux设备驱动开发详解
I2C体系结构——Linux设备驱动开发详解

 上图为Linux下I2C的驱动框架,我们在实现时,需要使用具体的结构体进行描述,可以将其替换成下述的具体结构体!

I2C系统结构图——知乎

 驱动的核心在于其结构,了解了I2C的大致框架之后,我们再来看一下具体的结构体。I2C结构体之间会有相互联系,如下图所示。

I2C驱动各种结构体的关系——Linux设备驱动开发详解

I2C设备的结构体

这么多的结构体,那么我们需要优先或重点关注什么结构体呢?

1、i2c_adapter:一般是由半导体厂商编写的,比如I.MX6U的I2C适配器驱动(i2c_adatper)已经由NXP编写好了,不需要用户去编写。因此I2C总线驱动对于SOC使用者来说是被屏蔽的。

2、i2c_client、i2c_driver:用户需要专注于自己挂载在总线上的I2C设备驱动/从机(i2c_client、i2c_driver)。

3、i2c_core:不依赖硬件平台的接口函数,是I2C总线驱动和设备驱动的纽带,一般不需要修改,但是需要理解其中的主函数。如:增加/删除i2c_adapter、增加/删除i2c_driver、I2C传输,发送和接收

因此,我们需要做的是先实现自己挂载的I2C设备的i2c_client和i2c_driver结构体。

i2c_client

就如上图所示,一个设备对应一个i2c_client,每检测到一个I2C设备就会给其分配一个i2c_client。其结构体如下图所示:

struct i2c_client {unsigned short flags;                 /* div., see below */#define I2C_CLIENT_PEC 0x04           /* Use Packet Error Checking */#define I2C_CLIENT_TEN 0x10           /* we have a ten bit chip address *//* Must equal I2C_M_TEN below */#define I2C_CLIENT_SLAVE 0x20         /* we are the slave */#define I2C_CLIENT_HOST_NOTIFY 0x40   /* We want to use I2C host notify */#define I2C_CLIENT_WAKE 0x80          /* for board_info; true iff can wake */#define I2C_CLIENT_SCCB 0x9000        /* Use Omnivision SCCB protocol *//* Must match I2C_M_STOP|IGNORE_NAK */unsigned short addr;                  /* chip address - NOTE: 7bit *//* addresses are stored in the *//* _LOWER_ 7 bits */char name[I2C_NAME_SIZE];struct i2c_adapter *adapter;          /* the adapter we sit on */struct device dev;                    /* the device structure */int init_irq;                         /* irq set at initialization */int irq;                              /* irq issued by device */struct list_head detected;#if IS_ENABLED(CONFIG_I2C_SLAVE)i2c_slave_cb_t slave_cb;              /* callback for slave mode */#endifvoid *devres_group_id;                /* ID of probe devres group */
};

主要成员说明如下:

1、  addr 设备地址,7bit
2、  name 设备名称,设备树I2C子节点中的节点名@前面便是I2C设备名
3、  adapter 适配器,实际上指的是主机,从机指的是客户端。
4、  dev 设备对象
I2C客户端结构体比较简单,因为其规范很成熟了,特别是对于某些自带I2C控制器的传感器而言,I2C接口只是用来传输信息,用户要做的只是对寄存器进行读写而已。
I2C_Client代表一个物理的I2C设备,因此,我们需要在设备树中添加对应的设备节点,比如在I.MX6ULL的I2C控制器1下,增加一个I2C设备:mag3110磁力计,因此,我们需要在设备树文件下的&i2c1节点下进行添加:
 &i2c1 {clock-frequency = <100000>;pinctrl-names = "default";pinctrl-0 = <&pinctrl_i2c1>;status = "okay";ap3216c@1e {compatible = "alientek,ap3216c";reg = <0x1e>;};
};

i2c_driver

该结构体是我们I2C设备驱动处理的重点,我们需要做的就是在构建该结构体之后,向Linux内核使用 i2c_register_driver进行注册。
struct i2c_driver {unsigned int class;int (*probe)(struct i2c_client *client, const struct i2c_device_id *id);int (*remove)(struct i2c_client *client);int (*probe_new)(struct i2c_client *client);void (*shutdown)(struct i2c_client *client);void (*alert)(struct i2c_client *client, enum i2c_alert_protocol protocol,unsigned int data);int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);struct device_driver driver;const struct i2c_device_id *id_table;int (*detect)(struct i2c_client *client, struct i2c_board_info *info);const unsigned short *address_list;struct list_head clients;
};

我们主要关注的是probe、remove、shutdown、driver即可,其他的很少使用到。

probe:在驱动与设备匹配时,会执行probe函数,从而对该设备进行初始化和注册操作。

id_table:传统的、未使用设备树的设备匹配的ID表

device_driver:使用设备树的要,需要设置device_driver的id_table成员。

 /* i2c 驱动结构体 */
static struct i2c_driver xxx_driver = {.probe = xxx_probe,.remove = xxx_remove,.driver = {.owner = THIS_MODULE,.name = "xxx",.of_match_table = xxx_of_match, /* 设备树匹配方式列表 */},.id_table = xxx_id,             /* 传统匹配方式ID列表 */
}/* 设备树匹配列表 */
static const struct of_device_id xxx_of_match[] = {{ .compatible = "xxx" },{ /* Sentinel */ }
};/* 驱动入口函数 */
static int __init xxx_init(void){int ret = 0;ret = i2c_add_driver(&xxx_driver);return ret;
}/* 驱动出口函数 */
static void __exit xxx_exit(void){i2c_del_driver(&xxx_driver);
}

上述是i2c_driver的部分模板,在编写完i2c_driver结构体之后,正如在驱动入口函数所见,我们可以使用i2c_register_driver或者i2c_add_driver向内核注册该I2C_driver,实际上是注册到I2C核心(i2c_core)。正如上述所说的,实际上是使用了i2c_core的核心函数,增加/删除i2c_adapter,我们稍后再对i2c_core进行介绍,我们先讲一下I2C总线的结构体。

i2c_bus_type

我们知道I2C设备去驱动的匹配遵循了Linux的设备、总线、驱动原则,那么上述我们介绍了设备和驱动结构体,还需要总线的结构体。I2C设备和驱动的匹配过程是由I2C总线完成的,其结构体为i2c_bus_type。
struct bus_type i2c_bus_type = {.name = "i2c",.match = i2c_device_match,.probe = i2c_device_probe,.remove = i2c_device_remove,.shutdown = i2c_device_shutdown,
};

其中match函数就是总线的设备和驱动匹配函数,如设备树匹配、ACPI形式匹配等。

i2c_core

在上文提到,I2C核心很少需要被修改,但是我们需要了解他的主要函数和功能:

(1)增加/删除i2c_driver

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
int i2c_add_driver (struct i2c_driver *driver)
void i2c_del_driver(struct i2c_driver *driver)

上述的注册与删除函数,我们可以在i2c_driver的驱动入口函数和出口函数见到。

(2)增加/删除i2c_adapter

int i2c_add_adapter(struct i2c_adapter *adapter)
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
void i2c_del_adapter(struct i2c_adapter * adap)

i2c_adapter的内容一般不需要我们实现,我们先跳过,放到最后学习。

(3)I2C传输、发送和接收

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
int i2c_master_send(struct i2c_client *client, const char *buf, int count);
int i2c_master_recv(struct i2c_client *client, char *buf, int count);

i2c_transfer用于I2C适配器与设备之间进行一组信息交互,其中第二个参数是指向一个i2c_msg数组的指针,所以i2c_transfer一次性可以传输多个i2c_msg。

对于时序简单的外设,i2c_master_send和i2c_master_recv会调用i2c_transfer分别完成一条写消息和读一条消息,但是i2c_transfer本身不具备驱动适配器物理硬件以完成信息交互的能力,只是寻找与i2c_adapter对应的i2c_algorithm,使用i2c_algorithm的x_fer()函数真正驱动硬件流程。

I2C设备收发数据流程

接下来,我们先了解一下,I2C设备是怎么发送与接收数据的。

Linux内核为I2C数据传输实现了两套通信方式:

(1)传统的I2C通信方式

(2)SMBus(System Management Bus)通信方式,是较为推荐的一种方式,在1995年Inter提出的,该总线与I2C基本一致,但是在I2C总线上进行了扩展。

但是!本文只讲解传统方式下的I2C数据传输。

Linux在内核提供了两个函数供给用户使用,即i2c_master_send和i2c_master_recv,前者发送数据,后者接收数据。上面已经给出了这两个函数具体的函数声明。

Linux内核还提供了一种传输方式,该函数既可以传输数据,也可以接收数据:i2c_transfer,但是,其参数已经不像前面两个函数一样,是简单的buf了,而是i2c_msg结构体。

i2c_msg

struct i2c_msg {__u16 addr;__u16 flags;#define I2C_M_RD 0x0001             /* guaranteed to be 0x0001! */#define I2C_M_TEN 0x0010            /* use only if I2C_FUNC_10BIT_ADDR */#define I2C_M_DMA_SAFE 0x0200       /* use only in kernel space */#define I2C_M_RECV_LEN 0x0400       /* use only if I2C_FUNC_SMBUS_READ_BLOCK_DATA */#define I2C_M_NO_RD_ACK 0x0800      /* use only if I2C_FUNC_PROTOCOL_MANGLING */#define I2C_M_IGNORE_NAK 0x1000     /* use only if I2C_FUNC_PROTOCOL_MANGLING */#define I2C_M_REV_DIR_ADDR 0x2000   /* use only if I2C_FUNC_PROTOCOL_MANGLING */#define I2C_M_NOSTART 0x4000        /* use only if I2C_FUNC_NOSTART */#define I2C_M_STOP 0x8000           /* use only if I2C_FUNC_PROTOCOL_MANGLING */__u16 len;__u8 *buf;
};

addr:I2C客户端地址,即从设备地址

flag:信息标志,即传输方向,0写数据,1(I2C_M_RD )读数据,其他标志位看上面代码的宏定义

len:表示数据传输长度

buf:所传输的数据的首地址

如下面的代码就实现了一个简单的数据发送过程。

int i2c_write_and_read_data(i2c_client *client, unsigned int *send_buf, unsigned int *recv_buf){struct i2c_msg msgs[2];msgs[0].addr = client->addr;     //获取客户端(从机地址)msgs[0].len = sizeof(send_buf);msgs[0].buf = &send_buf;msgs[0].flags = 0;               //传输为发送数据msgs[1].addr = client->addr;     //获取客户端(从机地址)msgs[1].len = 10;msgs[1].buf = recv_buf;msgs[1].flags = I2C_M_RD;        //传输为接收数据ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));if (ret < 0)return ret;else if (ret != ARRAY_SIZE(msgs))return -EIO;return 0;
}

而最后 i2c_transfer()函数会调用i2c_algorithm的master_xfer()函数和functionality()函数真正驱动硬件流程,i2c_transfer函数如下所示:

i2c_transfer()函数——Linux设备驱动开发详解

 其中,functionality()函数很简单,返回algorithm所支持的通信协议,如I2C_FUNC_I2C、I2C_FUNC_10BIT_ADDR等。

master_xfer()函数在I2C适配器上完成传递给它的i2c_msg数组中的每个I2C消息。函数模板如下:

master_xfer()函数——Linux设备驱动开发详解

 master_xfer()函数中的i2c_adapter_xxx_start()、i2c_adapter_xxx_setaddr()、i2c_adapter_xxx_wait_ack()、i2c_adapter_xxx_readbytes()、i2c_adapter_xxx_writebytes()都是和硬件直接相关的,需要工程师根据芯片手册来实现。

i2c_algorithm

其实,上述所说到的functionality、master_xfer都是属于i2c_algorithm结构体中的内容,我们看一下这个结构体都是由什么内容组成的 

struct i2c_algorithm {/* If an adapter algorithm can't do I2C-level access, set master_xferto NULL. If an adapter algorithm can do SMBus access, setsmbus_xfer. If set to NULL, the SMBus protocol is simulatedusing common I2C messages *//* master_xfer should return the number of messages successfullyprocessed, or a negative value on error */int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,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);/* To determine what the adapter supports */u32 (*functionality) (struct i2c_adapter *);
};

 可以看到,master_xfer 函数用于数据传送和读取,functionality 用来获取 IIC 控制器支持情况,(下面会讲到)在调用i2c_adapter的probe 的时候,已经将其操作集 i2c_algorithm 挂接到了 i2c_adapter 结构,一并注册到了 i2c 核心。那么我们就来看一下i2c_adapter的内容吧。

i2c_adapter

I2C适配器驱动其实就是I2C控制器驱动,一般都是由SOC厂商去编写。比如NXP就编写好了I.MX6U的I2C适配器驱动

Linux总线、设备、驱动模型实际上是一个树形结构,每个节点虽然可能成为别人的总控制器,都是自己也被认为是从上一级总线枚举出来的,所以I2C总控制器通常是在内存上,尽管它给别人提供了总线,都是它也被认为是接在platform总线上的一个客户,需要通过platform_driver和platform_device的匹配来执行。

/* I2C1 控制器节点 */
i2c1: i2c@021a0000 {#address-cells = <1>;#size-cells = <0>;compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c";reg = <0x021a0000 0x4000>;interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;clocks = <&clks IMX6UL_CLK_I2C1>;status = "disabled";
}

根据compatible值,我们在Linux源码里面可以找到对应的驱动文件:drivers/i2c/busses/i2c-imx.c

static struct platform_device_id imx_i2c_devtype[] = {
{.name = "imx1-i2c",.driver_data = (kernel_ulong_t)&imx1_i2c_hwdata,}, {.name = "imx21-i2c",.driver_data = (kernel_ulong_t)&imx21_i2c_hwdata,}, {/* sentinel */}
};
MODULE_DEVICE_TABLE(platform, imx_i2c_devtype);static const struct of_device_id i2c_imx_dt_ids[] = {{ .compatible = "fsl,imx1-i2c", .data = &imx1_i2c_hwdata, },{ .compatible = "fsl,imx21-i2c", .data = &imx21_i2c_hwdata, },{ .compatible = "fsl,vf610-i2c", .data = &vf610_i2c_hwdata, },{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, i2c_imx_dt_ids);......static struct platform_driver i2c_imx_driver = {.probe = i2c_imx_probe,.remove = i2c_imx_remove,.driver = {.name = DRIVER_NAME,.owner = THIS_MODULE,.of_match_table = i2c_imx_dt_ids,.pm = IMX_I2C_PM,},.id_table = imx_i2c_devtype,
};static int __init i2c_adap_imx_init(void){return platform_driver_register(&i2c_imx_driver);
}subsys_initcall(i2c_adap_imx_init);static void __exit i2c_adap_imx_exit(void){platform_driver_unregister(&i2c_imx_driver);
}
module_exit(i2c_adap_imx_exit);

可以看出,I2C适配器就是一个标准的platform驱动。因此,当设备和驱动匹配之后,就会执行probe函数,我们可以在该函数中完成I2C适配器的初始化工作。主要完成以下工作:

1、初始化 i2c_adapter ,设置 i2c_algorithm i2c_imx_algo ,最后向 Linux 内核注册
i2c_adapter
2、初始化 I2C1 控制器的相关寄存器。
static int i2c_imx_probe(struct platform_device *pdev){irq = platform_get_irq(pdev, 0);res = platform_get_resource(pdev, IORESOURCE_MEM, 0);base = devm_ioremap_resource(&pdev->dev, res);i2c_imx = devm_kzalloc(&pdev->dev, sizeof(*i2c_imx),GFP_KERNEL);/* Setup i2c_imx driver structure */i2c_imx->adapter.owner = THIS_MODULE;i2c_imx->adapter.algo = &i2c_imx_algo;i2c_imx->adapter.dev.parent = &pdev->dev;i2c_imx->adapter.nr = pdev->id;i2c_imx->adapter.dev.of_node = pdev->dev.of_node;i2c_imx->base = base;/* Request IRQ */ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr,IRQF_NO_SUSPEND, pdev->name, i2c_imx);/* Set up clock divider */i2c_imx->bitrate = IMX_I2C_BIT_RATE;ret = of_property_read_u32(pdev->dev.of_node,"clock-frequency", &i2c_imx->bitrate);/* Set up chip registers to defaults */imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,i2c_imx,IMX_I2C_I2CR);imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx,IMX_I2C_I2SR);/* Add I2C adapter */ret = i2c_add_numbered_adapter(&i2c_imx->adapter);/* Init DMA config if supported */i2c_imx_dma_request(i2c_imx, phy_addr);
}

i2c_dev

我们需要给驱动层留一个访问i2c的接口,所以有了i2c_dev,i2c 核心只是做了一些管理和提供接口的工作,那么具体的支撑用户层进行访问的算是这个 i2c_dev 了。接下来的内容就和字符设备驱动差不多了。

static int __init i2c_dev_init(void)
{int res;res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);/* Bind to already existing adapters right away */i2c_for_each_dev(NULL, i2cdev_attach_adapter);return 0;}

同时提供了fops的用户层的操作集合:

static const struct file_operations i2cdev_fops = {.owner		= THIS_MODULE,.llseek		= no_llseek,.read		= i2cdev_read,.write		= i2cdev_write,.unlocked_ioctl	= i2cdev_ioctl,.open		= i2cdev_open,.release	= i2cdev_release,
};

那么,比如在read函数中,就会使用到i2c_master_recv()函数

static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count, loff_t *offset){......ret = i2c_master_recv(client, tmp, count);if (ret >= 0)ret = copy_to_user(buf, tmp, count) ? -EFAULT : ret;}

I2C结构体框图与调用流程

最后,我们再来看IIC框架的结构图,就会清晰很多了。

IIC大致结构体之间的注册和初始化——知乎

用户层调用流程——知乎

Linux下IIC驱动代码实践

还没整理好,后续有空再补写。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/206543.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【UE5】瞬移+马赛克过渡效果

效果 步骤 1. 新建一个工程&#xff0c;创建一个Basic关卡 2. 添加第三人称游戏资源到内容浏览器 3. 新建一个材质&#xff0c;这里命名为“M_Pixel” 打开“M_Pixel”&#xff0c;设置材质域为“后期处理” 在材质图表中添加如下节点 此时效果如下&#xff0c;已经有马赛克的…

视频处理关键知识

1 引言 视频技术发展到现在已经有100多年的历史&#xff0c;虽然比照相技术历史时间短&#xff0c;但在过去很长一段时间之内都是最重要的媒体。由于互联网在新世纪的崛起&#xff0c;使得传统的媒体技术有了更好的发展平台&#xff0c;应运而生了新的多媒体技术。而多媒体技术…

【教程】逻辑回归怎么做多分类

目录 一、逻辑回归模型介绍 1.1 逻辑回归模型简介 1.2 逻辑回归二分类模型 1.3 逻辑回归多分类模型 二、如何实现逻辑回归二分类 2.1 逻辑回归二分类例子 2.2 逻辑回归二分类实现代码 三、如何实现一个逻辑回归多分类 3.1 逻辑回归多分类问题 3.1 逻辑回归多分类的代…

Leetcode—198.打家劫舍【中等】

2023每日刷题&#xff08;五十二&#xff09; Leetcode—198.打家劫舍 算法思想 具体思路 首先&#xff0c;我们从上面的题目描述中抽象出题意。 ● 从一个非负整数数组中找到一个子序列&#xff0c;并且该子序列的和最大 ● 子序列中每个数的位置不能够相邻。举例来讲&…

Leetcode—1466.重新规划路线【中等】

2023每日刷题&#xff08;五十二&#xff09; Leetcode—1466.重新规划路线 算法思想 实现代码 class Solution { public:int minReorder(int n, vector<vector<int>>& connections) {vector<pair<int, int>> g[n];for(auto e: connections) {in…

JS的变量提升ES6基础

JS的变量提升&ES6基础 变量var关键字var声明作用域实例一实例二多个变量 变量提升 let关键字暂时性死区全局声明for循环中使用let const关键字 变量 ECMAScript变量时松散类型的&#xff0c;意思是变量可以用于保存任何类型的数据。 声明变量&#xff1a;var 、const、let …

阶梯电价1_分支结构 C语言xdoj27

题目&#xff1a;阶梯电价计费 类别&#xff1a;流程控制 时间限制&#xff1a;2S 内存限制&#xff1a;10000Kb 问题描述&#xff1a; 电价分三个档次&#xff0c;[0,110]度电&#xff0c;每度电0.5元&#xff1b;(110,210]度电&#xff0c;超出110部分每度电0.55元&…

git-vscode

git-vscode ctrlshiftp 创建分支 create branch 直接切到新的分支了 切换分支 直接点左下角自己选择 vscode中配置仓库 https://blog.csdn.net/zora_55/article/details/129709251 推送tag tag作用就是在 Git 中&#xff0c;标记存储库历史记录中特定提交的一种方式。t…

【Linux】无法使用 screenfetch 查看系统信息,报错 command not found: screenfetch

问题描述 screenfetch是一个命令行工具&#xff0c;用于在终端显示系统的硬件和软件信息。它会收集各种系统和环境的信息&#xff0c;并以彩色 ASCII 艺术的形式在终端中展示出来。 当你在终端中运行screenfetch命令时&#xff0c;它会检测你的操作系统、主机名、内核版本、C…

IntelliJ IDEA 2023.3发布,更新AI助手,运行相当流畅,再也不卡了

这两天Jetbrains来了一波大的更新&#xff0c;推出了2023.3正式版&#xff0c;均做了不少优化&#xff0c;最重要的是大家期待已久的Ai Assistant插件本次更新也正式推出&#xff0c;助力大家提高Coding效率。但是很遗憾&#xff0c;目前我们无法使用&#xff0c;因为该插件底层…

[架构之路-256]:目标系统 - 设计方法 - 软件工程 - 软件设计 - 架构设计 - 软件系统不同层次的复用与软件系统向越来越复杂的方向聚合

目录 前言&#xff1a; 一、CPU寄存器级的复用&#xff1a;CPU寄存器 二、指令级复用&#xff1a;二进制指令 三、过程级复用&#xff1a;汇编语言 四、函数级复用&#xff1a;C语言 五、对象级复用&#xff1a;C, Java, Python 六、组件级复用 七、服务级复用 八、微…

计算机视觉-03-使用U-Net实现肾脏CT分割(包含数据和代码)

文章目录 0. 数据获取1. 介绍1.1 简介1.2 任务介绍1.3 数据集介绍1.3.1 介绍1.3.2 数据预处理建议 1.4 代码实现参考1.5 训练过程1.5.1 参数设置1.5.2 可视化1.5.3 结果分析 0. 数据获取 关注公众号&#xff1a;『AI学习星球』 回复&#xff1a;肾脏CT分割 即可获取数据下载。…

高精度时钟芯片SD2405

概要 SD2405是一款非常优秀的RTC解决方案&#xff0c;为了能让用户在Arduino上有一款方便易用的时钟模块。该模块是一款内置晶振&#xff0c;支持IIC串行接口的高精度时钟模块&#xff1b;内置一次性工业级电池&#xff0c;可保证外部掉电的情况下&#xff0c;可以继续工作5~8…

实例分割 Mask-RCNN

参考文章 使用LabelMe标注目标检测数据集并转换为COCO2017格式_labelme转coco-CSDN博客 数据集选择 voc 这次不选择voc&#xff0c;因为文件组织太难了 voc2012文件夹组织 COCO COCO介绍 MC COCO2017年主要包含以下四个任务&#xff1a;目标检测与分割、图像描述、人体关…

KP 2sv Authenticator一款免费处理亚马逊两步验证码的软件

KP 2sv Authenticator 被誉为一款免费而强大的亚马逊两步验证软件&#xff0c;操作简便轻松。 软件使用方法极为简单&#xff0c;用户只需直接输入身份验证应用程序生成的代码&#xff0c;即可迅速生成随机验证码&#xff0c;帮助用户顺利完成亚马逊的两步验证流程。这款小软件…

有了安卓模拟器,就能在Windows 10或11上像使用安卓操作系统一样使用安卓

你可以使用Android模拟器在Windows 11或Windows 10中运行Android应用程序。如果你喜欢的应用程序只在手机上运行,但你想在电脑上使用,这些模拟器会很有用。 BlueStacks 与整个操作系统模拟器不同,BlueStacks只在Windows上模拟Android应用程序。它真的很容易使用,所以你不需…

香港云服务器:全面介绍与使用场景分析

这几年基于国内互联网技术的发展&#xff0c;各类海外贸易的兴起&#xff0c;很多网站都启用了海外云服务。这其中&#xff0c;香港的 IDC 市场异常火爆。也不奇怪&#xff0c;就目前来看&#xff0c;国内大多数网站的访问用户在国内外均有涉及&#xff0c;而香港云服务器恰好满…

Java第二十一章总结

网络编程三要素 ip地址&#xff1a;计算机在网络中的唯一标识 端口&#xff1a;应用程序在计算机中唯一标识 协议&#xff1a;通信协议&#xff0c;常见有UDP和TCP协议 InetAddress类 表示Internet协议地址 //返回InetAddress对象 InetAddress byName InetAddress.…

全国公共汽车、出租车拥有情况及客运量、货运量数据,shp、excel数据均有,多指标可查询

基本信息. 数据名称: 全国公共汽车、出租车拥有情况及客运量、货运量数据 数据格式: Shp、Excel 数据时间: 2020-2022年 数据几何类型: 面 数据坐标系: WGS84 数据来源&#xff1a;中国城市统计年鉴 数据字段&#xff1a; 序号字段名称字段说明1xzqhdm行政区划代码…

机器学习基础知识分享:深度学习

深度学习&#xff08;Deep Learning&#xff09;是近年来发展十分迅速的研究领域&#xff0c;并且在人工智能的很多子领域都取得了巨大的成功&#xff0e;从根源来讲&#xff0c;深度学习是机器学习的一个分支&#xff0c;是指一类问题以及解决这类问题的方法。 深度学习 为了…