软件I2C
软件I2C
注意:
SDA(串行数据线)和SCL(串行时钟线)都是双向I/O线,接口电路为开漏输出。需通过上拉电阻接电源VCC。
软件I2C说明
说明,有的单片机没有硬件I2C的功能,或者因为电路设计失误不得不使用软件I2C。
软件I2C虽然引脚设定灵活,但效果自然是比不上硬件I2C的……
本人按照STM32的硬件I2C风格编写了软件I2C,这样便于移植更换
具体来说:
虽然软件I2C(也称为Bit-banging I2C)提供了极大的灵活性,允许开发者在任意GPIO引脚上实现I2C通信,但它也有几个明显的缺点:
-
CPU占用率高:由于所有的I2C协议处理(如时序控制、信号的高低电平转换等)都由CPU直接管理,而不是专用硬件模块完成,因此会占用大量的CPU资源。这意味着,在进行I2C通信时,CPU不能执行其他任务,除非通过中断或DMA等方式来部分缓解这个问题。
-
速度受限:由于需要依靠CPU指令来精确地控制时序,软件I2C的速度通常比硬件I2C慢得多。特别是在高速模式下(例如I2C Fast-mode Plus,速度可达1MHz),准确地生成和检测信号变得更加困难,可能无法达到标准要求的速度。
-
时序精度问题:不同CPU架构和运行频率下的指令周期时间不同,这使得编写可移植性好的软件I2C代码变得复杂。此外,中断或其他系统活动可能会干扰软件I2C的时序,导致通信失败。
-
可靠性较低:与硬件I2C相比,软件I2C更容易受到电磁干扰(EMI)和其他电气噪声的影响,因为其不具备硬件级别的过滤和错误纠正能力。同时,软件实现难以完全保证严格的I2C协议时序要求,尤其是在复杂的系统环境中。
-
不支持高级特性:许多现代微控制器的硬件I2C接口支持诸如多主控、时钟扩展(clock stretching)、中断驱动传输、DMA支持等高级特性。而这些特性通常很难或者不可能通过软件I2C来实现。
-
开发和维护成本:虽然对于简单的应用来说,软件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
相关文章:

软件I2C
软件I2C 注意: SDA(串行数据线)和SCL(串行时钟线)都是双向I/O线,接口电路为开漏输出。需通过上拉电阻接电源VCC。 软件I2C说明 说明,有的单片机没有硬件I2C的功能,或者因为电路设计…...
通过实例讲解螺旋模型
目录 一、螺旋模型的核心概念 二、螺旋模型在电子商城系统开发中的应用示例 第 1 次螺旋:项目启动与风险初探...

Brooks Polycold快速循环水蒸气冷冻泵客户使用手含电路图,适用于真空室应用
Brooks Polycold快速循环水蒸气冷冻泵客户使用手含电路图,适用于真空室应用...
winfrom中创建webapi
参照一下两篇 Winform窗体利用WebApi接口实现ModbusTCP数据服务_winform webapi-CSDN博客 C#.NET WebApi返回各种类型(图片/json数据/字符串),.net图片转二进制流或byte - 冰封的心 - 博客园...
unity XCharts插件生成曲线图在UICanvas中
【推荐100个unity插件之22】基于UGUI的功能强大的简单易用的Unity数据可视化图表插件——XCharts3.0插件的使用_unity xcharts-CSDN博客...
Pichome 开源网盘程序index.php 文件读取漏洞(CVE-2025-1743)
免责声明 本文档所述漏洞详情及复现方法仅限用于合法授权的安全研究和学术教育用途。任何个人或组织不得利用本文内容从事未经许可的渗透测试、网络攻击或其他违法行为。使用者应确保其行为符合相关法律法规,并取得目标系统的明确授权。 对于因不当使用本文信息而造成的任何直…...

关于在Unity项目中使用Post Processing插件打包到web端出现的问题
关于在Unity项目中使用Post Processing插件打包到web端出现的问题 解决方法:是不激活摄像机上的Post Processing有关组件,拉低场景中的Directional Light平行光的强度进行web端打包。 (烘焙灯光时是可以激活。) web端支持这个Pos…...

