软件I2C

软件I2C

在这里插入图片描述
注意:
SDA(串行数据线)和SCL(串行时钟线)都是双向I/O线,接口电路为开漏输出。需通过上拉电阻接电源VCC。

软件I2C说明

说明,有的单片机没有硬件I2C的功能,或者因为电路设计失误不得不使用软件I2C。
软件I2C虽然引脚设定灵活,但效果自然是比不上硬件I2C的……
本人按照STM32的硬件I2C风格编写了软件I2C,这样便于移植更换

具体来说:
虽然软件I2C(也称为Bit-banging I2C)提供了极大的灵活性,允许开发者在任意GPIO引脚上实现I2C通信,但它也有几个明显的缺点:

  1. CPU占用率高:由于所有的I2C协议处理(如时序控制、信号的高低电平转换等)都由CPU直接管理,而不是专用硬件模块完成,因此会占用大量的CPU资源。这意味着,在进行I2C通信时,CPU不能执行其他任务,除非通过中断或DMA等方式来部分缓解这个问题。

  2. 速度受限:由于需要依靠CPU指令来精确地控制时序,软件I2C的速度通常比硬件I2C慢得多。特别是在高速模式下(例如I2C Fast-mode Plus,速度可达1MHz),准确地生成和检测信号变得更加困难,可能无法达到标准要求的速度。

  3. 时序精度问题:不同CPU架构和运行频率下的指令周期时间不同,这使得编写可移植性好的软件I2C代码变得复杂。此外,中断或其他系统活动可能会干扰软件I2C的时序,导致通信失败。

  4. 可靠性较低:与硬件I2C相比,软件I2C更容易受到电磁干扰(EMI)和其他电气噪声的影响,因为其不具备硬件级别的过滤和错误纠正能力。同时,软件实现难以完全保证严格的I2C协议时序要求,尤其是在复杂的系统环境中。

  5. 不支持高级特性:许多现代微控制器的硬件I2C接口支持诸如多主控、时钟扩展(clock stretching)、中断驱动传输、DMA支持等高级特性。而这些特性通常很难或者不可能通过软件I2C来实现。

  6. 开发和维护成本:虽然对于简单的应用来说,软件I2C可以快速实现,但对于更复杂的应用场景,如需要支持多种速率、处理各种异常情况等,则需要更多的开发工作,并且调试起来也更加困难。

综上所述,尽管软件I2C在某些特定情况下(比如当硬件I2C引脚已被占用,或者需要在多个不同的GPIO引脚上实现I2C通信)非常有用,但考虑到性能、稳定性和功能完整性等方面,硬件I2C通常是更好的选择。如果条件允许,使用硬件I2C可以减少开发时间和提高系统的整体性能。

软件I2C代码

注意,根据自己使用的单片机修改IO口输入输出的相关实现!
SDA(串行数据线)和SCL(串行时钟线)都是双向I/O线,接口电路为开漏输出。需通过上拉电阻接电源VCC。
实现软件微秒级延时的代码

微秒级延时头文件

#pragma once#ifdef __cplusplus
extern "C" {
#endif#include <stdint.h>//移植时请根据自身单片机修改/*** @brief 软件微秒级延时* * @param us 微秒数* * @note 需要根据系统频率简单修改* @note 不怎么准,但一般不需要很准* @note 准确的延时需要借助硬件定时器等*/
void softDelayMicro(uint8_t us);#ifdef __cplusplus
}
#endif

微秒级延时源文件

#include "soft_delay.h"// 定义每微秒需要执行的大约循环次数
// 注意:这个值可能需要根据实际情况进行调整
// 例如我的stm32f405频率168MHz
#define DELAY_MICROSECOND_LOOP_COUNT (168 / 4)#pragma GCC push_options    //禁止编译器优化这段代码
#pragma GCC optimize ("O0")void softDelayMicro(uint8_t us)
{while (us--) {volatile uint32_t counter = DELAY_MICROSECOND_LOOP_COUNT;while (counter--);}
}#pragma GCC pop_options

