linux platform架构下I2C接口驱动开发

目录

概述

1 认识I2C协议

1.1 初识I2C

1.2 I2C物理层

1.3 I2C协议分析

1.3.1 Start、Stop、ACK 信号

1.3.2 I2C协议的操作流程

1.3.3 操作I2C注意的问题

2 linux platform驱动开发

2.1 更新设备树

2.1.1 添加驱动节点

2.1.2 编译.dts

2.1.3 更新板卡中的.dtb

2.2 驱动程序设计要点

2.2.1  match设备节点

2.2.2 读写函数的注意点

2.2.2.1 读函数

2.2.2.1 写函数

3 驱动程序实现

3.1 编写驱动程序

3.2 编写Makefile 

3.3 编译驱动

4 测试

4.1 编写测试代码

4.2 编写测试程序的Makefile

4.3 编译和运行测试代码


概述

       本文主要详细介绍了I2C的知识,使用linux platform驱动架构开发一个基于i2c接口的驱动程序,其中包括编写和更新设备树文件,搭建驱动架构,编写驱动代码和测试代码。本文还是以AT24C02为例,介绍linux platform驱动下i2c类型设备驱动程序的设计方法。并介绍如何使用read和write函数来实现eeprom的读/写功能。 

1 认识I2C协议

1.1 初识I2C

     I2C 通讯协议(Inter-Integrated Circuit)是由 Philips 公司开发的一种简单、双向二线制同步串行总线, 只需要两根线即可在连接于总线上的器件之间传送信息。I2C 协议占用引脚特别少, 硬件实现简单, 可扩展型强, 现在被广泛地使用在系统内多个集成电路(IC)间的通讯。

1.2 I2C物理层

I2C 通讯设备之间的常用连接方式

物理层结构有如下特点:

1) 一条I2C总线上可以挂载多个设备,不同的设备地址必须不同

2)I2C总线由两条物理线路构成,分别为SCL和SDA,SCL为同步时钟线,SDA为数据线路

3)I2C可支持3中工作模式:标准模式(100k bit/ s),快速模式( 400k bit/ s),高速模式( 3.4M bit/ s)

1.3 I2C协议分析

完整的I2C工作时序图:

1.3.1 Start、Stop、ACK 信号

Start信号:

在空闲状态时,SDA为高电平,SCL也为高电平。当有数据需要传输时,Master首先发起start信号,SDA: 1-->0, SCL: 1

Stop信号:

数据传输完成后,SDA: 0-->1, SCL: 1

ACK信号:

      在I2C协议中,数据传输的单位为byte, 传输完成一个数据时,需要8个bit, 在第9个bit( SCL电平: 0-->1)时,SDA : 0。该信号为ACK信号。

1.3.2 I2C协议的操作流程

需要注意的是I2C协议传输数据以字节为单位,每个字节有8个bit,传输完成一个字节后,还会发发送一个响应信号,即ACK信号,所以,其完成一个byte传输,实际需要9个bit。

Step-1:   Master 发起Start信号 , SDA: 1---> 0, SCL: 1

Step-2: 传输数据,当SCL: 0 ->1, SDA发送一个bit,总共8个bit

Step-3:    ACK信号,SCL: 0->1, SDA 1->0

Step-4:    传送下一个数据(循环执行: step-2 - > step-3)

Step-5:    Master 发起Stop信号,SDA: 0--->1, SCL: 1

1.3.3 操作I2C注意的问题

1)空闲状态时,SDA=1, SCL1 =1

2)  SCL 电平 0 ->1变化后,高电平保持期间,SDA上的数据才为有效bit

2 linux platform驱动开发

2.1 更新设备树

2.1.1 添加驱动节点

AT24C02引脚和IMX.6ULL引脚对应关系:

AT24C02   IO IMX.6ULL PIN
SCLI2C2_SCL
SDAI2C2_SDA

.dts文件路径:/home/mftang/linux_workspace/study_atk_dl6y2c/kernel/atk-dl6u2c/arch/arm/boot/dts/imx6ull-14x14-evk.dts

在.dts文件中添加如下代码:

    at24c02: at24c02@50 {compatible = "atk-dl6y2c,at24c02";reg = <0x50>;};

其在imx6ull-14x14-evk.dts中位置:

2.1.2 编译.dts

编译.dts文件,并把编译生成的.dtb文件发送到NFS共享目录下,便于在板卡中操作该文件。

