这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » 国产MCU » 【AIcubeV1.01L】使用软件配置硬件的SPI驱动存储芯片

共1条 1/1 1 跳转至

【AIcubeV1.01L】使用软件配置硬件的SPI驱动存储芯片

高工
2026-05-11 19:55:20     打赏

一:芯片知识介绍:

W25X40CL 是 Winbond (华邦) 推出的一款 4Mbit (512K x 8) 容量的串行 NOR Flash 存储器,属于其经典的 SpiFlash 系列。

这款芯片最大特点是具备 Dual SPI 模式,能将读取速度提升至等效 208MHz,非常适合有代码下载、系统启动和少量数据存储需求的嵌入式应用。它目前仍在 大规模量产 状态,市场上货源充足。

以下是其核心参数与功能特性的详细介绍。

  容量:4Mbit (4兆位) / 512K x 8 字节

  接口:支持标准 SPI 和 Dual SPI

  最高时钟频率:104MHz (Dual 模式下等效 208MHz)

  工作电压:2.3V ~ 3.6V (宽电压范围,功耗更低)

  工作温度:-40℃ ~ +85℃

  架构特点:256 字节/页,支持 4KB 扇区擦除、32KB/64KB 块擦除

  耐用性:至少 10万次 擦写,数据可保存 20年

  封装形式:SO-8、VSOP-8、WSON、USON、PDIP 等多种封装

  生产状态:Active (量产中)

存储架构与性能

该芯片的内部存储主要组织如下,结构清晰,使用灵活:

页编程:芯片有 2048 个可编程页,每页大小为 256 字节。每次可编程 1 到 256 字节,典型编程时间(页写入时间)为 0.4 毫秒 至 0.8 毫秒。

灵活擦除:为了让数据管理更灵活,它支持三种不同粒度的擦除方式:

  4KB 扇区:共 128 个,适合删除少量数据。32KB 块:共 16 个。

  64KB 块:共 8 个。典型块擦除时间在 120 到 150 毫秒 之间。

读性能:即支持标准的 SPI 读取,也支持 Dual SPI 模式。在该模式下,传输速率等效于 208MHz,能实现高效的“连续读取模式”,使微控制器能直接从 Flash 中执行代码 (XIP,Execute in Place),无需复制到内存中。

关键技术与特性

除了基础读写操作,W25X40CL 还有一些对设计和保护很重要的特性:

低功耗设计:非常适合电池供电或低功耗设备。正常工作电流仅约 1mA,掉电模式下功耗更是低至 1µA。

数据保护机制:

  软件保护:可通过配置状态寄存器,对特定存储区域设置写保护。

  硬件保护:通过 WP# (Write Protect) 引脚,可以硬件级地锁定状态寄存器,防止关键配置被意外修改。

高级功能:支持 UID (唯一标识符) 和 OTP (一次性可编程) 功能,可用于设备序列号、加密密钥存储或防伪认证。

编程/擦除暂停与恢复:在执行长时间擦除操作时,可以暂停该操作去执行其他高优先级任务,之后再恢复擦除,提高了系统的实时响应能力。

二:硬件SPI配置

09-1.png

主要配置SPI的通讯速度,相位,使能中断发送功能。

三:软件代码:3.1 SPI 的初始化