软件I2C头文件

/*** @file soft_I2C.h* @author your name (you@domain.com)* @brief 软件I2C* @version 0.1* @date 2025-05-12* * @copyright Copyright (c) 2025* * @note //根据你的单片机修改IO口的输入输出的相关内容!* @note SDA(串行数据线)和SCL(串行时钟线)都是双向I/O线,接口电路为开漏输出。*/#pragma once#ifdef __cplusplusextern "C" {#endif#include <stdbool.h>#include "gpio.h"//根据你的单片机修改IO口的输入输出的相关内容!#include "soft_delay.h"/*** @brief 保存某个I2C总线使用的端口引脚信息* * @note SDA(串行数据线)和SCL(串行时钟线)都是双向I/O线,接口电路为开漏输出。*/typedef struct{GPIO_TypeDef* SCL_Port; //时钟线端口uint32_t SCL_Pin;       //时钟线引脚GPIO_TypeDef* SDA_Port; //数据线端口uint32_t SDA_Pin;       //数据线引脚}Soft_I2C_Handle;/*** @brief 设置软件I2c使用的端口和引脚* * @param si2c 指向Soft_I2C_Handle结构体的指针,该结构体包含指定软件I2C的配置信息。* @param scl_port 时钟线端口* @param scl_pin 时钟线引脚* @param sda_port 数据线端口* @param sda_pin 数据线引脚* * @note 请先完成对应端口的初始化配置,然后再使用这个函数* @note SDA(串行数据线)和SCL(串行时钟线)都是双向I/O线,接口电路为开漏输出。*/void Soft_I2C_Config(Soft_I2C_Handle* si2c, GPIO_TypeDef* scl_port, uint32_t scl_pin, GPIO_TypeDef* sda_port, uint32_t sda_pin);/*** @brief 软件I2C以主模式发送一定数量的数据。** @param si2c 指向Soft_I2C_Handle结构体的指针,该结构体包含指定软件I2C的配置信息。* @param DevAddress 目标设备地址:在调用接口之前,必须将数据表中的设备7位地址值左移1位* @param pData 指向数据缓冲区的指针* @param Size 要发送的数据量* @return true 成功* @return false 失败*/bool Soft_I2C_Master_Transmit(Soft_I2C_Handle* si2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);/*** @brief 软件I2C以主模式接收一定数量的数据。* * @param si2c 指向Soft_I2C_Handle结构体的指针,该结构体包含指定软件I2C的配置信息。* @param DevAddress 目标设备地址:在调用接口之前,必须将数据表中的设备7位地址值左移1位* @param pData 指向数据缓冲区的指针* @param Size 要接收的数据量* @return true 成功* @return false 失败*/bool Soft_I2C_Master_Receive(Soft_I2C_Handle* si2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);/** @defgroup I2C_Memory_Address_Size I2C Memory Address Size* @{*/#define SOFT_I2C_MEMADD_SIZE_8BIT            0x00000001U#define SOFT_I2C_MEMADD_SIZE_16BIT           0x00000010U/*** @brief 软件I2C以阻塞方式从特定内存地址读取一定数量的数据* * @param si2c 指向Soft_I2C_Handle结构体的指针,该结构体包含指定软件I2C的配置信息。* @param DevAddress 目标设备地址:在调用接口之前,必须将数据表中的设备7位地址值左移1位* @param MemAddress 目标设备内部存储器地址* @param MemAddSize 目标设备内部存储器地址的大小,只能是SOFT_I2C_MEMADD_SIZE_8BIT或者SOFT_I2C_MEMADD_SIZE_16BIT* @param pData 指向数据缓冲区的指针* @param Size 要接收的数据量* @return true 成功* @return false 失败* */bool Soft_I2C_Mem_Read(Soft_I2C_Handle *si2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size);/*** @brief 软件I2C以阻塞方式将一定数量的数据写入特定的内存地址* * @param si2c 指向Soft_I2C_Handle结构体的指针,该结构体包含指定软件I2C的配置信息。 * @param DevAddress 目标设备地址:在调用接口之前,必须将数据表中的设备7位地址值左移1位* @param MemAddress 目标设备内部存储器地址* @param MemAddSize 目标设备内部存储器地址的大小,只能是SOFT_I2C_MEMADD_SIZE_8BIT或者SOFT_I2C_MEMADD_SIZE_16BIT* @param pData 指向数据缓冲区的指针* @param Size 要发送的数据量* @return true 成功* @return false 失败* */bool Soft_I2C_Mem_Write(Soft_I2C_Handle *si2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size);#ifdef __cplusplus}#endif

