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

news2024/11/18 20:27:49

目录

概述

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 here
    err = 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 here
    err = 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_COMPILE

obj-m:= drv_15_at24cxx.o

all:
	$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules


clean:
	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-strip


test_15_at24cxx: test_15_at24cxx.o
	$(CC) $(CFLAGS) -o test_15_at24cxx test_15_at24cxx.o
	$(STRIP) -s test_15_at24cxx

clean:
	rm -f test_15_at24cxx test_15_at24cxx.o

4.3 编译和运行测试代码

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

运行该程序后可以看见:

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

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

相关文章

Win11蓝屏开不了机进入安全模式的快速方法

最近&#xff0c;很多使用Win11电脑的用户都在反映自己遇到了蓝屏问题&#xff0c;这时候想通过进入系统的安全模式&#xff0c;来解决电脑蓝屏的问题&#xff0c;却不知道进入安全模式的具体操作方法&#xff0c;下面给大家介绍最简单快速的进入方法&#xff0c;帮助大家轻松解…

NDK的log.h使用__android_log_print报错app:buildCMakeDebug[x86_64]

org.gradle.api.tasks.TaskExecutionException: Execution failed for task :app:buildCMakeDebug[x86_64] 重点是 Execution failed for task :app:buildCMakeDebug[x86_64]. 我的代码&#xff1a; #include <android/log.h> #define LOG_TAG "MyJNI" #d…

游戏同步+游戏中的网络模块

原文链接&#xff1a;游戏开发入门&#xff08;九&#xff09;游戏同步技术_游戏数据同步机制流程怎么开发-CSDN博客 游戏开发入门&#xff08;十&#xff09;游戏中的网络模块_游戏开发组网-CSDN博客 3.同步技术的基本常识&#xff1a; a.同步给谁&#xff1f;某个用户&…

二叉树基础知识总结

目录 二叉树基础知识 概念 : 根节点的五个形态 : 特殊的二叉树 满二叉树 : 完全二叉树 : 二叉搜索树 : 平衡二叉搜索树 : 二叉树的性质 : 二叉树的存储结构 二叉树的顺序存储结构 二叉树的链式存储结构 二叉树的遍历方式 : 基础概念 前中后遍历 层序遍历 :…

解决Ultra 5 125H处理器核显使用solidworks卡顿问题

硬件环境&#xff1a;机械革命 无界14pro Ultra 5 125H 软件环境&#xff1a;windows11 solidworks2023 现象&#xff1a;在使用solidworks作图时&#xff0c;软件卡顿&#xff0c;鼠标无法拖动模型 解决办法&#xff1a; 1&#xff0c;下载并安装solidworks官方给出的修补程…

c语言经典测试题2

1.题1 我们来思考一下它的结果是什么&#xff1f; 我们来分析一下&#xff1a;\\是转义为字符\&#xff0c;\123表示的是一个八进制&#xff0c;算一个字符&#xff0c;\t算一个字符&#xff0c;加上\0&#xff0c;应该有13个&#xff0c;但是strlen只计算\0前的字符个数。所以…

3个脚本练习

1.判断当前磁盘剩余空间是否有20G&#xff0c;如果小于20G&#xff0c;则将报警邮件发送给管理员&#xff0c;每天检查一次磁盘剩余空间。 1.下载mailx服务并设置 yum install mailx -y vim /etc/mail.rc 将下列内容写入文件末尾即可 ​​​​​​​set from自己邮箱qq.…

【计算机网络】socket 网络套接字

网络套接字 一、端口号1. 认识端口号2. socket 二、认识TCP协议和UDP协议1. TCP协议2. UDP协议 三、网络字节序四、socket 编程1. socket 常见API2. sockaddr 结构3. 编写 UDP 服务器&#xff08;1&#xff09;socket()&#xff08;2&#xff09;bind()&#xff08;3&#xff0…

SQL注入之DNSLog外带注入

一、认识&#xff1a; 什么是dnslog呢&#xff1f; DNS就是域名解析服务&#xff0c;把一个域名转换成对应的IP地址&#xff0c;转换完成之后&#xff0c;DNS服务器就会有一个日志记录本次转换的时间、域名、域名对应的ip、请求方的一些信息&#xff0c;这个日志就叫DNSLog。…

单体微服务K8S笔记