Prompt Tuning:高效微调大模型的新利器
Prompt Tuning(提示调优)是什么 Prompt Tuning(提示调优) 是大模型参数高效微调(Parameter-Efficient Fine-Tuning, PEFT)的重要技术之一,其核心思想是通过优化 连续的提示向量(而非整个模型参数)来适配特定任务。以下是关于 Prompt Tuning 的详细解析: 一、核心概念…...
OpenCV 第6课 图像处理之几何变换(重映射)
1. 概述 简单来说,重映射就是把一副图像内的像素点按照规则映射到到另外一幅图像内的对应位置上去,形成一张新的图像。 因为原图像与目标图像的像素坐标不是一一对应的。一般情况下,我们通过重映射来表达每个像素的位置(x,y),像这样: g(x,y)=f(h(x,y)) 在这里g()是目标图…...

C++初阶-vector的底层
目录 1.序言 2.std::sort(了解) 3.vector的底层 3.1讲解 3.2构造函数 3.3push_back函数 3.4begin()和end()函数 3.5capacity()和size()函数和max_size函数 3.5.1size()函数 为什么这样写? 底层原理 3.5.2max_size()函数 为什么这…...
获取文件夹下所有文件的名称
一、为什么写这个程序 因为我要做一个类似于目录树的结构,需要把文件夹下的所有名字全部导出了。 二、Python和vscode的安装 先安装Python在安装vscode Python安装 vscode的安装 三、源码 import os# 定义要扫描的文件夹列表(使用原始字符串避免转义…...

C语言指针深入详解(五):回调函数、qsort函数
目录 一、回调函数 1、使用回调函数改造前 2、使用回到函数改造后 二、qsort使用举例 1、使用qsort函数排序整型数据 2、使用qsort排序结构数据 三、qsort函数模拟实现 结语 🔥个人主页:艾莉丝努力练剑 🍓专栏传送门:《…...

数据结构进阶:AVL树与红黑树
目录 前言 AVL树 定义 结构 插入 AVL树插入的大致过程 更新平衡因子 旋转 右单旋 左单旋 左右双旋 右左双旋 实现 红黑树 定义 性质 结构 插入 实现 总结 前言 在学习了二叉搜索树之后,我们了解到其有个致命缺陷——当树的形状呈现出一边倒…...
容器化-K8s-镜像仓库使用和应用
一、K8s 镜像仓库使用 1、启动镜像仓库 cd/usr/local/harbor ./install.sh2、配置镜像仓库地址 在 master 节点和 slaver 节点上,需要配置 Docker 的镜像仓库地址,以便能够访问本地的镜像仓库。编辑 Docker 的配置文件 vi /etc/docker/daemon.json(如果不存在则创建),添…...

基于Spring Boot + Vue的教师工作量管理系统设计与实现
一、项目简介 随着高校信息化管理的发展,教师工作量管理成为教务系统中不可或缺的一部分。为此,我们设计并开发了一个基于 Spring Boot Vue 的教师工作量管理系统,系统结构清晰,功能完备,支持管理员和教师两个角色。…...
预先学习:构建智能系统的 “未雨绸缪” 之道
一、预先学习:训练阶段的 “模型预构建” 哲学 1.1 核心定义与生物启发 预先学习的本质是模拟人类的 “经验积累 - 快速决策” 机制:如同医生通过大量病例总结诊断规则,算法在训练阶段利用全量数据提炼规律,生成固化的 “决策模型…...

完善网络安全等级保护,企业需注意:
在数字化转型加速的当下,网络安全成为企业发展的基石。网络安全等级保护作为保障网络安全的重要举措,企业必须高度重视并积极落实。以下要点,企业在完善网络安全等级保护工作中需格外关注: 一、准确开展定级备案 企业首先要依据相…...

Trae 04.22版本深度解析:Agent能力升级与MCP市场对复杂任务执行的革新
我正在参加Trae「超级体验官」创意实践征文,本文所使用的 Trae 免费下载链接:Trae - AI 原生 IDE 目录 引言 一、Trae 04.22版本概览 二、统一对话体验的深度整合 2.1 Chat与Builder面板合并 2.2 统一对话的优势 三、上下文能力的显著增强 3.1 W…...