void SPI_Init(void)
{
    SPI_SwitchP2n();                    //选择SPI数据口: SS(P2.2), MOSI(P2.3), MISO(P2.4), SCLK(P2.5)

    SPI_MasterMode();                   //设置SPI为主机模式
    SPI_IgnoreSS();                     //忽略SS脚
    SPI_DataMSB();                      //设置SPI数据顺序为MSB (高位在前)
    SPI_SetMode0();                     //设置SPI工作模式0 (CPOL=0, CPHA=0)
    SPI_SetClockDivider8();             //设置SPI时钟分频

    HSSPI_Enable();                     //使能SPI高速模式
    HSSPI_SetSSSetupTime(3);            //设置SS建立时间
    HSSPI_SetSSHoldTime(3);             //设置SS保持时间
    HSSPI_SetSSDeactTime(3);            //设置SS空闲时间

    SPI_SetIntPriority(0);              //设置中断为最低优先级
    SPI_EnableInt();                    //使能SPI中断
    fSPITransBusy = 0;                  //清除SPI忙标志位

    SPI_Enable();                       //使能SPI功能

    DMA_SPI_AutoSS();                   //使能SPI DMA自动控制SS
    DMA_SPI_SetAutoSSP22();             //DMA自动控制SS (P2.2)
    DMA_SPI_SetAmount(255);             //设置SPI DMA发送/接收字节数
    DMA_SPI_SetTxAddress(pu8SPIDMATxBuffer); //设置SPI DMA发送缓冲区地址
    DMA_SPI_SetRxAddress(pu8SPIDMARxBuffer); //设置SPI DMA接收缓冲区地址
    DMA_SPI_ClearFIFO();                //清空SPI DMA FIFO缓冲区
    DMA_SPI_ClearFlag();                //清除SPI DMA中断标志
    DMA_SPI_EnableTx();                 //使能发送数据
    DMA_SPI_EnableRx();                 //使能接收数据
    DMA_SPI_SetBusPriority(0);          //设置总线访问为最低优先级
    DMA_SPI_SetIntPriority(0);          //设置中断为最低优先级
    DMA_SPI_EnableInt();                //使能SPI DMA中断
    DMA_SPI_Enable();                   //使能SPI DMA功能
  DMA_SPI_MasterTrigger();            //触发SPI主机DMA

    //<<AICUBE_USER_SPI_INITIAL_BEGIN>>
    // 在此添加用户初始化代码  
    //<<AICUBE_USER_SPI_INITIAL_END>>
}
3.2 SPI发送一个字节函数:

////////////////////////////////////////
// SPI主机模式发送字节函数
// 入口参数: dat (待发送的字节数据)
// 函数返回: 无
////////////////////////////////////////
void SPI_WriteByte(uint8_t dat)
{
    fSPITransBusy = 1;                  //设置发送忙标志
    SPI_SendData(dat);                  //触发主机发送数据
    while (fSPITransBusy);              //等待发送完成
}
3.3 SPI读取一个字节函数:

////////////////////////////////////////
// SPI主机模式读取字节函数
// 入口参数: 无
// 函数返回: 读取的字节数据
////////////////////////////////////////
uint8_t SPI_ReadByte(void)
{
    fSPITransBusy = 1;                  //设置读取忙标志
    SPI_SendData(0xff);                 //触发主机读取数据(主机发送时钟信号)
    while (fSPITransBusy);              //等待读取完成
    return SPI_ReadData();
}
3.4 发送硬件SPI字节函数存储:

void SPI_Write_Nbytes(u32 addr, u8 size)
{
    if(size == 0)   return;
    if(!B_FlashOK)  return;
    while(DmaFlag);                     //DMA忙检测
    while(CheckFlashBusy() > 0);        //Flash忙检测

    FlashWriteEnable();                 //使能Flash写命令

    SPI_CE_Low();                       // enable device
    SPI_WriteByte(SFC_PAGEPROG);        // 发送页编程命令
    SPI_WriteByte(((u8 *)&addr)[1]);    //设置起始地址
    SPI_WriteByte(((u8 *)&addr)[2]);
    SPI_WriteByte(((u8 *)&addr)[3]);

    DmaFlag = 1;
    DMA_SPI_AMT = size-1;        //设置传输总字节数:n+1
    DMA_SPI_CR |= 0x40;     //开始SPI_DMA主模式操作
}
3.5 发送硬件SPI字节函数读取部分:

