https://item.taobao.com/item.htm?id=643663364434&mi_id=0000kQ3l-yHFJ4NI2oQRgc6KtbMi9MOZAtVKERJZEHa5i3g&spm=a21xtw.29978518.0.0&xxc=shop&skuId=4639727285420

https://item.taobao.com/item.htm?id=567263971997&mi_id=00007DFFcCPiDLySKgTpP_DpAd2yRCrbX4KS5OZhHwFLNUg&skuId=4359131388614&spm=a21xtw.29978518.0.0&xxc=shop

1、

2、

3、w25q32.h

#ifndef __W25Q32_H
#define __W25Q32_H

#include "spi.h"
#include "gpio.h"
#include <stdint.h>

// W25Q32命令
#define W25QXX_WRITE_ENABLE     0x06
#define W25QXX_WRITE_DISABLE    0x04
#define W25QXX_READ_STATUS1     0x05
#define W25QXX_WRITE_STATUS1    0x01
#define W25QXX_READ_DATA        0x03
#define W25QXX_FAST_READ        0x0B
#define W25QXX_PAGE_PROGRAM     0x02
#define W25QXX_SECTOR_ERASE     0x20
#define W25QXX_BLOCK_ERASE      0xD8
#define W25QXX_CHIP_ERASE       0xC7
#define W25QXX_POWER_DOWN       0xB9
#define W25QXX_RELEASE_POWER_DOWN 0xAB
#define W25QXX_DEVICE_ID        0x90
#define W25QXX_MANUF_DEVICE_ID  0x90
#define W25QXX_JEDEC_DEVICE_ID  0x9F

// 片选引脚 (PA4)
#define W25QXX_CS_LOW()   HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET)
#define W25QXX_CS_HIGH()  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET)

// 函数声明
uint8_t W25Q32_Init(void);
uint16_t W25Q32_ReadID(void);
uint8_t W25Q32_ReadStatus(void);
void W25Q32_WriteEnable(void);
void W25Q32_WaitBusy(void);
void W25Q32_WriteByte(uint32_t addr, uint8_t data);
void W25Q32_ReadBytes(uint32_t addr, uint8_t *data, uint16_t len);
void W25Q32_WriteBytes(uint32_t addr, uint8_t *data, uint16_t len);
void W25Q32_EraseSector(uint32_t addr);

#endif

w25q32.c

#include "w25q32.h"
#include "spi.h"
#include "gpio.h"

// SPI收发一个字节
static uint8_t W25Q32_SPI_ReadWriteByte(uint8_t txData)
{
    uint8_t rxData;
    HAL_SPI_TransmitReceive(&hspi1, &txData, &rxData, 1, 100);
    return rxData;
}

// 初始化
uint8_t W25Q32_Init(void)
{
    W25QXX_CS_HIGH();
    
    uint16_t id = W25Q32_ReadID();
    
    // 支持多个制造商:0xEF=Winbond, 0x20=Micron, 0xC2=Macronix, 0x1F=Adesto
    if((id >> 8) == 0xEF || (id >> 8) == 0x20 || (id >> 8) == 0xC2 || (id >> 8) == 0x1F)
        return 1;
    else
        return 0;
}

// 读取芯片ID
uint16_t W25Q32_ReadID(void)
{
    uint16_t temp = 0;
    uint8_t manufacturer_id, device_id;
    
    W25QXX_CS_LOW();
    HAL_Delay(1);
    
    W25Q32_SPI_ReadWriteByte(0x9F);
    
    manufacturer_id = W25Q32_SPI_ReadWriteByte(0xFF);
    device_id = W25Q32_SPI_ReadWriteByte(0xFF);
    W25Q32_SPI_ReadWriteByte(0xFF);
    
    W25QXX_CS_HIGH();
    
    temp = (manufacturer_id << 8) | device_id;
    
    return temp;
}

// 读取状态寄存器
uint8_t W25Q32_ReadStatus(void)
{
    uint8_t byte = 0;
    
    W25QXX_CS_LOW();
    W25Q32_SPI_ReadWriteByte(W25QXX_READ_STATUS1);
    byte = W25Q32_SPI_ReadWriteByte(0xFF);
    W25QXX_CS_HIGH();
    
    return byte;
}

