I2C通信协议(软件I2C和硬件I2C)
相比于之前学的异步全双工且需要两条通信线的串口通信,I2C则为同步半双工,仅需要一条通信线,全双工与半双工区别如下:
全双工(Full Duplex) | 半双工(Half Duplex) | |
数据传输方式 | 同时双向传输 | 交替单向传输 |
通道数量 | 两条独立通道(一条发送,一条接收) | 一条通道(或交替使用同一条通道) |
效率 | 高(同时发送和接收) | 较低(不能同时发送和接收) |
延迟 | 低(无需等待对方发送完毕) | 较高(需要等待对方发送完毕) |
成本 | 较高(需要更多硬件支持) | 较低(硬件需求较少) |
系统复杂度 | 较高(设计和维护较复杂) | 较低(设计和维护较简单) |
应用场景 | 电话通信、视频通话、实时数据传输等 | 对讲机通信、低功耗无线传感器网络等 |
I2C通信一共需要四根线:VCC,GND,SDA(数据线),SCL(时钟线)因为有时钟线,I2C通信为同步时序,因此对时间的要求不是那么严格,通常可以很好的用软件进行模拟;
需要用I2C通信的模块一般有:OLED显示屏,MPU6050(陀螺仪),AT24C02(存储器)
在串口通信中,通常为两个设备通过串口点对点的通信,而在I2C通信中,有一条I2C总线,多个设备挂载到总线上,同时进行通信,所以I2C又分为:一主多从,固定多主多从,可变多主多从
一主多从:通信时只有一个主机,而从机可以有多个,主机控制整个通信过程,包括初始化通信,开始通信,发送数据,接收数据,以及终止通信等等,在该过程中,主机就像一个教室里的班主任,而从机就像学生,学生的一切动作都需要听从班主任安排;
固定多主多从:通信时有多个主机(且主机数量以固定,其他从机不可变为主机),多个从机,通信时需要多个主机(只有一个起作用)中的一个跳出来充当主机,若多个主机同时想充当主机时,则需要进行总线仲裁,获胜的设备可以充当主机,在该过程中,多个主机就相当于多位老师一起在讲台上,正所谓一山不容二虎,老师们需要比较之后才能得到上台讲话的机会;
可变多主多从:通信时有多个主机(所有设备都可以充当主机),在通信时需要一个设备跳出来充当主机,在该过程中,所有设备相当于学生开始全坐在,需要发言时举手,然后站起来说话;
当然我们平常在使用stm32时只需要掌握一主多从模式就行了,多主多从模式了解就行
下面是一张一主多从的硬件电路图:
在硬件接线上所有设备SCL都要连一起,SDA同理;
为了避免两个设备同时输出且一个输出高电平,一个输出低电平导致的电源短路,SDA和SCL均要配置成开漏输出模式,SDA和SCL个添加一个上拉电阻(一般为4.7KΩ)此时为弱上拉;
SCL线从始至终都只能由主机控制,而SDA在主机接收数据和接收应答的时候可由从机暂时控制;
I2C时序基本单元:(SDA与SCL默认上拉为高电平)
起始条件:SCL高电平期间,SDA下降沿;
终止条件:SCL高电平期间,SDA上升沿;
发送一个字节:SCL低电平期间,主机将数据位依次放到SDA线上(高位先行),然后释放SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,循环上述过程8次,即可发送一个字节
接收一个字节:SCL低电平期间,从机将数据位依次放到SDA线上(高位先行),然后释放SCL,主机将在SCL高电平期间读取数据位,循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA,从机对SDA进行控制)
发送应答:主机在接收完一个字节之后,在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答
接收应答:主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA)
从机地址:用于主机确定从机设备的名字,每个从机都具有唯一的地址,从机地址具有7位和10位,通常用7位地址就够了,7位地址比较简单1且应用范围最广,例:MPU6050的地址为1101000,地址中分为可变部分和不可变部分,一般为最后几位,MPU6050的可变地址为最后一位,可以由AD0引脚确定,引脚接低电平,则它的地址为1101000,反之则为1101001,可变部分用于两个相同的模块在一条总线上时区分
I2C时序:
指定地址写:对于指定设备(Slave Address)在指定地址(Reg Address)下写入指定数据Data
时序逻辑:主机首先发送一个起始条件,然后发送一个字节(8位:7位地址加一位读写位(0为写入,1为读出)),之后从机给应答位(0为应答,1为非应答),主机再发送一个字节(此时为要操作的寄存器的地址),从机发送应答位,之后主机再向从机发送要写入的寄存器的数据了,从机产生非应答,最后主机产生停止条件
当前地址读:对于指定设备(Slave Address)在当前地址指针指示的地址下,读取从机数据
时序逻辑:主机首先发送一个起始条件,然后发送一个字节(地址加读写位),之后从机给应答位,主机接收一个字节,主机产生非应答,最后主机产生停止条件;通过与指定地址写对比,当前地址读缺少了主机发送操作的寄存器的地址,那么主机读取的数据来自从机的哪个寄存器?在从机中所有寄存器被分配到了一个线性区域中,一个单独的指针指向寄存器,这个指针上电默认指向0地址,每写入一个字节或读出一个字节后,该指针自动自增一次;由此可知,那么主机读取的数据就是当前指针指示的寄存器,并且指针自增加一;
例:若先调用一次指定地址写,操作的寄存器地址为0x19,那当前指针的值就变为0x1A了,再调用一次当前地址读,此时读取的数据就是0x1A寄存器中的数据了,并且由于指针自加一,下次再调用当前地址读就是读取0x1B的数据了;
指定地址读:由于当前地址读的应用范围有限,所以有了另一个时序:指定地址读,对于指定设备(Slave Address)在指定地址(Reg Address)下读取从机数据
指定地址读为指定地址写的前半部分刚指定地址还没开始写数据的时候,重新发送一个起始条件,然后执行当前地址读操作
时序逻辑:主机首先发送一个起始条件,然后发送一个字节(地址加读写位),之后从机给应答位,主机再发送一个字节(此时为要读取的寄存器的地址),从机发送应答位,之后重新发送起始条件,因为改变读写操作只能在起始条件的下一个字节的最后一位,然后发送一个字节(地址加读写位),之后主机接收一个字节,主机产生非应答,最后主机产生停止条件
软件I2C工程代码:
I2C初始化:
void MyI2C_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);
}
写SCL,SDA,读SDA:
void MyI2C_W_SCL(uint8_t BitValue)
{GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue);Delay_us(10);
}void MyI2C_W_SDA(uint8_t BitValue)
{GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue);Delay_us(10);
}uint8_t MyI2C_R_SDA(void)
{uint8_t BitValue;BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);Delay_us(10);return BitValue;
}
BitAction 为枚举类型,之中的值非0即1
起始条件:
void MyI2C_Start(void)
{MyI2C_W_SDA(1);MyI2C_W_SCL(1);MyI2C_W_SDA(0);MyI2C_W_SCL(0);
}
停止条件:
void MyI2C_Stop(void)
{MyI2C_W_SDA(0);MyI2C_W_SCL(1);MyI2C_W_SDA(1);
}
发送一个字节:
void MyI2C_SendByte(uint8_t Byte)
{uint8_t i;for (i = 0; i < 8; i ++){MyI2C_W_SDA(Byte & (0x80 >> i));MyI2C_W_SCL(1);MyI2C_W_SCL(0);}
}
接收一个字节:
uint8_t MyI2C_ReceiveByte(void)
{uint8_t i, Byte = 0x00;MyI2C_W_SDA(1);for (i = 0; i < 8; i ++){MyI2C_W_SCL(1);if (MyI2C_R_SDA() == 1){Byte |= (0x80 >> i);}MyI2C_W_SCL(0);}return Byte;
}
发送应答位:
void MyI2C_SendAck(uint8_t AckBit)
{MyI2C_W_SDA(AckBit);MyI2C_W_SCL(1);MyI2C_W_SCL(0);
}
接收应答位:
uint8_t MyI2C_ReceiveAck(void)
{uint8_t AckBit;MyI2C_W_SDA(1);MyI2C_W_SCL(1);AckBit = MyI2C_R_SDA();MyI2C_W_SCL(0);return AckBit;
}
MPU6050利用软件I2C通信:MPU6050从机地址为0xD0,所以将地址宏定义为MPU6050_ADDRESS
MPU6050指定地址写数据:
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{MyI2C_Start();MyI2C_SendByte(MPU6050_ADDRESS);MyI2C_ReceiveAck();MyI2C_SendByte(RegAddress);MyI2C_ReceiveAck();MyI2C_SendByte(Data);MyI2C_ReceiveAck();MyI2C_Stop();
}
MPU6050指定地址读数据:
uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{uint8_t Data;MyI2C_Start();MyI2C_SendByte(MPU6050_ADDRESS);MyI2C_ReceiveAck();MyI2C_SendByte(RegAddress);MyI2C_ReceiveAck();MyI2C_Start();MyI2C_SendByte(MPU6050_ADDRESS | 0x01);MyI2C_ReceiveAck();Data = MyI2C_ReceiveByte();MyI2C_SendAck(1);MyI2C_Stop();return Data;
}
MPU6050寄存器宏定义:
#define MPU6050_SMPLRT_DIV 0x19
#define MPU6050_CONFIG 0x1A
#define MPU6050_GYRO_CONFIG 0x1B
#define MPU6050_ACCEL_CONFIG 0x1C#define MPU6050_ACCEL_XOUT_H 0x3B
#define MPU6050_ACCEL_XOUT_L 0x3C
#define MPU6050_ACCEL_YOUT_H 0x3D
#define MPU6050_ACCEL_YOUT_L 0x3E
#define MPU6050_ACCEL_ZOUT_H 0x3F
#define MPU6050_ACCEL_ZOUT_L 0x40
#define MPU6050_TEMP_OUT_H 0x41
#define MPU6050_TEMP_OUT_L 0x42
#define MPU6050_GYRO_XOUT_H 0x43
#define MPU6050_GYRO_XOUT_L 0x44
#define MPU6050_GYRO_YOUT_H 0x45
#define MPU6050_GYRO_YOUT_L 0x46
#define MPU6050_GYRO_ZOUT_H 0x47
#define MPU6050_GYRO_ZOUT_L 0x48#define MPU6050_PWR_MGMT_1 0x6B
#define MPU6050_PWR_MGMT_2 0x6C
#define MPU6050_WHO_AM_I 0x75
MPU6050初始化:
void MPU6050_Init(void)
{MyI2C_Init();//软件I2C引脚初始化MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);//配置电源管理器1(解除睡眠模式)MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);//配置电源管理器2MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09);//配置采样率分频--十分频(值越小越快)MPU6050_WriteReg(MPU6050_CONFIG, 0x06);//配置外部同步和数字低通滤波寄存器MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);//配置陀螺仪寄存器MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);//配置量程寄存器
}
MPU6050接收数据:(在主函数里传六个变量的地址达到返回六个值的目的,还可以将这六个变量打包成一个结构体,在主函数里直接传结构体的地址即可)
void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
{uint8_t DataH, DataL;DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);*AccX = (DataH << 8) | DataL;DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);*AccY = (DataH << 8) | DataL;DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);*AccZ = (DataH << 8) | DataL;DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);*GyroX = (DataH << 8) | DataL;DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);*GyroY = (DataH << 8) | DataL;DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);*GyroZ = (DataH << 8) | DataL;
}
在主函数里接收数据:
int16_t AX, AY, AZ, GX, GY, GZ;
MPU6050_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ);
MPU6050读取ID号:
uint8_t MPU6050_GetID(void)
{return MPU6050_ReadReg(MPU6050_WHO_AM_I);
}
硬件I2C:
硬件I2C简介:
I2C基本结构:数据的收发由数据寄存器和移位寄存器来控制,并且在移位寄存器中高位先行
硬件I2C工程代码:由于硬件I2C有自己的库函数,所以不需要用户自己再建立I2C模块了,只需初始化对应GPIO口(复用开漏输出)
在软件模拟I2C中,每个时序都是加了延时的阻塞型,而在硬件I2C中,每完成一步,产生相应的标志位,可以通过判断标志位的状态来控制时序的进行,而有的状态需要多个标志位,所以在硬件I2C中将标志位融合成了事件,即通过判断事件是否发生即可知道时序的进行状态,在判断事件的产生时有许多的while死循环,一旦某个环节出错了之后,程序就会卡死,所以检查事件的产生还要加上一个超时判断
MPU6050事件是否产生:
void MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{uint32_t Timeout;Timeout = 10000;while (I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS){Timeout --;if (Timeout == 0){break;}}
}
MPU6050指定地址写数据:
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{I2C_GenerateSTART(I2C2, ENABLE);MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);I2C_SendData(I2C2, RegAddress);MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING);I2C_SendData(I2C2, Data);MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);I2C_GenerateSTOP(I2C2, ENABLE);
}
MPU6050指定地址读数据:
uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{uint8_t Data;I2C_GenerateSTART(I2C2, ENABLE);MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);I2C_SendData(I2C2, RegAddress);MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);I2C_GenerateSTART(I2C2, ENABLE);MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Receiver);MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);I2C_AcknowledgeConfig(I2C2, DISABLE);I2C_GenerateSTOP(I2C2, ENABLE);MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED);Data = I2C_ReceiveData(I2C2);I2C_AcknowledgeConfig(I2C2, ENABLE);return Data;
}
MPU6050初始化:
void MPU6050_Init(void)
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);I2C_InitTypeDef I2C_InitStructure;I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;I2C_InitStructure.I2C_ClockSpeed = 50000;//配置I2C时钟频率I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;//配置I2C时钟占空比I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;//配置I2C应答位使能I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//从机地址7位还是10位I2C_InitStructure.I2C_OwnAddress1 = 0x00;//从机地址I2C_Init(I2C2, &I2C_InitStructure);I2C_Cmd(I2C2, ENABLE);MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09);MPU6050_WriteReg(MPU6050_CONFIG, 0x06);MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);
}
相关文章:

