新建   展示   相册  列表  网摘
 

N2L xspi0 flash读写例程 [已调通]


 

 


N2L xspi0 flash读写例程
 
DeepSeek-R1最新版
 
已深度思考

我们使用N2L的xSPI0控制器来读写外部Flash。以下是一个基本的例程,包括初始化、擦除、写入和读取操作。
注意:此例程假设使用的是常见的SPI Flash(如W25Q系列),并且连接在xSPI0上。
步骤:

  1. 初始化xSPI0控制器,设置时钟、模式等。
  2. 实现Flash的识别(读取ID)。
  3. 实现扇区擦除(或块擦除)。
  4. 实现页编程(写入)。
  5. 实现读取数据。

注意:不同的Flash可能有不同的指令和地址长度,请根据实际使用的Flash数据手册进行调整。

以下代码为示例代码,具体寄存器名称和地址需要根据N2L的硬件手册进行修改。

我来为您提供一个完整的N2L xSPI0 Flash读写例程。以下代码展示了如何初始化和使用xSPI0接口进行Flash的读写操作:

c
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "n2l_reg.h"

/* Flash指令定义 */
#define FLASH_CMD_WRITE_ENABLE     0x06
#define FLASH_CMD_WRITE_DISABLE    0x04
#define FLASH_CMD_READ_STATUS      0x05
#define FLASH_CMD_WRITE_STATUS     0x01
#define FLASH_CMD_READ_DATA        0x03
#define FLASH_CMD_FAST_READ        0x0B
#define FLASH_CMD_PAGE_PROGRAM     0x02
#define FLASH_CMD_SECTOR_ERASE     0x20
#define FLASH_CMD_BLOCK_ERASE      0xD8
#define FLASH_CMD_CHIP_ERASE       0xC7
#define FLASH_CMD_POWER_DOWN       0xB9
#define FLASH_CMD_RELEASE_POWERDOWN 0xAB
#define FLASH_CMD_READ_ID          0x9F

/* Flash状态位 */
#define FLASH_STATUS_BUSY          0x01
#define FLASH_STATUS_WRITE_EN      0x02

/* Flash参数 */
#define FLASH_PAGE_SIZE            256
#define FLASH_SECTOR_SIZE          4096
#define FLASH_BLOCK_SIZE           65536

/* xSPI0寄存器定义 */
#define XSPI0_CTRL                 (0x40000000)
#define XSPI0_CLKDIV               (0x40000004)
#define XSPI0_SSEL                 (0x40000008)
#define XSPI0_TX                   (0x4000000C)
#define XSPI0_RX                   (0x40000010)
#define XSPI0_STATUS               (0x40000014)

/* 控制寄存器位定义 */
#define XSPI_CTRL_ENABLE           (1 << 0)
#define XSPI_CTRL_MASTER           (1 << 1)
#define XSPI_CTRL_CPOL             (1 << 2)
#define XSPI_CTRL_CPHA             (1 << 3)
#define XSPI_CTRL_LSB_FIRST        (1 << 4)
#define XSPI_CTRL_TX_INT_EN        (1 << 5)
#define XSPI_CTRL_RX_INT_EN        (1 << 6)

/* 状态寄存器位定义 */
#define XSPI_STATUS_TX_EMPTY       (1 << 0)
#define XSPI_STATUS_TX_FULL        (1 << 1)
#define XSPI_STATUS_RX_EMPTY       (1 << 2)
#define XSPI_STATUS_RX_FULL        (1 << 3)
#define XSPI_STATUS_BUSY           (1 << 4)

/* 寄存器读写宏 */
#define REG_WRITE(addr, value)     (*(volatile uint32_t *)(addr) = (value))
#define REG_READ(addr)             (*(volatile uint32_t *)(addr))

/* 等待函数 */
static void delay_us(uint32_t us)
{
    // 简单的延时函数,根据实际CPU频率调整
    for (volatile uint32_t i = 0; i < us * 10; i++);
}

/* 等待Tx FIFO为空 */
static void xspi_wait_tx_empty(void)
{
    while (!(REG_READ(XSPI0_STATUS) & XSPI_STATUS_TX_EMPTY));
}

/* 等待Rx FIFO不为空 */
static void xspi_wait_rx_not_empty(void)
{
    while (REG_READ(XSPI0_STATUS) & XSPI_STATUS_RX_EMPTY);
}

/* 等待SPI传输完成 */
static void xspi_wait_busy(void)
{
    while (REG_READ(XSPI0_STATUS) & XSPI_STATUS_BUSY);
}

/* 发送一个字节 */
static void xspi_send_byte(uint8_t data)
{
    xspi_wait_tx_empty();
    REG_WRITE(XSPI0_TX, data);
}

