linux-2.6.22.6内核i2c驱动框架源码分析

news2024/11/13 23:04:57

i2c是常见的通信协议,协议比较简单,只有数据和时钟两条线(SDA和SCL),i2c的通信分为主机和从机,主机一般占主导地位,从机可以有多个。

i2c通信的数据格式为(SDA上的数据):开始的7位里面指定了设备地址(因为有多个从机),第8位是读或写信号,表示此次传输是读还是写,第9位是ack信号,也就是当某个从机收到信号好需要发ack信号(低电平)确认。

接下来就是发数据了,这个过程是由8个数据位和一个ack位组成,如果是读,这8位数据由从机驱动,第9位ack由主机发出(因为此时是主机向从机读数据,主机需要确认是否收到数据),如果是写,则这8位由主机驱动,第9位ack由从机发出,发数据这个过程是可以反复进行的,一旦发送或者读取完成,主机就会发出一个停止信号(SDA由低变高)来结束通信,这所有的过程都是在SDA上进行的。
在这里插入图片描述
i2c通信可以分为读和写,读比写的过程更复杂,要读则先写,读取某设备内某地址的数据过程为:先发送设备地址,然后还要写入设备内要读取的地址,最后再发送设备地址后才可进行数据读取。写入数据则少了再发设备地址这一步,写是直接发送设备地址和设备内要写入的地址后就可以发送要写入的数据了。

写:
在这里插入图片描述
读:
在这里插入图片描述
内核驱动框架分析:

在开发i2c驱动程序时,我们可以自己根据芯片手册进行相关寄存器和时序等设置,这种开发比较原始,完全从底层开始,工作量大并且出错概率大,这种其实就是裸机开发了。如果是在Linux内核上进行开发,则完全可以利用内核提供的i2c框架进行开发。i2c框架主要分为两大部分,一部分是用平台设备总线驱动模型实现的,主要封装了主机(s3c2440芯片)i2c控制器的寄存器和时序设置等操作,另一部分就是设备驱动(相当于从机),主要提供主机部分具体操作所需要的数据信息,如果用总线设备驱动模型来看,这一部分就相当于提供设备信息,所以这两大部分又可以看成总线设备驱动模型。

i2c框架代码在/linux-2.6.22.6/drivers/i2c这个目录下,这里面又有algos、busses、chips三个部分,algos主要是协议算法相关的东西,busses里是主机(s3c2440芯片)i2c控制器的寄存器和时序设置等操作部分,chips里就是具体的设备驱动(相当于从机),框架入口就在busses下i2c-s3c2410.c这个文件里。

先分析i2c-s3c2410.c

static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
	struct s3c24xx_i2c *i2c = &s3c24xx_i2c;
	struct resource *res;
	int ret;

	/* find the clock and enable it */

	i2c->dev = &pdev->dev;
	i2c->clk = clk_get(&pdev->dev, "i2c");
	if (IS_ERR(i2c->clk)) {
		dev_err(&pdev->dev, "cannot get clock\n");
		ret = -ENOENT;
		goto err_noclk;
	}

	dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);

	clk_enable(i2c->clk);

	/* map the registers */

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (res == NULL) {
		dev_err(&pdev->dev, "cannot find IO resource\n");
		ret = -ENOENT;
		goto err_clk;
	}

	i2c->ioarea = request_mem_region(res->start, (res->end-res->start)+1,
					 pdev->name);

	if (i2c->ioarea == NULL) {
		dev_err(&pdev->dev, "cannot request IO\n");
		ret = -ENXIO;
		goto err_clk;
	}

	i2c->regs = ioremap(res->start, (res->end-res->start)+1);

	if (i2c->regs == NULL) {
		dev_err(&pdev->dev, "cannot map IO\n");
		ret = -ENXIO;
		goto err_ioarea;
	}

	dev_dbg(&pdev->dev, "registers %p (%p, %p)\n", i2c->regs, i2c->ioarea, res);

	/* setup info block for the i2c core */

	i2c->adap.algo_data = i2c;
	i2c->adap.dev.parent = &pdev->dev;

	/* initialise the i2c controller */

	ret = s3c24xx_i2c_init(i2c);
	if (ret != 0)
		goto err_iomap;

	/* find the IRQ for this unit (note, this relies on the init call to
	 * ensure no current IRQs pending 
	 */

	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
	if (res == NULL) {
		dev_err(&pdev->dev, "cannot find IRQ\n");
		ret = -ENOENT;
		goto err_iomap;
	}

	ret = request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED,
			  pdev->name, i2c);

	if (ret != 0) {
		dev_err(&pdev->dev, "cannot claim IRQ\n");
		goto err_iomap;
	}

	i2c->irq = res;
		
	dev_dbg(&pdev->dev, "irq resource %p (%lu)\n", res,
		(unsigned long)res->start);

	ret = i2c_add_adapter(&i2c->adap);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to add bus to i2c core\n");
		goto err_irq;
	}

	platform_set_drvdata(pdev, i2c);

	dev_info(&pdev->dev, "%s: S3C I2C adapter\n", i2c->adap.dev.bus_id);
	return 0;

 err_irq:
	free_irq(i2c->irq->start, i2c);

 err_iomap:
	iounmap(i2c->regs);

 err_ioarea:
	release_resource(i2c->ioarea);
	kfree(i2c->ioarea);

 err_clk:
	clk_disable(i2c->clk);
	clk_put(i2c->clk);

 err_noclk:
	return ret;
}