// 写使能
void W25Q32_WriteEnable(void)
{
    W25QXX_CS_LOW();
    W25Q32_SPI_ReadWriteByte(W25QXX_WRITE_ENABLE);
    W25QXX_CS_HIGH();
}

// 等待忙标志
void W25Q32_WaitBusy(void)
{
    while((W25Q32_ReadStatus() & 0x01) == 0x01);
}

// 写入一个字节
void W25Q32_WriteByte(uint32_t addr, uint8_t data)
{
    W25Q32_WaitBusy();
    W25Q32_WriteEnable();
    
    W25QXX_CS_LOW();
    W25Q32_SPI_ReadWriteByte(W25QXX_PAGE_PROGRAM);
    W25Q32_SPI_ReadWriteByte((uint8_t)(addr >> 16));
    W25Q32_SPI_ReadWriteByte((uint8_t)(addr >> 8));
    W25Q32_SPI_ReadWriteByte((uint8_t)addr);
    W25Q32_SPI_ReadWriteByte(data);
    W25QXX_CS_HIGH();
    
    W25Q32_WaitBusy();
}

// 读取多个字节
void W25Q32_ReadBytes(uint32_t addr, uint8_t *data, uint16_t len)
{
    W25Q32_WaitBusy();
    
    W25QXX_CS_LOW();
    W25Q32_SPI_ReadWriteByte(W25QXX_READ_DATA);
    W25Q32_SPI_ReadWriteByte((uint8_t)(addr >> 16));
    W25Q32_SPI_ReadWriteByte((uint8_t)(addr >> 8));
    W25Q32_SPI_ReadWriteByte((uint8_t)addr);
    
    for(uint16_t i = 0; i < len; i++)
    {
        data[i] = W25Q32_SPI_ReadWriteByte(0xFF);
    }
    W25QXX_CS_HIGH();
}

// 写入多个字节
void W25Q32_WriteBytes(uint32_t addr, uint8_t *data, uint16_t len)
{
    uint16_t i;
    
    W25Q32_WaitBusy();
    W25Q32_WriteEnable();
    
    W25QXX_CS_LOW();
    W25Q32_SPI_ReadWriteByte(W25QXX_PAGE_PROGRAM);
    W25Q32_SPI_ReadWriteByte((uint8_t)(addr >> 16));
    W25Q32_SPI_ReadWriteByte((uint8_t)(addr >> 8));
    W25Q32_SPI_ReadWriteByte((uint8_t)addr);
    
    for(i = 0; i < len; i++)
    {
        W25Q32_SPI_ReadWriteByte(data[i]);
    }
    W25QXX_CS_HIGH();
    
    W25Q32_WaitBusy();
}

// 擦除扇区(4KB)
void W25Q32_EraseSector(uint32_t addr)
{
    W25Q32_WaitBusy();
    W25Q32_WriteEnable();
    
    W25QXX_CS_LOW();
    W25Q32_SPI_ReadWriteByte(W25QXX_SECTOR_ERASE);
    W25Q32_SPI_ReadWriteByte((uint8_t)(addr >> 16));
    W25Q32_SPI_ReadWriteByte((uint8_t)(addr >> 8));
    W25Q32_SPI_ReadWriteByte((uint8_t)addr);
    W25QXX_CS_HIGH();
    
    W25Q32_WaitBusy();
}

at24c02.h

#ifndef __AT24C02_H
#define __AT24C02_H

#include "i2c.h"
#include <stdint.h>

#define AT24C02_ADDR        0xA0  // AT24C02设备地址(写)
#define AT24C02_PAGE_SIZE   8     // 页大小8字节

// 函数声明
uint8_t AT24C02_WriteByte(uint16_t addr, uint8_t data);
uint8_t AT24C02_ReadByte(uint16_t addr);
uint8_t AT24C02_WritePage(uint16_t addr, uint8_t *data, uint16_t len);
uint8_t AT24C02_ReadBytes(uint16_t addr, uint8_t *data, uint16_t len);

#endif

at24c02.c

#include "at24c02.h"
#include "i2c.h"
#include <string.h>

