1 概述
1.1 實驗目的
????????本實驗旨在通過?STM32 微控制器的硬件 I2C 接口,對?AT24C02 外部 EEPROM 存儲芯片?進行讀寫操作。實驗演示了芯片地址配置、單字節/多字節讀寫、跨頁寫入(Page Write)功能。并提供完整的驅動代碼,幫助讀者深入理解?STM32 硬件 I2C 通信協議?的工作機制,并掌握基于?I2C 總線的低速存儲設備?的數據存儲與訪問方法。
????????通過本實驗,讀者不僅可以掌握?I2C 接口的基本使用技巧,還能夠熟悉?AT24C02 的命令結構、存儲特性及應用方法。這對于日后在嵌入式系統開發中涉及?小容量數據存儲(如配置參數、掉電保存數據、傳感器校準值等)?提供了技術積累和實踐基礎,具有良好的工程指導意義。
1.2 IIC協議
????????I2C(Inter-Integrated Circuit)是一種同步、半雙工、多主多從的串行通信協議。它通過兩根信號線(SCL時鐘線、SDA數據線)實現設備間通信,廣泛應用于傳感器、存儲器、顯示器等低速外設與主控制器(如MCU)的連接。接口引腳
引腳名稱 | 全稱 | 功能說明 | 方向 | 特點 |
---|---|---|---|---|
SCL | Serial Clock | 串行時鐘信號,由主設備(Master)產生,用于同步數據傳輸 | 主輸出從輸入 | 開漏/開集電極輸出,需要上拉電阻 |
SDA | Serial Data | 串行數據信號,雙向傳輸數據(主、從設備之間共用) | 雙向 | 開漏/開集電極輸出,需要上拉電阻 |
1.3 AT24C02介紹
1.3.1 芯片引腳
引腳號 | 名稱 | 功能說明 |
---|---|---|
1 | A0 | 器件地址選擇引腳(Address Input 0),用于設定 I2C 從機地址的低位。可接 GND 或 VCC。 |
2 | A1 | 器件地址選擇引腳(Address Input 1),作用同 A0。 |
3 | A2 | 器件地址選擇引腳(Address Input 2),作用同 A0。 |
4 | GND | 電源地。 |
5 | SDA | 串行數據線(Serial Data)。I2C 雙向數據線,開漏輸出,需要上拉電阻。 |
6 | SCL | 串行時鐘線(Serial Clock)。由 I2C 主機產生,開漏輸出,需要上拉電阻。 |
7 | WP | 寫保護(Write Protect)。高電平時禁止寫操作(只讀),低電平時可讀可寫。 |
8 | VCC | 電源正極(常用 2.7V–5.5V)。 |
1.3.2 芯片地址
????????AT24C 系列 EEPROM 的 I2C 從機地址由固定的高 4 位(1010
)和由引腳 A2、A1、A0 決定的 3 位地址位組成,最后一位是讀寫控制位(R/W)。A0~A2 引腳可接高電平(VCC)或低電平(GND)來改變芯片地址,從而在同一總線上使用多個 EEPROM 設備。
寫操作地址:R/W = 0
讀操作地址:R/W = 1
因此,芯片最多可以有8種地址,枚舉如下
A2 | A1 | A0 | 7 位地址(十六進制) | 寫地址(8 位) | 讀地址(8 位) |
---|---|---|---|---|---|
0 | 0 | 0 | 0x50 | 0xA0 | 0xA1 |
0 | 0 | 1 | 0x51 | 0xA2 | 0xA3 |
0 | 1 | 0 | 0x52 | 0xA4 | 0xA5 |
0 | 1 | 1 | 0x53 | 0xA6 | 0xA7 |
1 | 0 | 0 | 0x54 | 0xA8 | 0xA9 |
1 | 0 | 1 | 0x55 | 0xAA | 0xAB |
1 | 1 | 0 | 0x56 | 0xAC | 0xAD |
1 | 1 | 1 | 0x57 | 0xAE | 0xAF |
1.3.3?其他型號
型號 | 尋址范圍 | 頁大小(Byte) | 頁數量 | 總容量(Byte) | 總容量(Kbit) |
---|---|---|---|---|---|
AT24C01 | 0x00 – 0x7F | 8 | 16 | 128 | 1 Kbit |
AT24C02 | 0x00 – 0xFF | 8 | 32 | 256 | 2 Kbit |
AT24C04 | 0x000 – 0x1FF | 16 | 32 | 512 | 4 Kbit |
AT24C08 | 0x000 – 0x3FF | 16 | 64 | 1024 | 8 Kbit |
AT24C16 | 0x000 – 0x7FF | 16 | 128 | 2048 | 16 Kbit |
AT24C32 | 0x0000 – 0x0FFF | 32 | 128 | 4096 | 32 Kbit |
AT24C64 | 0x0000 – 0x1FFF | 32 | 256 | 8192 | 64 Kbit |
AT24C128 | 0x0000 – 0x3FFF | 64 | 256 | 16384 | 128 Kbit |
AT24C256 | 0x0000 – 0x7FFF | 64 | 512 | 32768 | 256 Kbit |
AT24C512 | 0x0000 – 0xFFFF | 128 | 512 | 65536 | 512 Kbit |
注:在閱讀芯片資料時,AT24C02的頁大小是8字節,但在實際測試過程中,我發現芯片頁大小其實是16字節。其實是芯片版本不同導致的,我芯片具體型號是Atmel327 24C02BN 其中?“24C02BN” 里的 B
版本,就是 16 Byte 頁大小的版本。而 N
一般表示封裝(SOIC、PDIP、TSSOP 等)及無鉛環保版本,不影響存儲結構。
版本標識 | 頁大小 | 總容量 | 備注 |
---|---|---|---|
AT24C02A / AT24C02C | 8 Byte | 256 Byte | 經典版本,很多資料引用這個 |
AT24C02B / AT24C02BN | 16 Byte | 256 Byte | 后期改進版,頁寫效率更高 |
1.3.4 與flash區別
對比項 | AT24C02(I2C EEPROM) | W25Q32(SPI NOR Flash) |
---|---|---|
總容量 | 256 Byte | 4 MByte |
接口 | I2C(2 線) | SPI(4 線) |
最小寫入單元 | 1 Byte(頁寫一次最多 8 Byte) | 1 Byte(頁編程一次最多 256 Byte) |
最小擦除單元 | 無需擦除,自動覆蓋 | 4KB 扇區 |
擦寫機制 | 自動擦寫,直接覆蓋 | 擦除→寫入(1→0,0→1 需擦除) |
寫入延遲 | 固定 5ms 左右/頁寫 | 擦除慢(50~150ms),寫快(<1ms) |
壽命 | 100 萬次寫/字節 | 10 萬次擦/扇區 |
斷電數據保持 | 典型 100 年 | 典型 20 年 |
使用場景 | 小容量配置存儲 | 大容量固件、資源存儲 |
????????AT24C02芯片是EEPROM類型芯片,寫入不用擦除,相對方便,字節在對應地址中寫入數據即可,如果原來有就覆蓋。
2. STM32CubeMX 配置
2.1 SYS配置
不管你用的是STLink 還是JLink,都屬于并行調試下載,所以在配置SYS時,均以下圖為準。
2.2 RCC配置
2.3 USART配置
用于串口打印日志,調試用
2.4 IIC配置
????????由于芯片連接的是stm32通用引腳PB10和PB11,這兩個引腳的復用接口是stm32第二個IIC的接口,所以此處配置要選IIC2和硬件連線相匹配。
2.5 project配置
3. Keil MDK配置
3.1 debug配置
?根據自己的下載器選擇debug現象,作者用的JLink
避免每次下載完程序后,還要按復位鍵才能跑最新程序
3.2 target配置
為了使用串口進行調試重定向打印調試信息,在target設置卡中引入LIB包
3.3 新增文件夾及文件
3.4 配置工程目錄
3.5 配置工程文件
4. VSCode代碼
4.1 驅動文件
#include "at24c02.h"
#include "string.h"
/**
特性 M24C02 (2Kbit) M24C32 (32Kbit)
容量 256字節 (0x00-0xFF) 4096字節 (0x000-0xFFF)
地址位寬 8位地址 16位地址(需2字節)
頁大小 16字節/頁 32字節/頁
頁數量 16頁 128頁
設備地址 固定(A2-A0=0) 固定(A2-A0=0)24C32
第 0 頁:0x0000 ~ 0x001F(32 字節)
第 1 頁:0x0020 ~ 0x003F(32 字節)
第 2 頁:0x0040 ~ 0x005F(32 字節)
...
第 127 頁:0x0F80 ~ 0x0FFF(32 字節)
總字節數:128 頁 × 32 字節/頁 = 4,096 字節(與 AT24C32 容量一致)。24C02
第 0 頁:0x00 ~ 0x0F(16 字節)
第 1 頁:0x10 ~ 0x1F(16 字節)
第 2 頁:0x20 ~ 0x2F(16 字節)
...
第 15 頁:0xF0 ~ 0xFF(16 字節)
總字節數:16 頁 × 16 字節/頁 = 256 字節(與 AT24C02 容量一致)。*///向EEPROM寫入一個字節
void AT24C02_WriteByte(uint8_t innerAddr, uint8_t byte){HAL_I2C_Mem_Write(&hi2c2, W_ADDR, innerAddr, I2C_MEMADD_SIZE_8BIT , &byte , 1 , 1000);// 延遲等待寫入周期結束HAL_Delay(5);
}
//讀取EEPROM一個字節
uint8_t AT24C02_ReadByte(uint8_t innerAddr){uint8_t byte;HAL_I2C_Mem_Read(&hi2c2, R_ADDR, innerAddr, I2C_MEMADD_SIZE_8BIT , &byte , 1 ,1000);return byte;
}
//連續寫入多個字節(頁寫入)
void AT24C02_WriteBytes(uint8_t innerAddr, uint8_t *bytes, uint8_t size){HAL_I2C_Mem_Write(&hi2c2, W_ADDR, innerAddr, I2C_MEMADD_SIZE_8BIT , bytes, size , 1000);// 延遲等待寫入周期結束HAL_Delay(5);
}
//連續讀出多個字節(頁讀出 等價于 跨頁讀出)
void AT24C02_ReadBytes(uint8_t innerAddr, uint8_t *buffer, uint8_t size){HAL_I2C_Mem_Read(&hi2c2, R_ADDR, innerAddr, I2C_MEMADD_SIZE_8BIT , buffer , size ,1000);
}
//跨頁寫入
void AT24C02_WriteBytesPro(uint8_t innerAddr, uint8_t *data, uint8_t size) {while (size > 0) {uint8_t page_offset = innerAddr % EEPROM_PAGE_SIZE; // AT24C02 頁大小通常是 8 或 16uint8_t page_space = EEPROM_PAGE_SIZE - page_offset;uint8_t bytes_to_write = (size < page_space) ? size : page_space;// 使用 HAL_I2C_Mem_Write 自動處理地址HAL_I2C_Mem_Write(&hi2c2, W_ADDR, innerAddr, I2C_MEMADD_SIZE_8BIT,data, bytes_to_write, HAL_MAX_DELAY);HAL_Delay(5); // AT24C02 寫入需要 5ms// 更新指針和剩余長度innerAddr += bytes_to_write; // 確保這里正確遞增data += bytes_to_write;size -= bytes_to_write;}
}
#ifndef __AT24C02_H__
#define __AT24C02_H__#include "i2c.h"#define W_ADDR 0xA0
#define R_ADDR 0xA1
// 每頁寫入限制大小16字節
#define EEPROM_PAGE_SIZE 16//向EEPROM寫入一個字節
void AT24C02_WriteByte(uint8_t innerAddr, uint8_t byte);
//讀取EEPROM一個字節
uint8_t AT24C02_ReadByte(uint8_t innerAddr);
//連續寫入多個字節(頁寫入)
void AT24C02_WriteBytes(uint8_t innerAddr, uint8_t * bytes, uint8_t size);
//連續讀出多個字節(可跨頁讀出)
void AT24C02_ReadBytes(uint8_t innerAddr, uint8_t * buffer, uint8_t size);//跨頁寫入函數
void AT24C02_WriteBytesPro(uint8_t innerAddr, uint8_t *data, uint8_t size);
#endif
4.2 usart重定向代碼
/* USER CODE BEGIN Header */
/********************************************************************************* @file usart.c* @brief This file provides code for the configuration* of the USART instances.******************************************************************************* @attention** Copyright (c) 2025 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "usart.h"/* USER CODE BEGIN 0 *//* USER CODE END 0 */UART_HandleTypeDef huart1;/* USART1 init function */void MX_USART1_UART_Init(void)
{/* USER CODE BEGIN USART1_Init 0 *//* USER CODE END USART1_Init 0 *//* USER CODE BEGIN USART1_Init 1 *//* USER CODE END USART1_Init 1 */huart1.Instance = USART1;huart1.Init.BaudRate = 115200;huart1.Init.WordLength = UART_WORDLENGTH_8B;huart1.Init.StopBits = UART_STOPBITS_1;huart1.Init.Parity = UART_PARITY_NONE;huart1.Init.Mode = UART_MODE_TX_RX;huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;huart1.Init.OverSampling = UART_OVERSAMPLING_16;if (HAL_UART_Init(&huart1) != HAL_OK){Error_Handler();}/* USER CODE BEGIN USART1_Init 2 *//* USER CODE END USART1_Init 2 */}void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{GPIO_InitTypeDef GPIO_InitStruct = {0};if(uartHandle->Instance==USART1){/* USER CODE BEGIN USART1_MspInit 0 *//* USER CODE END USART1_MspInit 0 *//* USART1 clock enable */__HAL_RCC_USART1_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();/**USART1 GPIO ConfigurationPA9 ------> USART1_TXPA10 ------> USART1_RX*/GPIO_InitStruct.Pin = GPIO_PIN_9;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);GPIO_InitStruct.Pin = GPIO_PIN_10;GPIO_InitStruct.Mode = GPIO_MODE_INPUT;GPIO_InitStruct.Pull = GPIO_NOPULL;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);/* USART1 interrupt Init */HAL_NVIC_SetPriority(USART1_IRQn, 4, 0);HAL_NVIC_EnableIRQ(USART1_IRQn);/* USER CODE BEGIN USART1_MspInit 1 *//* USER CODE END USART1_MspInit 1 */}
}void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{if(uartHandle->Instance==USART1){/* USER CODE BEGIN USART1_MspDeInit 0 *//* USER CODE END USART1_MspDeInit 0 *//* Peripheral clock disable */__HAL_RCC_USART1_CLK_DISABLE();/**USART1 GPIO ConfigurationPA9 ------> USART1_TXPA10 ------> USART1_RX*/HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10);/* USART1 interrupt Deinit */HAL_NVIC_DisableIRQ(USART1_IRQn);/* USER CODE BEGIN USART1_MspDeInit 1 *//* USER CODE END USART1_MspDeInit 1 */}
}/* USER CODE BEGIN 1 */
int fputc(int ch, FILE * file){ //打印重定向HAL_UART_Transmit(&huart1,(uint8_t*)&ch,1,1000);return ch;
}
/* USER CODE END 1 */
/* USER CODE BEGIN Header */
/********************************************************************************* @file usart.h* @brief This file contains all the function prototypes for* the usart.c file******************************************************************************* @attention** Copyright (c) 2025 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __USART_H__
#define __USART_H__#ifdef __cplusplus
extern "C" {
#endif/* Includes ------------------------------------------------------------------*/
#include "main.h"/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */extern UART_HandleTypeDef huart1;/* USER CODE BEGIN Private defines *//* USER CODE END Private defines */void MX_USART1_UART_Init(void);/* USER CODE BEGIN Prototypes *//* USER CODE END Prototypes */#ifdef __cplusplus
}
#endif#endif /* __USART_H__ */
4.3 主函數
/* USER CODE BEGIN Header */
/********************************************************************************* @file : main.c* @brief : Main program body******************************************************************************* @attention** Copyright (c) 2025 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "at24c02.h"
/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 *//* USER CODE END 0 *//*** @brief The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_USART1_UART_Init();MX_I2C2_Init();/* USER CODE BEGIN 2 */printf("hello my at24c02\t\n");//讀寫單個字節AT24C02_WriteByte(0x00,'A');uint8_t byte1 = AT24C02_ReadByte(0x00);printf("byte1= %c\t" , byte1);// 讀寫多個字節AT24C02_WriteBytes(0x00,"123456",6);uint8_t buffer[100] = {0};AT24C02_ReadBytes(0x00,buffer,6);printf("buffer= %s\n" , buffer);// 跨頁讀寫uint8_t bufferPro[256] = {0};AT24C02_WriteBytesPro(0x00,"Hello darkness, my old friend,I've come to talk with you again,Because a vision softly creeping, Left its seeds while I was sleeping, And the vision that was planted in my brain Still remains Within the sound of silence.",255);AT24C02_ReadBytes(0x00,bufferPro,255);printf("bufferPro= %s\n" , bufferPro);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */HAL_Delay(3000);}/* USER CODE END 3 */
}/*** @brief System Clock Configuration* @retval None*/
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;RCC_OscInitStruct.HSIState = RCC_HSI_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks*/RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK){Error_Handler();}
}/* USER CODE BEGIN 4 *//* USER CODE END 4 *//*** @brief This function is executed in case of error occurrence.* @retval None*/
void Error_Handler(void)
{/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state */__disable_irq();while (1){}/* USER CODE END Error_Handler_Debug */
}#ifdef USE_FULL_ASSERT
/*** @brief Reports the name of the source file and the source line number* where the assert_param error has occurred.* @param file: pointer to the source file name* @param line: assert_param error line source number* @retval None*/
void assert_failed(uint8_t *file, uint32_t line)
{/* USER CODE BEGIN 6 *//* User can add his own implementation to report the file name and line number,ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
5. 結論
????????本次實驗?成功驗證了 AT24C02 EEPROM 的基本讀寫功能,包括單字節、多字節連續讀寫和跨頁讀寫。通過STM32硬件I2C 協議與芯片通信,確保了數據存儲的準確性和可靠性。實驗結果符合預期,為后續?數據掉電存儲、傳感器數據記錄?等應用奠定了基礎。注意事項:
- I2C 地址參數:AT24C02 必須使用?
I2C_MEMADD_SIZE_8BIT
。 - 頁邊界處理:跨頁寫入需分頁處理,避免數據丟失,芯片24C02帶后綴B版本頁大小為16字節。
- 寫入延遲:EEPROM 寫入后需等待?5ms?確保數據固化。
- 芯片地址:注意查看芯片地址引腳接線,判斷地址。
????????如果讀者采用AT24C32芯片,那么上述驅動文件是不可共用的,因為AT24C32的地址是兩個字節,且頁存儲為32字節,共128頁,總存儲32Kbit,是AT24C02的16倍,存儲空間結構完全不同。如果讀者感興趣,可以評論區留言,作者提供AT24C32相關的驅動文件。