1)在内核根目录下使用如下命令编译.dts文件

make dtbs

2) 复制 .dtb 文件至NFS共享目录

cp arch/arm/boot/dts/imx6ull-14x14-emmc-4.3-480x272-c.dtb  /home/mftang/nfs/atk_dl6y2c/

2.1.3 更新板卡中的.dtb

复制.dtb文件到相应的运行目录,然后重新板卡

cp /mnt/atk_dl6y2c/imx6ull-14x14-emmc-4.3-480x272-c.dtb /run/media/mmcblk1p1

reboot板卡后,内核会重新读取.dtb文件。然后在/proc/device-tree目录下查看板卡device tree,使用如下命令:

cd /sys/bus/i2c/devices
ls

查看地址下设备名称

cat 1-0050/name

2.2 驱动程序设计要点

2.2.1  match设备节点

在板卡的.dts 文件中,定义的设备节点为:

在设备驱动,需要设计相应的匹配表来match该信息,驱动程序的代码如下:

static const struct of_device_id atk_dl6y2c_at24cxx[] = {{ .compatible = "atk-dl6y2c,at24c02" },{ },
};static const struct i2c_device_id at24c02_ids[] = {{ "xxxxyyy",  (kernel_ulong_t)NULL },{ /* END OF LIST */ }
};/*  platform_driver */
static struct i2c_driver at24cxx_driver = {.probe      = at24cxx_probe,.remove     = at24cxx_remove,.driver     = {.name   = "atk_at24cxx",.of_match_table = atk_dl6y2c_at24cxx,},.id_table = at24c02_ids,
};

2.2.2 读写函数的注意点

2.2.2.1 读函数

         为了实现随机读取EEPROM中的数据,在用户层需要传递一个地址字节,于是该接口设计如下:

int at24cxx_read( unsigned char address, unsigned char *buff, unsigned int len)
{int ret;unsigned char addrbuff[1];struct i2c_msg msg[2];struct i2c_client *client = at24cxxdev.client;addrbuff[0] = address;/* msg[0]为发送要读取的首地址 */msg[0].addr = client->addr;            /* at24c02 地址 */msg[0].flags = 0;                      /* 标记为发送数据 */msg[0].buf = addrbuff;                 /* 读取的首地址 */msg[0].len = 1;                         /* reg长度*//* msg[1]读取数据 */msg[1].addr = client->addr;             /* at24c02 地址 */msg[1].flags = I2C_M_RD;                /* 标记为读取数据*/msg[1].buf = buff;                      /* 读取数据缓冲区 */msg[1].len = len;                       /* 要读取的数据长度*/ret = i2c_transfer(client->adapter, msg, 2);mdelay(20);if(ret < 0){printk("i2c rd failed=%d len=%d\n",ret, len);}return ret;
}

       和设备层相关的read 函数中,使用copy_from_user, 以得到用户层传递进来的参数,具体实现如下:

static ssize_t at24cxx_drv_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{unsigned char tempbuff[size];unsigned char kernel_buf[1];int err, i;unsigned char addr;// get address hereerr = copy_from_user(kernel_buf, buf,1);addr = kernel_buf[0];at24cxx_read( addr, tempbuff, size );size = copy_to_user(buf, tempbuff, size);return size;
}
2.2.2.1 写函数

        要实现随机写AT24C02内存的功能,就需要写数据时,先传递给它一个地址,然后在写数据,所以在驱动程序是这样实现该功能的:
 

int at24cxx_write(  unsigned char *buff, unsigned int len)
{int ret;struct i2c_msg msg[1];struct i2c_client *client = at24cxxdev.client;/* msg[0]为发送要写的首地址 */msg[0].addr = client->addr;       /* at24c02 地址 */msg[0].flags = 0;                 /* 标记为发送数据 */msg[0].buf = buff;                /* 写的首地址 */msg[0].len = len;                 /* 数据长度*/ret = i2c_transfer(client->adapter, msg, 1);mdelay(20);if(ret < 0) {printk("i2c write failed=%d len=%d\n",ret, len);}return ret;
}

和driver 层相关的write函数如下,其中buff中的数据包含两部分:

 buf[0] : 为地址信息,

buf[1 ~ n ] :user层要写的data数据:

static ssize_t at24cxx_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{unsigned char kernel_buf[size];int err, i;size = copy_from_user(kernel_buf, buf, size);at24cxx_write(kernel_buf, size );return size;
}

