设计NOR Flash(FMSC接口)的Flashloader(MCU: stm32f4)

news2025/1/18 10:08:12

目录

概述

1 硬件

1.1 MCU和S29GL128P10TFI01控制电路

1.1.1  S29GL128P10TFI01

 1.1.2 MCU与NOR Flash接口

1.2 STM32F4的FSMC接口

1.2.1 时序信号

1.2.2 外部存储器接口信号

2 Flash leader功能实现

2.1 框架结构介绍

2.2 S29GL128P10TFI01的Flash leader框架

 2.3 NOR 驱动程序

2.3 Dev_Inf的参数

3 Keil中的配置

3.1 配置参数

 3.2 编译项目

4 测试

4.1 ST-link连接板卡

4.2 下载文件测试


源代码下载地址:

M29W128GL-STM32F4-ALY源代码资源-CSDN文库

概述

本文主要介绍基于STM32F407芯片,NOR Flash芯片为S29GL128P10TFI01。使用其设计一个Flashloader 程序,并且在STM32CubeProgrammer工具中使用该文件,实现NOR Flash擦除数据,编程数据,读取数据的功能。

1 硬件

1.1 MCU和S29GL128P10TFI01控制电路

1.1.1  S29GL128P10TFI01

芯片S29GL128P10TFI01是一款闪存芯片,由Spansion公司生产。它具有128Mbit(16MB)的容量,工作电压为2.7V至3.6V,采用主要的块擦除技术和512字节的页面编程。该芯片采用NOR闪存架构,可用于嵌入式系统、网络设备、消费电子产品等领域。它具有较快的读取和编程速度,适用于需要大容量存储和快速数据访问的应用场景。

 1.1.2 MCU与NOR Flash接口

NOR Flash与MCU通过FSMC接口进行连接,其接口框图图形如下,NOR Flash和SRAM共用一个FSMC接口,可通过配置下表寄存器的值,选择相应的区域,以确定Flash对应的接口。

STM32407与 S29GL128P10TFI01芯片的硬件电路图,该Flash在MCU上的操作起始地址为0x6400 0000。

1.2 STM32F4的FSMC接口

1.2.1 时序信号

FSMC 会生成适当的信号时序,以驱动以下类型的存储器:
● 异步 SRAM 和 ROM
— 8 位
— 16 位
— 32 位
● PSRAM( Cellular RAM)
— 异步模式
— 突发模式
— 复用或非复用
● NOR Flash
— 异步模式或突发模式
— 复用或非复用

FSMC 会为每个存储区域输出唯一的片选信号 NE[4:1]。所有其它信号(地址、数据和控制)均为共享信号。对于同步访问, FSMC 只有在读/写事务期间才会向所选的外部器件发出时钟 (CLK)。 HCLK时钟频率是该时钟的整数倍。每个存储区域的大小固定,均为 64 MB。每个存储区域都通过专用的寄存器进行配置。存储器的可编程参数包括访问时序和对等待管理的支持(用于在突发模式下访问 NOR Flash和 PSRAM)。

1.2.2 外部存储器接口信号

1)非复用 I/O NOR Flash

NOR Flash 存储器采用 16 位字寻址。最大容量为 512 Mb( 26 个地址线)。

2)复用 I/O NOR Flash

NOR-Flash 存储器采用 16 位字寻址。最大容量为 512 Mb( 26 个地址线)。

1.3 软硬件信息

软硬件信息版本信息
STM32 MCUSTM32F407IGTx
NOR FlashS29GL128P10TFI01
KeilMDK ARM 5.38
调试工具:st-linkST-LINK/V2-1
STM32CubeProgrammerv2.16.0

2 Flash leader功能实现

2.1 框架结构介绍

安装STM32CubeProgrammer软件之后,ST提供了许多Demo可供参考,在\STM32CubeProgrammer\bin\ExternalLoader目录下有一个基于S29GL128P10TFI01的外部Flash loader。

打开该代码之后可以看见如下代码结构,该项目的代码分为两个部分

leader文件目录: Flash leader驱动相关接口

STM32xxx目录: 和MCU相关的驱动目录

2.2 S29GL128P10TFI01的Flash leader框架

参考M29W128GL_STM3210E-EVAL设计S29GL128P10TFI01_STM32F4的Flash leader功能程序。复制一份M29W128GL_STM3210E-EVAL的代码,命名如下:

打开项目文件,得到如下目录结构,让后将STM32F1xx的库文件,换成STM32F4XX的库文件

 2.3 NOR 驱动程序

创建stm32f4_fsmc_nor.c文件,编写如下代码:

#include "stm32f4_fsmc_nor.h"

#define ADDR_SHIFT(A)               (NOR_FLASH_ADDR + (2 * (A)))
#define NOR_WRITE(Address, Data)  (*(__IO uint16_t *)(Address) = (Data))

/* 判忙时的执行语句循环次数 */
#define BlockErase_Timeout    ((uint32_t)0x00A00000)
#define ChipErase_Timeout     ((uint32_t)0x30000000)
#define Program_Timeout       ((uint32_t)0x00001400)

/* PD6 是NOR Flash输出到STM32的忙信号, 通过GPIO查询方式判忙 */
#define NOR_IS_BUSY()    (GPIO_ReadInputDataBit(GPIOD, GPIO_Pin_6) == RESET)

static void NOR_QuitToReadStatus(void);
static uint8_t NOR_GetStatus(uint32_t Timeout);


