当前位置: 首页 > news >正文

Linux I2C 驱动实验

目录

一、Linux I2C 驱动简介

1、I2C 总线驱动

2、I2C 设备驱动

1、 i2c_client 结构体

2、 i2c_driver 结构体

二、硬件分析

三、设备树编写

1、pinctrl_i2c1

2、在 i2c1 节点追加 ap3216c 子节点

3、验证

四、 代码编写

1、makefile

2、ap3216c.h

 3、ap3216c.c

①、头文件

②、驱动出入口 

③、 i2c驱动结构体 

④、匹配函数

⑤、probe 函数

⑥、remove 函数

 ⑦、函数入口出口添加注册i2c_drive

⑧、读取AP3216C的N个寄存器

⑨、向AP3216C的N个寄存器写数据

⑩、读、写AP3216C一个寄存器

⑩①、读取AP3216C的数据

⑩②完善ap3216c_open、read函数

 代码如下

 

4、ap3216c.c


一、Linux I2C 驱动简介

        在IMX6ULL开发篇中"IIC实验"写了四个文件: bsp_i2c.c、bsp_i2c.h、 bsp_ap3216c.c 和 bsp_ap3216c.h。其中前两个是 I.MX6U 的 IIC 接口驱动,后两个文件是 AP3216C 这个 I2C 设备驱动文件。相当于有两部分驱动:
①、 I2C 主机驱动。
②、 I2C 设备驱动。
        对于 I2C 主机驱动,一旦编写完成就不需要再做修改,其他的 I2C 设备直接调用主机驱动提供的 API 函数完成读写操作即可。这个正好符合 Linux 的驱动分离与分层的思想,因此 Linux内核也将 I2C 驱动分为两部分:
①、 I2C 总线驱动, I2C 总线驱动就是 SOC 的 I2C 控制器驱动,也叫做 I2C 适配器驱动。
②、 I2C 设备驱动, I2C 设备驱动就是针对具体的 I2C 设备而编写的驱动

1、I2C 总线驱动

        在”platform驱动实验"的时候就说过, platform 是虚拟出来的一条总线,目的是为了实现总线、设备、驱动框架。对于 I2C 而言,不需要虚拟出一条总线,直接使用 I2C总线即可。 I2C 总线驱动重点是 I2C 适配器(也就是 SOC 的 I2C 接口控制器)驱动,Linux 内核将 SOC 的 I2C 适配器(控制器)抽象成 i2c_adapter, i2c_adapter 结构体定义在 include/linux/i2c.h 文件中,i2c_algorithm 类型的指针变量 algo,对于一个 I2C 适配器,肯定要对外提供读写 API 函数,设备驱动程序可以使用这些 API 函数来完成读写操作。 i2c_algorithm 就是 I2C 适配器与 IIC 设备进行通信的方法;i2c_algorithm 结构体定义在 include/linux/i2c.h 文件中,其中的master_xfer 就是 I2C 适配器的传输函数,可以通过此函数来完成与 IIC 设备之间的通信,smbus_xfer 是 SMBUS 总线的传输函数

        一般 SOC 的 I2C 总线驱动都是由半导体厂商编写的,比如 I.MX6U 的 I2C 适配器驱动 NXP 已经编写好了,这个不需要用户去编写。因此 I2C 总线驱动对我们这些 SOC 使用者来说是被屏蔽掉的,我们只要专注于 I2C 设备驱动即可。

2、I2C 设备驱动

        I2C 设备驱动重点关注两个数据结构: i2c_client 和 i2c_driver,i2c_client 就是描述设备信息的, i2c_driver 描述驱动内容,类似于 platform_driver

1、 i2c_client 结构体

i2c_client 结构体定义在 include/linux/i2c.h 文件中,内容如下:

struct i2c_client {unsigned short flags;     /* 标志 */unsigned short addr;     /* 芯片地址, 7 位,存在低 7 位*/
......char name[I2C_NAME_SIZE]; /* 名字 */struct i2c_adapter *adapter; /* 对应的 I2C 适配器 */struct device dev;     /* 设备结构体 */int irq;             /* 中断 */struct list_head detected;
......};

一个设备对应一个 i2c_client,每检测到一个 I2C 设备就会给这个 I2C 设备分配一个i2c_client

2、 i2c_driver 结构体

i2c_driver 类似 platform_driver,i2c_driver 结构体定义在 include/linux/i2c.h 文件中,部分如下

 struct i2c_driver {........./* Standard driver model interfaces */
1    int (*probe)(struct i2c_client *, const struct i2c_device_id *);......
2    struct device_driver driver;
3    const struct i2c_device_id *id_table;..........}