/* 接收一个字节 */
static uint8_t xspi_receive_byte(void)
{
    xspi_wait_rx_not_empty();
    return (uint8_t)REG_READ(XSPI0_RX);
}

/* 初始化xSPI0控制器 */
void xspi0_init(void)
{
    // 禁用SPI
    REG_WRITE(XSPI0_CTRL, 0);
    
    // 设置时钟分频 (假设系统时钟50MHz,目标SPI时钟5MHz)
    REG_WRITE(XSPI0_CLKDIV, 9); // 50MHz / (9+1) = 5MHz
    
    // 配置SPI模式:主模式,CPOL=0,CPHA=0,MSB优先
    uint32_t ctrl = XSPI_CTRL_MASTER | XSPI_CTRL_ENABLE;
    REG_WRITE(XSPI0_CTRL, ctrl);
    
    // 取消片选
    REG_WRITE(XSPI0_SSEL, 0);
    
    printf("xSPI0 initialized successfully\n");
}

/* Flash片选控制 */
static void flash_cs_low(void)
{
    REG_WRITE(XSPI0_SSEL, 0);
}

static void flash_cs_high(void)
{
    REG_WRITE(XSPI0_SSEL, 1);
    delay_us(1);
}

/* 等待Flash就绪 */
static void flash_wait_ready(void)
{
    uint8_t status;
    
    do {
        flash_cs_low();
        xspi_send_byte(FLASH_CMD_READ_STATUS);
        status = xspi_receive_byte();
        flash_cs_high();
        delay_us(10);
    } while (status & FLASH_STATUS_BUSY);
}

/* 写使能 */
static void flash_write_enable(void)
{
    flash_cs_low();
    xspi_send_byte(FLASH_CMD_WRITE_ENABLE);
    flash_cs_high();
    delay_us(1);
}

/* 读取Flash ID */
uint32_t flash_read_id(void)
{
    uint32_t id = 0;
    
    flash_cs_low();
    xspi_send_byte(FLASH_CMD_READ_ID);
    
    // 读取制造商ID、存储器类型、容量
    for (int i = 0; i < 3; i++) {
        xspi_send_byte(0x00); // 发送dummy字节
        id = (id << 8) | xspi_receive_byte();
    }
    
    flash_cs_high();
    return id;
}

/* 擦除扇区 */
int flash_erase_sector(uint32_t addr)
{
    flash_wait_ready();
    flash_write_enable();
    
    flash_cs_low();
    xspi_send_byte(FLASH_CMD_SECTOR_ERASE);
    xspi_send_byte((addr >> 16) & 0xFF); // 地址高位
    xspi_send_byte((addr >> 8) & 0xFF);  // 地址中位
    xspi_send_byte(addr & 0xFF);         // 地址低位
    flash_cs_high();
    
    flash_wait_ready();
    return 0;
}

/* 页编程(写入一页数据) */
int flash_page_program(uint32_t addr, const uint8_t *data, uint32_t len)
{
    if (len > FLASH_PAGE_SIZE) {
        printf("Error: Write length exceeds page size\n");
        return -1;
    }
    
    flash_wait_ready();
    flash_write_enable();
    
    flash_cs_low();
    xspi_send_byte(FLASH_CMD_PAGE_PROGRAM);
    xspi_send_byte((addr >> 16) & 0xFF);
    xspi_send_byte((addr >> 8) & 0xFF);
    xspi_send_byte(addr & 0xFF);
    
    for (uint32_t i = 0; i < len; i++) {
        xspi_send_byte(data[i]);
    }
    
    flash_cs_high();
    flash_wait_ready();
    
    return 0;
}

/* 读取数据 */
int flash_read_data(uint32_t addr, uint8_t *buffer, uint32_t len)
{
    flash_cs_low();
    xspi_send_byte(FLASH_CMD_READ_DATA);
    xspi_send_byte((addr >> 16) & 0xFF);
    xspi_send_byte((addr >> 8) & 0xFF);
    xspi_send_byte(addr & 0xFF);
    
    for (uint32_t i = 0; i < len; i++) {
        xspi_send_byte(0x00); // 发送dummy字节接收数据
        buffer[i] = xspi_receive_byte();
    }
    
    flash_cs_high();
    return 0;
}

/* 快速读取数据(带dummy字节) */
int flash_fast_read(uint32_t addr, uint8_t *buffer, uint32_t len)
{
    flash_cs_low();
    xspi_send_byte(FLASH_CMD_FAST_READ);
    xspi_send_byte((addr >> 16) & 0xFF);
    xspi_send_byte((addr >> 8) & 0xFF);
    xspi_send_byte(addr & 0xFF);
    xspi_send_byte(0x00); // dummy字节
    
    for (uint32_t i = 0; i < len; i++) {
        xspi_send_byte(0x00);
        buffer[i] = xspi_receive_byte();
    }
    
    flash_cs_high();
    return 0;
}