I2C通信协议(软件I2C和硬件I2C)
相比于之前学的异步全双工且需要两条通信线的串口通信,I2C则为同步半双工,仅需要一条通信线,全双工与半双工区别如下: 全双工(Full Duplex)半双工(Half Duplex)数据传输方式同时双向…...

Linux入门——08 进程间通讯——管道
1.进程间通讯 1.1什么是通讯 进程具有独立性(每个进程都有自己的PCB,独立地址空间,页表)但是要进行进程的通信,通信的成本一定不低,打破了独立性 进程间通信目的 数据传输:一个进程需要将它的数据发送给…...

深入探讨SD NAND的SD模式与SPI模式初始化
在嵌入式系统和存储解决方案中,SD NAND的广泛应用是显而易见的。CS创世推出的SD NAND支持SD模式和SPI模式,这两种模式在功能和实现上各有优劣。在本文中,我们将深入探讨这两种模式的初始化过程,并比较它们在不同应用场景下的优劣&…...
【jvm】栈和堆的区别
目录 1. 用途2. 线程共享性3. 内存分配和回收4. 生命周期5. 性能特点 1. 用途 1.堆:主要用于存储对象实例和数组。在Java中,所有通过new关键字创建的对象都会被分配到堆上。堆是一个大的内存池,用于存储所有的Java对象,包括实例变…...
智能的意义是降低世界的不确定性
世界充满着不确定性,而智能天生就追求一定的确定性,因为不确定性会危及智能的生存。智能本身是一种有序、相对确定的结构产生的,虽然也有一定的不确定性,而且这些不确定性有利于智能的进化,但是,相对而言&a…...