static struct platform_driver s3c2440_i2c_driver = {
	.probe		= s3c24xx_i2c_probe,
	.remove		= s3c24xx_i2c_remove,
	.resume		= s3c24xx_i2c_resume,
	.driver		= {
		.owner	= THIS_MODULE,
		.name	= "s3c2440-i2c",
	},
};

static int __init i2c_adap_s3c_init(void)
{
	int ret;

	ret = platform_driver_register(&s3c2410_i2c_driver);
	if (ret == 0) {
		ret = platform_driver_register(&s3c2440_i2c_driver);
		if (ret)
			platform_driver_unregister(&s3c2410_i2c_driver);
	}

	return ret;
}


static struct resource s3c_i2c_resource[] = {
	[0] = {
		.start = S3C24XX_PA_IIC,
		.end   = S3C24XX_PA_IIC + S3C24XX_SZ_IIC - 1,
		.flags = IORESOURCE_MEM,
	},
	[1] = {
		.start = IRQ_IIC,
		.end   = IRQ_IIC,
		.flags = IORESOURCE_IRQ,
	}

};

devs.c

static struct resource s3c_i2c_resource[] = {
	[0] = {
		.start = S3C24XX_PA_IIC,
		.end   = S3C24XX_PA_IIC + S3C24XX_SZ_IIC - 1,
		.flags = IORESOURCE_MEM,
	},
	[1] = {
		.start = IRQ_IIC,
		.end   = IRQ_IIC,
		.flags = IORESOURCE_IRQ,
	}

};

struct platform_device s3c_device_i2c = {
	.name		  = "s3c2410-i2c",
	.id		  = -1,
	.num_resources	  = ARRAY_SIZE(s3c_i2c_resource),
	.resource	  = s3c_i2c_resource,
};

devs.c和i2c-s3c2410.c是标准的总线设备驱动模型,主要提供主机(s3c2440芯片)i2c控制器的寄存器和时序设置等操作,当设备和驱动匹配后,会调用s3c24xx_i2c_probe,s3c24xx_i2c_probe里又会通过ret = i2c_add_adapter(&i2c->adap);去调剂一个adapter,这个adapter就是给设备驱动(从机部分)开了一个口子,使得这两大部分又构成一个设备驱动模型。

分析i2c_add_adapter(i2c-core.c)

int i2c_add_adapter(struct i2c_adapter *adapter)
{
	int	id, res = 0;

retry:
	if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)
		return -ENOMEM;

	mutex_lock(&core_lists);
	/* "above" here means "above or equal to", sigh */
	res = idr_get_new_above(&i2c_adapter_idr, adapter,
				__i2c_first_dynamic_bus_num, &id);
	mutex_unlock(&core_lists);

	if (res < 0) {
		if (res == -EAGAIN)
			goto retry;
		return res;
	}

	adapter->nr = id;
	return i2c_register_adapter(adapter);
}

static int i2c_register_adapter(struct i2c_adapter *adap)
{
  list_add_tail(&adap->list, &adapters);
	....
	....
	
	list_for_each(item,&drivers) {
		driver = list_entry(item, struct i2c_driver, list);
		if (driver->attach_adapter)
			/* We ignore the return code; if it fails, too bad */
			driver->attach_adapter(adap);
	}

}