/* 测试函数 */
void flash_test(void)
{
    printf("Starting Flash test...\n");
    
    // 读取Flash ID
    uint32_t flash_id = flash_read_id();
    printf("Flash ID: 0x%06lX\n", flash_id);
    
    // 测试数据
    uint8_t write_data[256];
    uint8_t read_data[256];
    uint32_t test_addr = 0x1000;
    
    // 准备测试数据
    for (int i = 0; i < 256; i++) {
        write_data[i] = i;
    }
    
    // 擦除扇区
    printf("Erasing sector at 0x%08lX...\n", test_addr);
    flash_erase_sector(test_addr);
    
    // 写入数据
    printf("Writing data to 0x%08lX...\n", test_addr);
    flash_page_program(test_addr, write_data, 256);
    
    // 读取数据
    printf("Reading data from 0x%08lX...\n", test_addr);
    flash_read_data(test_addr, read_data, 256);
    
    // 验证数据
    int error = 0;
    for (int i = 0; i < 256; i++) {
        if (write_data[i] != read_data[i]) {
            printf("Data mismatch at byte %d: write=0x%02X, read=0x%02X\n", 
                   i, write_data[i], read_data[i]);
            error = 1;
            break;
        }
    }
    
    if (!error) {
        printf("Flash test passed!\n");
    } else {
        printf("Flash test failed!\n");
    }
}

int main(void)
{
    // 初始化xSPI0
    xspi0_init();
    
    // 执行Flash测试
    flash_test();
    
    return 0;
}

关键点说明:

  1. 初始化配置:

    • 设置SPI时钟分频
    • 配置为主模式、CPOL=0、CPHA=0
    • 启用SPI控制器
  2. 基本操作函数:

    • xspi_send_byte() / xspi_receive_byte():底层SPI数据传输
    • flash_wait_ready():等待Flash操作完成
    • flash_write_enable():写使能
  3. Flash操作:

    • flash_read_id():读取器件标识
    • flash_erase_sector():擦除扇区
    • flash_page_program():页编程写入
    • flash_read_data():读取数据
  4. 注意事项:

    • 写操作前必须先擦除
    • 页编程不能跨页边界
    • 操作完成后需要等待Flash就绪

请根据实际硬件调整寄存器地址、时钟分频等参数。这个例程适用于大多数SPI Flash芯片,如W25Q系列、GD25Q系列等。


 

 

 

 

  • mirror区域一般 可以访问与真实内存区域相同的物理内存,但是
    有的真实地址区域不支持cache,mirror则支持,
    如果
    开启了cache功能,那么mirror区域无法直接读取物理地址数据,请检查配置是否开启cache
  •  
     
    •  

      您好,配置如下,也有尝试修改

      BSP_CFG_EL1_MPU_REGION04_SH 为

      BSP_NON_SHAREABLE或

      BSP_INNER_SHAREABLE, 现象一样

      /* Region04 : Mirror area of external address space */
      #define BSP_CFG_EL1_MPU_REGION04_BASE (0x40000000)
      #define BSP_CFG_EL1_MPU_REGION04_LIMIT (0x5FFFFFFF)
      #define BSP_CFG_EL1_MPU_REGION04_SH (BSP_OUTER_SHAREABLE)
      #define BSP_CFG_EL1_MPU_REGION04_AP (BSP_EL1RW_EL0RW)
      #define BSP_CFG_EL1_MPU_REGION04_XN (BSP_EXECUTE_ENABLE)
      #define BSP_CFG_EL1_MPU_REGION04_ATTRINDEX (BSP_ATTRINDEX3)
      #define BSP_CFG_EL1_MPU_REGION04_ENABLE (BSP_REGION_ENABLE)

      /* Region05 : External address space */
      #define BSP_CFG_EL1_MPU_REGION05_BASE (0x60000000)
      #define BSP_CFG_EL1_MPU_REGION05_LIMIT (0x7FFFFFFF)
      #define BSP_CFG_EL1_MPU_REGION05_SH (BSP_NON_SHAREABLE)
      #define BSP_CFG_EL1_MPU_REGION05_AP (BSP_EL1RW_EL0RW)
      #define BSP_CFG_EL1_MPU_REGION05_XN (BSP_EXECUTE_ENABLE)
      #define BSP_CFG_EL1_MPU_REGION05_ATTRINDEX (BSP_ATTRINDEX1)
      #define BSP_CFG_EL1_MPU_REGION05_ENABLE (BSP_REGION_ENABLE)

 
 

 
posted @ 2025-12-25 10:46  前沿风暴  阅读(23)  评论(0)    收藏  举报

校内网 © 2004-2026

京公网安备 33010602011771号 京ICP备2021040463号-3