python实现指数平滑法进行时间序列预测
python实现指数平滑法进行时间序列预测 一、指数平滑法定义 1、指数平滑法是一种常用的时间序列预测算法,有一次、二次和三次平滑,通过加权系数来调整历史数据权重; 2、主要思想是:预测值是以前观测值的加权和,且对不同的数据给予不同的权数,新数据给予较大的权数,旧数…...

linux文件——用户缓冲区——概念深度探索、IO模拟实现
前言:本篇文章主要讲解文件缓冲区。 讲解的方式是通过抛出问题, 然后通过分析问题, 将缓冲区的概念与原理一步一步地讲解。同时, 本节内容在最后一部分还会带友友们模拟实现一下c语言的printf, fprintf接口,…...

Hive3:常用查询语句整理
一、数据准备 建库 CREATE DATABASE itheima; USE itheima;订单表元数据 1 1000000 100058 6 -1 509.52 0.00 28155.40 499.33 0 0 lisi shanghai 157 2019-06-22 17:28:15 2019-06-22 17:28:15 1 2 5000000 100061 72 -1 503.86 0.00 38548.00 503.86 1 0 zhangsan shangha…...

Ubuntu下载安装教程|Ubuntu最新长期支持(LTS)版本24.04 LTS下载安装
安装Ubuntu Ubuntu最新长期支持(LTS)版本24.04 LTS Ubuntu 24.04 LTS | 概览 Ubuntu长期支持(LTS)版本,LTS意为“长期支持”,一般为5年。LTS版本将提供免费安全和维护更新至 2029年4月。 Ubuntu 24.04 LTS(代号“Noble Numbat”,…...

通知:《自然语言及语音处理设计开发工程师》即将开课!
自然语言及语音处理设计开发工程师:未来职业的黄金选择 下面我们来看看证书颁发的背景: 为进一步贯彻落实中共中央印发《关于深化人才发展体制机制改革的意见》和国务院印发《关于“十四五”数字经济发展规划》等有关工作的部署要求,深入实…...

Vim youcompleteme Windows 安装保姆级教程
不说废话。 准备 检查 Vim 的 Python 配置 安装好 vim 和 python 后(python 必须 ≥ \ge ≥ 3.6),在 cmd 下运行 vim --version会弹出以下窗口。 如果发现 python/dyn 和 python3/dyn 都是 - (我不知道只有前者是 能不能运行…...
港迪技术IPO提交注册,拟募资6.56亿元
武汉港迪技术股份有限公司(下称“港迪技术”)拟在创业板IPO上市,并于近期在深交所提交招股书(注册稿),进入提交注册阶段。 港迪技术IPO招股书(注册稿)显示,公司是一家专…...
retinaface在ubuntu20.04(wsl2)下使用tensorrt(c++)部署
1. 参考博客: 1. Retinaface Tensorrt Python/C部署:https://blog.csdn.net/weixin_45747759/article/details/124534079 2. B站视频教程:https://www.bilibili.com/video/BV1Nv4y1K727/ 3. Retinaface_…...

vue打包设置 自定义的NODE_ENV
默认NODE_ENV 自定义process.env.NODE_ENV的值_process.node.env的值-CSDN博客 NODE_ENV开发环境下:NODE_ENVdevelopment(默认) 生产环境下:NODE_ENVproduction(默认) NODE_ENV 除了默认的 development 和 production 以外,确实可以自定义…...

python爬虫521
爬虫521 记录 记录 最近想学爬虫,尝试爬取自己账号下的文章标题做个词云 csdn有反爬机制 原理我就不说啦 大家都写了 看到大家结果是加cookie 但是我加了还是521报错 尝试再加了referer 就成功了(╹▽╹) import matplotlib import requests from wordcloud impor…...
CSS中flex:1是什么属性
flex: 1 是 CSS 中的一个简写属性,用于设置 Flex 项目的灵活伸缩比例(flex-grow)、收缩比例(flex-shrink)以及基础大小(flex-basis)。具体来说,flex: 1 实际上是以下三个属性的简写&…...
网络硬件升级指南:提升性能的策略与实践
随着企业对网络依赖程度的增加,网络性能的提升已成为信息技术部门的首要任务。本文将探讨如何通过升级网络硬件来提高网络性能,包括选择正确的硬件、实施升级策略和考虑未来网络的可扩展性。 一、网络性能的重要性 在数字化时代,网络是企业…...

XSS-过滤特殊符号的正则绕过
目录 靶场练习地址:https://xss.pwnfunction.com/ 题目源码: 代码分析: 方法一:匿名函数 方法二:使用eval函数绕过限制 示例: 方法三:利用hash绕过 靶场练习地址:https://xs…...

CocosCreator3.8 IOS 构建插屏无法去除的解决方案
CocosCreator3.8 IOS 构建插屏无法去除的解决方案 在实际项目开发过程中,我们通常无需CocosCreator 自带的插屏,一般采用自定义加载页面。 然后在构建IOS 项目时,启用(禁用)插屏无法操作,如下图所示&#…...

Linux软件编程---数据库
目录 一、数据库 1.1.概念 1.2.类型 1.关系型数据库 2.非关系型数据库 1.3.SQL语言 1.4.如何在Linux安装sqlite数据库 1.确保虚拟机可以上网 2.配置apt-get工具集合 3.安装sqlite数据库 1.5.sqlite3 1.创建数据库 2.查看数据表 3.退出数据库 4.SQL语句 二、数…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件
在选煤厂、化工厂、钢铁厂等过程生产型企业,其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进,需提前预防假检、错检、漏检,推动智慧生产运维系统数据的流动和现场赋能应用。同时,…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...

Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...

群晖NAS如何在虚拟机创建飞牛NAS
套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...

Axure 下拉框联动
实现选省、选完省之后选对应省份下的市区...