i2c_add_adapter里调用了i2c_register_adapter,i2c_register_adapter里先把自己放入adapters数组,然后会循环遍历drivers这个链表,drivers存放的是设备驱动(从机部分),这个操作会调用
drivers里的每个driver的attach_adapter函数,这个函数会检测这个driver对应的设备是否匹配,这个后面会讲到。

接下来看下设备驱动(从机部分)代码,以chips下eeprom.c为例:

static int __init eeprom_init(void)
{
	return i2c_add_driver(&eeprom_driver);
}
static inline int i2c_add_driver(struct i2c_driver *driver)
{
	return i2c_register_driver(THIS_MODULE, driver);
}

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
	...
	...

	list_add_tail(&driver->list,&drivers);
	
		list_for_each_entry(adapter, &adapters, list) {
			driver->attach_adapter(adapter);
		}
	
...
...
}

eeprom.c里也会去注册一个driver,在i2c_register_driver里先把自己加入到drivers这个链表里,然后遍历adapters,取出每一个adapter然后调用driver的attach_adapter,这个步骤和前面注册adapter时处理一样,下面就看下attach_adapter这个方法。

static struct i2c_driver eeprom_driver = {
	.driver = {
		.name	= "eeprom",
	},
	.id		= I2C_DRIVERID_EEPROM,
	.attach_adapter	= eeprom_attach_adapter,
	.detach_client	= eeprom_detach_client,
};

static int eeprom_attach_adapter(struct i2c_adapter *adapter)
{
	return i2c_probe(adapter, &addr_data, eeprom_detect);
}

attach_adapter就是eeprom_driver 的attach_adapter 方法,这个方法会调用i2c_probe(adapter, &addr_data, eeprom_detect);addr_data是从设备地址,如果检测到从设备匹配成功则会触发eeprom_detect函数的执行。

下面分析i2c_probe:

int i2c_probe(struct i2c_adapter *adapter,
	      struct i2c_client_address_data *address_data,
	      int (*found_proc) (struct i2c_adapter *, int, int))
{

	...
		err = i2c_probe_address(adapter, address_data->normal_i2c[i],
					-1, found_proc);
   ...
	
}

static int i2c_probe_address(struct i2c_adapter *adapter, int addr, int kind,
			     int (*found_proc) (struct i2c_adapter *, int, int))
{
	...
	if (kind < 0) {
		if (i2c_smbus_xfer(adapter, addr, 0, 0, 0,
				   I2C_SMBUS_QUICK, NULL) < 0)
			return 0;

		/* prevent 24RF08 corruption */
		if ((addr & ~0x0f) == 0x50)
			i2c_smbus_xfer(adapter, addr, 0, 0, 0,
				       I2C_SMBUS_QUICK, NULL);
	}
	...
}

s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags,
                   char read_write, u8 command, int size,
                   union i2c_smbus_data * data)
{
	...
		res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,
	                                      command,size,data);

...
}

static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
                                   unsigned short flags,
                                   char read_write, u8 command, int size,
                                   union i2c_smbus_data * data)
{
...
	if (i2c_transfer(adapter, msg, num) < 0)
		return -1;
		...
		}

int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)
{
...
		ret = adap->algo->master_xfer(adap,msgs,num);
		...
}

i2c_probe里面的层级调用关系如下:
i2c_probe
i2c_probe_address
i2c_smbus_xfer
i2c_smbus_xfer_emulated
i2c_transfer
adap->algo->master_xfer(adap,msgs,num);
可以看到,i2c_probe最终会调用到adapter里面 algo结构体的 master_xfer函数,adapter是在i2c-s3c2410.c 里构造的,也就是在设置是主机(s3c2440芯片)i2c控制器的寄存器和时序等操作时构造的。也就是说,master_xfer是一个真正i2c通信时数据传输函数,最终这个函数里会去发出中断和开始信号,并设置相关寄存器发起数据传输。

static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
	.master_xfer		= s3c24xx_i2c_xfer,
	.functionality		= s3c24xx_i2c_func,
};

