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

I2C 外设知识体系:从基础到 STM32 硬件实现

文章目录

    • I2C外设简介
        • I2C 通信实现方式对比
          • 1. 软件模拟 I2C
          • 2. 硬件实现 I2C
        • STM32 I2C 外设核心功能
          • 1. 硬件特性
          • 2. 寄存器与引脚
      • I2C框图
        • 一、引脚接口
        • 二、数据处理模块
        • 三、时钟控制模块
        • 四、控制逻辑模块
        • 五、辅助功能
      • I2C基本结构
      • 主机发送
        • 一、7 位主发送序列
        • 二、10 位主发送序列
        • 三、关键事件(EV)说明
        • 四、注意事项
      • 主机接收
        • 一、7 位主接收序列
        • 二、10 位主接收序列
        • 三、关键事件(EV)说明
        • 四、注意事项
      • 软件/硬件波形对比
      • 硬件实现I2C使用MPU6050通信

I2C外设简介

I2C 通信实现方式对比
1. 软件模拟 I2C
  • 原理:通过手动翻转 GPIO 引脚电平,模拟 I2C 时序(时钟线 SCL 和数据线 SDA)。
    • 例:用软件控制 SCL 拉低 / 释放,逐位判断数据位并操作 SDA,生成通信波形。
  • 优势
    • 灵活性高,无需硬件支持,适用于简单场景。
    • 资源限制少,只需复制代码即可开辟多路 I2C 总线。
  • 局限
    • 依赖软件延时,时序精度较低(同步时序允许一定不规整)。
    • 占用 CPU 资源,不适合高频或复杂通信。
2. 硬件实现 I2C
  • 原理:通过单片机内置的 I2C 外设自动生成时序,软件只需配置寄存器。
    • 例:STM32 的 I2C 外设可自动处理起始 / 终止条件、时钟生成、数据收发、应答机制等。
  • 优势
    • 时序精准,效率高,释放 CPU 资源。
    • 支持完整协议特性(如多主机模型、DMA 传输、CRC 校验)。
  • 局限
    • 依赖硬件资源,引脚固定(如 STM32 I2C1 对应 PB6/PB7 或重映射引脚)。
    • 配置复杂度高于软件模拟(需理解寄存器和状态机)。
STM32 I2C 外设核心功能
1. 硬件特性
  • 集成收发电路:自动处理时序逻辑,软件通过操作寄存器(CR 控制寄存器、DR 数据寄存器、SR 状态寄存器)控制通信。
  • 多主机模型
    • 固定多主机:总线上有固定主机和从机,主机间需总线仲裁(如果多主设备同时发起通信)。
    • 可变多主机:设备可动态切换主从角色(STM32 采用此模型,默认从模式,需主动申请主机权限)。
  • 地址模式
    • 7 位地址:最常用(128 个地址,支持地址低位可配置)。
    • 10 位地址:扩展至 1024 个地址,通过两字节寻址(首字节高 5 位为标志位 “11110”)。
  • 通信速度
    • 标准模式:最高 100kHz,快速模式:最高 400kHz(同步时序允许非严格频率)。
  • 扩展功能:支持 DMA 传输(提升多字节收发效率)、兼容 SMBus 协议(系统管理总线)。
2. 寄存器与引脚
  • 引脚复用
    • I2C1:默认 PB6(SCL)、PB7(SDA),可重映射至 PB8/PB9。
    • I2C2:默认 PB10(SCL)、PB11(SDA)。
    • 注意:硬件 I2C 引脚固定,需配置为 “复用开漏输出” 模式(确保总线电平正确)。
  • 核心寄存器
    • 控制寄存器(CR):设置起始 / 终止条件、应答使能、主从模式等。
    • 数据寄存器(DR):存放待发送 / 已接收的数据,与移位寄存器配合实现逐位传输。
    • 状态寄存器(SR):包含标志位(如 SB 起始条件发送完成、ADDR 地址传输完成、TXE 发送寄存器空等),用于监测通信状态。

I2C框图

一、引脚接口
  • SDA(数据线):双向传输数据,通过 “数据控制” 模块与内部电路交互,支持开漏输出(需外接上拉电阻)。
  • SCL(时钟线):由 “时钟控制” 模块管理,用于同步数据传输,主机模式下生成时钟信号。
  • SMBALERT:用于 SMBus 协议的警报信号,连接至 “控制逻辑电路”,常规 I²C 通信中较少使用。
