【IMX6ULL驱动开发学习】09.Linux之I2C框架简介和驱动程序模板

news2025/4/26 21:50:31

 参考:Linux之I2C驱动_linux i2c驱动_风间琉璃•的博客-CSDN博客​​​​​​

目录

一、I2C驱动框架简介

1.1 I2C总线驱动

1.2 I2C设备驱动

二、I2C总线-设备-驱动模型

2.1 i2c_driver

2.2 i2c_client

2.3 I2C 设备数据收发和处理

三、Linux I2C驱动程序模板


一、I2C驱动框架简介

在 Linux 内核中 I2C 的体系结构分为 3 个部分:

  • I2C 核心: I2C 核心提供了 I2C 总线驱动和设备驱动的注册、 注销方法
  • I2C 总线驱动: I2C 总线驱动是对 I2C 硬件体系结构中适配器端的实现, 适配器可由CPU 控制, 甚至可以直接集成在 CPU 内部。I2C 总线驱动就是 SOC 的 I2C 控制器驱动,也叫做 I2C 适配器驱动
  • I2C 设备驱动: I2C 设备驱动是对 I2C 硬件体系结构中设备端的实现 设备一般挂接在受 CPU 控制的 I2C 适配器上, 通过 I2C 适配器与 CPU 交换数据

1.1 I2C总线驱动

I2C 总线和 platform 总线类似, 区别在于platform 总线是虚拟的一条总线, 而 I2C 总线是实际
存在的
。 对于使用 I2C 通信的设备, 在驱动中直接使用 I2C 总结即可。 I2C 总线驱动的重点是 I2C 适配器驱动, 主要涉及到两个结构体: i2c_adapter 和 i2c_algorithm。 在 Linux 内核中用 i2c_adapter 结构体来表示 I2C 适配器。 i2c_adapter 结构体定义在 include/linux/i2c.h 文件中

struct i2c_adapter {
    struct module *owner;
    unsigned int class; /* classes to allow probing for */
    const struct i2c_algorithm *algo; /* 总线访问算法 */
    void *algo_data;
 
    /* data fields that are valid for all devices */
    struct rt_mutex bus_lock;
 
    int timeout; /* in jiffies */
    int retries;
    struct device dev; /* the adapter device */
 
    int nr;
    char name[48];
    struct completion dev_released;
 
    struct mutex userspace_clients_lock;
    struct list_head userspace_clients;
 
    struct i2c_bus_recovery_info *bus_recovery_info;
    const struct i2c_adapter_quirks *quirks;
};

i2c_algorithm 类型的指针变量algo, 对于一个 I2C 适配器, 要对外提供读写 API 函数, 设备驱动程序可以使用这些 API 函数来完成读写操作。 i2c_algorithm 是 I2C 适配器与 IIC 设备进行通信的方法i2c_algorithm 结构体定义在 include/linux/i2c.h 文件中

 
struct i2c_algorithm 
{
    ......
    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 *);
    ......
};

一般 SOC 的 I2C 总线驱动都是由半导体厂商编写的,所以大多数只要专注于 I2C 设备驱动即可。

1.2 I2C设备驱动

在 I2C 设备驱动中主要有两个重要的结构体: i2c_client 和 i2c_driver。 i2c_client 是描述设备信息的,i2c_driver 描述驱动内容

当驱动和设备匹配成功后,每检测到一个 I2C 设备就会给这个 I2C 设备分配一个i2c_client,这个ic_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_driver 类似 platform_driver,是编写 I2C 设备驱动重点要处理的内容, i2c_driver 结构体定义在 include/linux/i2c.h 文件中

struct i2c_driver {
    unsigned int class;
 
    /* Notifies the driver that a new bus has appeared. You should
    * avoid using this, it will be removed in a near future.
    */
    int (*attach_adapter)(struct i2c_adapter *) __deprecated;
 