static struct s3c24xx_i2c s3c24xx_i2c = {
	.lock		= __SPIN_LOCK_UNLOCKED(s3c24xx_i2c.lock),
	.wait		= __WAIT_QUEUE_HEAD_INITIALIZER(s3c24xx_i2c.wait),
	.tx_setup	= 50,
	.adap		= {
		.name			= "s3c2410-i2c",
		.owner			= THIS_MODULE,
		.algo			= &s3c24xx_i2c_algorithm,
		.retries		= 2,
		.class			= I2C_CLASS_HWMON,
	},
};
static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
			struct i2c_msg *msgs, int num)
{
	struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;
	int retry;
	int ret;

	for (retry = 0; retry < adap->retries; retry++) {

		ret = s3c24xx_i2c_doxfer(i2c, msgs, num);

		if (ret != -EAGAIN)
			return ret;

		dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);

		udelay(100);
	}

	return -EREMOTEIO;
}
static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, struct i2c_msg *msgs, int num)
{
	unsigned long timeout;
	int ret;

	ret = s3c24xx_i2c_set_master(i2c);
	if (ret != 0) {
		dev_err(i2c->dev, "cannot get bus (error %d)\n", ret);
		ret = -EAGAIN;
		goto out;
	}

	spin_lock_irq(&i2c->lock);

	i2c->msg     = msgs;
	i2c->msg_num = num;
	i2c->msg_ptr = 0;
	i2c->msg_idx = 0;
	i2c->state   = STATE_START;

	s3c24xx_i2c_enable_irq(i2c);
	s3c24xx_i2c_message_start(i2c, msgs);
	spin_unlock_irq(&i2c->lock);
	
	timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);

	ret = i2c->msg_idx;

	/* having these next two as dev_err() makes life very 
	 * noisy when doing an i2cdetect */

	if (timeout == 0)
		dev_dbg(i2c->dev, "timeout\n");
	else if (ret != num)
		dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret);

	/* ensure the stop has been through the bus */

	msleep(1);

 out:
	return ret;
}


static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, 
				      struct i2c_msg *msg)
{
	unsigned int addr = (msg->addr & 0x7f) << 1;
	unsigned long stat;
	unsigned long iiccon;

	stat = 0;
	stat |=  S3C2410_IICSTAT_TXRXEN;

	if (msg->flags & I2C_M_RD) {
		stat |= S3C2410_IICSTAT_MASTER_RX;
		addr |= 1;
	} else
		stat |= S3C2410_IICSTAT_MASTER_TX;

	if (msg->flags & I2C_M_REV_DIR_ADDR)
		addr ^= 1;

	// todo - check for wether ack wanted or not
	s3c24xx_i2c_enable_ack(i2c);

	iiccon = readl(i2c->regs + S3C2410_IICCON);
	writel(stat, i2c->regs + S3C2410_IICSTAT);
	
	dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr);
	writeb(addr, i2c->regs + S3C2410_IICDS);
	
	/* delay here to ensure the data byte has gotten onto the bus
	 * before the transaction is started */

	ndelay(i2c->tx_setup);

	dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon);
	writel(iiccon, i2c->regs + S3C2410_IICCON);
	
	stat |=  S3C2410_IICSTAT_START;
	writel(stat, i2c->regs + S3C2410_IICSTAT);
}

总结:i2c驱动框架也是一个总线设备驱动模型,它可以分为两大部分,第一部分就是标准的总线设备驱动模型,这一部分主要封装对主机(s3c2440芯片)i2c控制器的相关操作,这些操作就是对应芯片手册里的相关寄存器设置等操作,并且还会去注册一个adapter链表,这个adapter里封装一些真正数据传输函数,这样的好处时我们在开发一个具体从设备驱动时,不用关系主机控制器设置这些复杂操作了,我们只需要调用它提供的传输函数就可完成数据的收发。

开发一个i2c驱动时,我们只需关心从设备部分,主要提供主机控制器操作所需要的从设备信息,这就是另一部分。在注册adapter和从设备driver时,两变都会把自己添加进自己的链表里,然后遍历对方链表去调用driver的attach_adapter方法进行匹配。从这个层面看,这两大部分又构成了一个总线设备驱动模型,而从设备驱动类型设备,主要提供主机部分所需操作信息。
在这里插入图片描述
i2c驱动实例:

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/fs.h>
#include <asm/uaccess.h>

