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

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的共享目录下。在板卡中运行该测试程序:

运行该程序后可以看见:

相关文章:

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 …...

基于机器学习的青藏高原高寒沼泽湿地蒸散发插补研究_王秀英_2022

基于机器学习的青藏高原高寒沼泽湿地蒸散发插补研究_王秀英_2022 摘要关键词 1 材料和方法1.1 研究区概况与数据来源1.2 研究方法 2 结果和分析2.1 蒸散发通量观测数据缺省状况2.2 蒸散发与气象因子的相关性分析2.3 不同气象因子输入组合下各模型算法精度对比2.4 随机森林回归模…...

Failed at the node-sass@4.14.1 postinstall script.

问题描述 安装sass # "node-sass": "^4.9.0" npm i node-sass报错如下 npm ERR! code ELIFECYCLE npm ERR! errno 1 npm ERR! node-sass4.14.1 postinstall: node scripts/build.js npm ERR! Exit status 1 npm ERR! npm ERR! Failed at the node-sass4…...

【鸿蒙系统学习笔记】网络请求

一、介绍 资料来自官网&#xff1a;文档中心 网络管理模块主要提供以下功能&#xff1a; HTTP数据请求&#xff1a;通过HTTP发起一个数据请求。WebSocket连接&#xff1a;使用WebSocket建立服务器与客户端的双向连接。Socket连接&#xff1a;通过Socket进行数据传输。 日常…...

LabVIEW风力机智能叶片控制系统

​LabVIEW风力机智能叶片控制系统 介绍了一种风力机智能叶片控制系统的开发。通过利用LabVIEW软件与CDS技术&#xff0c;该系统能够实时监测并调整风力机叶片的角度&#xff0c;优化风能转换效率。此项技术不仅提高了风力发电的稳定性和效率&#xff0c;而且为风力机的智能化管…...

HarmonyOS Stage模型 权限申请

配置声明权限 在module.json5配置文件中声明权限。不论是system_grant还是user_grant类型都需要声明权限&#xff0c;否则应用将无法获得授权。 {"module" : {// ..."requestPermissions":[{"name": "ohos.permission.DISCOVER_BLUETOOTH…...

标题:从预编译到链接:探索C/C++程序的翻译环境全貌

引言 在软件开发的世界里&#xff0c;我们通常会遇到两种不同的环境——翻译环境与运行环境。今天&#xff0c;我们将聚焦于前者&#xff0c;深入剖析C/C程序生命周期中至关重要的“翻译环境”&#xff0c;即从源代码到可执行文件这一过程中涉及的四个关键阶段&#xff1a;预编…...

深入理解单端模拟多路复用器DG406DW-E3 应用于高速数据采集、ATE系统和航空电子设备解决方案

DG406DW-E3是一款16通道单端模拟多路复用器设计用于将16个输入中的一个连接到公共端口由4位二进制地址确定的输出。应用包括高速数据采集、音频信号切换和路由、ATE系统和航空电子设备。高性能低功耗损耗使其成为电池供电和电池供电的理想选择远程仪器应用。采用44V硅栅CMOS工艺…...

Redis篇----第六篇

系列文章目录 文章目录 系列文章目录前言一、Redis 的持久化机制是什么?各自的优缺点?二、Redis 常见性能问题和解决方案:三、redis 过期键的删除策略?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章…...

【LeetCode】509. 斐波那契数(简单)——代码随想录算法训练营Day38

题目链接&#xff1a;509. 斐波那契数 题目描述 斐波那契数 &#xff08;通常用 F(n) 表示&#xff09;形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始&#xff0c;后面的每一项数字都是前面两项数字的和。也就是&#xff1a; F(0) 0&#xff0c;F(1) 1 F(n) F(n -…...

[OpenGL教程05 ] glAccum() 函数对累积缓存设置

Accumulation Buffer&#xff1a;累积缓存 一、说明 openGL编程之所以困难&#xff0c;是因为它是三维图表示&#xff1b;简简单单加入一个Z轴&#xff0c;却使得几何遮挡、光线过度、运动随影等搞得尤其复杂。它的核心处理环节是像素缓存&#xff0c;本篇的积累缓存就是其一个…...

BeautifulSoup的使用与入门

1. 介绍 BeautifulSoup是用来从HTML、XML文档中提取数据的一个python库&#xff0c;安装如下: pip install beautifulsoup4 它支持多种解析器&#xff0c;包括python标准库、lxml HTML解析器、lxml XML解析器、html5lib等。结合稳定性和速度&#xff0c;这里推荐使用lxml HT…...

LLM之RAG实战(二十七)| 如何评估RAG系统