// 写入一个字节
uint8_t AT24C02_WriteByte(uint16_t addr, uint8_t data)
{
    uint8_t status;
    
    // 发送起始信号和设备地址(写)
    status = HAL_I2C_Mem_Write(&hi2c1, AT24C02_ADDR, addr, I2C_MEMADD_SIZE_8BIT, &data, 1, 100);
    
    if(status == HAL_OK)
        return 1;
    else
        return 0;
}

// 读取一个字节
uint8_t AT24C02_ReadByte(uint16_t addr)
{
    uint8_t data;
    
    HAL_I2C_Mem_Read(&hi2c1, AT24C02_ADDR, addr, I2C_MEMADD_SIZE_8BIT, &data, 1, 100);
    
    return data;
}

// 写入一页数据(最多8字节)
// 写入一页数据(最多8字节,且不能跨页)
uint8_t AT24C02_WritePage(uint16_t addr, uint8_t *data, uint16_t len)
{
    uint16_t i;
    uint8_t status;
    
    for(i = 0; i < len; i++)
    {
        // 单字节写入
        status = HAL_I2C_Mem_Write(&hi2c1, AT24C02_ADDR, 
                                   addr + i, I2C_MEMADD_SIZE_8BIT, 
                                   &data[i], 1, 100);
        if(status != HAL_OK)
            return 0;
        
        HAL_Delay(10);  // 等待写入完成
    }
    
    return 1;
}

// 连续读取多个字节
uint8_t AT24C02_ReadBytes(uint16_t addr, uint8_t *data, uint16_t len)
{
    uint8_t status = HAL_I2C_Mem_Read(&hi2c1, AT24C02_ADDR, addr, I2C_MEMADD_SIZE_8BIT, data, len, 100);
    
    return (status == HAL_OK) ? 1 : 0;
}

4、main.c

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2026 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 "spi.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h> 
#include <string.h> 
#include "at24c02.h"
#include "w25q32.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 */
uint8_t test_data[] = "Hello STM32!";
uint8_t read_data[32] = {0};
uint8_t eeprom_data[16] = {0};
/* 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--------------------------------------------------------*/
  HAL_Init();
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */
  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_I2C1_Init();
  MX_USART1_UART_Init();
  MX_SPI1_Init();

  /* USER CODE BEGIN 2 */
printf("\r\n=== EEPROM & FLASH Test ===\r\n");
HAL_Delay(100);

/* ========== AT24C02 Test ========== */
printf("\r\n[1] AT24C02:\r\n");

AT24C02_WritePage(0x00, test_data, strlen((char*)test_data));
HAL_Delay(20);

AT24C02_ReadBytes(0x00, eeprom_data, strlen((char*)test_data));

if(strcmp((char*)test_data, (char*)eeprom_data) == 0)
    printf("    [OK] Write & Read Passed!\r\n");
else
    printf("    [FAIL] Data Mismatch!\r\n");

/* ========== W25Q32 Test ========== */
printf("\r\n[2] W25Q32:\r\n");

if(W25Q32_Init())
{
    W25Q32_EraseSector(0x000000);
    HAL_Delay(500);
    
    W25Q32_WriteBytes(0x000000, test_data, strlen((char*)test_data));
    HAL_Delay(20);
    
    W25Q32_ReadBytes(0x000000, read_data, strlen((char*)test_data));
    
    if(strcmp((char*)test_data, (char*)read_data) == 0)
        printf("    [OK] Erase, Write & Read Passed!\r\n");
    else
        printf("    [FAIL] Data Mismatch!\r\n");
}
else
{
    printf("    [FAIL] Init Failed!\r\n");
}

printf("\r\n=== Test Complete ===\r\n");
/* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_8);
    HAL_Delay(500);
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  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();
  }

  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 */
int fputc(int ch, FILE *f)      
{
    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
    return ch;
}
int fgetc(FILE *f)            
{
    uint8_t ch;
    HAL_UART_Receive(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
    return ch;
}
/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  __disable_irq();
  while (1) { }
}

#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* USER CODE END 6 */
}
#endif

5、接线

AT24C02左侧的最外侧四个是VCC,中间的才是WP/A0/A1/A2引脚,最里面是GND,所以键帽给中间和最里面盖上就已经接地了

6、测试跑通的结果,并未添加任何功能。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