/*
*********************************************************************************************************
*    函 数 名: bsp_InitNorFlash
*    功能说明: 配置连接外部NOR Flash的GPIO和FSMC
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
void bsp_InitNorFlash(void)
{
    FSMC_NORSRAMInitTypeDef  FSMC_NORSRAMInitStructure;
    FSMC_NORSRAMTimingInitTypeDef  p;
    GPIO_InitTypeDef GPIO_InitStructure;

    /* 使能GPIO时钟 */
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_GPIOF |
                 RCC_AHB1Periph_GPIOG, ENABLE);

    /* 使能 FSMC 时钟 */
    RCC_AHB3PeriphClockCmd(RCC_AHB3Periph_FSMC, ENABLE);

    /* NOR Flash 的 GPIO :
        PD0/FSMC_D2
        PD1/FSMC_D3
        PD4/FSMC_NOE
        PD5/FSMC_NWE

        PD6/FSMC_NWAIT        - 忙信号,配置为GPIO,输入模式,通过软件查询方式判忙

        PD8/FSMC_D13
        PD9/FSMC_D14
        PD10/FSMC_D15
        PD11/FSMC_CLE/FSMC_A16
        PD12/FSMC_ALE/FSMC_A17
        PD13/FSMC_A18
        PD14/FSMC_D0
        PD15/FSMC_D1

        PE3/FSMC_A19
        PE4/FSMC_A20
        PE5/FSMC_A21
        PE6/FSMC_A22
        PE7/FSMC_D4
        PE8/FSMC_D5
        PE9/FSMC_D6
        PE10/FSMC_D7
        PE11/FSMC_D8
        PE12/FSMC_D9
        PE13/FSMC_D10
        PE14/FSMC_D11
        PE15/FSMC_D12

        PF0/FSMC_A0
        PF1/FSMC_A1
        PF2/FSMC_A2
        PF3/FSMC_A3
        PF4/FSMC_A4
        PF5/FSMC_A5
        PF12/FSMC_A6
        PF13/FSMC_A7
        PF14/FSMC_A8
        PF15/FSMC_A9

        PG0/FSMC_A10
        PG1/FSMC_A11
        PG2/FSMC_A12
        PG3/FSMC_A13
        PG4/FSMC_A14
        PG5/FSMC_A15
        PG9/FSMC_NE2    - 片选信号
    */

    /* GPIOD configuration */
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource0, GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource1, GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource4, GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource5, GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource8, GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource9, GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource10, GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource11, GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource12, GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource13, GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource14, GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource15, GPIO_AF_FSMC);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0  | GPIO_Pin_1  | GPIO_Pin_4  | GPIO_Pin_5  |
                        GPIO_Pin_8  | GPIO_Pin_9  | GPIO_Pin_10 | GPIO_Pin_11 |
                        GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;

    GPIO_Init(GPIOD, &GPIO_InitStructure);

    /* GPIOE configuration */
    GPIO_PinAFConfig(GPIOE, GPIO_PinSource3 , GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOE, GPIO_PinSource4 , GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOE, GPIO_PinSource5 , GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOE, GPIO_PinSource6 , GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOE, GPIO_PinSource7 , GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOE, GPIO_PinSource8 , GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOE, GPIO_PinSource9 , GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOE, GPIO_PinSource10 , GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOE, GPIO_PinSource11 , GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOE, GPIO_PinSource12 , GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOE, GPIO_PinSource13 , GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOE, GPIO_PinSource14 , GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOE, GPIO_PinSource15 , GPIO_AF_FSMC);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 |
                        GPIO_Pin_4  | GPIO_Pin_5  | GPIO_Pin_6 | GPIO_Pin_7 |
                        GPIO_Pin_8  | GPIO_Pin_9  | GPIO_Pin_10 | GPIO_Pin_11|
                        GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;

    GPIO_Init(GPIOE, &GPIO_InitStructure);


    /* GPIOF configuration */
    GPIO_PinAFConfig(GPIOF, GPIO_PinSource0 , GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOF, GPIO_PinSource1 , GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOF, GPIO_PinSource2 , GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOF, GPIO_PinSource3 , GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOF, GPIO_PinSource4 , GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOF, GPIO_PinSource5 , GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOF, GPIO_PinSource12 , GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOF, GPIO_PinSource13 , GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOF, GPIO_PinSource14 , GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOF, GPIO_PinSource15 , GPIO_AF_FSMC);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0  | GPIO_Pin_1  | GPIO_Pin_2  | GPIO_Pin_3  |
                        GPIO_Pin_4  | GPIO_Pin_5  | GPIO_Pin_12 | GPIO_Pin_13 |
                        GPIO_Pin_14 | GPIO_Pin_15;

    GPIO_Init(GPIOF, &GPIO_InitStructure);


    /* GPIOG configuration */
    GPIO_PinAFConfig(GPIOG, GPIO_PinSource0 , GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOG, GPIO_PinSource1 , GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOG, GPIO_PinSource2 , GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOG, GPIO_PinSource3 , GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOG, GPIO_PinSource4 , GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOG, GPIO_PinSource5 , GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOG, GPIO_PinSource9 , GPIO_AF_FSMC);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0  | GPIO_Pin_1  | GPIO_Pin_2  | GPIO_Pin_3 |
                        GPIO_Pin_4  | GPIO_Pin_5  | GPIO_Pin_9;

    GPIO_Init(GPIOG, &GPIO_InitStructure);

    /* PD6 作为忙信号, 配置为GPIO输入模式,软件查询 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;        /* 输入模式 */
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOD, &GPIO_InitStructure);

    /*-- FSMC Configuration ------------------------------------------------------*/
    p.FSMC_AddressSetupTime = 0x06;             /* 0x05正常, 0x04 出错 */
    p.FSMC_AddressHoldTime = 0x01;
    p.FSMC_DataSetupTime = 0x0C;               /* 0x0B正常, 0x0A 出错 */
    p.FSMC_BusTurnAroundDuration = 0x00;
    p.FSMC_CLKDivision = 0x00;
    p.FSMC_DataLatency = 0x00;
    p.FSMC_AccessMode = FSMC_AccessMode_B;

    FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM2;
    FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable;
    FSMC_NORSRAMInitStructure.FSMC_MemoryType = FSMC_MemoryType_NOR;
    FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;
    FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable;
    FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait = FSMC_AsynchronousWait_Disable;
    FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
    FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable;
    FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;
    FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable;
    FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable;
    FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable;
    FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable;
    FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &p;
    FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &p;

    FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure);

    /*!< Enable FSMC Bank1_SRAM2 Bank */
    FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM2, ENABLE);
}