二、数据处理模块
  • 数据寄存器(DATA REGISTER):存储待发送或已接收的字节数据,是软件与硬件交互的核心接口。
  • 数据移位寄存器:与数据寄存器协作,实现数据逐位发送(高位先行)或接收(高位先入)。发送时从数据寄存器获取数据,接收时将数据存入数据寄存器。
  • 比较器:将接收到的地址与 “自身地址寄存器”(单地址模式)或 “双地址寄存器”(双地址模式)对比,判断是否为目标设备地址,确保通信寻址准确。
  • 帧错误校验(PEC)计算:生成或校验帧错误校验码,用于数据完整性检查,结果存储于 “PEC 寄存器”,提升通信可靠性。
三、时钟控制模块
  • 时钟控制寄存器(CCR):配置时钟频率(如标准模式 100kHz、快速模式 400kHz)和相位,精确控制 SCL 的生成时序,确保通信同步。
四、控制逻辑模块
  • 控制寄存器(CR1 & CR2)
    • 设置工作模式(主 / 从、发送 / 接收)、触发起始 / 终止条件、使能应答(ACK)等关键操作。
    • 例如,主机发送时通过 CR1 设置 “START=1” 生成起始信号,从机通过地址寄存器响应寻址。
  • 状态寄存器(SR1 & SR2)
    • 实时反馈通信状态,如起始条件完成(SB 标志)、地址传输完成(ADDR 标志)、数据寄存器空(TXE 标志)等。
    • 软件通过查询这些标志位(或依赖中断)监控通信流程,确保时序正确。
  • 控制逻辑电路
    • 协调各模块工作,根据控制寄存器指令生成控制信号,触发中断和 DMA 请求。
    • 例如,数据传输完成时,向 CPU 发送中断;大批量数据传输时,发起 DMA 请求以减轻 CPU 负担。
五、辅助功能
  • 中断:当特定事件(如地址匹配、数据传输完成、应答失败)发生时,控制逻辑电路触发中断,通知 CPU 及时处理异常或继续后续操作。
  • DMA 请求与响应:支持直接内存访问,适用于大量数据收发场景,避免 CPU 频繁干预,提升系统效率

I2C基本结构

这个图是上面总线,进行简化之后的基本结构图,后面会根据这个图进行程序的编写。

主机发送

一、7 位主发送序列
  1. 起始条件(S):产生起始信号,触发 EV5(SB = 1),表示起始条件已发送。软件通过读状态寄存器 SR1 确认后,将 7 位从机地址(含写位)写入数据寄存器 DR,清除 EV5。
  2. 发送地址:地址通过 SDA 发送,从机应答(A)后触发 EV6(ADDR = 1),表示地址传输完成。软件读 SR1 后读 SR2,清除 EV6。
  3. 发送数据
    • 首次发送数据(如数据 1)前,触发 EV8_1(TxE = 1,移位寄存器空),软件将数据写入 DR。
    • 后续数据发送时,触发 EV8(TxE = 1,移位寄存器非空),每次数据写入 DR 后清除该事件。每个数据字节后等待从机应答(A)。
  4. 发送结束:最后一个数据(数据 N)发送后,触发 EV8_2(TxE = 1,BTF = 1),软件请求设置停止位(P)。硬件在产生停止条件时,自动清除 TxE 和 BTF 位。
二、10 位主发送序列
  1. 起始条件(S):同 7 位模式,触发 EV5(SB = 1),写帧头(10 位地址高 8 位,含写位)到 DR 清除。
  2. 发送帧头与地址
    • 帧头发送后,从机应答(A),触发 EV9(ADDR10 = 1),读 SR1 后写 10 位地址低 2 位到 DR 清除。
    • 地址低 2 位发送后,从机应答(A),触发 EV6(ADDR = 1),读 SR1 和 SR2 清除。
  3. 发送数据:与 7 位模式一致,通过 EV8_1EV8 事件逐字节发送数据,每个数据后等待应答(A)。
  4. 发送结束:同 7 位模式,触发 EV8_2 后设置停止位(P),结束传输。