软件I2C源文件

/*** @file soft_I2C.c* @author your name (you@domain.com)* @brief 软件I2C* @version 0.1* @date 2025-05-12* * @copyright Copyright (c) 2025* */#include "soft_I2C.h"/****************************** 1. 配置函数 *****************************************///移植时请根据自身单片机修改void Soft_I2C_Config(Soft_I2C_Handle* si2c, GPIO_TypeDef* scl_port, uint32_t scl_pin, GPIO_TypeDef* sda_port, uint32_t sda_pin)
{si2c->SCL_Port = scl_port;si2c->SCL_Pin = scl_pin;si2c->SDA_Port = sda_port;si2c->SDA_Pin = sda_pin;
}/****************************** 2. 辅助宏/函数定义 *****************************************/#define I2C_PIN_HIGH GPIO_PIN_SET
#define I2C_PIN_LOW  GPIO_PIN_RESET//移植时请根据自身单片机修改static inline void SCL_Low(Soft_I2C_Handle* si2c) {HAL_GPIO_WritePin(si2c->SCL_Port, si2c->SCL_Pin, GPIO_PIN_RESET);
}static inline void SCL_High(Soft_I2C_Handle* si2c) {HAL_GPIO_WritePin(si2c->SCL_Port, si2c->SCL_Pin, GPIO_PIN_SET);
}static inline void SDA_Low(Soft_I2C_Handle* si2c) {HAL_GPIO_WritePin(si2c->SDA_Port, si2c->SDA_Pin, GPIO_PIN_RESET);
}static inline void SDA_High(Soft_I2C_Handle* si2c) {HAL_GPIO_WritePin(si2c->SDA_Port, si2c->SDA_Pin, GPIO_PIN_SET);
}static inline GPIO_PinState SDA_Read(Soft_I2C_Handle* si2c) {return HAL_GPIO_ReadPin(si2c->SDA_Port, si2c->SDA_Pin);
}/*** @brief 设置SDA引脚为输入* * @param si2c */
static void SDA_SetInput(Soft_I2C_Handle* si2c)
{GPIO_InitTypeDef GPIO_InitStruct = {0};GPIO_InitStruct.Pin = si2c->SDA_Pin;GPIO_InitStruct.Mode = GPIO_MODE_INPUT;GPIO_InitStruct.Pull = GPIO_NOPULL;HAL_GPIO_Init(si2c->SDA_Port, &GPIO_InitStruct);
}/*** @brief 设置SDA引脚为输出* * @param si2c */
static void SDA_SetOutput(Soft_I2C_Handle* si2c)
{GPIO_InitTypeDef GPIO_InitStruct = {0};GPIO_InitStruct.Pin = si2c->SDA_Pin;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;HAL_GPIO_Init(si2c->SDA_Port, &GPIO_InitStruct);
}/****************************** 3. I2C 协议辅助函数 *****************************************/
/******************** 为了内部实现I2C功能而创造,通常不需要直接调用 ******************************//*** @brief 产生IIC起始信号** @param si2c* * @note SCL为高电平时,SDA由高变低表示起始信号;*/
void I2C_Start(Soft_I2C_Handle *si2c);/*** @brief 产生IIC停止信号* * @param si2c * * @note SCL为高电平时,SDA由低变高表示停止信号;*/
void I2C_Stop(Soft_I2C_Handle *si2c);/*** @brief 发送一字节数据* * @param si2c * @param byte * * @note 先传送最高位,后传送低位*/
void I2C_SendByte(Soft_I2C_Handle *si2c, uint8_t byte);/*** @brief 等待应答信号到来* * @param si2c * @return true 接收应答成功* @return false 接收应答失败* * @note 在SCL为高电平期间,若检测到SDA引脚为低电平,则接收设备响应正常* @note 发送器发送完一个字节数据后接收器必须发送1位应答位来回应发送器* */
bool I2C_Wait_Ack(Soft_I2C_Handle *si2c);/*** @brief 接收1字节数据* * @param si2c * @return uint8_t */
uint8_t I2C_ReceiveByte(Soft_I2C_Handle *si2c);/*** @brief 产生ACK应答* * @param si2c * * @note 在SCL为高电平期间,SDA引脚输出为低电平,产生应答信号*/
void I2C_Ack(Soft_I2C_Handle *si2c);/*** @brief 不产生ACK应答* * @param si2c * * @note 在SCL为高电平期间,若SDA引脚为高电平,产生非应答信号*/
void I2C_Nack(Soft_I2C_Handle *si2c);/*** @brief 发送一字节数据,并等待应答* * @param si2c * @param byte * @return true 应答* @return false 无应答*/
bool I2C_TransmitByte(Soft_I2C_Handle* si2c, uint8_t byte);/*** @brief 向目标设备发送内存地址* * @param si2c 指向Soft_I2C_Handle结构体的指针* @param MemAddress 目标设备地址:在调用接口之前,必须将数据表中的设备7位地址值左移1位* @param MemAddSize 目标设备内部存储器地址的大小* * @note 根据 MemAddSize 决定发送长度 只能是SOFT_I2C_MEMADD_SIZE_8BIT或者SOFT_I2C_MEMADD_SIZE_16BIT*/
bool I2C_TransmitMemoryAddress(Soft_I2C_Handle* si2c, uint16_t MemAddress, uint16_t MemAddSize);void I2C_Start(Soft_I2C_Handle* si2c) {SDA_SetOutput(si2c);  SDA_High(si2c);SCL_High(si2c);softDelayMicro(10);SDA_Low(si2c);softDelayMicro(10);SCL_Low(si2c);//钳住I2C总线,准备发送或接收数据
}void I2C_Stop(Soft_I2C_Handle* si2c) {SDA_SetOutput(si2c);SCL_Low(si2c);SDA_Low(si2c);softDelayMicro(10);SCL_High(si2c);SDA_High(si2c);//发送I2C总线结束信号softDelayMicro(10);
}void I2C_SendByte(Soft_I2C_Handle* si2c, uint8_t byte) {SDA_SetOutput(si2c);for (int i = 0; i < 8; i++) {if (byte & 0x80)SDA_High(si2c);elseSDA_Low(si2c);softDelayMicro(10);SCL_High(si2c);softDelayMicro(10);SCL_Low(si2c);softDelayMicro(10);byte <<= 1;//左移准备发下一位}
}bool I2C_Wait_Ack(Soft_I2C_Handle* si2c) {SDA_High(si2c);SDA_SetInput(si2c);softDelayMicro(10);SCL_High(si2c);softDelayMicro(10);uint8_t cycleTimes = 0;while(SDA_Read(si2c) != I2C_PIN_LOW)// 从机拉低表示 ACK{if(cycleTimes > 5){// I2C_Stop(si2c);SCL_Low(si2c);softDelayMicro(10);return false;}cycleTimes++;softDelayMicro(10);}SCL_Low(si2c);softDelayMicro(10);return true;
}bool I2C_TransmitByte(Soft_I2C_Handle* si2c, uint8_t byte)
{I2C_SendByte(si2c, byte);return I2C_Wait_Ack(si2c);
}uint8_t I2C_ReceiveByte(Soft_I2C_Handle* si2c) {uint8_t byte = 0;SDA_SetInput(si2c);for (int i = 0; i < 8; i++) {SCL_Low(si2c);softDelayMicro(10);SCL_High(si2c);byte <<= 1;if (SDA_Read(si2c) == I2C_PIN_HIGH)byte |= 0x01;softDelayMicro(10);}return byte;
}void I2C_Ack(Soft_I2C_Handle* si2c) {SDA_SetOutput(si2c);SCL_Low(si2c);softDelayMicro(10);    SDA_Low(si2c);softDelayMicro(10);SCL_High(si2c);softDelayMicro(10);SCL_Low(si2c);// SCL输出低时,SDA应立即拉高,释放总线SDA_High(si2c);softDelayMicro(10);
}void I2C_Nack(Soft_I2C_Handle* si2c) {SDA_SetOutput(si2c);SCL_Low(si2c);softDelayMicro(10);SDA_High(si2c);softDelayMicro(10);SCL_High(si2c);softDelayMicro(10);SCL_Low(si2c);softDelayMicro(10);
}bool I2C_TransmitMemoryAddress(Soft_I2C_Handle* si2c, uint16_t MemAddress, uint16_t MemAddSize) 
{if (MemAddSize == SOFT_I2C_MEMADD_SIZE_8BIT){if(!I2C_TransmitByte(si2c, (uint8_t)(MemAddress & 0xFF))) return false;}else if (MemAddSize == SOFT_I2C_MEMADD_SIZE_16BIT){if(!I2C_TransmitByte( si2c, (uint8_t)((MemAddress >> 8) & 0xFF) ) ) return false;if(!I2C_TransmitByte( si2c, (uint8_t)(MemAddress & 0xFF) ) ) return false;}return true;
}/****************** 4. 软件I2C读写函数实现 ***************/bool Soft_I2C_Master_Transmit(Soft_I2C_Handle* si2c, uint16_t DevAddress, uint8_t* pData, uint16_t Size) {I2C_Start(si2c);// 启动IIC通信if(!I2C_TransmitByte(si2c, DevAddress | 0x00)) return false;//发送写数据指令for (uint16_t i = 0; i < Size; i++) {if(!I2C_TransmitByte(si2c, pData[i])) return false;}I2C_Stop(si2c);return true;
}bool Soft_I2C_Master_Receive(Soft_I2C_Handle* si2c, uint16_t DevAddress, uint8_t* pData, uint16_t Size) {I2C_Start(si2c);if(!I2C_TransmitByte(si2c, DevAddress | 0x01)) return false;//发送读数据指令for (uint16_t i = 0; i < Size; i++) {pData[i] = I2C_ReceiveByte(si2c);if (i < Size - 1)I2C_Ack(si2c);elseI2C_Nack(si2c);}I2C_Stop(si2c);return true;
}bool Soft_I2C_Mem_Read(Soft_I2C_Handle* si2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size)
{// 第一阶段:发送设备地址 + 内存地址I2C_Start(si2c); // 假设启动信号总是成功I2C_SendByte(si2c, DevAddress | 0x00); // 写模式if (!I2C_Wait_Ack(si2c)) return false;//向目标设备发送内存地址if( !I2C_TransmitMemoryAddress(si2c, MemAddress, MemAddSize) ) return false;// 第二阶段:重复启动,切换为读模式I2C_Stop(si2c); // 停止信号I2C_Start(si2c); // 重新启动if(!I2C_TransmitByte( si2c, DevAddress | 0x01 ) ) return false;// 读模式// 第三阶段:接收数据for (uint16_t i = 0; i < Size; i++) {pData[i] = I2C_ReceiveByte(si2c);if (i < Size - 1)I2C_Ack(si2c);elseI2C_Nack(si2c);}I2C_Stop(si2c);return true;
}bool Soft_I2C_Mem_Write(Soft_I2C_Handle *si2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size)
{I2C_Start(si2c); // 假设启动信号总是成功if(!I2C_TransmitByte( si2c, DevAddress | 0x00 ) ) return false;// 写模式//向目标设备发送内存地址if( !I2C_TransmitMemoryAddress(si2c, MemAddress, MemAddSize) ) return false;// 发送数据for (uint16_t i = 0; i < Size; i++) {if(!I2C_TransmitByte( si2c, pData[i] ) ) return false;}I2C_Stop(si2c);return true;
}

代码使用说明

1、根据自己的单片机修改相关内容
2、创造句柄并配置引脚(和硬件I2C类似)
3、进行读写操作(和硬件I2C类似)

以温湿度传感器AHT20为例
aht20.h

#ifndef __DHT20_H__
#define __DHT20_H__#ifdef __cplusplus
extern "C" {
#endif#include "main.h"
#include "soft_I2C.h"// 初始化AHT20
void AHT20_Init();// 获取温度和湿度
void AHT20_Read(float *Temperature, float *Humidity);#ifdef __cplusplus
}
#endif#endif

aht20.c

#include "aht20.h"#define AHT20_ADDRESS 0x70uint8_t readBuffer[6] = {0};Soft_I2C_Handle AHT20_I2C;/*** @brief  初始化AHT20*/
void AHT20_Init()
{Soft_I2C_Config(&AHT20_I2C, AHT20_SCL_GPIO_Port, AHT20_SCL_Pin, AHT20_SDA_GPIO_Port, AHT20_SDA_Pin);uint8_t readBuffer;HAL_Delay(40);Soft_I2C_Master_Receive(&AHT20_I2C, AHT20_ADDRESS, &readBuffer, 1);if ((readBuffer & 0x08) == 0x00){uint8_t sendBuffer[3] = {0xBE, 0x08, 0x00};Soft_I2C_Master_Transmit(&AHT20_I2C, AHT20_ADDRESS, sendBuffer, 3);}
}/*** @brief  获取温度和湿度* @param  Temperature: 存储获取到的温度* @param  Humidity: 存储获取到的湿度*/
void AHT20_Read(float *Temperature, float *Humidity)
{uint8_t sendBuffer[3] = {0xAC, 0x33, 0x00};uint8_t readBuffer[6] = {0};Soft_I2C_Master_Transmit(&AHT20_I2C, AHT20_ADDRESS, sendBuffer, 3);HAL_Delay(75);Soft_I2C_Master_Receive(&AHT20_I2C, AHT20_ADDRESS, readBuffer, 6);if ((readBuffer[0] & 0x80) == 0x00){uint32_t data = 0;data = ((uint32_t)readBuffer[3] >> 4) + ((uint32_t)readBuffer[2] << 4) + ((uint32_t)readBuffer[1] << 12);*Humidity = data * 100.0f / (1 << 20);data = (((uint32_t)readBuffer[3] & 0x0F) << 16) + ((uint32_t)readBuffer[4] << 8) + (uint32_t)readBuffer[5];*Temperature = data * 200.0f / (1 << 20) - 50;}
}

参考资料

https://blog.csdn.net/zhangduang_KHKW/article/details/121953275
https://baike.baidu.com/item/I2C%E6%80%BB%E7%BA%BF/918424

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

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

相关文章

C++性能测试工具——Vtune的使用

一、Intel Vtune的安装 在前面初步认识了一下几个性能的测试工具&#xff0c;本篇重点介绍一下Intel VTune Profiler&#xff0c;VTune是一个强大的性能分析工具&#xff0c;它属于Intel oneAPI工具包中工具的一种。VTune的安装只介绍在Linux平台下的场景&#xff08;Windows安…

互联网大厂Java求职面试:优惠券服务架构设计与AI增强实践-6

互联网大厂Java求职面试&#xff1a;优惠券服务架构设计与AI增强实践-6 场景设定&#xff1a;技术总监张总坐在会议室里&#xff0c;郑薪苦带着自信的微笑走了进来。今天他们要围绕优惠券服务的架构设计及如何结合AI进行增强展开讨论。 第一轮面试&#xff1a;基础架构设计 …

nginx模块使用、过滤器模块以及handler模块

一、如何使用nginx的模块 1.ngx_code.c: #include "ngx_config.h" #include "ngx_conf_file.h" #include "nginx.h" #include "ngx_core.h" #include "ngx_string.h" #include "ngx_palloc.h" #include "n…

【Odoo】Pycharm导入运行Odoo15

【Odoo】Pycharm导入运行Odoo15 前置准备1. Odoo-15项目下载解压2. PsrtgreSQL数据库 项目导入运行1. 项目导入2. 设置项目内虚拟环境3. 下载项目中依赖4. 修改配置文件odoo.conf 运行Pycharm快捷运行 前置准备 1. Odoo-15项目下载解压 将下载好的项目解压到开发目录下 2. …

网络安全-等级保护(等保) 2-5 GB/T 25070—2019《信息安全技术 网络安全等级保护安全设计技术要求》-2019-05-10发布【现行】

################################################################################ GB/T 22239-2019 《信息安全技术 网络安全等级保护基础要求》包含安全物理环境、安全通信网络、安全区域边界、安全计算环境、安全管理中心、安全管理制度、安全管理机构、安全管理人员、安…

【SpringBoot】✈️整合飞书群机器人发送消息

&#x1f4a5;&#x1f4a5;✈️✈️欢迎阅读本文章❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;本篇文章阅读大约耗时3分钟。 ⛳️motto&#xff1a;不积跬步、无以千里 &#x1f4cb;&#x1f4cb;&#x1f4cb;本文目录如下&#xff1a;&#x1f381;&#x1f381;&am…

我的多条件查询

背景&#xff1a;2个表&#xff0c;是一对多的关系&#xff0c;一个实时视频帧可以出现多个检测结果 要求&#xff0c;可以根据&#xff0c;ids&#xff0c;起始时间&#xff0c;识别出的鸟的种类&#xff0c;来进行删除。 出现的问题&#xff0c; 一致性没有实现&#xff1a…

关于网站提交搜索引擎

发布于Eucalyptus-blog 一、前言 将网站提交给搜索引擎是为了让搜索引擎更早地了解、索引和显示您的网站内容。以下是一些提交网站给搜索引擎的理由&#xff1a; 提高可见性&#xff1a;通过将您的网站提交给搜索引擎&#xff0c;可以提高您的网站在搜索结果中出现的机会。当用…

【Oracle专栏】扩容导致数据文件 dbf 丢失,实操

Oracle相关文档,希望互相学习,共同进步 风123456789~-CSDN博客 1.背景 同事检查扩容情况,发现客户扩容后数据盘后,盘中原有文件丢失,再检查发现数据库没有启动。通过检查发现数据盘中丢失的是oracle的 dbf 表空间文件。数据库无法启动。 检查情况:1)没有rman备份 …