/*
*********************************************************************************************************
*    函 数 名: NOR_ReadID
*    功能说明: 读取NOR Flash的器件ID
*    形    参: 无
*    返 回 值: 器件ID,32Bit, 高8bit 是Manufacturer_Code, 低24bit是器件ID
*********************************************************************************************************
*/
uint32_t NOR_ReadID(void)
{
    uint32_t uiID;
    uint8_t id1, id2, id3, id4;

    NOR_WRITE(ADDR_SHIFT(0x0555), 0x00AA);
    NOR_WRITE(ADDR_SHIFT(0x02AA), 0x0055);
    NOR_WRITE(ADDR_SHIFT(0x0555), 0x0090);

    id1 = *(__IO uint16_t *) ADDR_SHIFT(0x0000);
    id2 = *(__IO uint16_t *) ADDR_SHIFT(0x0001);
    id3 = *(__IO uint16_t *) ADDR_SHIFT(0x000E);
    id4 = *(__IO uint16_t *) ADDR_SHIFT(0x000F);

    uiID = ((uint32_t)id1 << 24) | ((uint32_t)id2 << 16)  | ((uint32_t)id3 << 8) | id4;

    NOR_WRITE(NOR_FLASH_ADDR, 0x00F0 );        /* 退出ID模式 */

    return uiID;
}

/*
*********************************************************************************************************
*    函 数 名: NOR_QuitToReadStatus
*    功能说明: 复位NOR,退到读状态
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void NOR_QuitToReadStatus(void)
{
    NOR_WRITE(ADDR_SHIFT(0x00555), 0x00AA);
    NOR_WRITE(ADDR_SHIFT(0x002AA), 0x0055);
    NOR_WRITE(NOR_FLASH_ADDR, 0x00F0 );
}

/*
*********************************************************************************************************
*    函 数 名: NOR_GetStatus
*    功能说明: 读取NOR的操作状态
*    形    参: 无
*    返 回 值: 0表示成功.   NOR_SUCCESS, NOR_ERROR, NOR_TIMEOUT
*********************************************************************************************************
*/
static uint8_t NOR_GetStatus(uint32_t Timeout)
{
    uint16_t val1 = 0x00;
    uint16_t val2 = 0x00;
    uint8_t status = NOR_ONGOING;
    uint32_t timeout = Timeout;

    /* 等待NOR输出忙信号,高电平时等待。避免NOR的忙信号还未反映过来导致CPU提前认为不忙了 */
    while ((!NOR_IS_BUSY()) && (timeout > 0))
    {
        timeout--;
    }

    /* 等待NOR忙信号结束,低电平时等待 */
    timeout = Timeout;
    while(NOR_IS_BUSY() && (timeout > 0))
    {
        timeout--;
    }

    /*
        - DQ 6 编程时跳变
        - DQ 6 和 DQ 2 在擦除时跳变
        - DQ 2 在擦除挂起时跳变
        - DQ 1 在编程错误时置1
        - DQ 5 在超时时置1
    */
    /* 通过读取DQ6, DQ5 的数据位是否存在翻转现象判断NOR 内部操作是否完成。如果正忙,则第2次读和第1次读的数据不同 */
    while ((Timeout != 0x00) && (status != NOR_SUCCESS))
    {
        Timeout--;

        /* Read DQ6 */
        val1 = *(__IO uint16_t *)(NOR_FLASH_ADDR);
        val2 = *(__IO uint16_t *)(NOR_FLASH_ADDR);

        /* If DQ6 did not toggle between the two reads then return NOR_Success */
        if ((val1 & 0x0040) == (val2 & 0x0040))
        {
            return NOR_SUCCESS;
        }

        /* Read DQ2 */
        if((val1 & 0x0020) != 0x0020)
        {
            status = NOR_ONGOING;
        }

        val1 = *(__IO uint16_t *)(NOR_FLASH_ADDR);
        val2 = *(__IO uint16_t *)(NOR_FLASH_ADDR);

        if((val1 & 0x0040) == (val2 & 0x0040))
        {
            return NOR_SUCCESS;
        }
        else if ((val1 & 0x0020) == 0x0020)
        {
            status = NOR_ERROR;
            NOR_QuitToReadStatus();
        }
    }

    if (Timeout == 0x00)
    {
        status = NOR_TIMEOUT;
        NOR_QuitToReadStatus();
    }

    /* 返回操作状态 */
    return (status);
}

/*
*********************************************************************************************************
*    函 数 名: NOR_EraseChip
*    功能说明: 擦除NOR Flash整个芯片
*    形    参: 无
*    返 回 值: 0表示成功
*********************************************************************************************************
*/
uint8_t NOR_EraseChip(void)
{
    NOR_WRITE(ADDR_SHIFT(0x0555), 0x00AA);
    NOR_WRITE(ADDR_SHIFT(0x02AA), 0x0055);
    NOR_WRITE(ADDR_SHIFT(0x0555), 0x0080);
    NOR_WRITE(ADDR_SHIFT(0x0555), 0x00AA);
    NOR_WRITE(ADDR_SHIFT(0x02AA), 0x0055);
    NOR_WRITE(ADDR_SHIFT(0x0555), 0x0010);

    return (NOR_GetStatus(ChipErase_Timeout));
}