单体微服务K8S笔记 https://blog.csdn.net/m0_48341969/article/details/126063832思路参考以上博客 //测试 https://gitee.com/yangbuyi/yi项目组织参考以上git 单体&#xff1a; 不特地介绍 微服务&#xff1a; rpc:远程过程调用 拆分&#xff0c;分别部署&#xff0…

无人机快递(物流)技术方案,无人机快递(物流)基础知识

无人机快递技术是一种利用无人机进行快递配送的先进技术。通过利用无人机&#xff0c;快递企业能够在偏远地区或难以通行的地区提供配送服务&#xff0c;同时提高配送效率并降低人力成本。 无人机基本情况 无人驾驶飞机简称“无人机”&#xff0c;是利用无线电遥控设备和自备的…

跨境电商选品推荐:如何在Shopee上找到热销商品?

在当今全球化的商业环境中&#xff0c;跨境电商成为越来越多企业和个人的选择。而在跨境电商中&#xff0c;选品是至关重要的一环&#xff0c;因为选对了产品&#xff0c;销售就迈出了成功的一步。在众多跨境电商平台中&#xff0c;Shopee作为新兴的明星平台&#xff0c;备受关…

pytest基本应用

文章目录 1.pytest安装2.用例运行规则3.常用参数断言运行参数用例控制setup和teardownini配置文件 4.常用插件5.pytest高阶用法用例跳过参数化 6.pytest之Fixture使用fixture使用装饰器usefixtures 7.pytest之conftest.py8.conftestfixtureyieldyield介绍前后置使用 1.pytest安…

Stable Diffusion 绘画入门教程(webui)-ControlNet(姿态预处理器openpose)

本片文章接着上篇文章ControlNet介绍他的控制类型&#xff0c;本篇介绍的预处理器为openpose 预处理器&#xff1a;openpose 模型&#xff1a;control_v11p_sd15_openpose 没下载模型的看上篇文章去下载一下哦&#xff0c;不然用不了 文章目录 一、干什么用的二、详细用法1、选…

船舶维保管理系统|基于springboot船舶维保管理系统设计与实现(源码+数据库+文档)

船舶维保管理系统目录 目录 基于springboot船舶维保管理系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、船舶列表 2、公告信息管理 3、公告类型管理 4、维保计划管理 5、维保计划类型管理 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、…

数字化转型导师坚鹏:国家、城市、行业与企业数字化转型

国家、城市、行业与企业数字化转型 ——从战略到执行 课程背景&#xff1a; 很多机构存在以下问题&#xff1a; 不清楚国家、城市、行业与企业数字化转型整体框架&#xff1f; 不清楚国家、城市、行业与企业数字化转型评估模型&#xff1f; 不清楚国家、城市、行业与企…

Git合并固定分支的某一部分至当前分支

在 Git 中&#xff0c;通常使用 git merge 命令来将一个分支的更改合并到另一个分支。如果你只想合并某个分支的一部分代码&#xff0c;可以使用以下两种方法&#xff1a; 1.批量文件合并 1.1.创建并切换到一个新的临时分支 首先&#xff0c;从要合并的源分支&#xff08;即要…

【Visual studio code下载与安装步骤】

简介&#xff1a;VS Code 是一款跨平台的免费代码编辑器&#xff0c;它支持多种编程语言&#xff0c;包括JavaScript、TypeScript、Python、C# 等&#xff0c;提供了丰富的插件和调试工具&#xff0c;可用于开发Web应用程序、桌面应用程序、移动应用程序等不同类型项目。 VS C…

Collection集合体系(ArrayList,LinekdList,HashSet,LinkedHashSet,TreeSet,Collections)

目录 一.Collection 二.List集合 三.ArrayList集合 四.LinkedList集合 五.Set集合 六.hashSet集合 七.LinkedHashSet集合 八.TreeSet集合 九.集合工具类Collections 集合体系概述 单列集合&#xff1a;Collection代表单列集合&#xff0c;每个元素&#…

Tomcat 学习之 Servlet

目录 1 Servlet 介绍 2 创建一个 Servlet 3 web.xml 介绍&#xff08;不涉及 filter 和 listener 标签&#xff09; 3.1 display-name 3.2 welcome-file-list 3.3 servlet 3.4 session-config 3.5 error-page 4 Tomcat 如何根据 URL 定位到 Servlet 5 执行 Servlet …