第 1 行,当 I2C 设备和驱动匹配成功以后 probe 函数就会执行,和 platform 驱动一样。
第 2行, device_driver 驱动结构体,如果使用设备树的话,需要设置 device_driver

                of_match_table 成员变量,也就是驱动的兼容(compatible)属性。
第3行, id_table 是传统的、未使用设备树的设备匹配 ID 表

重点工作就是构建 i2c_driver,构建完成以后需要向Linux 内核注册这个 i2c_driver

二、硬件分析

这部分在在IMX6ULL开发篇中"IIC实验"有详细介绍,下面就简单介绍

打开AP3216C原理图,这是一个IIC接口的器件,是一个环境光传感器

 I2C1_SCL: 使用的是UART4_TXD这个IO;I2C1_SDA: 使用的是UART4_RXD这个IO

打开ap3216数据手册

地址为0x1e

三、设备树编写

1、pinctrl_i2c1

打开设备树,找到i2c1,pinctrl_i2c1 就是 I2C1 的 IO 节点

        这里将 UART4_TXD 和 UART4_RXD 这两个 IO 分别复用为 I2C1_SCL 和 I2C1_SDA,电气属性都设置为 0x4001b8b0

2、在 i2c1 节点追加 ap3216c 子节点

        AP3216C 是连接到 I2C1 上的,因此需要在 i2c1 节点下添加 ap3216c 的设备子节点,默认内容如下

305-316行,NXP 官方的 EVK 开发板上接了 mag3110和fxls8471,这里使用的开发板并没有这这两个器件,所以删掉,并添加上我们使用的

300行,clock-frequency 属性为 I2C 频率,这里设置为 100KHz

302行,inctrl-0 属性指定 I2C 所使用的 IO 为上面的pinctrl_i2c1

305行,ap3216c 子节点, @后面的“1e”是 ap3216c 的器件地址

306行,设置 compatible 值为“my,ap3216c”

307行,reg 属性也是设置 ap3216c 器件地址的,因此 reg 设置为 0x1e

3、验证

改完成以后使用“make dtbs”重新编译一下,然后使用新的设备树启动 Linux 内核

/sys/bus/i2c/devices 目录下存放着所有 I2C 设备,如果设备树修改正确的话,会在/sys/bus/i2c/devices 目录下看到一个名为“0-001e”的子目录,如下

“0-001e”就是 ap3216c 的设备目录,“1e”就是 ap3216c 器件地址。进入0-001e 目录,可以看到“name”文件, name 问价就保存着此设备名字,在这里就是“ap3216c”,如下

四、 代码编写

1、makefile

需要的文件如下图左,修改makefile

2、ap3216c.h

定义器件及其寄存器地址

 3、ap3216c.c

①、头文件

②、驱动出入口 

 

③、 i2c驱动结构体 

46行,当 I2C 设备和驱动匹配成功以后 probe 函数就会执行,和 platform 驱动一样

47行,当关闭 驱动的时候remove 函数就会执行,和 platform 驱动一样

49行,name是在无设备树时用于和设备进行匹配,也就是上面写的实验,也作为驱动名字;

        使用设备树就设置of_match_table变量,也就是驱动的兼容(compatible)属性
53行,id_table 是传统的、未使用设备树的设备匹配 ID 表

④、匹配函数

i2c驱动结构体 里对应的函数

 236行,无设备树的时候匹配 ID 表
241行,设备树所使用的匹配表

⑤、probe 函数

在这里面实现基本的字符设备

#define AP3216C_CNT 1
#define AP3216C_NAME "ap3216c"struct ap3216c_dev{int devid;int major;int minor;struct cdev cedv;struct class *class;struct device *device;void *private_data;/*私有数据*/unsigned short ir, als, ps;		/* 三个光传感器数据 */ 
};
static struct ap3216c_dev ap3216cdev;
static int ap3216c_open(struct inode *inode, struct file *filp)
{return 0;
}static int ap3216c_release(struct inode *inode, struct file *filp)
{printk("ap3216c_release\r\n");return 0;
}
static ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{return 0;
}static struct file_operations ap3216c_fops  = {.owner  =   THIS_MODULE,.open   =   ap3216c_open,.read   =   ap3216c_read,.release = ap3216c_release,
};static int ap3216c_probe(struct i2c_client *client,const struct i2c_device_id *id)
{int ret =0;/*搭建字符设备驱动框架*//*1、创建字符设备*/ap3216cdev.major=0;if(ap3216cdev.major){ap3216cdev.devid = MKDEV(ap3216cdev.major,0);ret = register_chrdev_region(ap3216cdev.devid,AP3216C_CNT,AP3216C_NAME);}else{ret = alloc_chrdev_region(&ap3216cdev.devid,0,AP3216C_CNT,AP3216C_NAME);ap3216cdev.major = MAJOR(ap3216cdev.devid);ap3216cdev.minor = MINOR(ap3216cdev.devid);}if(ret < 0){printk("ap3216cdev_chrdev_region error!\r\n");goto fail_devid;}printk("ap3216cde major: %d minor: %d\r\n",ap3216cdev.major,ap3216cdev.minor);/*2、注册字符设备*/ap3216cdev.cedv.owner = THIS_MODULE;cdev_init(&ap3216cdev.cedv,&ap3216c_fops);ret = cdev_add(&ap3216cdev.cedv,ap3216cdev.devid,AP3216C_CNT);if(ret < 0){printk("cdev_add error!\r\n");goto fail_cdev;}/*3、自动创建设备节点*/ap3216cdev.class = class_create(THIS_MODULE,AP3216C_NAME);if(IS_ERR(ap3216cdev.class)){ret = PTR_ERR(ap3216cdev.class);goto fail_class;}ap3216cdev.device = device_create(ap3216cdev.class, NULL,ap3216cdev.devid, NULL,AP3216C_NAME);if(IS_ERR(ap3216cdev.device)){ret = PTR_ERR(ap3216cdev.device);goto fail_device;}printk("ap3216c_probe\r\n");ap3216cdev.private_data = client;return 0;
fail_device:class_destroy(ap3216cdev.class);
fail_class:cdev_del(&ap3216cdev.cedv);
fail_cdev:unregister_chrdev_region(ap3216cdev.devid,AP3216C_CNT);
fail_devid:return ret;
}