    /* Standard driver model interfaces */
    int (*probe)(struct i2c_client *, const struct i2c_device_id *);
    int (*remove)(struct i2c_client *);
 
    /* driver model interfaces that don't relate to enumeration */
    void (*shutdown)(struct i2c_client *);
 
    /* Alert callback, for example for the SMBus alert protocol.
    * The format and meaning of the data value depends on the
    * protocol.For the SMBus alert protocol, there is a single bit
    * of data passed as the alert response's low bit ("eventflag"). */
    void (*alert)(struct i2c_client *, unsigned int data);
    /* a ioctl like command that can be used to perform specific
    * functions with the device.
    */
    int (*command)(struct i2c_client *client, unsigned int cmd,void *arg);
 
    struct device_driver driver;
    const struct i2c_device_id *id_table;
 
    /* Device detection callback for automatic device creation */
    int (*detect)(struct i2c_client *, struct i2c_board_info *);
    const unsigned short *address_list;
    struct list_head clients;
};

当 I2C 设备和驱动匹配成功以后 probe 函数就会执行。device_driver 驱动结构体,如果使用设备树,需要设置 device_driver 的of_match_table 成员变量,即驱动的兼容(compatible)性。 未使用设备树的设备需要设置id_table 设备匹配 ID 表

构造i2c_driver结构体,里面会表明支持那些设备,入口函数里注册i2c_driver结构体,如果i2c_driver发现能够支持的i2c_client的话,probe函数就被调用,在probe函数里记录client信息、注册字符设备、注册file_operations、自动创建设备节点

I2C驱动程序与普通的字符设备驱动程序没有本质差别,唯一差别:发起数据传输时用到i2c_transfer,该函数需要用到适配器(i2c控制器),probe函数被调用时内核会传入i2c_client,i2c_client里含有i2c控制器。

二、I2C总线-设备-驱动模型

2.1 i2c_driver

2c_driver表明能支持哪些设备:

  • 使用of_match_table来判断

    • 设备树中,某个I2C控制器节点下可以创建I2C设备的节点

      • 如果I2C设备节点的compatible属性跟of_match_table的某项兼容,则匹配成功

    • i2c_client.name跟某个of_match_table[i].compatible值相同,则匹配成功

  • 使用id_table来判断

    • i2c_client.name跟某个id_table[i].name值相同,则匹配成功

i2c_driver跟i2c_client匹配成功后,就调用i2c_driver.probe函数。

/* 传统匹配方式 ID 列表 */
static const struct i2c_device_id xxx_id[] = {
    {"xxx", 0},
    {}
};
 
 /* 设备树匹配列表 */
static const struct of_device_id xxx_of_match[] = {
    { .compatible = "xxx" },
    { /* Sentinel */ }
};

/* 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,   //未使用设备树
};

2.2 i2c_client

i2c_client表示一个I2C设备,创建i2c_client的方法有4种:

(1)方法1:通过设备树来创建(常用)

i2c1: i2c@400a0000 {
		/* ... master properties skipped ... */
		clock-frequency = <100000>;

		flash@50 {
			compatible = "atmel,24c256";
			reg = <0x50>;
		};

		pca9532: gpio@60 {
			compatible = "nxp,pca9532";
			gpio-controller;
			#gpio-cells = <2>;
			reg = <0x60>;
		};
	};

向 i2c1 添加 flash 子节点,flash@50是子节点名字, @后面的50是I2C 器件地址。compatible 属性值为atmel,24c256。reg属性也是设置I2C的器件地址的。I2C 设备节点的创建主要是 compatible 属性和 reg属性的设置, 一个用于匹配驱动, 一个用于设置器件地址

(2)方法2:

有时候无法知道该设备挂载哪个I2C bus下,无法知道它对应的I2C bus number。 但是可以通过其他方法知道对应的i2c_adapter结构体。 可以使用下面两个函数来创建i2c_client:

