云计算百科
云计算领域专业知识百科平台

STM32G474VET6驱动SST39VF800A Flash操作代码实现

通过网盘分享的文件:STM32G474VET6+NOR FLASH 验证.zip
链接: https://pan.baidu.com/s/1323CdUaAsV1m6juSMh9i9g?pwd=pbbn 提取码: pbbn 
–来自百度网盘超级会员v4的分享

CUBE MAX配置:

本文将通过分析提供的代码和流程图,详细解释STM32与SST39VF800A Flash存储器之间的操作流程,特别是字编程、扇区擦除和设备ID读取的实现。

配FMC好后你往BANK中地址赋值,FMC会自动根据地质与数值生成对应信号时序

比如:

就是:

#define FLASH_WORD_TO_BYTE(addr_word) ((addr_word) << 1)

*((volatile uint16_t*)(0x60000000 + FLASH_WORD_TO_BYTE(0x2AAA))) = (0x0055);

也就是FLASH内部一个地址代表stm32映射的两个地址所以要乘以2

他就会生成往地址0x00002AAA地址赋值

一、核心概念:Flash命令序列

从流程图中可以看出,SST39VF800A的所有操作都遵循命令序列模式。与RAM的简单读写不同,Flash需要特定的"解锁-命令-数据/地址"序列来执行编程、擦除等操作。