static unsigned short ignore[]      = { I2C_CLIENT_END };
static unsigned short normal_addr[] = { 0x50, I2C_CLIENT_END }; /* 地址值是7位 */
                                        /* 改为0x60的话, 由于不存在设备地址为0x60的设备, 所以at24cxx_detect不被调用 */

static unsigned short force_addr[] = {ANY_I2C_BUS, 0x60, I2C_CLIENT_END};
static unsigned short * forces[] = {force_addr, NULL};
										
static struct i2c_client_address_data addr_data = {
	.normal_i2c	= normal_addr,  /* 要发出S信号和设备地址并得到ACK信号,才能确定存在这个设备 */
	.probe		= ignore,
	.ignore		= ignore,
	//.forces     = forces, /* 强制认为存在这个设备 */
};

static struct i2c_driver at24cxx_driver;


static int major;
static struct class *cls;
struct i2c_client *at24cxx_client;

static ssize_t at24cxx_read(struct file *file, char __user *buf, size_t size, loff_t * offset)
{
	unsigned char address;
	unsigned char data;
	struct i2c_msg msg[2];
	int ret;
	
	/* address = buf[0] 
	 * data    = buf[1]
	 */
	if (size != 1)
		return -EINVAL;
	
	copy_from_user(&address, buf, 1);

	/* 数据传输三要素: 源,目的,长度 */

	/* 读AT24CXX时,要先把要读的存储空间的地址发给它 */
	msg[0].addr  = at24cxx_client->addr;  /* 目的 */
	msg[0].buf   = &address;              /* 源 */
	msg[0].len   = 1;                     /* 地址=1 byte */
	msg[0].flags = 0;                     /* 表示写 */

	/* 然后启动读操作 */
	msg[1].addr  = at24cxx_client->addr;  /* 源 */
	msg[1].buf   = &data;                 /* 目的 */
	msg[1].len   = 1;                     /* 数据=1 byte */
	msg[1].flags = I2C_M_RD;                     /* 表示读 */


	ret = i2c_transfer(at24cxx_client->adapter, msg, 2);
	if (ret == 2)
	{
		copy_to_user(buf, &data, 1);
		return 1;
	}
	else
		return -EIO;
}

static ssize_t at24cxx_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
	unsigned char val[2];
	struct i2c_msg msg[1];
	int ret;
	
	/* address = buf[0] 
	 * data    = buf[1]
	 */
	if (size != 2)
		return -EINVAL;
	
	copy_from_user(val, buf, 2);

	/* 数据传输三要素: 源,目的,长度 */
	msg[0].addr  = at24cxx_client->addr;  /* 目的 */
	msg[0].buf   = val;                   /* 源 */
	msg[0].len   = 2;                     /* 地址+数据=2 byte */
	msg[0].flags = 0;                     /* 表示写 */

	ret = i2c_transfer(at24cxx_client->adapter, msg, 1);
	if (ret == 1)
		return 2;
	else
		return -EIO;
}


static struct file_operations at24cxx_fops = {
	.owner = THIS_MODULE,
	.read  = at24cxx_read,
	.write = at24cxx_write,
};

static int at24cxx_detect(struct i2c_adapter *adapter, int address, int kind)
{	
	printk("at24cxx_detect\n");

	/* 构构一个i2c_client结构体: 以后收改数据时会用到它 */
	at24cxx_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
	at24cxx_client->addr    = address;
	at24cxx_client->adapter = adapter;
	at24cxx_client->driver  = &at24cxx_driver;
	strcpy(at24cxx_client->name, "at24cxx");
	i2c_attach_client(at24cxx_client);
	
	major = register_chrdev(0, "at24cxx", &at24cxx_fops);

	cls = class_create(THIS_MODULE, "at24cxx");
	class_device_create(cls, NULL, MKDEV(major, 0), NULL, "at24cxx"); /* /dev/at24cxx */
	
	return 0;
}

static int at24cxx_attach(struct i2c_adapter *adapter)
{
	return i2c_probe(adapter, &addr_data, at24cxx_detect);
}

static int at24cxx_detach(struct i2c_client *client)
{
	printk("at24cxx_detach\n");
	class_device_destroy(cls, MKDEV(major, 0));
	class_destroy(cls);
	unregister_chrdev(major, "at24cxx");

	i2c_detach_client(client);
	kfree(i2c_get_clientdata(client));

	return 0;
}