" return 0 "前一行,将此函数的第一 个参数 client 传递给 ap3216cdev 的 private_data 成员变量

当设备和驱动匹配成功后,probe函数执行,传递的第一个参数就是i2c_client ,i2c_client 每检测到一个 I2C 设备就会给这个 I2C 设备分配一个i2c_client,这里用 ap3216cdev结构体中的private_data 成员接收

⑥、remove 函数

 ⑦、函数入口出口添加注册i2c_drive

 

50行,调用 i2c_add_driver 来向 Linux 内 核注册 i2c_driver,也就是 ap3216c_driver

i2c_add_driver 是一个宏,定义如下:

#define i2c_add_driver(driver) \
         i2c_register_driver(THIS_MODULE, driver)

i2c_add_driver 就是对 i2c_register_driver 做了一个简单的封装,只有一个参数,就是要注册的 i2c_driver。

66行,调用 i2c_del_driver 来注销掉前面 注册的 ap3216c_driver,函数原型如下:

void i2c_del_driver(struct i2c_driver *driver)

driver:要注销的 i2c_driver。返回值: 无。

⑧、读取AP3216C的N个寄存器

 这里先看61行,一般需要在 probe 函数里面初始化 I2C 设备,要初始化 I2C 设备就必须能够对 I2C 设备寄存器进行读写操作,这里就要用到 i2c_transfer 函数了,i2c_transfer 函数最终会调用 I2C 适配器中 i2c_algorithm 里面的 master_xfer 函数,对于 I.MX6U 而言就是i2c_imx_xfer 这个函数。 i2c_transfer 函数原型如下:

int i2c_transfer(struct i2c_adapter *adap,

                                struct i2c_msg *msgs,

                                int num)

adap: 所使用的 I2C 适配器, i2c_client 会保存其对应的 i2c_adapter。
msgs: I2C 要发送的一个或多个消息。
num: 消息数量,也就是 msgs 的数量。
返回值: 负值,失败,其他非负值,发送的 msgs 数量

 从上面介绍i2c_client结构体就能看到有adapter成员,在probe函数里面已经获取到ap3216cdev设备的private_data成员中

48行,就是把private_data成员中把私有数据提出来,就在61行第一个参数中使用i2c_client中的adapter成员

继续看 i2c_transfer 函数msgs 这个参数,这是一个 i2c_msg 类型的指针参数, I2C 进行数据收发
说白了就是消息的传递, Linux 内核使用 i2c_msg 结构体来描述一个消息。 i2c_msg 结构体定义
在 include/uapi/linux/i2c.h 文件中

 69行,从机地址

 70行,标志

72行,read data,读数据

79行,要发送消息(本 msg)长度

80行,发送消息的数据

47行,定义结构体数组,按照I2C读时序把操作分为两部分

前面两段作为一部分,就是50-54行;后面两段作另一部分,就是56-59行

50-54行,发送器件寄存器地址。从i2c_client中获取从机地址,标志为0,表示发送数据,要发送的数据,也就是器件寄存器地址,寄存器地址长度为1个字节

56-59行,读取数据。从i2c_client中获取从机地址,标志表示读数据,读出来的数据保存在val,最后为读取的长度

再回到61行,第二个参数就是结构体数组msg,第三个参数就是2,结构体数组msg的长度

⑨、向AP3216C的N个寄存器写数据

 76行,这里根据I2C写时序,寄存器地址和数据一次可以发完,所以只需要一个msg即可