OceanBase 开发者大会:详解 Data × AI 战略,数据库一体化架构再升级
OceanBase 2025 开发者大会与5月17日在广州举行。这是继 4 月底 OceanBase CEO 杨冰宣布公司全面进入AI 时代后的首场技术盛会。会上,OceanBase CTO 杨传辉系统性地阐述了公司的 DataAI 战略,并发布了三大产品:PowerRAG、共享存储,…...
正则表达式进阶(三):递归模式与条件匹配的艺术
在正则表达式的高级应用中,递归模式和条件匹配是处理复杂嵌套结构和动态模式的利器。它们突破了传统正则表达式的线性匹配局限,能够应对嵌套括号、HTML标签、上下文依赖等复杂场景。本文将详细介绍递归模式((?>...)、 (?R) 等࿰…...

ubuntu环境下 基于Python 打包的 批量命令行可视化操作工具 GUI
文章目录 一.需求:二.原理支撑:三.简单Demo四.封装成GUI1.依赖库2.代码 五.打包成可执行文件六.命令行的配置七.运行效果 一.需求: 作为测试工程师,为了到现场高效的调试,部署工作,需要一个可视化的工具&a…...
docker介绍与常用命令汇总
docker简介 docker是什么? Docker 是一个开源的应用容器引擎,它可以让开发者将应用与运行环境打包成一个标准的、可移植的容器(Container),在任何地方都可以快速部署和运行,无需关心底层环境是否一致。 …...
[创业之路-369]:企业战略管理案例分析-9-战略制定-差距分析的案例之华为
一、综合案例 在战略制定中,华为通过差距分析明确战略方向,以应对市场挑战和实现长期发展目标。 以下为具体案例与分析: 1、案例背景 华为在通信设备领域崛起过程中,始终将差距分析作为战略制定的核心环节。面对国际竞争对手&…...

谷歌宣布推出 Android 的新安全功能,以防止诈骗和盗窃
在上周二的 Android Show 上,也就是Google I/O 开发者大会之前,谷歌宣布了 Android 的全新安全和隐私功能。这些新功能包括对通话、屏幕共享、消息、设备访问和系统级权限的全新保护。谷歌希望通过这些功能保护用户免遭诈骗,在设备被盗或被攻…...

Qt/C++编写音视频实时通话程序/画中画/设备热插拔/支持本地摄像头和桌面
一、前言 近期有客户提需求,需要在嵌入式板子上和电脑之间音视频通话,要求用Qt开发,可以用第三方的编解码组件,能少用就尽量少用,以便后期移植起来方便。如果换成5年前的知识储备,估计会采用纯网络通信收发…...
Android trace presentFence屏幕显示的帧
Android trace presentFence屏幕显示的帧 presentFence :当帧成功显示到屏幕时,present fence就会signal。 FrameMissed/GpuFrameMissed/HwcFrameMissed表示上一次合成的结果,当SurfaceFlinger合成后显示到屏幕上,present fence就…...
Spring是如何实现scope作用域支持
众所周知在Spring的Bean当中是存在两种作用域的,即单例模式与多例模式,可通过scope来指定 下面就是注册一个多例Bean <bean id"people" class"org.qlspringframework.beans.ioc.bean.People" scope"prototype"> …...
Helm Chart 中配置多个 Docker Registry 地址以实现备用访问
在 Helm Chart 中配置多个 Docker Registry 地址以实现备用访问,可以通过以下几种方式实现: 1. 在 values.yaml 中定义多个 Registry 在 values.yaml 中定义主 Registry 和备用 Registry,以便在部署时灵活切换: # values.yaml …...
FreeSWITCH rtcp-mux 测试
rtcp 跟 rtp 占用同一个端口,这就是 rtcp 复用 Fs 呼出是这样的: originate [rtcp_muxtrue][rtcp_audio_interval_msec5000]user/1001 &echo 需要同时指定 rtcp_audio_interval_msec,否则 rtcp_mux 不能生效 Fs 呼入不需要配置…...

c++ 类的语法4
测试析构函数、虚函数、纯虚函数: void testClass5() {class Parent {public:Parent(int x) { cout << "Parent构造: " << x << endl; }~Parent() {cout << "调用Parent析构函数" << endl;}virtual string toSt…...