i2c_new_device

 static struct i2c_board_info sfe4001_hwmon_info = {
	I2C_BOARD_INFO("max6647", 0x4e),
  };

  int sfe4001_init(struct efx_nic *efx)
  {
	(...)
	efx->board_info.hwmon_client =
		i2c_new_device(&efx->i2c_adap, &sfe4001_hwmon_info);

	(...)
  }

i2c_new_probed_device

static const unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };

  static int usb_hcd_nxp_probe(struct platform_device *pdev)
  {
	(...)
	struct i2c_adapter *i2c_adap;
	struct i2c_board_info i2c_info;

	(...)
	i2c_adap = i2c_get_adapter(2);
	memset(&i2c_info, 0, sizeof(struct i2c_board_info));
	strscpy(i2c_info.type, "isp1301_nxp", sizeof(i2c_info.type));
	isp1301_i2c_client = i2c_new_probed_device(i2c_adap, &i2c_info,
						   normal_i2c, NULL);
	i2c_put_adapter(i2c_adap);
	(...)
  }

差别:

  • i2c_new_device:会创建i2c_client,即使该设备并不存在

  • i2c_new_probed_device:

    • 它成功的话,会创建i2c_client,并且表示这个设备肯定存在

    • I2C设备的地址可能发生变化,比如AT24C02的引脚A2A1A0电平不一样时,设备地址就不一样

    • 可以罗列出可能的地址

    • i2c_new_probed_device使用这些地址判断设备是否存在

(3)方法3(不推荐):由i2c_driver.detect函数来判断是否有对应的I2C设备并生成i2c_client  

(4)方法4:通过用户空间(user-space)生成 调试时、或者不方便通过代码明确地生成i2c_client时,可以通过用户空间来生成。

// 创建一个i2c_client, .name = "eeprom", .addr=0x50, .adapter是i2c-3
  # echo eeprom 0x50 > /sys/bus/i2c/devices/i2c-3/new_device
  
// 删除一个i2c_client
  # echo 0x50 > /sys/bus/i2c/devices/i2c-3/delete_device

2.3 I2C 设备数据收发和处理

在 I2C 设备驱动中首先要完成 i2c_driver 结构体的创建、 初始化和注册, 当设备和驱动匹配成功后,就会执行 probe 函数, probe 函数中就是执行字符设备驱动的一套流程。

一般需要在probe函数里面初始化 I2C 设备,要初始化 I2C 设备需要使用 i2c_transfer 函数对 I2C 设备寄存器进行读写操作。 i2c_transfer 函数会调用 I2C 适配器中 i2c_algorithm里面的 master_xfer 函数, 对于 I.MX6U 而言是 i2c_imx_xfer 这个函数。
 

int i2c_transfer(struct i2c_adapter *adap,struct i2c_msg *msgs,int num)

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

msgs参数是一个 i2c_msg 类型的指针参数,Linux内核使用 i2c_msg 结构体 描述一个消息。

struct i2c_msg {
    __u16 addr; /* 从机地址 */
    __u16 flags; /* 标志 */
    #define I2C_M_TEN 0x0010
    #define I2C_M_RD 0x0001
    #define I2C_M_STOP 0x8000
    #define I2C_M_NOSTART 0x4000
    #define I2C_M_REV_DIR_ADDR 0x2000
    #define I2C_M_IGNORE_NAK 0x1000
    #define I2C_M_NO_RD_ACK 0x0800
    #define I2C_M_RECV_LEN 0x0400
    __u16 len; /* 消息(本 msg)长度 */
    __u8 *buf; /* 消息数据 */
};

使用i2c_transfer函数发送数据前,要构建好 i2c_msg,使用 i2c_transfer 进行 I2C 数据收发的模板:

/* 设备结构体 */
struct xxx_dev {
    ......
    void *private_data; /* 私有数据,一般会设置为 i2c_client */
};
 