79行,寄存器地址保存在b[0],先发寄存器地址

80行,把要发送的数据拷贝到b数组里面

⑩、读、写AP3216C一个寄存器

 就是调用函数

⑩①、读取AP3216C的数据

⑩②完善ap3216c_open、read函数

 

 代码如下

 

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/atomic.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/string.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include "ap3216c.h"#define AP3216C_CNT 1
#define AP3216C_NAME "ap3216c"struct ap3216c_dev{int devid;/* 设备号 	 */int major;int minor;struct cdev cedv;/* cdev 	*/struct class *class;/* 类 		*/struct device *device;/* 设备 	 */void *private_data;/*私有数据*/unsigned short ir, als, ps;		/* 三个光传感器数据 */ 
};static struct ap3216c_dev ap3216cdev;/*读取AP3216C的N个寄存器*/
static int ap3216c_read_regs(struct ap3216c_dev *dev,u8 reg,void *val, int len)
{int ret;struct i2c_msg msg[2];struct i2c_client *client = (struct i2c_client *)dev->private_data;/*msg[0]发送要读取的寄存器首地址*/msg[0].addr = client->addr;/*从机地址,也就是ap3216c*/msg[0].flags = 0;/* 标记为发送数据 */msg[0].buf = &reg;/*要发送的数据,也就是寄存器地址*/msg[0].len = 1;/*要发送的寄存器地址长度为1*//*msg[1]读取数据*/msg[1].addr = client->addr;/*从机地址,也就是ap3216c*/msg[1].flags = I2C_M_RD;/*表示读数据*/msg[1].buf = val;/*接收到的从机地址*/msg[1].len = len;/*要读取的寄存器数据长度*/ret = i2c_transfer(client->adapter,msg,2);if(ret == 2){ret = 0;}else{printk("i2c rd failed=%d reg=%06x len=%d\n",ret, reg, len);ret = -EREMOTEIO;}return ret;
}/*向AP3216C的N个寄存器写数据*/
static int ap3216c_write_regs(struct ap3216c_dev *dev,u8 reg,u8 *buf , u8 len)
{u8 b[256];struct i2c_msg msg;struct i2c_client *client = (struct i2c_client *)dev->private_data;/*构建要发送的数据,也就是寄存器首地址+实际的数据*/b[0] = reg;/*msg[0]发送要读取的寄存器首地址*/memcpy(&b[1], buf, len);msg.addr = client->addr;/*从机地址,也就是ap3216c*/msg.flags = 0;/*表示为要发送的数据*/msg.buf = b;/*要发送的数据,寄存器首地址+实际的数据*/msg.len = len +1;/*要发送的数据长度:寄存器首地址+实际的数据*/return i2c_transfer(client->adapter,&msg,1);
}
/*读取AP3216C一个寄存器*/
static unsigned char ap3216c_read_reg(struct ap3216c_dev *dev,u8 reg)
{u8 data=0;ap3216c_read_regs(dev,reg,&data,1);return data;
}
/*向AP3216C一个寄存器写数据*/
static void ap3216c_write_reg(struct ap3216c_dev *dev, u8 reg , u8 data)
{u8 buf = data;ap3216c_write_regs(dev,reg,&buf,1);
}void ap3216c_readdata(struct ap3216c_dev *dev)
{unsigned char i =0;unsigned char buf[6];/* 循环读取所有传感器数据 */for(i = 0; i < 6; i++)	{buf[i] = ap3216c_read_reg(dev, AP3216C_IRDATALOW + i);	}if(buf[0] & 0X80) 	/* IR_OF位为1,则数据无效 */dev->ir = 0;					else 				/* 读取IR传感器的数据   		*/dev->ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0X03); 			dev->als = ((unsigned short)buf[3] << 8) | buf[2];	/* 读取ALS传感器的数据 			 */  if(buf[4] & 0x40)	/* IR_OF位为1,则数据无效 			*/dev->ps = 0;    													else 				/* 读取PS传感器的数据    */dev->ps = ((unsigned short)(buf[5] & 0X3F) << 4) | (buf[4] & 0X0F); 
}static int ap3216c_open(struct inode *inode, struct file *filp)
{unsigned char value = 0;filp->private_data = &ap3216cdev;printk("ap3216c_open\r\n");/*初始化ap3216c*/ap3216c_write_reg(&ap3216cdev,AP3216C_SYSTEMCONG,0x4);/*复位*/mdelay(50);/*50ms*/ap3216c_write_reg(&ap3216cdev,AP3216C_SYSTEMCONG,0x3);/*复位*/value = ap3216c_read_reg(&ap3216cdev,AP3216C_SYSTEMCONG);printk("AP3216C_SYSTEMCONG = %#x\r\n",value);return 0;
}static int ap3216c_release(struct inode *inode, struct file *filp)
{printk("ap3216c_release\r\n");return 0;
}
static ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{long err=0;short data[3];struct ap3216c_dev *dev = (struct ap3216c_dev *)filp->private_data;/*向应用返回AP3216C的原始数据*/ap3216c_readdata(dev);data[0] =dev->ir;data[1] =dev->als;data[2] =dev->ps;err = copy_to_user(buf, data,sizeof(data));return 0;
}static struct file_operations ap3216c_fops  = {.owner  =   THIS_MODULE,.open   =   ap3216c_open,.read   =   ap3216c_read,.release = ap3216c_release,
};static int ap3216c_probe(struct i2c_client *client,const struct i2c_device_id *id)
{int ret =0;/*搭建字符设备驱动框架*//*1、创建字符设备*/ap3216cdev.major=0;if(ap3216cdev.major){ap3216cdev.devid = MKDEV(ap3216cdev.major,0);ret = register_chrdev_region(ap3216cdev.devid,AP3216C_CNT,AP3216C_NAME);}else{ret = alloc_chrdev_region(&ap3216cdev.devid,0,AP3216C_CNT,AP3216C_NAME);ap3216cdev.major = MAJOR(ap3216cdev.devid);ap3216cdev.minor = MINOR(ap3216cdev.devid);}if(ret < 0){printk("ap3216cdev_chrdev_region error!\r\n");goto fail_devid;}printk("ap3216cde major: %d minor: %d\r\n",ap3216cdev.major,ap3216cdev.minor);/*2、注册字符设备*/ap3216cdev.cedv.owner = THIS_MODULE;cdev_init(&ap3216cdev.cedv,&ap3216c_fops);ret = cdev_add(&ap3216cdev.cedv,ap3216cdev.devid,AP3216C_CNT);if(ret < 0){printk("cdev_add error!\r\n");goto fail_cdev;}/*3、自动创建设备节点*/ap3216cdev.class = class_create(THIS_MODULE,AP3216C_NAME);if(IS_ERR(ap3216cdev.class)){ret = PTR_ERR(ap3216cdev.class);goto fail_class;}ap3216cdev.device = device_create(ap3216cdev.class, NULL,ap3216cdev.devid, NULL,AP3216C_NAME);if(IS_ERR(ap3216cdev.device)){ret = PTR_ERR(ap3216cdev.device);goto fail_device;}printk("ap3216c_probe\r\n");ap3216cdev.private_data = client;return 0;
fail_device:class_destroy(ap3216cdev.class);
fail_class:cdev_del(&ap3216cdev.cedv);
fail_cdev:unregister_chrdev_region(ap3216cdev.devid,AP3216C_CNT);
fail_devid:return ret;
}
static int ap3216c_remove(struct i2c_client *client)
{cdev_del(&ap3216cdev.cedv);unregister_chrdev_region(ap3216cdev.devid,AP3216C_CNT);device_destroy(ap3216cdev.class, ap3216cdev.devid);class_destroy(ap3216cdev.class);printk("ap3216c_remove\r\n");return 0;
}
/*传统匹配表*/
static struct i2c_device_id ap3216c_id[] = {{"my,ap3216c",0},{}
};
/*设备树匹配*/
static struct of_device_id ap3216c_of_match[] = {{.compatible = "my,ap3216c"},{}
};
/*i2c_driver*/
static struct i2c_driver ap3216c_driver= {.probe = ap3216c_probe,.remove = ap3216c_remove,.driver = {.name = "ap3216c",.owner = THIS_MODULE,.of_match_table = of_match_ptr(ap3216c_of_match),},.id_table = ap3216c_id,
};
/*驱动入口*/
static int __init ap3216c_init(void)
{int ret = 0;/*添加*/ret = i2c_add_driver(&ap3216c_driver);return ret;
}
/*驱动出口*/
static void __exit ap3216c_exit(void)
{i2c_del_driver(&ap3216c_driver);
}module_init(ap3216c_init);
module_exit(ap3216c_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ba che kai qi lai");

4、ap3216c.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <linux/input.h>/*argc:应用程序参数个数(argv数组元素个数)argv:具体参数,也可以写作char **argv./ap3216cAPP <filename>    ./ap3216cAPP  /dev/ap3216c
*/int main(int argc, char *argv[])
{int fd,err;char *filename;unsigned short ir, als, ps, data[3];/*判断命令行输入参数是否正确*/if(argc != 2){printf("error usage!\r\n");return -1;}/*用指针指向文件*/filename = argv[1];/*打开文件*/fd = open(filename , O_RDWR);if(fd < 0){printf("file open failed\r\n",filename);return -1;}while(1){err =read(fd,&data,sizeof(data));if(err == 0){ir=data[0];als=data[1];ps=data[2] ;printf("ap3216c ir = %d,als = %d,ps = %d\r\n",ir,als,ps);}usleep(200000);/*200ms*/}/*关闭文件*/close(fd);return 0;
}

 用APP测试驱动

 用手接近和用灯照ap3216c传感器,数值都会发生变化

相关文章:

Linux I2C 驱动实验

目录 一、Linux I2C 驱动简介 1、I2C 总线驱动 2、I2C 设备驱动 1、 i2c_client 结构体 2、 i2c_driver 结构体 二、硬件分析 三、设备树编写 1、pinctrl_i2c1 2、在 i2c1 节点追加 ap3216c 子节点 3、验证 四、 代码编写 1、makefile 2、ap3216c.h 3、ap3216c.c …...

DC-DC模块电源隔离直流升压高压稳压输出5v12v24v转60v100v110v150v220v250v300v400v500v

特点效率高达80%以上1*1英寸标准封装单电压输出稳压输出工作温度: -40℃~85℃阻燃封装&#xff0c;满足UL94-V0 要求温度特性好可直接焊在PCB 上应用HRB 0.2~10W 系列模块电源是一种DC-DC升压变换器。该模块电源的输入电压分为&#xff1a;4.5~9V、9~18V、及18~36VDC标准&#…...

EF有几种模式,EF的三种模式分别是什么?

EF有几种模式&#xff0c;EF的三种模式分别是什么&#xff1f; 第一种&#xff1a;DataBase First DataBase First传统的表驱动方式创建EDM&#xff0c;然后通过EDM生成模型和数据层代码。除生成实体模型和自跟踪实现模型&#xff0c;还支持生成轻型DbContext。 解释&#xf…...

数据可视化展示:打工人常见职业病,颈腰椎病占比最高达66.51%

身体健康才是一切的根本。只有身体健健康康才能更好的去享受世间的美好&#xff0c;无论是谁都应当注重身体健康&#xff0c;而不是无度的挥霍它&#xff01; 良好的身体&#xff0c;释放给工作&#xff0c;健壮的体魄&#xff0c;享受美好生活&#xff0c;良好的心态&#xff…...

【食品图像识别】Large Scale Visual Food Recognition

1 引言 视觉智能部与中科院计算所于2020-2021年度展开了《细粒度菜品图像识别和检索》科研课题合作&#xff0c;本文系双方联合在IEEE T-PAMI2023发布论文《Large Scale Visual Food Recognition》 (Weiqing Min, Zhiling Wang, Yuxin Liu, Mengjiang Luo, Liping Kang, Xiaom…...

RAN-in-the-Cloud:为 5G RAN 提供云经济性

RAN-in-the-Cloud&#xff1a;为 5G RAN 提供云经济性 5G 部署在全球范围内一直在加速。 许多电信运营商已经推出了5G服务并正在快速扩张。 除了电信运营商之外&#xff0c;企业也对使用 5G 建立私有网络产生了浓厚的兴趣&#xff0c;这些私有网络利用了更高的带宽、更低的延迟…...

vector、list、queue

引用&#xff1a;windows程序员面试指南 vector vector 类似于C语言中的数组 vector 支持随机访问&#xff0c;访问某个元素的时间复杂度 O(1) vector 插入和删除元素效率较低&#xff0c;时间复杂度O(n) vector 是连续存储&#xff0c;没有内存碎片&#xff0c;空间利用率高…...

操作系统面经

进程与线程区别 1.进程是资源分配的最小单位&#xff0c;线程是程序执行的最小单位&#xff08;资源调度的最小单位&#xff09; 2.进程有自己的独立地址空间&#xff0c;每启动一个进程&#xff0c;系统就会为它分配地址空间&#xff0c;建立数据表来维护代码段、堆栈段和数…...

一天约了4个面试,复盘一下面试题和薪资福利

除了最新的面经分享&#xff0c;还有字节大佬的求职面试答疑&#xff0c;告诉你关键问题是什么&#xff1f;少走弯路。**另外本文也汇总了6份大厂面试题&#xff1a;字节、腾讯、小米、腾讯云、滴滴、小米游戏。**希望对大家有帮助。 前言 昨天我的交流群里&#xff0c;有位宝…...

详解单链表(内有精美图示哦)

全文目录引言链表链表的定义与结构链表的分类单链表的实现及对数据的操作单链表的创建与销毁创建销毁单链表的打印单链表的头插与头删头插头删单链表的尾插与尾删尾插尾删单链表的查找单链表在pos位置后插入/删除插入删除单链表在pos位置插入/删除插入删除总结引言 在上一篇文…...

csdn文章导航

这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注…...

【Spring】掌握 Spring Validation 数据校验

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ Spring Validation 数据校验一、什么是 Spring…...

定语 从句

回顾能作定语的成分 形容词&#xff1a;She is a responsible girl&#xff0e;她是一个负责任的姑娘。&#xff08;前置定语&#xff09; The girl responsible was expelled&#xff0e;对此负责的姑娘被开除了。&#xff08;后置定语&#xff09; 代词&#xff1a;Whose f…...

【数据可视化工具】浅谈 DataEase 和 FineBI 支持的数据源

前言最近对市面上比较火热的数据可视化工具 DataEase 和 FineBI 进行了调研&#xff0c;在支持的数据源方面感觉不太一样&#xff0c;所以就有了这篇文章&#xff0c;话不多说&#xff0c;我们一起来看一下吧&#xff01;以下的内容&#xff0c;大多来自两个工具的官方文档&…...

100种思维模型之上帝视角思维模型-025

惊奇、愤怒、郁闷&#xff0c;我们觉得生活不精彩&#xff0c;事情乱作一团&#xff0c;但这仅仅是视角问题而已。 换个视角&#xff0c;可以看到不同的世界。 “上帝视角思维模型”&#xff0c;即以一个更高、更客观、更理性的角度来看问题&#xff0c;从而做出理性的决策。 …...

从这5个方面,总结我当PM的第一年

以下5个方面&#xff08;学习、思考、沟通、执行、产品&#xff09;的分享&#xff0c;都是我站在巨人的肩膀上&#xff0c;结合自己所学所做总结而来&#xff1b;同时&#xff0c;我也继续学习&#xff0c;不断完善这些知识。如有不当&#xff0c;欢迎大家指正~一、学习&#…...

ChatGPT可以作为一个翻译器吗?

论文地址&#xff1a;https://arxiv.org/abs/2301.08745.pdf 背景 自从OpenAI2022年11月30日发布ChatGPT以来&#xff0c;基本上把NLP所有任务大统一了&#xff0c;那么在机器翻译的表现到底如何呢&#xff1f;腾讯AI Lab在翻译Prompt、多语言翻译以及翻译鲁棒性三方面做了一…...

详述java的设计模式(三)

1.装饰者模式 装饰者模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其结构。这种类型的设计模式属于结构型模式&#xff0c;它是作为现有的类的一个包装。 使用场景&#xff1a; 在不影响其他对象的情况下&#xff…...

Linux命令·pwd

Linux中用 pwd 命令来查看”当前工作目录“的完整路径。 简单得说&#xff0c;每当你在终端进行操作时&#xff0c;你都会有一个当前工作目录。 在不太确定当前位置时&#xff0c;就会使用pwd来判定当前目录在文件系统内的确切位置。1&#xff0e;命令格式&#xff1a;pwd [选项…...

以图搜图服务快速搭建

以图搜图服务快速搭建 电商公司&#xff0c;管理的商品少则几千&#xff0c;多则上百万。如何帮助用户从多如牛毛的商品中找到类似的商品就成了问题。 以图搜图就可以很好的帮助解决这个问题&#xff0c;通过 Towhee&#xff08;resnet50 模型&#xff09; Milvus 如何实现本…...

【TensorFlow安装踩坑记录】

TensorFlow安装踩坑记录第一步&#xff0c;切换服务器cuda版本第二步&#xff0c;conda安装tensorflow记录一下最近安装Tensorflow v1时遇到的问题和解决办法第一步&#xff0c;切换服务器cuda版本 首先我想安装tensorflow 1.13.1&#xff0c;兼容的cuda版本是10.0&#xff0c…...

03.03回溯法

class Solution { public:vector<int> temp;vector<vector<int>> ans;void dfs(int cur,int n,int k){//剪枝 temp 长度加上区间 [cur, n] 的长度小于 k&#xff0c;不可能构造出长度为 k 的 tempif(temp.size()(n-cur1)<k){return;}if(temp.size()k){ans…...

I.MX6ULL内核开发0:linux内核模块

目录 简要 一、内核模块的概念 二、内核模块加载、卸载过程 简要 1、内核模块的概念 2、内核模块的原理&#xff1a;内核模块在内核的加载、卸载过程。 一、内核模块的概念 内核&#xff0c;是一个操作系统的核心。是基于硬件的第一层软件扩充&#xff0c;提供操作系统的最…...

qsort快速排序的实现以及模拟实现qsort的功能(狠狠的拿捏)

当你为错过太阳而哭泣的时候&#xff0c;你也要再错过群星了。 --泰戈尔 目录 一.qsort快速排序的实现 二.模拟实现一个qsort功能的函数 一.qsort快速排序的实现 下面是 qsort() 函数的声明&#xff1a; void qsort(void *base, size_t nitems, size_t size, int (…...

[Java·算法·中等]LeetCode215. 数组中的第K个最大元素

每天一题&#xff0c;防止痴呆题目示例分析思路1题解1分析思路2题解2分析思路3题解3&#x1f449;️ 力扣原文 题目 给定整数数组 nums 和整数 k&#xff0c;请返回数组中第 k 个最大的元素。 请注意&#xff0c;你需要找的是数组排序后的第 k 个最大的元素&#xff0c;而不…...

xgboost:算法数学原理

xgboost算法数学原理 1、求预测值 y^iϕ(xi)∑k1Kfk(xi),fk∈F,(1)\hat{y}_i\phi\left(\mathbf{x}_i\right)\sum_{k1}^K f_k\left(\mathbf{x}_i\right), \quad f_k \in \mathcal{F},\tag{1} y^​i​ϕ(xi​)k1∑K​fk​(xi​),fk​∈F,(1) F{f(x)wq(x)}(q:Rm→T,w∈RT)\mathca…...

map、multimap、unordered_map

引用&#xff1a;windows程序员面试指南 map map 红黑树 map 对value值无要求 map 有序&#xff0c;按照key值自动排序 map key值唯一 map 头文件&#xff1a;#include map 支持重载[]的运算符 map 为保持有序性&#xff0c;erase()开销大 multimap multimap 红黑树 multim…...

2023年全国最新会计专业技术资格精选真题及答案11

百分百题库提供会计专业技术资格考试试题、会计考试预测题、会计专业技术资格考试真题、会计证考试题库等&#xff0c;提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 一、选择题 1.下列各项中&#xff0c;仅将生产过程中消耗的变动成本计入产品成本…...

Centos7搭建NFS

1.NFS简介Network File System(网络文件系统&#xff0c;通过网络让不同的机器系统之间可以彼此共享文件和目录&#xff0c;类似Samba服务。2.NFS挂载原理 在网络中服务器和客户端进行连接都是通过端口进行数据传输&#xff0c;而NFS服务端的端口是随机的&#xff0c;从而导致N…...

ThreadLoca基本使用以及与synchronized的区别

文章目录1. ThreadLocal介绍1.1 官方介绍1.2 基本使用1.2.1 常用方法1.2.2 使用案例1.3 ThreadLocal类与synchronized关键字1.3.1 synchronized同步方式1.3.2 ThreadLocal与synchronized的区别2. 运用场景_事务案例2.1 转账案例2.1.1 场景构建2.1.2 引入事务2.2 常规解决方案2.…...

台州网站搭建/推广方案的推广内容怎么写

1&#xff0c;REINFORCE 在车杆环境中进行 REINFORCE 算法的实验&#xff1a; import gym import torch import torch.nn.functional as F import numpy as np import matplotlib.pyplot as plt from tqdm import tqdm import rl_utils 首先定义策略网络 PolicyNet&#xff0c;…...

广州大型网站建设/企业管理培训课程

电脑被病毒搞得只好重装系统&#xff0c;重装系统后发生了两件让我生气的小事&#xff1a;MSN Messager无法登陆,系统无法激活。SN无法登陆&#xff0c;错误代码为80048820&#xff0c;google了一下查询!答案是&#xff1a;出现这种问题一般是由于系统时间不对&#xff0c;先校…...

石狮网站建设报价/石嘴山网站seo

1.查出占用端口的进程&#xff1a; 比如我要查找的8080&#xff0c;1099端口&#xff1a; netstat -ano|findstr 8080 netstat -ano|findstr 1099 12424就是我们所查进程的id,然后干掉它 taskkill -f -pid 12424...

济南品牌网站建设/软文广告

本文来自CMU的博士&#xff0c;MIT的博士后&#xff0c;vision.ai的联合创始人Tomasz Malisiewicz的个人博客文章&#xff0c;阅读本文&#xff0c;你可以更好的理解计算机视觉是怎么一回事&#xff0c;同时对机器学习是如何随着时间缓慢发展的也有个直观的认识。 以下为正文&a…...

wordpress 联系地图/百度搜索高级搜索技巧

最近博客更新的少了&#xff0c;相对而言&#xff0c;我在自己的个人公众号里还是挺活跃的&#xff0c;大家可以扫描旁边的二维码&#xff0c;或者微信搜索公众号&#xff1a;“编程一生”加关注。 在分布式的年代&#xff0c;一个应用需要部署到多台服务器上。那么要查看日志文…...

广东省住房和城乡建设部网站/建网络平台要多少费用

&#xff11;、大力敲击回车键这个恐怕是人所共有的通病了&#xff0c;因为回车键通常是我们完成一件事情时&#xff0c;最后要敲击的一个键&#xff0c;大概是出于一种胜利的兴奋感&#xff0c;每个人在输入这个回车键时总是那么大力而爽快地敲击。本人的多个键盘就是这样报废…...