/* 1. 分配一个i2c_driver结构体 */
/* 2. 设置i2c_driver结构体 */
static struct i2c_driver at24cxx_driver = {
	.driver = {
		.name	= "at24cxx",
	},
	.attach_adapter = at24cxx_attach,
	.detach_client  = at24cxx_detach,
};

static int at24cxx_init(void)
{
	i2c_add_driver(&at24cxx_driver);
	return 0;
}

static void at24cxx_exit(void)
{
	i2c_del_driver(&at24cxx_driver);
}

module_init(at24cxx_init);
module_exit(at24cxx_exit);

MODULE_LICENSE("GPL");

在这个驱动程序里,首先会注册at24cxx_driver,这个注册会触发at24cxx_attach函数的执行,这个函数会通过 i2c_probe(adapter, &addr_data, at24cxx_detect)去触发内核框架函数检测设备地址addr_data是否能匹配成功,如果匹配成功则会调用at24cxx_detect函数。

在at24cxx_detect里,进行了字符设备驱动类和设备的创建,然后就是字符设备驱动那一套东西了,在at24cxx_read里构造好数据调用框架函数i2c_transfer进行数据读取,在at24cxx_write里构造好数据调用框架函数i2c_transfer进行数据写入。

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

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

相关文章

【完美复现】面向配电网韧性提升的移动储能预布局与动态调度策略【IEEE33节点】(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

机器学习实战8-基于XGBoost和LSTM的台风强度预测模型训练与应用

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下机器学习实战8-基于XGBoost和LSTM的台风强度预测模型训练与应用&#xff0c;今年夏天已经来了&#xff0c;南方的夏天经常会有台风登陆&#xff0c;给人们生活带来巨大的影响&#xff0c;本文主要基于XGBoost模型和…

Python3 错误和异常 | 菜鸟教程(十五)

目录 一、简述 二、语法错误 三、异常 四、异常处理 &#xff08;一&#xff09;try/except 1、异常捕捉可以使用 try/except 语句。 2、以下例子中&#xff0c;让用户输入一个合法的整数&#xff0c;但是允许用户中断这个程序&#xff08;使用 Control-C 或者操作系统提供…

ChatGPT从入门到精通学习路线

课程名称适应人群 ChatGPT从入门到精通&#xff0c;一站式掌握办公自动化/爬虫/数据分析和可视 点击上述名称&#xff0c;学习完整视频 全面AI时代就在转角&#xff0c;道路已经铺好了“局外人”or“先行者”就在此刻等你决定 1、对ChatGPT感兴趣并希望有人手把手教学的新手 …

apex/amp 安装问题解决

如何安装&#xff1a; 首先 pip uninstall apex卸载、apex文件夹也删除&#xff0c; 重新安装&#xff0c;具体安装步骤如下&#xff1a; git clone https://www.github.com/nvidia/apex cd apex python setup.py install --cuda_ext --cpp_ext 只要出现了下面的语句就是完成…

人工智能数学基础2:利用SciPy中的数值积分常用函数求定积分

利用SciPy中的数值积分常用函数求定积分 21/82.625 方法一&#xff1a;在 SciPy 库中&#xff0c;可以使用 quad 函数来进行数值积分。具体实现代码如下&#xff1a; from scipy.integrate import quaddef integrand(x):return x**2 1/x**4 # 定义被积函数result, error q…

vue+leaflet笔记之地图卷帘

vueleaflet笔记之地图卷帘 本文介绍了Web端使用Leaflet开发库实现地图卷帘效果的方法 (底图来源:中科星图)&#xff0c;结合leaflet-side-by-side插件可以快速简单地实现地图分屏对比效果 &#xff0c;示例效果如下图所示。 开发环境 Vue开发库&#xff1a;3.2.37 & Leaf…

FPGA-DFPGL22学习5-VERILOG

系列文章之 上章 FPGA-DFPGL22学习4-仿真平台学习 文章目录 系列文章之 上章前言一、Verilog 简介二、Verilog基础1.逻辑值2.进制3.标识符4.数据类型寄存器类型线网类型参数类型 5.运算符 三、Verilog 程序框架1.注释2.关键字3.程序框架 四、Verilog 程序语句1.结构语句2.赋值…

微信小程序+SpringBoot接入后台服务,接口数据来自后端

前言 前两天开发了一个微信小程序项目&#xff0c;只不过接口数据是自己设置的假数据。然后我就想将这些假数据替换掉。这些数据来自接口&#xff0c;之前做过前后端分离的项目&#xff0c;我就想能不能直接调用那些后端数据接口。结果是可以的。以下是自己编写的部分方法 步骤…

卷积神经网络--猫狗系列之下载、导入数据集

(由于是学习&#xff0c;所以文章会有一些报错及解决办法) 在Kaggle()获取数据集&#xff1a;&#xff08;没有账号先去注册一个账号&#xff0c;在注册时可能会出现的问题见Kaggle注册出现一排“Captcha must be filled out.”&#xff01;&#xff09; https://www.kaggle.…

DOM编程事件与简单编程

文章目录 事件绑定绑定式事件监听方法event 事件对象client、offsetkeyCode 键盘事件事件冒泡阻止浏览器默认行为 DOM编程用户名输入框长度限制鼠标移入移出复选框全选DOM编程实现动态时钟第一种&#xff1a;第二种&#xff1a;第三种&#xff1a; 事件绑定 DOM编程中的事件&a…

excel只显示想要的内容

是 后面的FG等列是不需要的&#xff0c;选择F列&#xff0c;ctrl shift 右箭头 。选中后隐藏。

Ansible创建逻辑卷

Ansible创建逻辑卷&#xff1a; 环境准备&#xff1a; 清单文件&#xff1a; [dev] 192.168.110.129 [prod] 192.168.110.132 [all:vars] ansible_userroot ansible_passwordredhat磁盘准备&#xff1a; 在一台主机上添加一块sata接口的磁盘&#xff0c;一块不添加。 192.…

Linux--获取一长串目录的结构指令:tree

注意&#xff1a;这个tree指令不是Linux自带的&#xff0c;需要下载 yum install -y tree (-y的作用是免确定) 示例&#xff1a;

UE5 读写本地JSON,发送HTTP请求(get)

UE5 读写本地JSON&#xff0c;发送HTTP请求&#xff08;get&#xff09; 没有使用插件&#xff0c;就用UE提供的库开发&#xff08;推荐使用插件VaRest在虚幻商城里有&#xff09; PCHUsage PCHUsageMode.UseExplicitOrSharedPCHs;PublicDependencyModuleNames.AddRange(new …

win10 编译hadoop源码报错

报错信息&#xff1a; 信息: 用提供的模式无法找到文件。 "devenv command was not found. Verify your compiler installation level."解决方案 右键&#xff0c;以管理员身份打开 之后再次执行mvn 命令即可. 也可以再打开的时候先执行一下命令&#xff1a; dev…

数字化是信息化的升级吗?数字化信息化这两者有什么联系和区别?

也可以这样说吧&#xff0c;但总是太抽象&#xff0c;不准确的&#xff0c;两者还是有很大区别的。 首先来看信息化、数字化具体是什么意思 想要明白两者之间的差异&#xff0c;首先要搞清楚他们的出处—— 所谓信息化、数字化还有现在提很多的智能化&#xff0c;其实都是从…

如何免费将springboot+vue项目部署上线(云服务器+宝塔面板)

本文整个流程是在博主完成一次项目上线全过程后复盘的记录&#xff0c;有没有写到的细节不清楚的可以私聊提问&#xff0c;这里选用的是阿里云服务器&#xff0c;阿里云对学生用户可以免费使用一个月服务器&#xff0c;不定期也有活动&#xff0c;我白嫖了7个月服务器&#xff…

ZYNQ——脉宽调制之呼吸灯实现

文章目录 原理简介实验代码软件仿真板上验证 原理简介 呼吸灯的实现过程就是把不同占空比的脉冲输出后加在LED上&#xff0c;LED灯就会显示不同的亮度&#xff0c;通过不断地调节方波的占空比&#xff0c;LED灯的亮度也会跟着变化&#xff0c;看起来就像是“呼吸”一样。 要得…

陪诊小程序系统|陪诊软件开发|陪诊系统功能和特点

随着医疗服务的逐步改善和完善&#xff0c;越来越多的人群开始走向医院就诊&#xff0c;而其中不少人往往需要有人陪同前往&#xff0c;这就导致了许多矛盾与问题的发生&#xff0c;比如长时间等待、找不到合适的陪诊人员等。因此为人们提供一种方便快捷的陪诊服务成为了一种新…