/*
*********************************************************************************************************
*    函 数 名: NOR_StartEraseChip
*    功能说明: 开始擦除NOR Flash整个芯片, 不等待结束
*    形    参: 无
*    返 回 值: 0表示成功
*********************************************************************************************************
*/
void NOR_StartEraseChip(void)
{
    NOR_WRITE(ADDR_SHIFT(0x0555), 0x00AA);
    NOR_WRITE(ADDR_SHIFT(0x02AA), 0x0055);
    NOR_WRITE(ADDR_SHIFT(0x0555), 0x0080);
    NOR_WRITE(ADDR_SHIFT(0x0555), 0x00AA);
    NOR_WRITE(ADDR_SHIFT(0x02AA), 0x0055);
    NOR_WRITE(ADDR_SHIFT(0x0555), 0x0010);
    
    NOR_GetStatus(1000);
}

/*
*********************************************************************************************************
*    函 数 名: NOR_CheckComplete
*    功能说明: 检测擦除是否完成
*    形    参: 无
*    返 回 值: 0表示成功   NOR_SUCCESS, NOR_ERROR, NOR_TIMEOUT
*********************************************************************************************************
*/
uint8_t NOR_CheckStatus(void)
{
    uint16_t val1 = 0x00;
    uint16_t val2 = 0x00;
    uint8_t status = NOR_ONGOING;
    uint32_t timeout = 10;
    
    /*
        - DQ 6 编程时跳变
        - DQ 6 和 DQ 2 在擦除时跳变
        - DQ 2 在擦除挂起时跳变
        - DQ 1 在编程错误时置1
        - DQ 5 在超时时置1
    */
    /* 通过读取DQ6, DQ5 的数据位是否存在翻转现象判断NOR 内部操作是否完成。如果正忙,则第2次读和第1次读的数据不同 */
    while ((timeout != 0x00) && (status != NOR_SUCCESS))
    {
        timeout--;

        /* Read DQ6 */
        val1 = *(__IO uint16_t *)(NOR_FLASH_ADDR);
        val2 = *(__IO uint16_t *)(NOR_FLASH_ADDR);

        /* If DQ6 did not toggle between the two reads then return NOR_Success */
        if ((val1 & 0x0040) == (val2 & 0x0040))
        {
            return NOR_SUCCESS;
        }

        /* Read DQ2 */
        if((val1 & 0x0020) != 0x0020)
        {
            status = NOR_ONGOING;
        }

        val1 = *(__IO uint16_t *)(NOR_FLASH_ADDR);
        val2 = *(__IO uint16_t *)(NOR_FLASH_ADDR);

        if((val1 & 0x0040) == (val2 & 0x0040))
        {
            return NOR_SUCCESS;
        }
        else if ((val1 & 0x0020) == 0x0020)
        {
            status = NOR_ERROR;
            NOR_QuitToReadStatus();
        }
    }

    if (timeout == 0x00)
    {
        status = NOR_TIMEOUT;
        //NOR_QuitToReadStatus();
    }

    /* 返回操作状态 */
    return (status);
}

/*
*********************************************************************************************************
*    函 数 名: NOR_EraseSector
*    功能说明: 擦除NOR Flash指定的扇区
*    形    参: 扇区地址
*    返 回 值: NOR_SUCCESS, NOR_ERROR, NOR_TIMEOUT
*********************************************************************************************************
*/
uint8_t NOR_EraseSector(uint32_t _uiBlockAddr)
{
    NOR_WRITE(ADDR_SHIFT(0x0555), 0x00AA);
    NOR_WRITE(ADDR_SHIFT(0x02AA), 0x0055);
    NOR_WRITE(ADDR_SHIFT(0x0555), 0x0080);
    NOR_WRITE(ADDR_SHIFT(0x0555), 0x00AA);
    NOR_WRITE(ADDR_SHIFT(0x02AA), 0x0055);
    NOR_WRITE((NOR_FLASH_ADDR + _uiBlockAddr), 0x30);

    return (NOR_GetStatus(BlockErase_Timeout));
}

/*
*********************************************************************************************************
*    函 数 名: NOR_ReadByte
*    功能说明: 读取单字节数据
*    形    参:     _uiWriteAddr : 偏移地址[0, 16*1024*1024 - 2]; 编程地址可以为偶数,也可以为奇数。
*    返 回 值: 读取到的数据
*********************************************************************************************************
*/
uint8_t NOR_ReadByte(uint32_t _uiWriteAddr)
{
    uint16_t usHalfWord;

    if (_uiWriteAddr % 2)    /* 奇数地址 */
    {
        usHalfWord = *(uint16_t *)(NOR_FLASH_ADDR + _uiWriteAddr - 1);
        return (usHalfWord >> 8);    /* 取高8Bit */
    }
    else    /* 偶数地址 */
    {
        usHalfWord = *(uint16_t *)(NOR_FLASH_ADDR + _uiWriteAddr);
        return usHalfWord;    /* 取低8Bit */
    }
}