/*
* @description : 读取 I2C 设备多个寄存器数据
* @param – dev : I2C 设备
* @param – reg : 要读取的寄存器首地址
* @param – val : 读取到的数据
* @param – len : 要读取的数据长度
* @return : 操作结果
*/
static int xxx_read_regs(struct xxx_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; /* I2C 器件地址 */
    msg[0].flags = 0; /* 标记为发送数据 */
    msg[0].buf = &reg; /* 读取的首地址 */
    msg[0].len = 1; /* reg 长度 */
 
    /* msg[1],第二条读消息,读取寄存器数据 */
    msg[1].addr = client->addr; /* I2C 器件地址 */
    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 
    {
        ret = -EREMOTEIO;
    }
    return ret;
}
 
/*
* @description : 向 I2C 设备多个寄存器写入数据
* @param – dev : 要写入的设备结构体
* @param – reg : 要写入的寄存器首地址
* @param – buf : 要写入的数据缓冲区
* @param – len : 要写入的数据长度
* @return : 操作结果
*/
static s32 xxx_write_regs(struct xxx_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; /* 寄存器首地址 */
    memcpy(&b[1],buf,len); /* 将要发送的数据拷贝到数组 b 里面 */
 
    msg.addr = client->addr; /* I2C 器件地址 */
    msg.flags = 0; /* 标记为写数据 */
 
    msg.buf = b; /* 要发送的数据缓冲区 */
    msg.len = len + 1; /* 要发送的数据长度 */
 
    return i2c_transfer(client->adapter, &msg, 1);
}

在设备结构体里面添加一个执行void的指针成员变量private_data,此成员变量用于保存设备的私有数据,在 I2C 设备驱动中一般将其指向 I2C 设备对应的i2c_client

 

xxx_read_regs 函数用于读取 I2C 设备多个寄存器数据,然后定义了一个i2c_msg 数组, 2 个数组元素。如图,I2C写时序图,因为 I2C 读取数据的时候要先发送要读取的寄存器地址,然后再读取数据,所以需要准备两个 i2c_msg。一个用于发送寄存器地址,一个用于读取寄存器值。

对于 msg[0], 将 flags设置为 0, 表示写数据。 msg[0]的 addr 是 I2C 设备的器件地址, msg[0]的 buf 成员变量就是要读取的寄存器地址。 对于 msg[1], 将 flags 设置为 I2C_M_RD, 表示读取数据。 msg[1]的 buf 成员变量用于保存读取到的数据, len 成员变量就是要读取的数据长度。 调用 i2c_transfer 函数完成 I2C 数据读操作。


 

 

xxx_write_regs 函数用于向 I2C 设备多个寄存器写数据。 数组 b 用于存放寄存器首地址和要发送的数据,msg 的 addr设置为 I2C 器件地址。然后设置 msg 的 flags 为 0, 也就是写数据。设置要发送的数据, 也就是数组 b。设置 msg 的 len 为 len+1, 因为要加上一个字节的寄存器地址。 最后通过 i2c_transfer 函数完成向 I2C 设备的写操作。

另外还有两个API函数分别用于I2C数据的收发操作,这两个函数都会调用i2c_transfer。

I2C 数据发送函数 i2c_master_send:

int i2c_master_send(const struct i2c_client *client,const char *buf,int count)

client: I2C 设备对应的 i2c_client
buf:要发送的数据
count: 要发送的数据字节数,要小于 64KB,以为 i2c_msg 的 len 成员变量是一个 u16(无
符号 16 位)类型的数据。
返回值: 负值,失败,其他非负值,发送的字节数

I2C 数据接收函数为 i2c_master_recv:

int i2c_master_recv(const struct i2c_client *client,char *buf,int count)

client: I2C 设备对应的 i2c_client
buf:要接收的数据

count: 要接收的数据字节数,要小于 64KB,以为 i2c_msg 的 len 成员变量是一个 u16(无
符号 16 位)类型的数据
返回值: 负值,失败,其他非负值,发送的字节数