负载均衡—会话保持技术详解

一、会话保持的定义 会话保持&#xff08;Session Persistence&#xff09;是一种负载均衡策略&#xff0c;其核心机制是确保来自同一客户端的连续请求&#xff0c;在特定周期内被定向到同一台后端服务器进行处理。这种机制通过记录和识别客户端的特定标识信息&#xff0c;打破…

CSRF攻击 + 观测iframe加载时间利用时间响应差异侧信道攻击 -- reelfreaks DefCamp 2024

参考: https://0x90r00t.com/2024/09/30/3708/ 题目信息 有些事情最好还是保持低调。当然&#xff0c;除非你是个真正的怪胎。 注意&#xff1a;该网站通过HTTPS提供服务 标志格式&#xff1a;DCTF&#xff5b;&#xff5d;题目实现了一个类似视频网站的东西 在其提供的数据库中…

JS逆向-某易云音乐下载器

文章目录 介绍下载链接Robots文件搜索功能JS逆向**函数a&#xff1a;生成随机字符串****函数b&#xff1a;AES-CBC加密****函数c&#xff1a;RSA公钥加密** 歌曲下载总结 介绍 在某易云音乐中&#xff0c;很多歌曲听是免费的&#xff0c;但下载需要VIP&#xff0c;此程序旨在“…