/*
*********************************************************************************************************
*    函 数 名: NOR_ReadBuffer
*    功能说明: 连续读取NOR Flash
*    形    参:     _pBuf : 字节型数据缓冲区,用于存放读出的数据
*                _uiWriteAddr : 偏移地址[0, 16*1024*1024 - 2]; 编程地址可以为偶数,也可以为奇数。
*                _uiBytes : 字节大小
*    返 回 值: 读取到的数据
*********************************************************************************************************
*/
void NOR_ReadBuffer8Bit(uint8_t *_pBuf, uint32_t _uiWriteAddr, uint32_t _uiBytes)
{
    uint16_t usHalfWord;
    uint16_t *pNor16;
    uint32_t i;
    uint32_t uiNum;

    uiNum = _uiBytes;
    /* 处理首字节 */
    if (_uiWriteAddr % 2)    /* 奇数地址 */
    {
        usHalfWord = *(uint16_t *)(NOR_FLASH_ADDR + _uiWriteAddr - 1);
        *_pBuf++ = (usHalfWord >> 8);    /* 取高8Bit */
        uiNum--;
        _uiWriteAddr++;        /* 变为偶数 */
    }

    /* 按照双字节模式连续读取NOR数据至缓冲区_pBuf */
    pNor16 = (uint16_t *)(NOR_FLASH_ADDR + _uiWriteAddr);
    for (i = 0; i < uiNum / 2; i++)
    {
        usHalfWord = *pNor16++;
        *_pBuf++ = usHalfWord;
        *_pBuf++ = usHalfWord >> 8;
        uiNum -= 2;
    }

    /* 处理最后1个字节 */
    if (uiNum == 1)
    {
        *_pBuf++ = *pNor16;
    }
}

/*
*********************************************************************************************************
*    函 数 名: NOR_WriteHalfWord
*    功能说明: 半字编程. 编程前执行解锁命令序列。编程完毕后,自动退到读取模式。半字编程可以是随机地址。
*                编程前需要保证存储单元是全0xFF状态。可以重复编程相同的数据。
*    形    参:     _uiWriteAddr : 偏移地址[0, 16*1024*1024 - 2]; 编程地址必须为偶数
*                _usData      : 数据 16Bit
*
*    返 回 值: NOR_SUCCESS, NOR_ERROR, NOR_TIMEOUT
*********************************************************************************************************
*/
uint8_t NOR_WriteHalfWord(uint32_t _uiWriteAddr, uint16_t _usData)
{
    NOR_WRITE(ADDR_SHIFT(0x0555), 0x00AA);
    NOR_WRITE(ADDR_SHIFT(0x02AA), 0x0055);
    NOR_WRITE(ADDR_SHIFT(0x0555), 0x00A0);
    NOR_WRITE(NOR_FLASH_ADDR + _uiWriteAddr, _usData);

    return (NOR_GetStatus(Program_Timeout));
}

/*
*********************************************************************************************************
*    函 数 名: NOR_WriteByte
*    功能说明: 字节编程. 编程前需要保证存储单元是全0xFF状态。可以重复编程相同的数据。
*    形    参:     _uiWriteAddr : 偏移地址[0, 16*1024*1024 - 1]; 编程地址可以为奇数也可以为偶数
*                _usData      : 数据 16Bit
*
*    返 回 值: NOR_SUCCESS, NOR_ERROR, NOR_TIMEOUT
*********************************************************************************************************
*/
uint8_t NOR_WriteByte(uint32_t _uiWriteAddr, uint8_t _ucByte)
{
    uint16_t usHalfWord;

    if (_uiWriteAddr % 2)    /* 奇数地址 */
    {
        /* 读出2字节数据,然后改写高字节,维持以前的低字节数据不变 */
        usHalfWord = *(uint16_t *)(NOR_FLASH_ADDR + _uiWriteAddr - 1);
        usHalfWord &= 0x00FF;
        usHalfWord |= (_ucByte << 8);
    }
    else
    {
        /* 读取NOR原始数据,保留高字节 */
        usHalfWord = *(uint16_t *)(NOR_FLASH_ADDR + _uiWriteAddr);
        usHalfWord &= 0xFF00;
        usHalfWord |= _ucByte;
    }
    return NOR_WriteHalfWord(_uiWriteAddr, usHalfWord);
}

/*
*********************************************************************************************************
*    函 数 名: NOR_WriteInPage.    
*    功能说明: 页面内编程(64字节一个页面). 编程前需要保证存储单元是全0xFF状态。可以重复编程相同的数据。
*    形    参:     pBuffer : 数据存放在此缓冲区
*                _uiWriteAddr : 偏移地址, 必须是偶数开始
*                _usNumHalfword      : 数据格式,双字节为1个单位. 值域: 1-32
*
*    返 回 值: NOR_SUCCESS, NOR_ERROR, NOR_TIMEOUT
*********************************************************************************************************
*/
uint8_t NOR_WriteInPage(uint16_t *pBuffer, uint32_t _uiWriteAddr,  uint16_t _usNumHalfword)
{
    uint32_t lastloadedaddress;
    uint32_t currentaddress;
    uint32_t endaddress;

    /* pdf 表7.7 写入缓冲器编程

        写入缓冲器编程允许系统在一个编程操作中写入最多32 个字。与标准的“ 字” 编程算法相比,这可以有效地
        加快字编程速度。
    */
    
    if (_usNumHalfword > 32)
    {
        return NOR_ERROR;
    }
    
    if ((_uiWriteAddr % 2) != 0)
    {
        return NOR_ERROR;
    }
    
    _uiWriteAddr = _uiWriteAddr / 2;

    currentaddress = _uiWriteAddr;
    endaddress = _uiWriteAddr + _usNumHalfword - 1;
    lastloadedaddress = _uiWriteAddr;

    /* 解锁命令序列 */
    NOR_WRITE(ADDR_SHIFT(0x00555), 0x00AA);
    NOR_WRITE(ADDR_SHIFT(0x02AA), 0x0055);

    /* Write Write Buffer Load Command */
    NOR_WRITE(ADDR_SHIFT(_uiWriteAddr), 0x0025);
    NOR_WRITE(ADDR_SHIFT(_uiWriteAddr), (_usNumHalfword - 1));

    /*  Load Data into NOR Buffer */
    while (currentaddress <= endaddress)
    {
        /* Store last loaded address & data value (for polling) */
        lastloadedaddress = currentaddress;

        NOR_WRITE(ADDR_SHIFT(currentaddress), *pBuffer++);
        currentaddress += 1;
    }

    NOR_WRITE(ADDR_SHIFT(lastloadedaddress), 0x29);

    return (NOR_GetStatus(Program_Timeout));
}