有没有想过今天的一些应用程序是如何看起来几乎神奇地智能的&#xff1f;这种魔力很大一部分来自于一种叫做RAG和LLM的东西。把RAG&#xff08;Retrieval Augmented Generation&#xff09;想象成人工智能世界里聪明的书呆子&#xff0c;它会挖掘大量信息&#xff0c;准确地找到…...

Linux Docker 关闭开机启动

说说自己为什么需要关闭自启动&#xff1a;Linux中安装Docker后&#xff0c;自启动会占用80和443端口&#xff0c;然后使用自己的SSL认证&#xff0c;导致自己Nginx配置的SSL认证失效&#xff0c;网站通过https打开显示不安全。 Docker是一个容器化平台&#xff0c;它可以让开…...

处理器管理补充——线程

传送门&#xff1a;操作系统——处理器管理http://t.csdnimg.cn/avaDO 1.1 线程的概念 回忆&#xff1a;[未引入线程前] 进程有两个基本属性&#xff1a;拥有资源的独立单位、处理器调度和分配的基本单位。 引入线程以后&#xff0c;线程将作为处理器调度和运行的基本单位&…...

RESTful 风格是指什么

RESTful&#xff08;Representational State Transfer&#xff09;是一种基于 HTTP 协议的软件架构风格&#xff0c;用于设计网络应用程序的接口。它的设计理念是利用 HTTP 协议中的方法&#xff08;如 GET、POST、PUT、DELETE 等&#xff09;来对资源进行 CRUD&#xff0c;使得…...

Python 二维矩阵加一个变量运算该如何避免 for 循环

Python 二维矩阵加一个变量运算该如何避免 for 循环 引言正文方法1------使用 for 循环方法2------不使用 for 循环引言 今天写代码的时候遇到了一个问题,比如我们需要做一个二维矩阵运算,其中一个矩阵是 2x2 的,另一个是 2x1 的。在这个二维矩阵中,其中各个参数会随着一个…...

Nginx 配置详解

官网&#xff1a;http://www.nginx.org/ 序言 Nginx是lgor Sysoev为俄罗斯访问量第二的rambler.ru站点设计开发的。从2004年发布至今&#xff0c;凭借开源的力量&#xff0c;已经接近成熟与完善。 Nginx功能丰富&#xff0c;可作为HTTP服务器&#xff0c;也可作为反向代理服务…...

python读写文件操作的三大基本步骤

目录 基本步骤 常用函数 open()函数 close()函数 read()函数 readlines()函数 readline()函数 write()函数 writelines()函数 with语句 读写操作的应用&#xff1a; 拷贝文件 with 语句的嵌套 逐行拷贝 基本步骤 1. 打开文件&#xff1a;open(filepath, mode, en…...

《Go 简易速速上手小册》第3章:数据结构(2024 最新版)

文章目录 3.1 数组与切片&#xff1a;Go 语言的动态队伍3.1.1 基础知识讲解3.1.2 重点案例&#xff1a;动态成绩单功能描述实现代码扩展功能 3.1.3 拓展案例 1&#xff1a;数据分析功能描述实现代码扩展功能 3.1.4 拓展案例 2&#xff1a;日志过滤器功能描述实现代码扩展功能 3…...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

Linux链表操作全解析

Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表&#xff1f;1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)

HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

PHP和Node.js哪个更爽?

先说结论&#xff0c;rust完胜。 php&#xff1a;laravel&#xff0c;swoole&#xff0c;webman&#xff0c;最开始在苏宁的时候写了几年php&#xff0c;当时觉得php真的是世界上最好的语言&#xff0c;因为当初活在舒适圈里&#xff0c;不愿意跳出来&#xff0c;就好比当初活在…...

Oracle查询表空间大小

1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

可靠性+灵活性:电力载波技术在楼宇自控中的核心价值

可靠性灵活性&#xff1a;电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中&#xff0c;电力载波技术&#xff08;PLC&#xff09;凭借其独特的优势&#xff0c;正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据&#xff0c;无需额外布…...

在Ubuntu24上采用Wine打开SourceInsight

1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

WPF八大法则:告别模态窗口卡顿

⚙️ 核心问题&#xff1a;阻塞式模态窗口的缺陷 原始代码中ShowDialog()会阻塞UI线程&#xff0c;导致后续逻辑无法执行&#xff1a; var result modalWindow.ShowDialog(); // 线程阻塞 ProcessResult(result); // 必须等待窗口关闭根本问题&#xff1a…...

tauri项目,如何在rust端读取电脑环境变量

如果想在前端通过调用来获取环境变量的值&#xff0c;可以通过标准的依赖&#xff1a; std::env::var(name).ok() 想在前端通过调用来获取&#xff0c;可以写一个command函数&#xff1a; #[tauri::command] pub fn get_env_var(name: String) -> Result<String, Stri…...