三、关键事件(EV)说明
  • EV5(SB = 1):起始条件发送完成,读 SR1 后写地址 / 帧头到 DR 清除。
  • EV6(ADDR = 1):地址传输完成(含应答),读 SR1 和 SR2 清除。
  • EV8_1(TxE = 1):移位寄存器空,写 DR 准备发送数据。
  • EV8(TxE = 1):数据寄存器空,写 DR 清除,确保数据连续发送。
  • EV8_2(TxE = 1,BTF = 1):最后一字节发送完成,请求停止条件,硬件自动清除相关位。
  • EV9(ADDR10 = 1):10 位地址帧头发送完成,读 SR1 后写地址低 2 位到 DR 清除。
四、注意事项
  1. SCL 拉长:EV5、EV6、EV9、EV8_1、EV8_2 事件会拉长 SCL 低电平,直至软件完成对应操作(如写 DR、读寄存器)。
  2. EV8 操作时序:EV8 的软件序列(写 DR)必须在当前字节传输结束前完成,避免时序错误

主机接收

一、7 位主接收序列
  1. 起始条件(S):产生起始信号,触发 EV5(SB = 1)。软件读状态寄存器 SR1 后,将 7 位从机地址(含读位)写入数据寄存器 DR,清除 EV5。
  2. 发送地址:地址通过 SDA 发送,从机应答(A)后触发 EV6(ADDR = 1)。软件读 SR1 后读 SR2,清除 EV6。
  3. 接收数据
    • 接收第一个数据(如数据 1)时,无对应事件标志(EV6_1 仅适用于接收 1 字节场景,在 EV6 后清除应答和停止条件产生位)。
    • 后续数据接收时,触发 EV7(RxNE = 1),表示数据寄存器非空,软件读 DR 清除该事件。
  4. 接收结束:最后一个数据(数据 N)接收时,触发 EV7_1(RxNE = 1)。软件读 DR 后,设置 ACK = 0(非应答)和 STOP 请求,结束通信。
二、10 位主接收序列
  1. 起始条件(S):同 7 位模式,触发 EV5(SB = 1),写 10 位地址帧头(高 8 位,含读位)到 DR 清除。
  2. 发送帧头与地址
    • 帧头发送后,从机应答(A),触发 EV9(ADDR10 = 1)。软件读 SR1 后,写 10 位地址低 2 位到 DR 清除。
    • 地址低 2 位发送后,从机应答(A),触发 EV6(ADDR = 1)。软件读 SR1 和 SR2 清除。若需继续接收,设置 CR2 的 START = 1,产生重复起始条件(Sr)。
  3. 接收数据:重复起始条件后,发送帧头(触发 EV6),后续数据接收同 7 位模式,通过 EV7 事件逐字节读取数据。
  4. 接收结束:同 7 位模式,最后一个数据触发 EV7_1,软件设置 ACK = 0STOP 请求,硬件生成停止条件,结束传输。
三、关键事件(EV)说明
  • EV5(SB = 1):起始条件发送完成,读 SR1 后写地址 / 帧头到 DR 清除。
  • EV6(ADDR = 1):地址传输完成(含应答),读 SR1 和 SR2 清除。10 位模式下,该事件后若需继续接收,设置 START = 1
  • EV7(RxNE = 1):数据寄存器非空,读 DR 清除,用于常规数据接收。
  • EV7_1(RxNE = 1):最后一字节数据接收,读 DR 后设置 ACK = 0STOP,结束通信。
  • EV9(ADDR10 = 1):10 位地址帧头发送完成,读 SR1 后写地址低 2 位到 DR 清除。
四、注意事项
  • EV6_1 特殊处理:仅适用于接收 1 字节场景,在 EV6 后需手动清除应答和停止条件产生位。
  • 10 位模式重复起始:若需连续接收,在 EV6 后设置 START = 1,重新生成起始条件,继续通信。

软件/硬件波形对比

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

硬件实现I2C使用MPU6050通信