/*
*********************************************************************************************************
*    函 数 名: NOR_WriteBuffer
*    功能说明: 连续编程操作。采取半字编程模式。
*                   S29GL 支持64字节页面大小的连续编程。本函数暂时不支持页面编程。
*    形    参: _pBuf : 8位数据缓冲区
*             _uiWriteAddr : 写入的存储单元首地址, 必须为偶数
*             _uiBytes : 字节个数
*    返 回 值: NOR_SUCCESS, NOR_ERROR, NOR_TIMEOUT
*********************************************************************************************************
*/
uint8_t NOR_WriteBuffer8Bit(uint8_t *_pBuf, uint32_t _uiWriteAddr, uint32_t _uiBytes)
{
    uint16_t usHalfWord;
    uint32_t i;
    uint32_t uiNum;
    uint8_t ucStatus;

    uiNum = _uiBytes;
    /* 处理首字节 */
    if (_uiWriteAddr % 2)    /* 奇数地址 */
    {
        /* 读出2字节数据,然后改写高字节,维持以前的低字节数据不变 */
        usHalfWord = *(uint16_t *)(NOR_FLASH_ADDR + _uiWriteAddr - 1);
        usHalfWord &= 0x00FF;
        usHalfWord |= ((*_pBuf++) << 8);

        ucStatus = NOR_WriteHalfWord(_uiWriteAddr - 1, usHalfWord);
        if (ucStatus != NOR_SUCCESS)
        {
            goto err_quit;
        }

        uiNum--;
        _uiWriteAddr++;        /* 变为偶数 */
    }

    /* 按照双字节模式连续编程NOR数据 */
    for (i = 0; i < uiNum / 2; i++)
    {
        usHalfWord = *_pBuf++;
        usHalfWord |= ((*_pBuf++) << 8);

        ucStatus = NOR_WriteHalfWord(_uiWriteAddr, usHalfWord);
        if (ucStatus != NOR_SUCCESS)
        {
            goto err_quit;
        }

        _uiWriteAddr += 2;
    }

    /* 处理最后1个字节 */
    if (uiNum % 2)
    {
        /* 读取NOR原始数据,保留高字节 */
        usHalfWord = *(uint16_t *)(NOR_FLASH_ADDR + _uiWriteAddr);
        usHalfWord &= 0xFF00;
        usHalfWord |= (*_pBuf++);

        ucStatus = NOR_WriteHalfWord(_uiWriteAddr, usHalfWord);
        if (ucStatus != NOR_SUCCESS)
        {
            goto err_quit;
        }
    }
    ucStatus = NOR_SUCCESS;
err_quit:
    return     ucStatus;
}

NOR_STATUS NOR_WriteBuffer(uint16_t* pBuffer, uint32_t WriteAddr, uint32_t NumHalfwordToWrite)
{
  NOR_STATUS status = NOR_ONGOING; 

  do
  {
    /*!< Transfer data to the memory */
    status = NOR_WriteHalfWord(WriteAddr, *pBuffer++);
    WriteAddr = WriteAddr + 2;
    NumHalfwordToWrite--;
  }
  while((status == NOR_SUCCESS) && (NumHalfwordToWrite != 0));
  
  return (status); 
}

void NOR_ReadBuffer(uint16_t* pBuffer, uint32_t ReadAddr, uint32_t NumHalfwordToRead)
{
  NOR_WRITE(ADDR_SHIFT(0x0555), 0x00AA);
  NOR_WRITE(ADDR_SHIFT(0x02AA), 0x0055);
  NOR_WRITE((NOR_FLASH_ADDR + ReadAddr), 0x00F0);

  for(; NumHalfwordToRead != 0x00; NumHalfwordToRead--) /*!< while there is data to read */
  {
    /*!< Read a Halfword from the NOR */
    *pBuffer++ = *(__IO uint16_t *)((NOR_FLASH_ADDR + ReadAddr));
    ReadAddr = ReadAddr + 2; 
  }  
}


创建stm32f4_fsmc_nor.h文件,编写如下代码:

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __STM32F4_FSMC_NOR_H
#define __STM32F4_FSMC_NOR_H

#ifdef __cplusplus
 extern "C" {
#endif

/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx.h"
     

#define NOR_FLASH_ADDR      ((uint32_t)0x64000000)

#define NOR_SECTOR_SIZE        (128 * 1024)        /* 扇区大小 */
#define NOR_SECTOR_COUNT    128                        /* 扇区个数 */
#define NOR_FLASH_SIZE        (NOR_SECTOR_SIZE * NOR_SECTOR_COUNT)

/*
    制造商ID:Spansion   0x01

    S29GL01GP    01 7E 28 01        1 Gigabit        128M字节
    S29GL512P    01 7E 23 01        512 Megabit        64M字节
    S29GL256P    01 7E 22 01        256 Megabit        32M字节
    S29GL128P    01 7E 21 01        128 Megabit        16M字节
*/
typedef enum
{
    S29GL128P = 0x017E2101,
    S29GL256P = 0x017E2201,
    S29GL512P = 0x017E2301
}NOR_CHIP_ID;

/* NOR Status */
typedef enum
{
    NOR_SUCCESS = 0,
    NOR_ONGOING = 1,
    NOR_ERROR   = 2,
    NOR_TIMEOUT = 3
}NOR_STATUS;

void bsp_InitNorFlash(void);
uint32_t NOR_ReadID(void);
uint8_t NOR_EraseChip(void);
uint8_t NOR_EraseSector(uint32_t _uiBlockAddr);
uint8_t NOR_ReadByte(uint32_t _uiWriteAddr);

void NOR_ReadBuffer(uint16_t *_pBuf, uint32_t _uiWriteAddr, uint32_t _uiBytes);

uint8_t NOR_WriteHalfWord(uint32_t _uiWriteAddr, uint16_t _usData);
uint8_t NOR_WriteByte(uint32_t _uiWriteAddr, uint8_t _ucByte);
uint8_t NOR_WriteInPage(uint16_t *pBuffer, uint32_t _uiWriteAddr,  uint16_t _usNumHalfword);

NOR_STATUS NOR_WriteBuffer(uint16_t *_pBuf, uint32_t _uiWriteAddr, uint32_t _uiBytes);

void NOR_StartEraseChip(void);
uint8_t NOR_CheckStatus(void);

#ifdef __cplusplus
}
#endif