三、Linux I2C驱动程序模板

i2c_drv.c

#include "linux/i2c.h"
#include <linux/module.h>
#include <linux/poll.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>

/* 主设备号                                                                 */
static int major = 0;
static struct class *my_i2c_class;

struct i2c_client *g_client;

static DECLARE_WAIT_QUEUE_HEAD(gpio_wait);
struct fasync_struct *i2c_fasync;


/* 实现对应的open/read/write等函数,填入file_operations结构体                   */
static ssize_t i2c_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	int err;

	struct i2c_msg msgs[2];

	/* 初始化i2c_msg */

	err = i2c_transfer(g_client->adapter, msgs, 2);

	/* copy_to_user  */
	
	return 0;
}

static ssize_t i2c_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
	int err;

	/* copy_from_user  */


	struct i2c_msg msgs[2];

	/* 初始化i2c_msg */

	err = i2c_transfer(g_client->adapter, msgs, 2);

	
	return 0;    
}


static unsigned int i2c_drv_poll(struct file *fp, poll_table * wait)
{
	//printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	poll_wait(fp, &gpio_wait, wait);
	//return is_key_buf_empty() ? 0 : POLLIN | POLLRDNORM;
	return 0;
}

static int i2c_drv_fasync(int fd, struct file *file, int on)
{
	if (fasync_helper(fd, file, on, &i2c_fasync) >= 0)
		return 0;
	else
		return -EIO;
}


/* 定义自己的file_operations结构体                                              */
static struct file_operations i2c_drv_fops = {
	.owner	 = THIS_MODULE,
	.read    = i2c_drv_read,
	.write   = i2c_drv_write,
	.poll    = i2c_drv_poll,
	.fasync  = i2c_drv_fasync,
};


static int i2c_drv_probe(struct i2c_client *client,
			const struct i2c_device_id *id)
{
	// struct device_node *np = client->dev.of_node;   从client获取设备节点
	// struct i2c_adapter *adapter = client->adapter;  从client获取控制器

	/* 记录client */
	g_client = client;

	/* 注册字符设备 */
	/* 注册file_operations 	*/
	major = register_chrdev(0, "100ask_i2c", &i2c_drv_fops);  /* /dev/gpio_desc */

	my_i2c_class = class_create(THIS_MODULE, "100ask_i2c_class");
	if (IS_ERR(my_i2c_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "100ask_i2c");
		return PTR_ERR(my_i2c_class);
	}

	device_create(my_i2c_class, NULL, MKDEV(major, 0), NULL, "myi2c"); /* /dev/myi2c */
	
	return 0;
}

static int i2c_drv_remove(struct i2c_client *client)
{
	/* 反注册字符设备 */
	device_destroy(my_i2c_class, MKDEV(major, 0));
	class_destroy(my_i2c_class);
	unregister_chrdev(major, "100ask_i2c");

	return 0;
}

static const struct of_device_id myi2c_dt_match[] = {
	{ .compatible = "100ask,i2cdev" },
	{},
};
static struct i2c_driver my_i2c_driver = {
	.driver = {
		   .name = "100ask_i2c_drv",
		   .owner = THIS_MODULE,
		   .of_match_table = myi2c_dt_match,
	},
	.probe = i2c_drv_probe,
	.remove = i2c_drv_remove,
};


static int __init i2c_drv_init(void)
{
	/* 注册i2c_driver */
	return i2c_add_driver(&my_i2c_driver);
}

static void __exit i2c_drv_exit(void)
{
	/* 反注册i2c_driver */
	i2c_del_driver(&my_i2c_driver);
}

/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */

module_init(i2c_drv_init);
module_exit(i2c_drv_exit);

MODULE_LICENSE("GPL");


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

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

相关文章

redis常用五种数据类型详解