/************************************************
从Flash中读取数据
入口参数:
    addr   : 地址参数
    buffer : 缓冲从Flash中读取的数据
    size   : 数据块大小
出口参数:
    无
************************************************/
void SPI_Read_Nbytes(u32 addr, u16 size)
{
    if(size == 0)   return;
    if(!B_FlashOK)  return;
    while(DmaFlag);                     //DMA忙检测
    while(CheckFlashBusy() > 0);        //Flash忙检测

    SPI_CE_Low();                       //enable device
    SPI_WriteByte(SFC_READ);            //read command

    SPI_WriteByte(((u8 *)&addr)[1]);    //设置起始地址
    SPI_WriteByte(((u8 *)&addr)[2]);
    SPI_WriteByte(((u8 *)&addr)[3]);

    DmaFlag = 1;
    DMA_SPI_AMT = size-1;        //设置传输总字节数:n+1
    DMA_SPI_CR |= 0x40;     //开始SPI_DMA主模式操作
}
3.6 检测外挂的存储芯片ID

void FlashCheckID(void)
{
                uint16_t Temp = 0;
    SPI_CE_Low();
    SPI_WriteByte(SFC_RDID);        //发送读取ID命令
    SPI_WriteByte(0x00);            //空读3个字节
    SPI_WriteByte(0x00);
    SPI_WriteByte(0x00);
    PM25LV040_ID1 = SPI_ReadByte();         //读取制造商ID1
    PM25LV040_ID  = SPI_ReadByte();         //读取设备ID
    PM25LV040_ID2 = SPI_ReadByte();         //读取制造商ID2
    SPI_CE_High();

                printf("ID1=%x\r\n",PM25LV040_ID1);
                printf("ID=%x\r\n",PM25LV040_ID);
                printf("ID2=%x\r\n",PM25LV040_ID2);
        
    if((PM25LV040_ID1 == 0x9d) && (PM25LV040_ID2 == 0x7f))  B_FlashOK = 1;  //检测是否为PM25LVxx系列的Flash
    else if(PM25LV040_ID == 0x12)  B_FlashOK = 2;                           //检测是否为W25X4x系列的Flash
    else if(PM25LV040_ID == 0x13)  B_FlashOK = 3;                           //检测是否为W25X8x系列的Flash
    else                                                    B_FlashOK = 0;
}
四:串口验证如下所示:

  1. 09-2 串口数据展示.png

五:字符串的读写测试:

u8 xdata SPIWRITEBUFFER[50]={"STC32G128 SPI TEST !!!"};
u8 xdata SPIREADBUFFER[50] ;

void SPI_TEST(void)
{
	UART1_DMA_Transmit(&SPIWRITEBUFFER,sizeof(SPIWRITEBUFFER));
	memcpy(&pu8SPIDMATxBuffer,&SPIWRITEBUFFER,sizeof(SPIWRITEBUFFER));
	delay_ms(100);
	UART1_DMA_Transmit(&pu8SPIDMATxBuffer,sizeof(SPIWRITEBUFFER));
	FlashSectorErase(0x001234);
	delay_ms(100);
	SPI_Write_Nbytes(0x001234,sizeof(SPIWRITEBUFFER));
	delay_ms(100);
	SPI_Read_Nbytes(0x001234,sizeof(SPIWRITEBUFFER));	
	delay_ms(10);	
	UART1_DMA_Transmit(&pu8SPIDMARxBuffer,sizeof(SPIWRITEBUFFER));
	delay_ms(100);
	memcpy(&SPIREADBUFFER,&pu8SPIDMARxBuffer,sizeof(SPIWRITEBUFFER));
	delay_ms(100);
	UART1_DMA_Transmit(&SPIREADBUFFER,sizeof(SPIWRITEBUFFER));
}

在实际使用过程中,只有再执行SPI数据读写时候,需要增加适量的延时,要不然程序运行会存在问题。

在执行写操作时候,需要添加一条块擦除函数。

串口验证如下所示:

09-3 SPI字符串读写函数.png




关键词: AIcubeV1.01L     硬件     存储     芯片    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]