#endif /* __STM32F4_FSMC_NOR_H */

2.3 Dev_Inf的参数

在Dev_Inf.c文件中实现如下代码(注意在该文件中不要添加任何头文件或者函数),修改对应的参数,使其和板卡上的芯片参数一致

源代码如下:

#include "Dev_Inf.h"

/* This structure containes information used by ST-LINK Utility to program and erase the device */
struct StorageInfo const StorageInfo  =  {
   "M29W128GL_STM32F407ALY",                           // Device Name + version number
   NOR_FLASH,                                          // Device Type
   0x64000000,                                          // Device Start Address
   0x01000000,                                          // Device Size in Bytes (16MBytes/128Mbits)
   0x00000010,                                          // Programming Page Size 16Bytes
   0xFF,                                                // Initial Content of Erased Memory
    
   // Specify Size and Address of Sectors (view example below)
   0x00000080, 0x00020000,                              // Sector Num : 128 ,Sector Size: 64KBytes 
   0x00000000, 0x00000000,
}; 

/*      
   Sector coding example
    A device with succives 16 Sectors of 1KBytes, 128 Sectors of 16 KBytes, 
    8 Sectors of 2KBytes and 16384 Sectors of 8KBytes
    
    0x00000010, 0x00000400,                                 // 16 Sectors of 1KBytes
    0x00000080, 0x00004000,                                 // 128 Sectors of 16 KBytes
    0x00000008, 0x00000800,                                 // 8 Sectors of 2KBytes
    0x00004000, 0x00002000,                                 // 16384 Sectors of 8KBytes
    0x00000000, 0x00000000,                                    // end
*/

/* End of this file */

3 Keil中的配置

3.1 配置参数

1) 选择MCU的类型

2)定义Output文件的文件名称

3)复制生成的.stldr文件到固定的目录

cmd.exe /C copy "!L" "..\..\..\@L.stldr"

 

 4)使能Library, 生成代码配置为图中选项

 5)配置汇编选项中的代码特征

6)配置链接文件

 3.2 编译项目

配置完成参数后,就可以编译项目,编译结果如下:

同时,在如下目录中看见生成的文件名称

4 测试

4.1 ST-link连接板卡

打开STM32CubeProgrammer工具,使用ST-link和板卡连接起来,选中Flash leader 源文件

 如果连接正常,可以看见,ST-Link从NOR Flash中读取到了数据

4.2 下载文件测试

 1)写Flash文件到Nor Flash之前,首先需要擦除芯片内的信息

擦除完成后,可以看见Flash中的数据全部为0xff,说明擦除Flash数据完成

 2)下载数据至Flash中

 3)读取数据,读取数据的地址位0x6400 0000, 比较读写的数据文件,二者完全相同

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1881294.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Camera Raw:编辑 - 混色器

Camera Raw “编辑”模块中的混色器 Color Mixer面板用于调整图像中的颜色&#xff0c;通过控制色相、饱和度和明亮度&#xff0c;可以对特定颜色进行微调&#xff0c;优化图像的色彩表现。 面板分为“混色器”和“点颜色”两个模式。 ◆ ◆ ◆ 使用方法与技巧 1、查看要调整…

【机器学习】Python zip 函数

目录&#xff1a; 什么是zip函数zip的基本使用zip与列表、字典的结合zip的长度匹配规则实际应用 1. 什么是zip函数 zip函数在Python中用于将多个可迭代对象&#xff08;如列表、元组、字符串等&#xff09;打包成一个元组的列表&#xff0c;其中每个元组包含的是原始对象中的…

邀请函 | 极限科技全新搜索引擎 INFINI Pizza 亮相 2024 可信数据库发展大会!

过去一年&#xff0c;在全球 AI 浪潮和国家数据局成立的推动下&#xff0c;数据库产业变革不断、热闹非凡。2024 年&#xff0c;站在中国数字经济产业升级和数据要素市场化建设的时代交汇点上&#xff0c;“2024 可信数据库发展大会” 将于 2024 年 7 月 16-17 日在北京悠唐皇冠…

使用vite官网和vue3官网分别都可以创建vue3项目

问: npm init vitelatest 和 npm create vuelatest创建的vue3项目有什么区别? 回答: npm init vitelatest 和 npm create vuelatest 分别是使用 Vite 和 Vue CLI 工具创建 Vue 项目的两种方式&#xff0c;它们之间有几个主要区别&#xff1a; 1. **构建工具&#xff1a;** …

WIFI模组ESP-12F 规格书分享

一、 产品概述 ESP-12F 是由安信可科技开发的 Wi-Fi 模块&#xff0c;该模块核心处理器 ESP8266 在较小尺 寸封装中集成了业界领先的 Tensilica L106 超低功耗 32 位微型 MCU&#xff0c;带有 16 位精 简模式&#xff0c;主频支持 80 MHz 和 160 MHz&#xff0c;支持 RTOS&…

【PB案例学习笔记】-25制作一个带底图的MDI窗口