3 驱动程序实现

3.1 编写驱动程序

创建一个.c 文件,编写代码。详细驱动代码如下:

/***************************************************************
Copyright  2024-2029. All rights reserved.
文件名     : drv_15_at24cxx.c
作者       : tangmingfei2013@126.com
版本       : V1.0
描述       : at24cxx 驱动程序
其他       : 无
日志       : 初版V1.0 2024/1/30  使用方法:
1) 在.dts文件中定义节点信息at24c02: at24c02@50 {compatible = "atk-dl6y2c,at24c02";reg = <0x50>;};2) 在驱动匹配列表 
static const struct of_device_id at24cxx_of_match[] = {{ .compatible = "atk-dl6y2c,at24c02" },{ } // Sentinel
};
***************************************************************/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ktime.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/irq.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/i2c.h>#define DEVICE_NAME      "at24cxx"     // dev/at24cxx/* at24cxxdev设备结构体 */
struct at24cxxstru_dev{dev_t   devid;                /* 设备号         */struct  cdev cdev;            /* cdev           */struct  class *class;         /* 类             */struct  device *device;       /* 设备           */int     major;                /* 主设备号       */struct  device_node *node;    /* at24cxx设备节点 */struct i2c_client *client;
};/* read or write at24cxx structure */static struct at24cxxstru_dev at24cxxdev;/*at24cxx driver 
*/
int at24cxx_read( unsigned char address, unsigned char *buff, unsigned int len)
{int ret;unsigned char addrbuff[1];struct i2c_msg msg[2];struct i2c_client *client = at24cxxdev.client;addrbuff[0] = address;/* msg[0]为发送要读取的首地址 */msg[0].addr = client->addr;            /* at24c02 地址 */msg[0].flags = 0;                      /* 标记为发送数据 */msg[0].buf = addrbuff;                 /* 读取的首地址 */msg[0].len = 1;                         /* reg长度*//* msg[1]读取数据 */msg[1].addr = client->addr;             /* at24c02 地址 */msg[1].flags = I2C_M_RD;                /* 标记为读取数据*/msg[1].buf = buff;                      /* 读取数据缓冲区 */msg[1].len = len;                       /* 要读取的数据长度*/ret = i2c_transfer(client->adapter, msg, 2);mdelay(20);if(ret < 0){printk("i2c rd failed=%d len=%d\n",ret, len);}return ret;
}int at24cxx_write(  unsigned char *buff, unsigned int len)
{int ret;struct i2c_msg msg[1];struct i2c_client *client = at24cxxdev.client;/* msg[0]为发送要写的首地址 */msg[0].addr = client->addr;       /* at24c02 地址 */msg[0].flags = 0;                 /* 标记为发送数据 */msg[0].buf = buff;                /* 写的首地址 */msg[0].len = len;                 /* 数据长度*/ret = i2c_transfer(client->adapter, msg, 1);mdelay(20);if(ret < 0) {printk("i2c write failed=%d len=%d\n",ret, len);}return ret;
}/*linux driver 驱动接口: 实现对应的open/read/write等函数,填入file_operations结构体
*/
static ssize_t at24cxx_drv_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{unsigned char tempbuff[size];unsigned char kernel_buf[1];int err, i;unsigned char addr;// get address hereerr = copy_from_user(kernel_buf, buf,1);addr = kernel_buf[0];at24cxx_read( addr, tempbuff, size );size = copy_to_user(buf, tempbuff, size);return size;
}static ssize_t at24cxx_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{unsigned char kernel_buf[size];int err, i;size = copy_from_user(kernel_buf, buf, size);at24cxx_write(kernel_buf, size );return size;
}static int at24cxx_drv_close(struct inode *node, struct file *file)
{printk(" %s line %d \r\n",  __FUNCTION__, __LINE__);return 0;
}static int at24cxx_drv_open(struct inode *inode, struct file *filp)
{return 0;
}/* 定义driver的file_operations结构体
*/
static struct file_operations at24cxx_fops = {.owner   = THIS_MODULE,.read    = at24cxx_drv_read,.write   = at24cxx_drv_write,.open    = at24cxx_drv_open,.release = at24cxx_drv_close,
};/* 1. 从platform_device获得GPIO* 2. gpio=>irq* 3. request_irq*/
static int at24cxx_probe( struct i2c_client *client, const struct i2c_device_id *id )
{printk("at24cxx driver and device was matched!\r\n");/* 1. 获得硬件信息 */at24cxxdev.client = client;/* register file_operations  */at24cxxdev.major = register_chrdev( 0, DEVICE_NAME,     /* device name */&at24cxx_fops);  /* create the device class  */at24cxxdev.class = class_create(THIS_MODULE, "at24cxx_class");if (IS_ERR(at24cxxdev.class)) {printk("%s line %d\n", __FUNCTION__, __LINE__);unregister_chrdev( at24cxxdev.major, DEVICE_NAME);return PTR_ERR( at24cxxdev.class );}/* 2. device_create */device_create( at24cxxdev.class, NULL, MKDEV( at24cxxdev.major, 0 ), NULL, DEVICE_NAME);        // device name return 0;
}static int at24cxx_remove(struct i2c_client *client)
{printk("%s line %d\n", __FUNCTION__, __LINE__);device_destroy( at24cxxdev.class, MKDEV( at24cxxdev.major, 0));class_destroy(at24cxxdev.class);unregister_chrdev(at24cxxdev.major, DEVICE_NAME);return 0;
}static const struct of_device_id atk_dl6y2c_at24cxx[] = {{ .compatible = "atk-dl6y2c,at24c02" },{ },
};static const struct i2c_device_id at24c02_ids[] = {{ "xxxxyyy",  (kernel_ulong_t)NULL },{ /* END OF LIST */ }
};/* 1. 定义platform_driver */
static struct i2c_driver at24cxx_driver = {.probe      = at24cxx_probe,.remove     = at24cxx_remove,.driver     = {.name   = "atk_at24cxx",.of_match_table = atk_dl6y2c_at24cxx,},.id_table = at24c02_ids,
};/* 2. 在入口函数注册platform_driver 
*/
static int __init at24cxx_init(void)
{int err;printk("%s line %d\n",__FUNCTION__, __LINE__);err = i2c_add_driver(&at24cxx_driver); return err;
}/* 3. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数*    卸载platform_driver*/
static void __exit at24cxx_exit(void)
{printk("%s line %d\n", __FUNCTION__, __LINE__);i2c_del_driver(&at24cxx_driver);
}/*4. 驱动入口和出口函数
*/
module_init(at24cxx_init);
module_exit(at24cxx_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("tangmingfei2013@126.com");

3.2 编写Makefile 

在驱动程序的同级目录下创建Makefile文件,然后编写代码 

PWD := $(shell pwd)KERNEL_DIR=/home/mftang/linux_workspace/study_atk_dl6y2c/kernel/atk-dl6u2c
ARCH=arm
CROSS_COMPILE=/home/ctools/gcc-linaro-4.9.4-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-export  ARCH  CROSS_COMPILEobj-m:= drv_15_at24cxx.oall:$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modulesclean:rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions *.order *.symvers

3.3 编译驱动

      使用Make命令编译驱动程序,然后将生成的.ko文件copy到NFS共享目录下,然后在板卡中安装该驱动。

使用 insmod 安装该驱动,安装成功后,会出现如下信息:

4 测试

编写一个测试程序,实现AT24CXX连续数据的读写功能

4.1 编写测试代码

创建一个.c文件,编写如下代码:

/***************************************************************
Copyright  2024-2029. All rights reserved.
文件名     : test_15_at24cxx.c
作者       : tangmingfei2013@126.com
版本       : V1.0
描述       : 测试at24cxx驱动程序
其他       : 无
日志       : 初版V1.0 2024/02/15
***************************************************************/
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/types.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <linux/fs.h>
#include <unistd.h>
#include <errno.h>
#include <assert.h>
#include <string.h>
#include <time.h>#define DEV_FILE                     "/dev/at24cxx"int main(void)
{int fd, ret;int i = 0;unsigned char databuff[9];unsigned char rdatabuff[8];fd = open(DEV_FILE, O_RDWR);if (fd == -1){printf("can not open file: %s \n", DEV_FILE);return -1;}printf("write to at24cxx:  \r\n ");for( i=0; i< sizeof(databuff); i++ ){databuff[i] = i;printf(" %x \t ", databuff[i]);}printf(" \r\n \r\n ");ret = write(fd, databuff, sizeof(databuff)); if( ret < 0 ){printf("%d %s %s i2c device write data failure: %s\n",__LINE__,  __FILE__, __FUNCTION__, strerror(errno));close(fd);return -1;}rdatabuff[0] = 0;   // 读数据,起始地址ret =  read( fd, rdatabuff, sizeof(rdatabuff));if( ret < 0 ){printf("%d %s %s i2c device read data failure: %s\n",__LINE__,  __FILE__, __FUNCTION__, strerror(errno));close(fd);return -1;}printf("read from at24cxx: \r\n ");for( i=0; i< sizeof(rdatabuff); i++ ){printf(" %x \t ", rdatabuff[i]);}printf(" \r\n \r\n ");close(fd);return 0;
}

4.2 编写测试程序的Makefile

在测试程序的同级目录下创建一个Makefile文件,实现如下代码:

CFLAGS= -Wall -O2
CC=/home/ctools/gcc-linaro-4.9.4-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc
STRIP=/home/ctools/gcc-linaro-4.9.4-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-striptest_15_at24cxx: test_15_at24cxx.o$(CC) $(CFLAGS) -o test_15_at24cxx test_15_at24cxx.o$(STRIP) -s test_15_at24cxxclean:rm -f test_15_at24cxx test_15_at24cxx.o

4.3 编译和运行测试代码

      使用make编译测试代码,然后将生成的可执行文件copy到NFS的共享目录下。在板卡中运行该测试程序:

运行该程序后可以看见:

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

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

相关文章

良好的 API 安全策略的重要性

根据 Cloudflare 2024 年 API 安全与管理报告&#xff0c;到 2024 年&#xff0c;API 请求占全球动态互联网流量的 57%&#xff0c;这证实 API 是现代软件开发的重要组成部分。但随着多年来它们的采用不断增加&#xff0c;相关的安全挑战也随之增加。 在过去两年中&#xff0c…

“目标检测”任务基础认识

“目标检测”任务基础认识 1.目标检测初识 目标检测任务关注的是图片中特定目标物体的位置。 目标检测最终目的&#xff1a;检测在一个窗口中是否有物体。 eg:以猫脸检测举例&#xff0c;当给出一张图片时&#xff0c;我们需要框出猫脸的位置并给出猫脸的大小&#xff0c;如…

Meta 发布 MMCSG (多模态智能眼镜对话数据集)

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

FFmpeg的HEVC解码器源代码学习笔记-1

一直想写一个HEVC的码流解析工具&#xff0c;看了雷神264码流解析工具&#xff0c;本来想尝试模仿写一个相似的265码流分析工具&#xff0c;但是发现265的解码过程和结构体和264的不太一样&#xff0c;很多结构体并没有完全暴露出来&#xff0c;没有想到很好的方法获得量化参数…

HAL STM32 HW I2C DMA + SSD1306/SH1106驱动示例

HAL STM32 HW I2C DMA SSD1306/SH1106驱动示例 &#x1f4cd;硬件I2C DMA驱动参考&#xff1a;https://blog.csdn.net/weixin_45065888/article/details/118225993 &#x1f516;本工程基于STM32F103VCT6&#xff0c;驱动程序独立&#xff0c;可以移植到任意STM32型号上使用。…

VSCODE中使用Django处理后端data和data models

链接&#xff1a; Python and Django tutorial in Visual Studio Code MVC的理解 在实际的程序中采用MVC的方式进行任务拆分。 Model&#xff08;模型&#xff09;负责封装应用程序的数据和业务逻辑部分。Model包含数据结构&#xff0c;数据处理逻辑以及相关的操作方法&#…

如何在debian上实现一键恢复操作系统?

在Debian或任何其他Linux发行版上实现一键恢复操作系统&#xff0c;需要创建一个系统镜像或快照&#xff0c;并设置一个简单的方法来从该镜像恢复。以下是创建和恢复系统的基本步骤&#xff1a; 1. 创建系统镜像&#xff1a; 使用像dd&#xff0c;rsync或专门的备份工具&#…

Kubernetes 卷存储 NFS | nfs搭建配置 原理介绍 nfs作为存储卷使用

1、NFS介绍 NFS&#xff08;Network File System&#xff09;是一种分布式文件系统协议&#xff0c;允许客户端远程访问服务器上的文件&#xff0c;实现数据共享。它整合多个存储设备为统一文件系统&#xff0c;方便数据存储和管理&#xff0c;支持负载均衡和故障转移&#xf…

【专利】专利缴费清单与汇款金额不一致的处理方法

缴纳专利年费时&#xff0c;很容易算错滞纳金。比如有个专利年费滞纳金应交690&#xff0c;结果我算成了660&#xff0c;报给财务转账660。第二天补录缴费信息时&#xff0c;汇款金额660&#xff0c;然后缴费清单填写总是690无法修改&#xff0c;导致无法进行下一步&#xff01…

【AI大模型】ChatGPT在地学、GIS、气象、农业、生态、环境等领域中的高级应用

以ChatGPT、LLaMA、Gemini、DALLE、Midjourney、Stable Diffusion、星火大模型、文心一言、千问为代表AI大语言模型带来了新一波人工智能浪潮&#xff0c;可以面向科研选题、思维导图、数据清洗、统计分析、高级编程、代码调试、算法学习、论文检索、写作、翻译、润色、文献辅助…

【MySQL】学习连接查询和案例演示

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-KOxr1rwR9cQTlydJ {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…

win32 汇编读文件

做了2个小程序&#xff0c;没有读成功&#xff1b;文件打开了&#xff1b; .386.model flat, stdcalloption casemap :noneinclude windows.inc include user32.inc includelib user32.lib include kernel32.inc includelib kernel32.lib include Comdlg32.inc includelib …

探索海洋世界,基于YOLOv7【tiny/l/x】不同系列参数模型开发构建海洋场景下海洋生物检测识别分析系统

前面的博文中&#xff0c;开发实践过海底相关生物检测识别的项目&#xff0c;对于海洋场景下的海洋生物检测则很少有所涉及&#xff0c;这里本文的主要目的就是想要开发构建基于YOLOv7不同系列参数模型的海洋场景下的海洋生物检测识别系统。 前文已有相关实践&#xff0c;感兴…

C#知识点-16(计算器插件开发、事件、递归、XML)

计算器插件开发 1、Calculator.cs using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace Calculator_DLL {//用来明确所有插件开发人员的开发规范public abstract class Calculator{public int N…

部署安装有道QanyThing

前提条件&#xff1a; 1、win10系统更新到最新的版本&#xff0c;系统版本最好为专业版本 winver 查看系统版本&#xff0c;内部版本要大于19045 2、CPU开启虚拟化 3、开启虚拟化功能&#xff0c;1、2、3每步完成后均需要重启电脑&#xff1b; 注&#xff1a;windows 虚拟…

(done) 如何判断一个矩阵是否可逆?

参考视频&#xff1a;https://www.bilibili.com/video/BV15H4y1y737/?spm_id_from333.337.search-card.all.click&vd_source7a1a0bc74158c6993c7355c5490fc600 这个视频里还暗含了一些引理 1.若 AX XB 且 X 和 A,B 同阶可逆&#xff0c;那么 A 和 B 相似。原因&#xff1…

Redis 工具类 与 Redis 布隆过滤器

Redis 工具类 1. 核心依赖 <!--redis--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency><groupId>com.google.guava…

如何修改docker容器的端口映射

要修改 Docker 容器的端口映射&#xff0c;你需要停止并删除现有的容器&#xff0c;然后使用新的端口映射重新运行容器。以下是详细步骤&#xff1a; 停止容器&#xff1a; 使用 docker stop 命令停止正在运行的容器。替换 <container_id> 为你要停止的容器的 ID 或者容器…

Python3零基础教程之Python解释器与开发环境搭建

大家好&#xff0c;我是千与编程&#xff0c;硕士毕业于北京大学&#xff0c;曾先后就职于字节跳动&#xff0c;京东等互联网大厂&#xff0c;目前在编程导航知识星球担任星球嘉宾&#xff0c;著有《AI算法毕设智囊袋》&#xff0c;《保姆级带你通关秋招教程》两大专栏。 今天开…

AndroidStudio 2024-2-21 Win10/11最新安装配置(Ktlion快速构建配置,gradle镜像源)

AndroidStudio 2024 Win10/11最新安装配置 教程目的&#xff1a; (从安装到卸载) &#xff0c;针对Kotlin开发配置&#xff0c;gradle-8.2-src/bin下载慢&#xff0c;以及Kotlin构建慢的解决 好久没玩AS了,下载发现装个AS很麻烦,就觉得有必要出个教程了(就是记录一下:嘻嘻) 因…