关键特点:

  • 三周期解锁:所有命令都需要先发送两个解锁序列

  • 地址/数据复用:相同地址(5555H/2AAAH)在不同命令中传输不同数据

  • 状态检测:操作完成后需要检测Toggle Bit或Data# Polling

  • 二、代码实现与流程图的对应关系

    1. 解锁序列实现 (SST_SendUnlockSequence)

    流程图要求:

    1. 向地址5555H写入数据00AAH
    2. 向地址2AAAH写入数据0055H

    代码实现:

    static void SST_SendUnlockSequence(void)
    {
    /* First unlock cycle – 使用Flash字地址 */
    SST_WRITE_CMD(SST_CMD_ADDR_1, SST_CMD_UNLOCK1); // 0x5555 -> 0x00AA

    /* 小延时确保命令完成 */
    Delay_Us(1);

    /* Second unlock cycle – 使用Flash字地址 */
    SST_WRITE_CMD(SST_CMD_ADDR_2, SST_CMD_UNLOCK2); // 0x2AAA -> 0x0055

    /* 小延时确保命令完成 */
    Delay_Us(1);
    }

    注意:代码中的地址是Flash字地址,不是STM32的字节地址。宏SST_WRITE_CMD内部会将字地址转换为字节地址。


    2. 字编程实现 (SST_ByteProgram)

    图18流程:

    1. 解锁序列(前两步)
    2. 向5555H写入A0H
    3. 向目标地址写入数据
    4. 等待操作完成

    代码实现:

    static bool SST_ByteProgram(uint32_t address, uint16_t data)
    {
    /* 1-3步:解锁序列 + 编程命令 */
    SST_SendUnlockSequence(); // 解锁步骤1-2
    SST_WRITE_CMD(SST_CMD_ADDR_1, SST_CMD_PROGRAM); // 步骤3: 0x5555 -> 0x00A0
    Delay_Us(1);

    /* 4步:向目标地址写入数据 */
    NOR_WRITE_BYTE(address, data); // 写入用户数据

    /* 5步:等待编程完成(使用Toggle Bit或Data# Polling) */
    return SST_WaitForOperationComplete(address, data, false);
    }

    重要说明:

    • address参数是字节地址,需要保证字对齐(偶数地址)

    • 写入操作前必须确保目标地址已擦除(值为0xFFFF)


    3. 扇区擦除实现 (SST_SectorErase)

    根据数据手册的6周期擦除序列:

    1. 解锁序列(5555H->AAH, 2AAAH->55H)
    2. 再次向5555H写入80H
    3. 再次解锁序列(5555H->AAH, 2AAAH->55H)
    4. 向扇区地址写入30H

    代码实现:

    static bool SST_SectorErase(uint32_t sector_address)
    {
    /* 转换为Flash字地址 */
    uint16_t sector_word_addr = FLASH_BYTE_TO_WORD(sector_address) & 0x7FFF;

    /* 1-2步:解锁序列 + 擦除设置命令 */
    SST_SendUnlockSequence(); // 步骤1-2
    SST_WRITE_CMD(SST_CMD_ADDR_1, 0x0080); // 步骤3: 5555H -> 80H
    Delay_Us(1);

    /* 3-4步:再次解锁序列 + 扇区擦除确认 */
    SST_SendUnlockSequence(); // 步骤4-5
    NOR_WRITE_BYTE(sector_address, SST_CMD_SECTOR_ERASE); // 步骤6: 扇区地址 -> 30H

    /* 等待擦除完成 */
    return SST_WaitForOperationComplete(sector_address, 0xFFFF, true);
    }

    关键点:

    • 擦除操作针对整个4KB扇区

    • 必须使用扇区对齐的地址(地址低12位为0)

    • 擦除时间较长(最长25ms),需要适当等待


    4. 设备ID读取 (SST_ReadID)

    图20软件ID入口序列:

    1. 解锁序列(5555H->AAH, 2AAAH->55H)
    2. 向5555H写入90H
    3. 读取设备ID
    4. 退出软件ID模式

    代码实现:

    static bool SST_ReadID(uint16_t* manufacturer_id, uint16_t* device_id)
    {
    /* 1-2步:解锁序列 + 进入软件ID模式 */
    SST_SendUnlockSequence(); // 解锁序列
    SST_WRITE_CMD(SST_CMD_ADDR_1, SST_CMD_AUTOSELECT); // 5555H -> 90H
    Delay_Us(10);

    /* 3步:读取制造商ID和器件ID */
    *manufacturer_id = NOR_READ_WORD(0x0000); // 字地址0000H -> 制造商ID
    *device_id = NOR_READ_WORD(0x0001); // 字地址0001H -> 器件ID

    /* 4步:退出软件ID模式 */
    SST_WRITE_CMD(0x0000, SST_CMD_RESET); // 任何地址写入F0H
    Delay_Us(10);

    return true;
    }

    ID地址说明:

    • 制造商ID:从字地址0000H读取(返回0x00BF表示SST)

    • 器件ID:从字地址0001H读取(返回0x2781表示SST39VF800A)


    三、状态检测机制详解

    图19展示了三种等待方式:

    1. Toggle Bit检测(代码中实现)

    static bool SST_CheckToggleBit(uint32_t address)
    {
    volatile uint16_t* flash_addr = (volatile uint16_t*)(NOR_FLASH_BASE_ADDR + address);
    uint16_t read1, read2;

    read1 = *flash_addr; // 第一次读取
    read2 = *flash_addr; // 第二次读取

    /* 检查DQ6位是否停止翻转 */
    return ((read1 & 0x0040) == (read2 & 0x0040));
    }

    原理:在编程/擦除期间,DQ6位会不断翻转。当操作完成时,DQ6停止翻转。

    2. Data# Polling检测(代码中实现)

    static bool SST_CheckDataPolling(uint32_t address, uint16_t expected_data)
    {
    volatile uint16_t* flash_addr = (volatile uint16_t*)(NOR_FLASH_BASE_ADDR + address);
    uint16_t read_data;

    read_data = *flash_addr;

    /* 检查DQ7位是否与期望数据匹配 */
    return ((read_data & 0x0080) == (expected_data & 0x0080));
    }

    原理:在编程期间,读取的DQ7位是被写入数据的反码。操作完成后,DQ7恢复为写入数据。

    3. 内部定时器(未在代码中使用)

    • Flash内部有定时器控制最大操作时间

    • 超时后自动终止操作


    四、代码中的关键设计模式

    1. 地址映射处理

    /* 将Flash字地址转换为STM32字节地址(字地址 × 2)*/
    #define FLASH_WORD_TO_BYTE(addr_word) ((addr_word) << 1)

    /* 将Flash字节地址转换为字地址(字节地址 ÷ 2)*/
    #define FLASH_BYTE_TO_WORD(addr_byte) ((addr_byte) >> 1)

    /* 基于字地址的访问宏 */
    #define NOR_READ_WORD(addr_word) (*((volatile uint16_t*)(NOR_FLASH_BASE_ADDR + FLASH_WORD_TO_BYTE(addr_word))))

    应用示例:

    // 读取制造商ID(字地址0x0000)
    uint16_t mfr_id = NOR_READ_WORD(0x0000);
    // 实际访问的STM32地址:0x60000000 + (0x0000 << 1) = 0x60000000

    2. 复合等待策略

    static bool SST_WaitForOperationComplete(uint32_t address, uint16_t expected_data, bool is_erase)
    {
    uint32_t timeout_us = is_erase ? SST_ERASE_TIMEOUT_MS * 1000 : SST_PROGRAM_TIMEOUT_US;

    while (elapsed_us < timeout_us) {
    /* 先尝试Toggle Bit检测 */
    if (SST_CheckToggleBit(address)) {
    /* 再使用Data Polling验证 */
    if (SST_CheckDataPolling(address, expected_data)) {
    /* 最后双重读取确保稳定 */
    uint16_t read1 = *flash_addr;
    uint16_t read2 = *flash_addr;

    if (read1 == read2) {
    return true;
    }
    }
    }
    // … 时间检查和延迟
    }
    return false; /* 超时 */
    }

    设计思想:结合两种检测方法,提高可靠性,避免误判。


    五、测试函数解析

    1. 基础测试 (LZY_WRITE_TEST)

    void LZY_WRITE_TEST(uint32_t address, uint16_t data)
    {
    // 1. 地址范围检查
    if (address > MAX_MUXED_ADDRESS) { … }

    // 2. 地址对齐检查(必须为偶数)
    if (address & 0x01) { … }

    // 3. 读取当前值
    uint16_t current_value = NOR_READ_BYTE(address);

    // 4. 判断是否需要擦除
    if (current_value == 0xFFFF) {
    // 直接编程
    SST_ByteProgram(address, data);
    } else {
    // 先擦除扇区
    uint32_t sector_base = address & ~(NOR_FLASH_SECTOR_SIZE – 1);
    SST_SectorErase(sector_base);
    SST_ByteProgram(address, data);
    }

    // 5. 验证结果
    uint16_t new_value = NOR_READ_BYTE(address);
    // 比较并输出结果
    }

    测试逻辑:

    • 检查是否已擦除(值为0xFFFF)

    • 未擦除时,必须先擦除整个扇区

    • 擦除后编程,并验证结果

    2. 地址空间测试 (Test_Full_Address_Range)

    void Test_Full_Address_Range(void)
    {
    // 测试低地址
    LZY_WRITE_TEST(0x000000, 0x5555);
    LZY_WRITE_TEST(0x001000, 0xAAAA);

    // 测试中间地址(根据配置的地址线计算)
    uint32_t middle_addr = MAX_MUXED_ADDRESS / 2;
    middle_addr = middle_addr & ~0x01; // 确保字对齐
    LZY_WRITE_TEST(middle_addr, 0x1234);

    // 测试高地址
    uint32_t high_addr = MAX_MUXED_ADDRESS – 0x1000;
    high_addr = high_addr & ~0x01; // 确保字对齐
    LZY_WRITE_TEST(high_addr, 0x5678);
    }

    测试策略:

    • 覆盖低、中、高三个地址区域

    • 考虑地址线配置限制(复用模式)

    • 确保地址对齐


    六、复用模式下的特殊考虑

    1. 地址线数量限制

    /* 在复用模式下,地址通过数据线传输,受引脚数量限制 */
    #define ADDR_BITS_USED 19 /* CubeMX中配置的复用地址线数量 */
    #define MAX_MUXED_ADDRESS ((1UL << ADDR_BITS_USED) – 1)

    影响:

    • 如果配置19位地址线,最大可访问地址为0x7FFFF(524,287字节)

    • 但SST39VF800A实际有19条地址线(A0-A18),可访问1MB空间

    • 如果STM32配置的地址线不足,高位地址会回绕

    2. 复用模式访问

    /* 复用模式下的地址访问宏 */
    #define NOR_READ_BYTE(addr_byte) (*((volatile uint16_t*)(NOR_FLASH_BASE_ADDR + (addr_byte))))

    工作原理:

  • STM32通过FMC的ALE信号锁存地址

  • 地址和数据通过同一组线分时传输

  • Flash在ALE为高时锁存地址,ALE为低时读写数据


  • 七、总结与最佳实践

    1. 操作流程总结

    任何Flash操作 = 解锁序列 + 命令 + 数据/地址 + 状态检测

    2. 关键时序要求

    • 解锁序列:每个命令前必须执行

    • 命令间隔:需要微小延时(1-10μs)

    • 操作超时:编程≤20μs,扇区擦除≤25ms

    3. 调试建议

  • 先读取ID:确认设备连接正确

  • 测试基本读写:从低地址开始

  • 检查地址映射:验证字地址到字节地址的转换

  • 监控状态位:使用Toggle Bit或Data# Polling调试操作状态

  • 4. 常见问题排查

    • ID读取失败:检查硬件连接、时序配置、解锁序列

    • 编程失败:确认地址已擦除、地址对齐、超时设置

    • 地址异常:检查地址线配置、复用模式设置

    通过深入理解流程图和代码的对应关系,可以更好地掌握SST39VF800A Flash的操作原理,并有效地调试和优化Flash访问代码。

    示例代码:
     

    /* 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 "usart.h"
    #include "gpio.h"
    #include "fmc.h"

    /* Private includes ———————————————————-*/
    /* USER CODE BEGIN Includes */
    #include <stdio.h>
    #include <string.h>
    #include <stdbool.h>
    /* USER CODE END Includes */

    /* Private typedef ———————————————————–*/
    /* USER CODE BEGIN PTD */

    /**
    * @brief NOR Flash Test Results Structure
    */
    typedef struct {
    bool write_test_passed;
    bool read_id_success;
    bool erase_test_passed;
    uint16_t manufacturer_id;
    uint16_t device_id;
    uint32_t capacity_bytes;
    const char* device_name;
    } NOR_Test_Results_t;

    /* USER CODE END PTD */

    /* Private define ————————————————————*/
    /* USER CODE BEGIN PD */

    /* NOR Flash Configuration – SST39VF800A Specific */
    #define NOR_FLASH_BASE_ADDR 0x60000000UL
    #define NOR_FLASH_SIZE_BYTES (1 * 1024 * 1024) /* 8Mbit = 1MB */
    #define NOR_FLASH_SECTOR_SIZE (4 * 1024) /* 2KWord = 4KB sectors */
    #define NOR_FLASH_BLOCK_SIZE (64 * 1024) /* 32KWord = 64KB blocks */

    /* SST39VF800A Command Addresses (in 16-bit words from datasheet) */
    #define SST_CMD_ADDR_1 0x5555 /* Word address 5555H */
    #define SST_CMD_ADDR_2 0x2AAA /* Word address 2AAAH */

    /* SST39VF800A Command Codes (from Table 4) */
    #define SST_CMD_UNLOCK1 0x00AA
    #define SST_CMD_UNLOCK2 0x0055
    #define SST_CMD_AUTOSELECT 0x0090
    #define SST_CMD_PROGRAM 0x00A0
    #define SST_CMD_CHIP_ERASE_1 0x0080
    #define SST_CMD_SECTOR_ERASE 0x0030
    #define SST_CMD_RESET 0x00F0

    /* SST39VF800A Manufacturer and Device IDs (from Table 2) */
    #define SST_MANUFACTURER_ID 0x00BF
    #define SST39VF800A_DEVICE_ID 0x2781

    /* Timing Parameters from datasheet */
    #define SST_PROGRAM_TIMEOUT_US 20000UL /* 20μs max from Table 17 */
    #define SST_ERASE_TIMEOUT_MS 100UL /* 100ms max for chip erase */
    #define SST_SECTOR_ERASE_TIMEOUT_MS 25UL /* 25ms max for sector erase */

    /* 复用模式下的关键宏定义 */
    /* 在复用模式下,地址通过数据线传输,需要ALE信号锁存 */
    #define ENABLE_ALE_LATCH() // 在复用模式下,FSMC会自动管理ALE

    /* USER CODE END PD */

    /* Private macro ————————————————————-*/
    /* USER CODE BEGIN PM */

    /* 复用模式下的地址映射 */
    /*
    * 在复用模式下,地址和数据共享同一组线
    * STM32的地址总线是字节地址,需要转换为Flash字地址
    * 注意:在CubeMX中配置为19位复用地址线
    */
    #define ADDR_BITS_USED 19 /* CubeMX中配置的复用地址线数量 */
    #define MAX_MUXED_ADDRESS ((1UL << ADDR_BITS_USED) – 1)

    /* 将Flash字地址转换为STM32字节地址(字地址 × 2)*/
    #define FLASH_WORD_TO_BYTE(addr_word) ((addr_word) << 1)

    /* 将Flash字节地址转换为字地址(字节地址 ÷ 2)*/
    #define FLASH_BYTE_TO_WORD(addr_byte) ((addr_byte) >> 1)

    /* 复用模式下的地址访问宏 */
    /*
    * 重要:在复用模式下,FSMC会自动处理地址/数据复用
    * 我们只需要提供字节地址,FSMC会通过ALE信号锁存地址
    * 然后通过同一组线传输数据
    */
    #define NOR_READ_BYTE(addr_byte) (*((volatile uint16_t*)(NOR_FLASH_BASE_ADDR + (addr_byte))))
    #define NOR_WRITE_BYTE(addr_byte, data) (*((volatile uint16_t*)(NOR_FLASH_BASE_ADDR + (addr_byte))) = (data))

    /* 基于Flash字地址的访问宏 */
    #define NOR_READ_WORD(addr_word) (*((volatile uint16_t*)(NOR_FLASH_BASE_ADDR + FLASH_WORD_TO_BYTE(addr_word))))
    #define NOR_WRITE_WORD(addr_word, data) (*((volatile uint16_t*)(NOR_FLASH_BASE_ADDR + FLASH_WORD_TO_BYTE(addr_word))) = (data))

    /* 命令写入宏 – 使用Flash字地址 */
    #define SST_WRITE_CMD(addr_word, data) \\
    NOR_WRITE_WORD(addr_word, data)

    /* 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 */

    /* SST39VF800A Specific Functions */
    static bool SST_ReadID(uint16_t* manufacturer_id, uint16_t* device_id);
    static bool SST_SectorErase(uint32_t sector_address);
    static bool SST_ByteProgram(uint32_t address, uint16_t data);
    static bool SST_CheckToggleBit(uint32_t address);
    static bool SST_CheckDataPolling(uint32_t address, uint16_t expected_data);
    static bool SST_WaitForOperationComplete(uint32_t address, uint16_t expected_data, bool is_erase);
    static void SST_SendUnlockSequence(void);
    static void Delay_Us(volatile uint32_t microseconds);

    /* LZY Test Functions */
    void LZY_NOR_Init(void);
    void LZY_NOR_DisplayInfo(void);
    void LZY_WRITE_TEST(uint32_t address, uint16_t data);
    void Test_Full_Address_Range(void);
    void Verify_Address_Mapping(void);
    void Test_Muxed_Mode_Compatibility(void);

    /* USER CODE END PFP */

    /* Private user code ———————————————————*/
    /* USER CODE BEGIN 0 */

    /**
    * @brief Delay for specified microseconds (approximate)
    * @param microseconds: Number of microseconds to delay
    * @retval None
    */
    static void Delay_Us(volatile uint32_t microseconds)
    {
    /* Assuming 85MHz system clock, 1us ≈ 85 cycles */
    volatile uint32_t cycles = microseconds * 85;
    while (cycles–) {
    __NOP();
    }
    }

    /**
    * @brief Send unlock sequence (common for all commands)
    * @retval None
    */
    static void SST_SendUnlockSequence(void)
    {
    /* 在复用模式下,FSMC会自动通过ALE锁存地址,然后传输数据 */

    /* First unlock cycle – 使用Flash字地址 */
    SST_WRITE_CMD(SST_CMD_ADDR_1, SST_CMD_UNLOCK1);

    /* 小延时确保命令完成 */
    Delay_Us(1);

    /* Second unlock cycle – 使用Flash字地址 */
    SST_WRITE_CMD(SST_CMD_ADDR_2, SST_CMD_UNLOCK2);

    /* 小延时确保命令完成 */
    Delay_Us(1);
    }

    /**
    * @brief Check Toggle Bit (DQ6) status
    * @param address: 字节地址 to check
    * @retval true if ready (stopped toggling), false if still busy
    */
    static bool SST_CheckToggleBit(uint32_t address)
    {
    volatile uint16_t* flash_addr = (volatile uint16_t*)(NOR_FLASH_BASE_ADDR + address);
    uint16_t read1, read2;

    /* Read twice to check for toggling */
    read1 = *flash_addr;
    read2 = *flash_addr;

    /* If DQ6 stops toggling, device is ready */
    return ((read1 & 0x0040) == (read2 & 0x0040));
    }

    /**
    * @brief Check Data Polling (DQ7) status
    * @param address: 字节地址 to check
    * @param expected_data: Expected data value
    * @retval true if ready (DQ7 matches), false if still busy
    */
    static bool SST_CheckDataPolling(uint32_t address, uint16_t expected_data)
    {
    volatile uint16_t* flash_addr = (volatile uint16_t*)(NOR_FLASH_BASE_ADDR + address);
    uint16_t read_data;

    read_data = *flash_addr;

    /* Check DQ7 (bit 7) */
    return ((read_data & 0x0080) == (expected_data & 0x0080));
    }

    /**
    * @brief Wait for operation to complete using both methods
    * @param address: 字节地址 to monitor
    * @param expected_data: Expected data (for programming) or 0xFFFF (for erase)
    * @param is_erase: true if waiting for erase, false for program
    * @retval true if operation completed, false if timeout
    */
    static bool SST_WaitForOperationComplete(uint32_t address, uint16_t expected_data, bool is_erase)
    {
    uint32_t timeout_us = is_erase ? SST_ERASE_TIMEOUT_MS * 1000 : SST_PROGRAM_TIMEOUT_US;
    uint32_t start_time = HAL_GetTick();
    uint32_t elapsed_us = 0;

    while (elapsed_us < timeout_us) {
    /* Try toggle bit first */
    if (SST_CheckToggleBit(address)) {
    /* Then verify with data polling */
    if (SST_CheckDataPolling(address, expected_data)) {
    /* Double-check by reading twice more as per datasheet */
    volatile uint16_t* flash_addr = (volatile uint16_t*)(NOR_FLASH_BASE_ADDR + address);
    uint16_t read1 = *flash_addr;
    uint16_t read2 = *flash_addr;

    if (read1 == read2) {
    return true;
    }
    }
    }

    /* Update elapsed time */
    elapsed_us = (HAL_GetTick() – start_time) * 1000;
    Delay_Us(10); /* Small delay between checks */
    }

    return false; /* Timeout */
    }

    /**
    * @brief Erase a 4KB sector in SST39VF800A
    * @param sector_address: Sector 字节地址 (must be sector aligned)
    * @retval true if successful, false otherwise
    */
    static bool SST_SectorErase(uint32_t sector_address)
    {
    /* Convert byte address to word address for command */
    uint16_t sector_word_addr = FLASH_BYTE_TO_WORD(sector_address) & 0x7FFF; /* A14-A0 for 8Mbit device */

    /* Sector-Erase Command Sequence (6 cycles) from Table 4 */
    /* First 3 cycles: Unlock sequence */
    SST_SendUnlockSequence();

    /* Fourth cycle: Setup erase command */
    SST_WRITE_CMD(SST_CMD_ADDR_1, 0x0080);
    Delay_Us(1);

    /* Fifth and sixth cycles: Unlock sequence again */
    SST_SendUnlockSequence();

    /* Seventh cycle: Sector address and erase confirm */
    NOR_WRITE_BYTE(sector_address, SST_CMD_SECTOR_ERASE);

    /* Wait for erase completion */
    return SST_WaitForOperationComplete(sector_address, 0xFFFF, true);
    }

    /**
    * @brief Program a single word (16-bit) in SST39VF800A
    * @param address: 字节地址 to program (must be word-aligned)
    * @param data: Data to program
    * @retval true if successful, false otherwise
    */
    static bool SST_ByteProgram(uint32_t address, uint16_t data)
    {
    /* Word-Program Command Sequence (4 cycles) from Table 4 */
    /* First 3 cycles: Unlock sequence */
    SST_SendUnlockSequence();

    /* Fourth cycle: Program command and data */
    SST_WRITE_CMD(SST_CMD_ADDR_1, SST_CMD_PROGRAM);
    Delay_Us(1);

    /* Write the data to the target address */
    NOR_WRITE_BYTE(address, data);

    /* Wait for programming completion */
    return SST_WaitForOperationComplete(address, data, false);
    }

    /**
    * @brief Read SST39VF800A Manufacturer and Device ID
    * @param manufacturer_id: Pointer to store manufacturer ID
    * @param device_id: Pointer to store device ID
    * @retval true if successful, false otherwise
    */
    static bool SST_ReadID(uint16_t* manufacturer_id, uint16_t* device_id)
    {
    /* Software ID Entry Command Sequence (3 cycles) */
    SST_SendUnlockSequence();
    SST_WRITE_CMD(SST_CMD_ADDR_1, SST_CMD_AUTOSELECT);
    Delay_Us(10);

    /* Read Manufacturer ID (at word address 0000H, A0=0) */
    *manufacturer_id = NOR_READ_WORD(0x0000);

    /* Read Device ID (at word address 0001H, A0=1) */
    *device_id = NOR_READ_WORD(0x0001);

    /* Exit Software ID mode */
    SST_WRITE_CMD(0x0000, SST_CMD_RESET);
    Delay_Us(10);

    return true;
    }

    /**
    * @brief 测试复用模式兼容性
    * @retval None
    */
    void Test_Muxed_Mode_Compatibility(void)
    {
    printf("=== 测试复用模式兼容性 ===\\n");

    // 检查CubeMX配置
    printf("1. CubeMX配置检查:\\n");
    printf(" – Memory type: Muxed NOR Flash\\n");
    printf(" – Data/Address: %d bits\\n", ADDR_BITS_USED);
    printf(" – 理论最大地址: 0x%06lX\\n", MAX_MUXED_ADDRESS);

    // 测试地址线是否工作
    printf("\\n2. 地址线测试:\\n");

    uint32_t test_addresses[] = {
    0x000000, // 地址线全0
    0x000002, // A0=1
    0x000004, // A1=1
    0x000008, // A2=1
    0x000010, // A3=1
    0x000020, // A4=1
    0x000040, // A5=1
    0x000080, // A6=1
    0x000100, // A7=1
    0x000200, // A8=1
    0x000400, // A9=1
    0x000800, // A10=1
    0x001000, // A11=1
    0x002000, // A12=1
    0x004000, // A13=1
    0x008000, // A14=1
    0x010000, // A15=1
    0x020000, // A16=1
    0x040000, // A17=1
    0x080000, // A18=1 (最高位)
    };

    bool all_passed = true;
    for (int i = 0; i < sizeof(test_addresses)/sizeof(test_addresses[0]); i++) {
    uint32_t addr = test_addresses[i];

    if (addr > MAX_MUXED_ADDRESS) {
    printf(" 地址 0x%06lX: 超出配置的地址线范围\\n", addr);
    continue;
    }

    // 尝试读取该地址
    uint16_t value = NOR_READ_BYTE(addr);

    printf(" 地址 0x%06lX (A%d): 读取值 0x%04X",
    addr, i, value);

    // 检查是否地址回绕
    if (addr >= NOR_FLASH_SIZE_BYTES) {
    printf(" [警告:超出Flash物理范围]\\n");
    } else {
    printf("\\n");
    }

    // 如果是擦除状态,可能是有效的
    if (value != 0xFFFF && value != 0x0000) {
    printf(" 注意:读取到非默认值,可能已编程\\n");
    }
    }

    // 测试高位地址回绕
    printf("\\n3. 高位地址回绕测试:\\n");

    uint32_t wrap_test_addrs[] = {
    0x100000, // 1MB位置(如果只有19位地址线,会回绕到0x000000)
    0x200000, // 2MB位置
    0x300000, // 3MB位置
    };

    for (int i = 0; i < sizeof(wrap_test_addrs)/sizeof(wrap_test_addrs[0]); i++) {
    uint32_t addr = wrap_test_addrs[i];
    uint16_t value = NOR_READ_BYTE(addr);

    printf(" 高位地址 0x%06lX: 读取值 0x%04X", addr, value);

    // 计算回绕地址
    uint32_t wrapped_addr = addr & MAX_MUXED_ADDRESS;
    printf(" (回绕到 0x%06lX)\\n", wrapped_addr);
    }

    printf("\\n4. 复用模式特定测试:\\n");

    // 检查ALE引脚是否配置
    #ifdef FMC_Bank1_ALE_ENABLE
    printf(" ALE引脚已启用 (复用模式)\\n");

    // 测试连续访问模式
    printf(" 测试连续访问…\\n");
    volatile uint16_t* base_ptr = (volatile uint16_t*)NOR_FLASH_BASE_ADDR;

    // 连续读取多个地址
    for (int i = 0; i < 10; i++) {
    uint32_t offset = i * 0x1000;
    if (offset > MAX_MUXED_ADDRESS) break;

    uint16_t val = base_ptr[offset / 2]; // 除以2因为是指针偏移
    printf(" 偏移 0x%04X: 0x%04X\\n", offset, val);
    }
    #else
    printf(" 警告:ALE引脚未启用,可能不是真正的复用模式\\n");
    #endif

    printf("===============================\\n\\n");
    }

    /**
    * @brief 验证地址映射的正确性
    * @retval None
    */
    void Verify_Address_Mapping(void)
    {
    printf("=== 验证地址映射 ===\\n");

    // 测试几个关键地址
    uint32_t test_offsets[] = {
    0x000000, // 起始地址
    0x07FFFE, // 第一个扇区末尾(4KB对齐)
    0x3FFFFE, // 19位地址线的最大地址(如果配置为19位)
    0x0FFFFE, // 1MB Flash的最大地址
    };

    for (int i = 0; i < sizeof(test_offsets)/sizeof(test_offsets[0]); i++) {
    uint32_t byte_offset = test_offsets[i];
    uint32_t word_offset = FLASH_BYTE_TO_WORD(byte_offset);

    printf("字节偏移: 0x%06lX, 字偏移: 0x%06lX\\n",
    byte_offset, word_offset);

    // 尝试读取
    uint16_t value = NOR_READ_BYTE(byte_offset);

    printf(" 读取值: 0x%04X", value);

    // 检查是否超出配置的地址线范围
    if (byte_offset > MAX_MUXED_ADDRESS) {
    printf(" [超出配置的%d位地址线范围]", ADDR_BITS_USED);
    }

    // 检查是否地址回绕
    if (byte_offset >= NOR_FLASH_SIZE_BYTES) {
    printf(" [警告:超出Flash物理范围!]\\n");
    } else {
    printf("\\n");
    }
    }

    printf("=====================\\n\\n");
    }

    /**
    * @brief LZY NOR Flash 初始化
    * @retval None
    */
    void LZY_NOR_Init(void)
    {
    uint16_t manufacturer_id, device_id;

    printf("=== SST39VF800A Flash Init ===\\n");

    // 在复用模式下,读取ID可能需要特殊处理
    printf("模式:复用模式 (Muxed Mode)\\n");
    printf("配置地址线:%d bits\\n", ADDR_BITS_USED);

    if (SST_ReadID(&manufacturer_id, &device_id)) {
    printf("Manufacturer: 0x%04X\\n", manufacturer_id);
    printf("Device ID: 0x%04X\\n", device_id);

    if (manufacturer_id == SST_MANUFACTURER_ID && device_id == SST39VF800A_DEVICE_ID) {
    printf("Device: SST39VF800A 8Mbit (1MB)\\n");
    printf("状态:设备识别成功!\\n");
    } else {
    printf("Unknown device!\\n");
    printf("可能的原因:\\n");
    printf(" 1. 复用模式配置不正确\\n");
    printf(" 2. 硬件连接问题\\n");
    printf(" 3. 时序配置不当\\n");
    }
    } else {
    printf("Device not found!\\n");
    printf("在复用模式下,可能需要检查:\\n");
    printf(" 1. ALE信号是否正确连接\\n");
    printf(" 2. 时序配置是否匹配Flash要求\\n");
    printf(" 3. 硬件连接是否正确\\n");
    }

    printf("Base Address: 0x%08lX\\n", NOR_FLASH_BASE_ADDR);
    printf("Sector Size: %d bytes\\n", NOR_FLASH_SECTOR_SIZE);
    printf("Total Size: %d bytes (1MB)\\n", NOR_FLASH_SIZE_BYTES);
    printf("配置地址线:%d bits (最大地址: 0x%06lX)\\n",
    ADDR_BITS_USED, MAX_MUXED_ADDRESS);
    printf("==============================\\n\\n");
    }

    /**
    * @brief LZY NOR Flash 显示信息
    * @retval None
    */
    void LZY_NOR_DisplayInfo(void)
    {
    printf("=== SST39VF800A Info ===\\n");
    printf("Capacity: 1MB (8Mbit)\\n");
    printf("Organization: 512K x 16-bit\\n");
    printf("Sector Size: 4KB\\n");
    printf("Block Size: 64KB\\n");
    printf("Voltage: 2.7-3.6V (3.3V typical)\\n");
    printf("Address Lines: A0-A18 (19条)\\n");
    printf("Data Bus: 16-bit\\n");
    printf("Interface: 非复用模式 (但STM32配置为复用)\\n");
    printf("注意事项:\\n");
    printf(" – STM32配置为复用模式\\n");
    printf(" – 需要外部地址锁存器或特殊硬件设计\\n");
    printf(" – 地址线配置:%d bits\\n", ADDR_BITS_USED);
    printf("=======================\\n\\n");
    }

    /**
    * @brief LZY Flash 写入测试函数
    * @param address: 要写入的字节地址
    * @param data: 要写入的数据(16位)
    * @retval None
    */
    void LZY_WRITE_TEST(uint32_t address, uint16_t data)
    {
    // 检查地址是否超出配置的地址线范围
    if (address > MAX_MUXED_ADDRESS) {
    printf("错误:地址 0x%08lX 超出配置的%d位地址线范围!\\n",
    NOR_FLASH_BASE_ADDR + address, ADDR_BITS_USED);
    printf("最大可访问地址:0x%06lX\\n", MAX_MUXED_ADDRESS);
    return;
    }

    // 确保地址是字对齐的(偶数地址)
    if (address & 0x01) {
    printf("错误:地址必须字对齐!0x%08lX\\n", NOR_FLASH_BASE_ADDR + address);
    return;
    }

    uint16_t current_value = NOR_READ_BYTE(address);

    printf("Addr: 0x%08lX, Current: 0x%04X -> ",
    NOR_FLASH_BASE_ADDR + address, current_value);

    /* 如果要写入数据,必须先检查是否已擦除 */
    if (current_value == 0xFFFF) {
    /* 位置已擦除,可以直接编程 */
    if (SST_ByteProgram(address, data)) {
    printf("Write 0x%04X OK", data);
    } else {
    printf("Write FAIL!");
    }
    } else {
    /* 必须先擦除整个扇区 */
    uint32_t sector_base = address & ~(NOR_FLASH_SECTOR_SIZE – 1);

    printf("Erasing sector 0x%08lX… ", NOR_FLASH_BASE_ADDR + sector_base);

    if (SST_SectorErase(sector_base)) {
    printf("Erased, ");
    if (SST_ByteProgram(address, data)) {
    printf("Write 0x%04X OK", data);
    } else {
    printf("Write FAIL!");
    }
    } else {
    printf("Erase FAIL!");
    }
    }

    /* 重新读取验证 */
    uint16_t new_value = NOR_READ_BYTE(address);
    printf(", Read: 0x%04X %s\\n",
    new_value,
    (new_value == data) ? "[OK]" : "[FAIL]");
    }

    /**
    * @brief 测试完整地址空间
    * @retval None
    */
    void Test_Full_Address_Range(void)
    {
    printf("=== 测试完整地址空间 ===\\n");

    // 注意:在复用模式下,我们只能测试配置的地址线范围内的地址

    // 测试低地址
    printf("1. 测试低地址区域:\\n");
    LZY_WRITE_TEST(0x000000, 0x5555);
    LZY_WRITE_TEST(0x001000, 0xAAAA);

    // 测试中间地址
    printf("\\n2. 测试中间地址区域:\\n");

    // 根据配置的地址线数量确定中间地址
    uint32_t middle_addr = MAX_MUXED_ADDRESS / 2;
    middle_addr = middle_addr & ~0x01; // 确保字对齐

    if (middle_addr < NOR_FLASH_SIZE_BYTES) {
    LZY_WRITE_TEST(middle_addr, 0x1234);
    } else {
    printf("中间地址超出Flash范围,跳过\\n");
    }

    // 测试高地址(在配置的地址线范围内)
    printf("\\n3. 测试高地址区域:\\n");

    // 测试接近最大配置地址的位置
    uint32_t high_addr = MAX_MUXED_ADDRESS – 0x1000;
    high_addr = high_addr & ~0x01; // 确保字对齐

    if (high_addr < NOR_FLASH_SIZE_BYTES) {
    LZY_WRITE_TEST(high_addr, 0x5678);
    } else {
    printf("高地址超出Flash范围,跳过\\n");
    }

    // 测试边界条件
    printf("\\n4. 测试边界条件:\\n");

    // 测试接近19位地址线最大地址的位置
    uint32_t boundary_addr = MAX_MUXED_ADDRESS – 0x20;
    boundary_addr = boundary_addr & ~0x01; // 确保字对齐

    if (boundary_addr < NOR_FLASH_SIZE_BYTES) {
    LZY_WRITE_TEST(boundary_addr, 0x9ABC);
    } else {
    printf("边界地址超出Flash范围,跳过\\n");
    }

    printf("========================\\n\\n");
    }

    /* USER CODE END 0 */

    /**
    * @brief The application entry point.
    * @retval int
    */
    int main(void)
    {
    /* MCU Configuration——————————————————–*/
    HAL_Init();
    SystemClock_Config();

    /* Initialize all configured peripherals */
    MX_GPIO_Init();
    MX_FMC_Init();
    MX_USART1_UART_Init();

    /* USER CODE BEGIN 2 */

    /* 系统信息 */
    printf("\\nSTM32 + SST39VF800A Flash Test\\n");
    printf("模式:复用模式 (Muxed Mode)\\n");
    printf("配置地址线:%d bits\\n", ADDR_BITS_USED);
    printf("===============================\\n\\n");

    /* 测试复用模式兼容性 */
    Test_Muxed_Mode_Compatibility();

    /* 初始化NOR Flash */
    LZY_NOR_Init();

    /* 验证地址映射 */
    Verify_Address_Mapping();

    /* 显示设备信息 */
    LZY_NOR_DisplayInfo();

    /* 测试完整地址空间(在配置的地址线范围内) */
    Test_Full_Address_Range();

    /* 初始测试 */
    printf("=== 初始测试 ===\\n");
    LZY_WRITE_TEST(0x1000, 0x5555); // 测试地址0x1000,写入0x5555
    LZY_WRITE_TEST(0x2000, 0xAAAA); // 测试地址0x2000,写入0xAAAA

    /* USER CODE END 2 */

    /* Infinite loop */
    /* USER CODE BEGIN WHILE */
    while (1)
    {
    /* 循环测试不同的地址和数据 */
    static uint32_t test_count = 0;

    // 在配置的地址线范围内循环测试
    uint32_t max_test_addr = MAX_MUXED_ADDRESS;
    if (max_test_addr > NOR_FLASH_SIZE_BYTES – 2) {
    max_test_addr = NOR_FLASH_SIZE_BYTES – 2;
    }

    // 生成测试地址(在安全范围内)
    uint32_t base_addr = 0x1000 + (test_count % 8) * 0x1000;

    // 确保地址在范围内
    if (base_addr > max_test_addr) {
    base_addr = max_test_addr – 0x8000; // 回退到一个安全地址
    }

    base_addr = base_addr & ~0x01; // 确保字对齐

    uint16_t test_data = 0x1234 + test_count * 0x1111;

    printf("Test #%lu: ", test_count);
    LZY_WRITE_TEST(base_addr, test_data);

    test_count++;
    HAL_Delay(2000); // 每2秒测试一次

    // 每10次测试打印一次状态
    if (test_count % 10 == 0) {
    printf("\\n=== 测试统计 ===\\n");
    printf("已执行测试次数: %lu\\n", test_count);
    printf("当前最大可访问地址: 0x%06lX\\n", max_test_addr);
    printf("配置地址线: %d bits\\n", ADDR_BITS_USED);
    printf("==================\\n\\n");
    }
    }
    /* USER CODE END 3 */
    }

    /* 其他函数保持不变… */

    /* USER CODE BEGIN 4 */

    /* USER CODE END 4 */

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

    /** Configure the main internal regulator output voltage
    */
    HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1_BOOST);

    /** 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.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV2;
    RCC_OscInitStruct.PLL.PLLN = 85;
    RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
    RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
    RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
    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_DIV1;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != 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 */
    printf("\\n[ERROR] Error_Handler called! System halted.\\n");
    __disable_irq();
    while (1)
    {
    // Flash LED rapidly to indicate error
    HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_0);
    HAL_Delay(100);
    }
    /* 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 */
    printf("[ERROR] Assert failed: file %s, line %d\\n", file, (int)line);
    /* USER CODE END 6 */
    }
    #endif /* USE_FULL_ASSERT */

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » STM32G474VET6驱动SST39VF800A Flash操作代码实现
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!