//MPU6050.c #include "stm32f10x.h"                  // Device header
#include "MPU6050Reg.h" 
#define MPU6050_Address 0xD0//超时等待函数 ,防止函数意外卡死
void MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{uint16_t Timeout = 10000;while(I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS){Timeout--;if(Timeout == 0){break;}}
}
//指定位置读
uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{uint8_t data;
//	//起始位
//	MyI2C_Start();
//	MyI2C_SendByte(MPU6050_Address);
//	//接收应答位
//	MyI2C_ReadAck();
//	//发送寄存器地址
//	MyI2C_SendByte(RegAddress);
//	MyI2C_ReadAck();
//	
//	//因为又要设置读写,所以要重启起始位
//	MyI2C_Start();
//	//发送从机地址,因为是读数据所以地址最后的读写位为1
//	MyI2C_SendByte(MPU6050_Address | 0x01);
//	//接收应答位
//	MyI2C_ReadAck();
//	data = MyI2C_ReadByte();
//	//发送应答位 表示要停止
//	MyI2C_SendAck(1);
//	MyI2C_End();//将上面的软件I2c转化为对应的硬件I2cI2C_GenerateSTART(I2C2, ENABLE);//等待EV5事件 从机转化为主机MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);//发送从机地址位I2C_Send7bitAddress(I2C2, MPU6050_Address, I2C_Direction_Transmitter);//不用接收应答位,发送接收都会自动接收应答位//等待EV6事件  转换为发送模式MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);//不用等待EV8_1事件,就是告诉可以往数据寄存器发送数据了 发送寄存器地址I2C_SendData(I2C2, RegAddress);//等待EV8事件  等待字节转移完成MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING);//重启起始位I2C_GenerateSTART(I2C2, ENABLE);//等待EV5事件 从机转化为主机MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);//发送要从机地址I2C_Send7bitAddress(I2C2, MPU6050_Address, I2C_Direction_Receiver);//不用接收应答位,发送接收都会自动接收应答位//等待EV6事件  转换为接收模式MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);//因为是接收一个字节所以要发送接收最后一个字节之前要配置应答位,因为接收完数据就晚了I2C_AcknowledgeConfig(I2C2, DISABLE);I2C_GenerateSTOP(I2C2, ENABLE);//等待EV7事件  等待数据接收完成MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED);//读取数据data = I2C_ReceiveData(I2C2);//完成之后再重新将标志位 设置为ENABLE 因为默认为使能,改为默认可以兼容在进行其他操作I2C_AcknowledgeConfig(I2C2, ENABLE);return data;
}
//指定地址写
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{
//	//起始位
//	MyI2C_Start();
//	MyI2C_SendByte(MPU6050_Address);
//	//接收应答位
//	MyI2C_ReadAck();
//	//发送寄存器地址
//	MyI2C_SendByte(RegAddress);
//	MyI2C_ReadAck();
//	//发送数据
//	MyI2C_SendByte(Data);
//	MyI2C_ReadAck();
//	//停止位
//	MyI2C_End();//将上面的软件I2c转化为对应的硬件I2cI2C_GenerateSTART(I2C2, ENABLE);//等待EV5事件 从机转化为主机MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);//发送从机地址位I2C_Send7bitAddress(I2C2, MPU6050_Address, I2C_Direction_Transmitter);//不用接收应答位,发送接收都会自动接收应答位//等待EV6事件  转换为发送模式MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);//不用等待EV8_1事件,就是告诉可以往数据寄存器发送数据了 发送寄存器地址I2C_SendData(I2C2, RegAddress);//等待EV8事件  等待字节转移完成MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING);//发送寄存器要写入的数据I2C_SendData(I2C2, Data);//等待EV8_2事件  等待字节转移完成MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);//产生结束位I2C_GenerateSTOP(I2C2, ENABLE);
}uint8_t MPU6050_GetID(void)
{return MPU6050_ReadReg(MPU6050_WHO_AM_I);
}
void MPU6050_Init(void)
{
//	MyI2C_Init();//硬件I2C初始化代码//1. 开启时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);//2.配置gpio口 10 11 为复用开漏//初始化gpioinit参数中的结构体GPIO_InitTypeDef GPIO_InitStructure;//将gpio口设置为推挽输出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);// 3. 初始化I2cI2C_InitTypeDef I2C_InitStruct;//开启应答位I2C_InitStruct.I2C_Ack = I2C_Ack_Enable;I2C_InitStruct.I2C_ClockSpeed = 50000;//占空比 2 比 1 其实都差不多I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2;I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;//应答地址,这里是主机所以不重要I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//是主机随便设置地址就可以了,不和其他硬件重合就可以了I2C_InitStruct.I2C_OwnAddress1 = 0x00;I2C_Init(I2C2, &I2C_InitStruct);//开启I2c总开关I2C_Cmd(I2C2, ENABLE);//配置相关的寄存器//电源管理寄存器 时钟选择陀螺仪x时钟MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);//电源管理寄存器2 配置待机模式,都不待机MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);//配置分频系数,值越小越快MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09);//配置寄存器 滤波模式 最平滑的滤波 0x06MPU6050_WriteReg(MPU6050_CONFIG, 0x06);//陀螺仪配置寄存器 选择最大量程 11 MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);//加速度计配置寄存器 最大量程MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);}
//经过上面寄存器的配置,数据都存在了寄存器中,直接设置读取寄存器的函数
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_H);*GyroZ = DataH << 8 | DataL;}