黑马k8s(十)

1.Pod生命周期-钩子函数 2.Pod生命周期-容器探测 因为没有hello.txt文件 查看详情&#xff1a; 修改为查看命令&#xff1a; 查看一下详情&#xff1a; 因为只有一个80端口&#xff0c;没有8080&#xff0c;所以会重启 查看详情&#xff1a; 修改成80&#xff1a; 因为没有…

每日算法刷题Day9 5.17:leetcode定长滑动窗口3道题,用时1h

9. 1652.拆炸弹(简单&#xff0c;学习) 1652. 拆炸弹 - 力扣&#xff08;LeetCode&#xff09; 思想 为了获得正确的密码&#xff0c;你需要替换掉每一个数字。所有数字会 同时 被替换。 如果 k > 0 &#xff0c;将第 i 个数字用 接下来 k 个数字之和替换。如果 k < 0…

Java IO及Netty框架学习小结

Netty netty官网: Netty 什么是Netty&#xff1f; Netty 是 一个异步事件驱动的网络应用程序框架&#xff0c;用于快速开发可维护的高性能协议服务器和客户端。Netty 是一个 NIO 客户端服务器框架&#xff0c;可以快速轻松地开发网络应用程序&#xff08;例如协议服务器和客…

计算机网络笔记(二十七)——4.9多协议标签交换MPLS

4.9.1MPLS的工作原理 一、MPLS基本工作原理 MPLS&#xff08;Multiprotocol Label Switching&#xff09;是一种介于数据链路层和网络层之间的转发技术&#xff0c;通过固定长度的标签进行高速数据转发。其核心特点是通过预建立的标签交换路径&#xff08;Label Switching Pa…

AI 赋能 Copula 建模:大语言模型驱动的相关性分析革新

技术点目录 R及Python语言及相关性研究初步二元Copula理论与实践&#xff08;一&#xff09;二元Copula理论与实践&#xff08;二&#xff09;【R语言为主】Copula函数的统计检验与选择【R语言为主】高维数据与Vine Copula 【R语言】正则Vine Copula&#xff08;一&#xff09;…

【洛谷P3386】二分图最大匹配之Kuhn算法/匈牙利算法:直观理解

题目&#xff1a;洛谷P3386 【模板】二分图最大匹配 &#x1f955; 匈牙利算法本来是针对带权图最大匹配的&#xff0c;这里由于题目只是求最大匹配的边数&#xff0c;所以我们也只考虑无权的情况。 &#x1f680; 本文旨在服务于看了别的关于匈牙利算法的文章但不甚理解的童…

【数据结构】二分查找(返回插入点)5.14

二分查找基础版 package 二分查找; public class BinarySearch { public static void main(String[] args) { // TODO Auto-generated method stub } public static int binarySearchBasic(int[] a,int target) { int i0,ja.length-1; //设置指针初值 while…

Ubuntu 命令

Ubuntu 命令速查表​ ​分类​​命令​​功能描述​​示例/常用选项​​​​文件与目录​ls列出目录内容ls -a&#xff08;显示隐藏文件&#xff09;; ls -lh&#xff08;详细列表易读大小&#xff09; cd切换目录cd ~&#xff08;主目录&#xff09;; cd ..&#xff08;上级…