写在前面 这是PB案例学习笔记系列文章的第25篇&#xff0c;该系列文章适合具有一定PB基础的读者。 通过一个个由浅入深的编程实战案例学习&#xff0c;提高编程技巧&#xff0c;以保证小伙伴们能应付公司的各种开发需求。 文章中设计到的源码&#xff0c;小凡都上传到了gite…

什么是未授权访问漏洞?Hadoop Redis靶场实战——Vulfocus服务攻防

什么是未授权访问漏洞&#xff1f;Hadoop & Redis靶场实战——Vulfocus服务攻防 一、介绍 未授权访问&#xff0c;也称为未经授权的访问或非法访问&#xff0c;是指在没有得到适当权限或授权的情况下&#xff0c;个人或系统访问了网络、计算机、数据库、文件、应用程序或…

C语言 | Leetcode C语言题解之第205题同构字符串

题目&#xff1a; 题解&#xff1a; struct HashTable {char key;char val;UT_hash_handle hh; };bool isIsomorphic(char* s, char* t) {struct HashTable* s2t NULL;struct HashTable* t2s NULL;int len strlen(s);for (int i 0; i < len; i) {char x s[i], y t[i]…

DETR End-to-End Object Detection with Transformers

End-to-End Object Detection with Transformers 论文链接&#xff1a;http://arxiv.org/abs/2005.12872 代码地址&#xff1a;https://github.com/facebookresearch/detr 一、摘要 提出了一种将目标检测视为直接集合预测问题的新方法。该方法简化了检测流程&#xff0c;有效…

【TB作品】20以内加减法训练机,ATMEGA128单片机,Proteus仿真

题目 7 &#xff1a;玩具电子琴 基于单片机设计一能够发出中音八个音阶的音乐信号的电子琴&#xff0c;能够实现弹奏和音符显示功 能。 具有 8 个音阶按键&#xff0c;每按下一个按键时&#xff0c;所对应的 LED 点亮&#xff0c;音符进行显示。 具体要求如下&#xff1a; &…

微积分-导数3(微分法则)

常见函数的导数 常量函数的导数 d d x ( c ) 0 \frac{d}{dx}(c) 0 dxd​(c)0 常量函数的图像是一条水平线 y c y c yc&#xff0c;它的斜率为0&#xff0c;所以我们必须有 f ′ ( x ) 0 f(x) 0 f′(x)0。从导数的定义来看&#xff0c;证明也很简单&#xff1a; f ′ …

系统工程与信息系统基础(上)

目录 系统工程 霍尔三维结构的三维&#xff1a; 切克兰德方法&#xff1a; 并行工程方法&#xff1a; 综合集成法&#xff1a; WSR系统方法&#xff1a; 系统工程生命周期阶段 探索性阶段 概念阶段 开发阶段 生产阶段 使用阶段 保障阶段 退役阶段 系统工程生命周…

sky18流水线设计

1.最大时钟频率确定 时钟周期要大于等于组合逻辑的delay&#xff08;最大的那条delay&#xff09; Freq_max(Mhz) 1000/T_delay(ns); 数据吞吐率Throughput Freq_max *Toggle_rate;//Toggle_rate&#xff1a;如两个时钟&#xff0c;输入变一次&#xff0c;就是50%&#xff1b…

业务链SFC简介

目录 业务链&#xff08;SFC&#xff09;简介什么是业务链&#xff1f;业务链的体系架构业务链的基本工作流程PBRPBR实现的SFC工作流程 NSHNSH报文NSH实现的SFC工作流程 区别 业务链的应用 配置指南 业务链&#xff08;SFC&#xff09;简介 业务链是网络功能虚拟化&#xff08…

javascript v8编译器的使用记录

我的机器是MacOS Mx系列。 一、v8源码下载构建 1.1 下载并更新depot_tools git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git export PATH/path/to/depot_tools:$PATH 失败的话可能是网络问题&#xff0c;可以试一下是否能ping通&#xff0c;连…

【轻量化】YOLOv8 更换骨干网络之 MobileNetv4 | 《号称最强轻量化网络》

论文地址:https://arxiv.org/pdf/2404.10518 代码地址:https://github.com/tensorflow/models/blob/master/official/vision/modeling/backbones/mobilenet.py 文章速览 文章摘要 MobileNetV4引入了一个名为Universal Inverted Bottleneck (UIB) 的新搜索模块,这个模块融合…

vue 组件el-tree添加结构指示线条

效果展示: 注意&#xff1a;组件中需要添加:indent"0" 进行子级缩进处理&#xff0c;否则会出现子级缩进逐级递增 :expand-on-click-node"false" 设置点击箭头图标才会展开或者收起 代码&#xff1a; <el-tree class"tree filter-tree" :da…

高并发场景下的热点key问题探析与应对策略

目录 一、问题描述 二、发现机制 三、解决策略分析 &#xff08;一&#xff09;解决策略一&#xff1a;多级缓存策略 客户端本地缓存 代理节点本地缓存 &#xff08;二&#xff09;解决策略二&#xff1a;多副本策略 &#xff08;三&#xff09;解决策略三&#xff1a;热点…

商标字体的选择:企业和个人申请注册商标攻略!

对于汉字商标&#xff0c;就会涉及到字体的选择&#xff0c;普推商标老杨也经常看到企业因为文字商标字体侵权收到相关字体公司的律师函&#xff0c;所以商标字体选择上要特别注意。 建议选择可以商用的免费字体&#xff0c;常见的有黑体、宋体等&#xff0c;如果这些字体前面…

vue 组件批量删除

element ui table表格中前面这个勾选框 对应 type"selection" 属性&#xff0c;绑定事件时selection-change,当你勾选全面的框时就会触发selection-change 对应的事件 绑定事件里面这样定义方法时&#xff0c;这个val 就是选中的时候那一行的数据&#xff0c;如下图…