相关文章:

I2C 外设知识体系:从基础到 STM32 硬件实现

文章目录 I2C外设简介I2C 通信实现方式对比1. 软件模拟 I2C2. 硬件实现 I2C STM32 I2C 外设核心功能1. 硬件特性2. 寄存器与引脚 I2C框图一、引脚接口二、数据处理模块三、时钟控制模块四、控制逻辑模块五、辅助功能 I2C基本结构主机发送一、7 位主发送序列二、10 位主发送序列…...

vue和uniapp聊天页面右侧滚动条自动到底部

1.vue右侧滚动条自动到底部 <div ref"newMessage1"></div> <!-- 定义<div ref"newMessage1"></div>与<div v-for”item in list“>循环同级定义-->定义方法 scrollToBottomCenter(){this.$nextTick(() > {this.$re…...

文件索引:数组、二叉树、二叉排序树、平衡树、红黑树、B树、B+树

参考链接&#xff1a;https://www.bilibili.com/video/BV1mY4y1W7pS 数据结构可视化工具&#xff1a;https://www.cs.usfca.edu/~galles/visualization/Algorithms.html 问题引出&#xff1a;一般是什么原因导致从磁盘查找数据效率低&#xff1f; 通过索引来更快的查询数据&a…...

PHP的namespace

文章目录 环境Java的packagepackage关键字包结构和目录结构访问权限import关键字总结 PHP的namespacenamespace关键字在同一个文件里使用资源限定&#xff0c;完全限定&#xff0c;非限定限定完全限定非限定 use关键字use VS 直接指定资源在不同的文件里使用总结 环境 Windows…...

《仿盒马》app开发技术分享-- 商品搜索页(顶部搜索bar热门搜索)(端云一体)

开发准备 随着开发功能的逐渐深入&#xff0c;我们的应用逐渐趋于完善&#xff0c;现在我们需要继续在首页给没有使用按钮以及组件添加对应的功能&#xff0c;这一节我们要实现的功能是商品搜索页面&#xff0c;这个页面我们从上到下开始实现功能&#xff0c;首先就是一个搜索…...

10_聚类

描述 聚类&#xff08;clustering&#xff09;是将数据集划分成组的任务&#xff0c;这些组叫作簇&#xff08;cluster&#xff09;。其目标是划分数据&#xff0c;使得一个簇内的数据点非常相似且不同簇内的数据点非常不同。与分类算法类似&#xff0c;聚类算法为每个数据点分…...

网络安全:网页密码防护与记住密码功能的安全

引言 在数字化时代&#xff0c;网页应用已成为人们生活和工作中不可或缺的一部分。用户登录作为网页应用的第一道防线&#xff0c;其密码防护机制至关重要。而 “记住密码” 功能虽然极大提升了用户体验&#xff0c;但也带来了诸多安全风险。从密码存储漏洞导致的数据泄露&…...

Tensorborad

一、tensorboard的基本操作 1.1 发展历史 TensorBoard 是 TensorFlow 生态中的官方可视化工具&#xff08;也可无缝集成 PyTorch&#xff09;&#xff0c;用于实时监控训练过程、可视化模型结构、分析数据分布、对比实验结果等。它通过网页端交互界面&#xff0c;将枯燥的训练…...

Kafka存储机制核心优势剖析

文章目录 Kafka存储机制核心优势剖析1. **写入路径:Page Cache vs. 应用层缓存**2. **Page Cache工作原理解析**3. **顺序写盘 vs. 随机写盘**4. **资源利用最优化****为什么Page Cache方案更优?**1. **双缓存问题彻底解决**2. **读写路径统一优化**3. **故障恢复优势****生产…...

day027-Shell自动化编程-基础

文章目录 1. 修改vim配置文件自动添加注释2. 故障案例&#xff1a;Windows上写的Shell脚本上传到Linux系统上运行报错3. 脚本运行方法4. 变量4.1 普通变量4.2 环境变量4.3 特殊变量4.4 案例&#xff1a;书写ping检查脚本&#xff0c;检查脚本传入的第一个参数4.5 面试题&#x…...

工业自动化DeviceNET从站转Ethernet/IP主站网关赋能冶金行业工业机器人高效运行

在冶金行业高速发展的当下&#xff0c;对生产效率与精度的要求不断攀升。工业机器人凭借其精准、高效的特性&#xff0c;在钻孔、铣削、切割、弯曲、冲压等加工工艺中广泛应用。然而&#xff0c;不同设备间的通信协议差异常成为制约系统协同的瓶颈。JH-DVN-EIP疆鸿智能DeviceNE…...

STM32启动文件学习(startup_stm32f40xx.s)

原代码 ;******************** (C) COPYRIGHT 2016 STMicroelectronics ******************** ;* File Name : startup_stm32f40xx.s ;* Author : MCD Application Team ;* version : V1.8.0 ;* date : 09-November-2016 ;* Desc…...

构建高效可靠的电商 API:设计原则与实践指南

引言 在数字化浪潮中&#xff0c;电商 API 接口技术已成为连接不同系统、实现数据高效流通的核心桥梁。通过标准化的协议和工具集合&#xff0c;API 不仅支撑了商品管理、订单处理等基础功能&#xff0c;还为个性化推荐、全球供应链协同等创新场景提供了底层支持。本文将结合行…...

开源数据库MySQL 与 PostgreSQL的巅峰对决。

MySQL 与 PostgreSQL 是两大主流开源关系型数据库&#xff0c;其核心差异主要体现在架构设计、功能特性、性能优化及适用场景上。结合最新技术对比和行业实践&#xff0c;以下为深度解析&#xff1a; &#x1f9e0; ​​一、架构与设计哲学​​ ​​维度​​​​PostgreSQL​​…...

从 LeetCode 到日志匹配:一行 Swift 实现规则识别

文章目录 摘要描述题解答案题解代码分析示例测试及结果时间复杂度空间复杂度总结 摘要 在开发中我们经常遇到“模式匹配”的问题&#xff0c;比如日志分类、用户意图识别、甚至是在一些权限系统中做规则映射判断。这类问题的本质是判断两个结构是否具有一致的对应关系。LeetCo…...

前端js获取当前经纬度(H5/pc/mac/window都可用)

前端JS获取当前位置的经纬度&#xff08;H5/PC/mac/window都可用&#xff0c;亲测&#xff01;&#xff09;&#xff0c;效果如下。 完整代码如下&#xff1a; <!-- 用原生api获取经纬度&#xff0c;转化为百度经纬度与服务端交互&#xff0c; 只支持https&#xff01; --&g…...

Meta计划借助AI实现广告创作全自动化

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…...

AI编程规范失控?三大策略用Cursor Rules精准约束

​在 AI 编程时代,如何让助手精准理解您的项目规范?当团队协作时,如何确保每位开发者生成的代码风格统一?Cursor Rules 正是您需要的终极解决方案——它如同一位永不疲倦的架构师,通过预设规则控制 AI 的每一次代码生成、重构与补全行为。 本教程将带您深入 Cursor Rules…...

4.大语言模型预备数学知识

大语言模型预备数学知识 复习一下在大语言模型中用到的矩阵和向量的运算&#xff0c;及概率统计和神经网络中常用概念。 矩阵的运算 矩阵 矩阵加减法 条件&#xff1a;行数列数相同的矩阵才能做矩阵加减法 数值与矩阵的乘除法 矩阵乘法 条件&#xff1a;矩阵A的列数 矩阵…...

免费开源Umi-OCR,离线使用,批量精准!

Umi-OCR&#xff08;Windows端&#xff09; Umi-OCR 是一款在 GitHub 上开源的免费 OCR 识别软件&#xff0c;它最大的亮点就是免费、开源、支持批量处理&#xff0c;而且识别准确度很高。这款软件不需要联网就能用&#xff0c;非常值得推荐&#xff01; 在 OCR 识别功能方面&…...

NLP驱动网页数据分类与抽取实战

一、性能瓶颈点&#xff1a;数据抽取中的「三座大山」 在使用NLP技术进行网页商品数据抽取时&#xff0c;很多工程师会遇到如下三类瓶颈&#xff1a; 1. 请求延迟高&#xff1a;目标站点反爬机制灵敏&#xff0c;普通请求频繁被封。2. 结构解析慢&#xff1a;HTML结构复杂&am…...

设计模式之单例模式(二): 心得体会

设计模式之单例模式(一)-CSDN博客 目录 1.背景 2.分析 2.1.违背面向对象设计原则&#xff0c;导致职责混乱 2.2.全局状态泛滥&#xff0c;引发依赖与耦合灾难 2.3.多线程场景下风险放大&#xff0c;性能与稳定性受损 2.4.测试与维护难度指数级上升 2.5.违背 “最小知识原…...

使用Python提取PDF元数据的完整指南

PDF文档中包含着丰富的元数据信息&#xff0c;这些信息对文档管理和数据分析具有重要意义。本文将详细介绍如何利用Python高效提取PDF元数据&#xff0c;并对比主流技术方案的优劣。 ## 一、PDF元数据概述 PDF元数据&#xff08;Metadata&#xff09;是包含在文档中的结构化信…...

uni-app学习笔记十八--uni-app static目录简介

本笔记内容摘录自工程简介 | uni-app官网 一个 uni-app 工程&#xff0c;就是一个 Vue 项目&#xff0c;在完成uni-app项目创建后&#xff0c;会生成一个static目录&#xff0c; 为什么需要static这样的目录&#xff1f; uni-app编译器根据pages.json扫描需要编译的页面&…...

阿里云ACP云计算备考笔记 (3)——云存储RDS

目录 第一章 云存储概览 1、云存储通用知识 ① 发展历史 ② 云存储的优势 2、云存储分类 3、文件存储业务场景 第二章 块存储 1、块存储分类 2、云盘的优势 3、创建云盘 4、管理数据盘 ① 格式化数据盘 ② 挂载数据盘 ③ 通过 API 挂载云盘 5、管理系统盘 ① 更…...

仓颉语言---Socket编程

一、什么是Socket编程&#xff1f; 1.定义 Socket&#xff08;套接字&#xff09;可以被理解为网络上两个进程之间通信的端点。它是网络通信的抽象表示&#xff0c;封装了底层网络协议的复杂性&#xff0c;为应用程序提供了一个简单统一的接口。 Socket 编程是一种网络编程范式…...

Mysql的B-树和B+树的区别总结

B 树也称 B- 树&#xff0c;全称为 多路平衡查找树&#xff0c;B 树是 B 树的一种变体。B 树和 B 树中的 B 是 Balanced&#xff08;平衡&#xff09;的意思。 目前大部分数据库系统及文件系统都采用 B-Tree 或其变种 BTree 作为索引结构。 B 树& B 树两者有何异同呢&…...

【Java EE初阶 --- 多线程(初阶)】多线程的实现案例

乐观学习&#xff0c;乐观生活&#xff0c;才能不断前进啊&#xff01;&#xff01;&#xff01; 我的主页&#xff1a;optimistic_chen 我的专栏&#xff1a;c语言 &#xff0c;Java 欢迎大家访问~ 创作不易&#xff0c;大佬们点赞鼓励下吧~ 文章目录 前言单例模式实现单例模式…...

制作一款打飞机游戏64:关卡设计

今天我想完成第一个音乐循环的关卡设计。 初始设置 首先&#xff0c;我要删除所有之前创建的敌人和“大脑”&#xff08;可能指敌人的行为模式或AI&#xff09;。我不想保留它们&#xff0c;我要从零开始&#xff0c;重新创建敌人。但我会保留精灵&#xff08;游戏中的角色或…...

开发常用的QT mql组件

Column Column 是一种将其子项沿单个列定位的类型。它是不使用锚点的情况下垂直定位一系列项目的便捷方式。 add : Transition bottomPadding : real leftPadding : real move : Transition padding : real populate : Transition rightPadding : real spacing : rea…...