目录 前言&#xff1a; string 相关命令 内部编码 应用场景 hash 相关命令 内部编码 应用场景 list 相关命令 内部编码 应用场景 set 相关命令 内部编码 应用场景 Zset 相关命令 内部编码 应用场景 渐进式遍历 前言&#xff1a; redis有多种数据类型&…

Java实现一个简单的图书管理系统(内有源码)

简介 哈喽哈喽大家好啊&#xff0c;之前作者也是讲了Java不少的知识点了&#xff0c;为了巩固之前的知识点再为了让我们深入Java面向对象这一基本特性&#xff0c;就让我们完成一个图书管理系统的小项目吧。 项目简介&#xff1a;通过管理员和普通用户的两种操作界面&#xff0…

【网络安全】防火墙知识点全面图解(三)

本系列文章包含&#xff1a; 【网络安全】防火墙知识点全面图解&#xff08;一&#xff09;【网络安全】防火墙知识点全面图解&#xff08;二&#xff09;【网络安全】防火墙知识点全面图解&#xff08;三&#xff09; 防火墙知识点全面图解&#xff08;三&#xff09; 39、什…

生成式人工智能的潜在有害影响与未来之路(二)

利润高于隐私&#xff1a;不透明数据收集增加 背景和风险 生成型人工智能工具建立在各种大型、复杂的机器学习模型之上&#xff0c;这些模型需要大量的训练数据才能发挥作用。对于像ChatGPT这样的工具&#xff0c;数据包括从互联网上抓取的文本。对于像Lensa或Stable Diffusi…

C语言练习1(巩固提升)

C语言练习1 选择题 前言 “人生在勤&#xff0c;勤则不匮。”幸福不会从天降&#xff0c;美好生活靠劳动创造。全面建成小康社会的奋斗目标&#xff0c;为广大劳动群众指明了光明的未来&#xff1b;全面建成小康社会的历史任务&#xff0c;为广大劳动群众赋予了光荣的使命&…

让你对es有一个初步的了解

首先es在海量数据的搜索能力非常好&#xff0c;es你可以把他看成一个搜索引擎数据库&#xff0c;他是个非关系型数据库。他的语法有很大的不同&#xff0c;好像都是json风格的。还有一点需要说的就是es 的数据是存在硬盘上的&#xff0c; 我们先来看一下mysql和es的区别吧。一…

边写代码边学习之Bidirectional LSTM

1. 什么是Bidirectional LSTM 双向 LSTM (BiLSTM) 是一种主要用于自然语言处理的循环神经网络。 与标准 LSTM 不同&#xff0c;输入是双向流动的&#xff0c;并且它能够利用双方的信息。 它也是一个强大的工具&#xff0c;可以在序列的两个方向上对单词和短语之间的顺序依赖…

4H-SiC UMOSFET 结构中带有 p+-polySi/SiC 被屏蔽区的模拟研究

标题&#xff1a;Simulation Study of 4H-SiC UMOSFET Structure With p -polySi/SiC Shielded Region 摘要 在这篇论文中&#xff0c;我们提出了一种改进效率的4H-SiC U型沟槽栅MOSFET&#xff08;UMOSFET&#xff09;结构。所提出的器件结构利用ppolySi/SiC被屏蔽区来降低导…

学习Linux的注意事项(使用经验;目录作用;服务器注意事项)

本篇分享学习Linux过程中的一些经验 文章目录 1. Linux系统的使用经验2. Linux各目录的作用3. 服务器注意事项 1. Linux系统的使用经验 Linux严格区分大小写Linux中所有内容以文件形式保存&#xff0c;包括硬件&#xff0c;Linux是以管理文件的方式操作硬件 硬盘文件是/dev/s…

mybatis概述及搭建

目录 1.概述 2.mybatis搭建 1.创建一个maven项目&#xff0c;添加mybatis、mysql所依赖的jar 2.创建一个数据库表&#xff0c;及对应的java类 3.创建一个mybatis的核心配置文件&#xff0c;配置数据库连接信息&#xff0c;配置sql映射文件 4.创建sql映射文件&#xff0c;…

