前言

本章聚焦 STM32 串口通信进阶应用,重点讲解 AT24C02 EEPROM 的配置与实战。从 I2C 总线初始化到参数掉电存储,将详细解析如何通过自定义协议实现温度告警参数的读写管理,让 STM32 具备数据 “记忆” 能力,适合想掌握工业级数据持久化方案的开发者学习。
本项目所有功能效果展示视频链接


一、AT24C02 EEPROM 驱动开发(I2C 通信核心)

模块定位:实现 STM32 与 AT24C02 的 I2C 通信,提供参数持久化存储能力。

1.I2C 总线初始化(底层通信基础):

void I2C1_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
    I2C_InitTypeDef I2C_InitStruct;

    // 1. 使能时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

    // 2. 配置 SCL (PB6) 和 SDA (PB7)
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;  // 复用开漏
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStruct);

    // 3. 配置 I2C
    I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;
    I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2;
    I2C_InitStruct.I2C_OwnAddress1 = 0x00;  // 主模式,地址随意
    I2C_InitStruct.I2C_Ack = I2C_Ack_Enable;
    I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_InitStruct.I2C_ClockSpeed = 100000;  // 100kHz
    I2C_Init(I2C1, &I2C_InitStruct);

    // 4. 使能 I2C
    I2C_Cmd(I2C1, ENABLE);
}

说明:

  • 开漏模式:I2C 总线需外接上拉电阻,确保总线空闲时为高电平,支持多设备共享总线。
  • 100kHz 速率:兼容 AT24C02 的标准模式(最高 100kHz),高速模式(400kHz)需额外配置。

2.EEPROM 读写操作

//读一个字节
uint8_t AT24C02_ByteRead(uint8_t RegAdd)
{
	uint8_t Data;
	
	I2C_GenerateSTART(I2C1, ENABLE);
	AT24C02_WaitEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT);//EV5
	
	I2C_Send7bitAddress(I2C1, 0xA0, I2C_Direction_Transmitter);
	AT24C02_WaitEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);//EV6
	
	I2C_Cmd(I2C1, ENABLE);
	
	I2C_SendData(I2C1, RegAdd);
	AT24C02_WaitEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED);//EV8_2
	
	I2C_GenerateSTART(I2C1, ENABLE);
	AT24C02_WaitEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT);//EV5
	
	I2C_Send7bitAddress(I2C1, 0xA0, I2C_Direction_Receiver);
	AT24C02_WaitEvent(I2C1,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);//EV6
	
	I2C_AcknowledgeConfig(I2C1, DISABLE);									//在接收最后一个字节之前提前将应答失能
	I2C_GenerateSTOP(I2C1, ENABLE);											//在接收最后一个字节之前提前申请停止条件
	
	AT24C02_WaitEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED);//EV7		
	Data =  I2C_ReceiveData(I2C1);
	
	I2C_AcknowledgeConfig(I2C1, ENABLE);									//将应答恢复为使能,为了不影响后续可能产生的读取多字节操作
	return Data;
}


//写入多个字节
/**
  * @brief  向EEPROM写入一页数据(最大8字节)
  * @param  pBuffer: 要写入的数据缓冲区
  * @param  WriteAddr: 写入起始地址 (0-255)
  * @param  NumByteToWrite: 要写入的字节数(不超过 I2C_PageSize)
  * @retval 无
  */
void I2C_EE_PageWrite(uint8_t* pBuffer, uint8_t WriteAddr, uint8_t NumByteToWrite)
{
    /* 启动I2C通信 */
    I2C_GenerateSTART(I2C1, ENABLE);
    AT24C02_WaitEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT);  // EV5: 检测START条件成功

    /* 发送设备地址(写模式) */
    I2C_Send7bitAddress(I2C1, 0xA0, I2C_Direction_Transmitter);
    AT24C02_WaitEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);  // EV6: 地址发送成功

    /* 发送要写入的EEPROM内部地址 */
    I2C_SendData(I2C1, WriteAddr);
    AT24C02_WaitEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING);  // EV8: 数据发送中

    /* 循环写入数据 */
    while (NumByteToWrite--)
    {
        I2C_SendData(I2C1, *pBuffer++);
        if (NumByteToWrite == 0)  // 如果是最后一个字节,等待传输完成
        {
            AT24C02_WaitEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED);  // EV8_2: 数据发送完成
        }
        else  // 否则等待继续发送
        {
            AT24C02_WaitEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING);  // EV8: 数据发送中
        }
    }

    /* 停止I2C通信 */
    I2C_GenerateSTOP(I2C1, ENABLE);
}

说明:

  • 设备地址:AT24C02 的 8 位地址为0xA0。
  • 页写入限制:AT24C02 每页 8 字节(I2C_PageSize=8),超过页大小需分多次写入,避免数据覆盖。

3.分页写入优化(提升批量写入效率)

/**
  * @brief  向EEPROM写入任意长度数据(自动处理跨页)
  * @param  pBuffer: 要写入的数据缓冲区
  * @param  WriteAddr: 写入起始地址 (0-255)
  * @param  NumByteToWrite: 要写入的字节数
  * @retval 无
  */