Matlab高光谱遥感数据处理与混合像元分解实践技术

光谱和图像是人们观察世界的两种方式&#xff0c;高光谱遥感通过“图谱合一”的技术创新将两者结合起来&#xff0c;大大提高了人们对客观世界的认知能力&#xff0c;本来在宽波段遥感中不可探测的物质&#xff0c;在高光谱遥感中能被探测。以高光谱遥感为核心&#xff0c;构建…

Mysql报错 mysqladmin flush-hosts

出现这个的原因是错误连接达到数据库设置的最大值。 此时需要释放重置连接最大值。 进入mysql使用命令 flush-hosts;环境说明&#xff1a; 内网测试服务器192.168.18.251 为WEB服务器&#xff0c;安装了mysql; 内网音视频转码服务器192.168.18.253安装了转码工具&#xff0…

IP协议报文结构

IP报文结构 4位版本号: 指定IP协议的版本, 对于IPv4来说, 就是4.4位头部长度: IP头部的长度是多少个32bit, 也就是 length * 4 的字节数. 4bit表示最大的数字是15, 因此IP头部最大长度是60字节.8位服务类型: 3位优先权字段(已经弃用) 4位TOS字段 1位保留字段(必须置为0). 4位…

【使用perf和火焰图分析PostgreSQL数据库的性能瓶颈】

Perf工具可用来对软件进行优化&#xff0c;包括算法优化&#xff08;空间复杂度、时间复杂度&#xff09;和代码优化&#xff08;提高执行速度、减少内存占用&#xff09;等等&#xff0c;perf 最常用的参数有top、stat、record&#xff0c;另外还有list和report等。 本文主要使…

枚举和反射

枚举 枚举 枚举是一种特殊的类&#xff0c;它可以有自己的属性、方法和构造方法。 两种枚举的方法 自定义枚举 a.将构造器私有化&#xff0c;防止外部直接new b.去掉set方法&#xff0c;防止属性被修改 c.在内部直接创建固定的对象 通过类名直接去访问 关键字枚举 用…

即拼七人拼团:突破传统模式,二二复制结合拼团快速引流

如何解决平台运营难的问题&#xff1f;企业主要关注的点则是新客的引流、用户的留存、平台活跃度提高以及如何变现等一些的问题&#xff0c;下面就针对这块分析提出一个七人拼团的解决方案。一个7人成团的即拼模式&#xff0c;为什么会在众多拼团模式中脱颖而出&#xff0c;即拼…

学会Mybatis框架:一文掌握MyBatis与GitHub插件分页的完美结合【三.分页】

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于Mybatis的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 一.Mybatis分页 1. Mybatis自带分页 2…

职业技术培训内容介绍

泰迪职业技术培训包括&#xff1a;Python技术应用、大数据技术应用、机器学习、大数据分析 、人工智能技术应用。 职业技术培训-Python技术应用 “Python技术应用工程师”职业技术认证是由工业和信息化部教育与考试中心推出一套专业化、科学化、系统化的人才考核标准&…

8.24随笔记录科研灵感

在B站浏览关于人脸3D的视频&#xff0c;无意看到这一部分“结构光-深度图信息” 【巧的是这也是微软的模型】 就想到了自己的V4.3版本的算法里面估计距离其实用的也是结构光&#xff08;红光&#xff09;来估测距离&#xff0c;而且目前水下图像处理领域并没有采用这个方法&am…

【DC】逻辑综合实战

DC实战 0. 学习目标1. Design1.1 Design Schematic1.2 Design Specification 2. 配置文件和约束文件2.1 配置文件(1) common_setup.tcl 文件(2) dc_setup.tcl 文件(3) .synopsys_dc.setup 文件 2.3 启动工具查看单元库信息(1) 查看目标库的时间单位 2.3 设计约束文件(1) 时钟约…