void I2C_EE_BufferWrite(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite)
{
    uint8_t remainingInPage;  // 当前页剩余可写入字节数
    uint8_t writeSize;        // 本次写入的字节数

    while (NumByteToWrite > 0)
    {
        /* 计算当前页剩余空间 */ //假设写入
        remainingInPage = I2C_PageSize - (WriteAddr % I2C_PageSize);
        
        /* 本次写入的字节数 = min(剩余数据, 当前页剩余空间) */
        if (NumByteToWrite > remainingInPage)
				{
					writeSize = remainingInPage;  // 本次只能写满当前页剩余空间
				}
				else
				{
					writeSize = NumByteToWrite;   // 剩余数据不足一页,全部写完
				}

        /* 执行页写入 */
        I2C_EE_PageWrite(pBuffer, WriteAddr, writeSize);
        
        /* 等待EEPROM内部写入完成(AT24C02需要约5ms) */
        Delay_ms(5);  // 或者用 AT24C02_WaitStandby() 检测ACK
        
        /* 更新指针、地址和剩余字节数 */
        pBuffer += writeSize;
        WriteAddr += writeSize;
        NumByteToWrite -= writeSize;
    }
}

说明:

  • 页写入特性:AT24C02 支持每页 8 字节连续写入,超过页边界会触发 “页翻转”,导致前半页数据被覆盖。
  • 延时必要性:EEPROM 内部写入需要时间(约 5ms),必须等待完成才能进行下一次写入。

二、参数初始化与掉电保护(系统数据管理)

模块定位:实现系统参数的上电初始化与掉电持久化,确保设备重启后参数不丢失。

1.上电初始化逻辑(首次配置参数)

// 上电检测与参数初始化
void Power_On_Write_Para(void)
{
    uint8_t checkFlag[2] = {0};
    // 读取EEPROM中的初始化标志位(地址:EEP_INIT_WRITEPARA_ADDR)
    Flash_Read_Fun(EEP_INIT_WRITEPARA_ADDR, checkFlag, 2);
    
    // 若标志位不是0x5D 0x5D,说明首次上电或参数异常
    if ((checkFlag[0] != 0x5D) || (checkFlag[1] != 0x5D)) {
        Write_Para();                  // 写入默认参数
        USART1_WritePara_Fun();        // 通过串口上报参数
    }
}

// 上电参数读取
void Power_On_Read_Para(void)
{
    uint8_t i;
    // 从EEPROM读取参数存入全局数组Para_Buf
    for (i = 0; i <= sizeof(threshold_params); i++) {
        Para_Buf[i] = AT24C02_ByteRead(SYSTEM_PARA_TEMP_ADDR + i);
    }
}

说明:

  • 标志位机制:通过检测特定地址的标志位(0x5D 0x5D)判断是否需要初始化,避免重复写入。
  • 数据一致性:参数读取后存入全局数组Para_Buf,供系统各模块共享访问。

三、温度告警参数管理(业务逻辑实现)

模块定位:基于自定义协议实现温度告警参数的串口传输与本地存储

1协议帧构造与发送(参数上报)

void SendTempParaData(void)
{
	uint8_t i = 0;
	ShareBuf[0] = 0x7E; // 起始符
	ShareBuf[1] = 0x46; // CID1
	ShareBuf[2] = 0x02; // RNT
	ShareBuf[3] = 0x20; // Len
	for(i=0;i<32;i++)
	{
		ShareBuf[4+i] = Para_Buf[i];
	}
	
	ShareBuf[36] = CalculateCRC8(&ShareBuf[1], ShareBuf[3] + 3);; // CRC
	ShareBuf[37] = 0xEF; // 结束符
	
	Usart1_Transmitter(ShareBuf, ShareBuf[3] + 6); // 发送数据

}

说明:

  • 协议帧结构:遵循 “SOI+CID1+CID2+LENGTH+INFO+CRC+EOI” 格式,与第二章定义一致
  • CRC 校验范围:仅校验 CID1、CID2、LENGTH、INFO,不包含 SOI 和 EOI。

2.参数修改与存储

void Wirte_TempParameter(void)
{
	uint8_t i = 0;
	for(i=0;i<32;i++)
	{
		Para_Buf[i] = Calibrate_Buf[i];
	}
	
	I2C_EE_BufferWrite(&Para_Buf[0],SYSTEM_PARA_TEMP_ADDR,32);

	Delay(5000);
}

说明:

  • 双缓冲区机制:Calibrate_Buf用于暂存修改参数,确认后同步到Para_Buf并写入 EEPROM
  • 延时保障:5ms 延时确保 EEPROM 完成内部写入,避免断电导致数据丢失。

四、模块协同工作流程(整体逻辑串联)

1.系统启动流程

上电 → I2C初始化 → 读取初始化标志位 → 若未初始化则写入默认参数 → 读取EEPROM参数到内存

2.参数修改流程

串口接收修改指令 → 解析参数存入Calibrate_Buf → 同步到Para_Buf → 分页写入EEPROM → 发送确认响应

2.参数上报流程

串口接收读取指令 → 从Para_Buf读取参数 → 构造协议帧 → 计算CRC → 串口发送响应帧

五、后续内容更新

  1. 下期预告
    CSDN栏 模块一章节一《STM32 串口通信进阶:控制指令与数据存储实战----故障存储配置》
  2. 互动福利
    评论区留言 “协议”,赠送《本系列通信协议》PDF。
    B站搜索相同ID:通信翻车员,可查看相关视频讲解
    STM32 串口通信协议开发实战全攻略:STM32 串口通信・数据存储・Bootloader 全案例解析
Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