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

目录

概述

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.mfbz.cn/a/760489.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

人工智能期末复习笔记(更新中)

分类问题 分类&#xff1a;根据已知样本的某些特征&#xff0c;判断一个新的样本属于哪种已知的样本类 垃圾分类、图像分类 怎么解决分类问题 分类和回归的区别 1. 逻辑回归分类 用于解决分类问题的一种模型。根据数据特征或属性&#xff0c;计算其归属于某一类别 的概率P,…

66、基于长短期记忆 (LSTM) 网络对序列数据进行分类

1、基于长短期记忆 (LSTM) 网络对序列数据进行分类的原理及流程 基于长短期记忆&#xff08;LSTM&#xff09;网络对序列数据进行分类是一种常见的深度学习任务&#xff0c;适用于处理具有时间或序列关系的数据。下面是在Matlab中使用LSTM网络对序列数据进行分类的基本原理和流…

XJTUSE-数据结构-homework1

任务 1 题目&#xff1a; 排序算法设计&#xff1a; 需要写Selection、Shell、Quicksort 和 Mergesort四种排序算法&#xff0c;书上讲述比较全面而且不需要进行额外的优化&#xff0c;下面我简要地按照自己的理解讲述。 Selection&#xff08;选择排序&#xff09;&#xff…

HarmonyOS Next开发学习手册——单选框 (Radio)

Radio是单选框组件&#xff0c;通常用于提供相应的用户交互选择项&#xff0c;同一组的Radio中只有一个可以被选中。具体用法请参考 Radio 。 创建单选框 Radio通过调用接口来创建&#xff0c;接口调用形式如下&#xff1a; Radio(options: {value: string, group: string})…

Linux常用工具使用方式

目录 常用工具&#xff1a; 安装包管理工具&#xff1a; 查找含有关键字的软件包 安装软件 安装文件传输工具 安装编辑器 C语言编译器 C编译器 安装调试器 安装项目版本管理工具 cmake 卸载软件 安装jsoncpp 安装boost库 安装mariadb 安装tree&#xff08;让目录…

Python28-3 朴素贝叶斯分类算法

朴素贝叶斯算法简介 朴素贝叶斯&#xff08;Naive Bayes&#xff09;算法是一种基于贝叶斯定理的分类算法。它广泛应用于文本分类、垃圾邮件检测和情感分析等领域。该算法假设特征之间是独立的&#xff0c;这个假设在实际情况中可能并不完全成立&#xff0c;但Naive Bayes在许…

java笔记(30)——反射的 API 及其 使用

文章目录 反射1. 什么是反射2. 获取class字段&#xff08;字节码文件对象&#xff09;方式1方式2方式3应用 3. 获取构造方法和权限修饰符前期准备获取所有的公共构造方法获取所有的构造方法获取无参构造方法获取一个参数的构造方法获取一个参数的构造方法获取两个参数的构造方法…

Java面试题--JVM大厂篇之G1 GC的分区管理方式如何减少应用线程的影响

目录 引言: 正文: 1. 区域划分&#xff08;Region&#xff09; 2. 并行和并发回收 3. 区域优先回收&#xff08;Garbage First&#xff09; 4. 可预测的停顿时间 5. 分阶段回收 6. 复制和压缩 实际效果: 场景举例 1. 减少单次GC的影响 2. 支持高并发环境 3. 优…

数学建模(1):期末大乱炖

1 概述&#xff01;&#xff01; 1.1 原型和模型 原型&#xff1a;客观存在的研究对象称为原型&#xff0c;也称为“系统”、“过程”。 机械系统、电力系统、化学反应过程、生产销售过程等都是原型&#xff1b; 研究原型的结构和原理&#xff0c; 从而进行优化、预测、评价…

一区算法MPA|海洋捕食者算法原理及其代码实现(Matlab/Python))

Matlab/Python&#xff1a; 本文KAU将介绍一个2020年发表在1区期刊ESWA上的优化算法——海洋捕食者算法 (Marine Predators Algorithm&#xff0c;MPA)[1] 该算法由Faramarzi等于2020年提出&#xff0c;其灵感来源于海洋捕食者之间不同的觅食策略、最佳相遇概率策略、海洋记…

【MySQL】Linux下MySQL的目录结构、用户、权限与角色

一、Linux下MySQL的目录结构 1、MySQL相关目录 数据库文件存放路径&#xff1a;/var/lib/mysql数据库命令存放路径&#xff1a;/user/bin和/user/sbin配置文件目录&#xff1a;/usr/share/mysql-8.0/、/usr/share/mysql/和/etc/my.cnf 2、假设我们创建了一个数据库dbtest1&a…

使用evo工具比较ORB-SLAM3的运行轨迹(从安装到解决报错)

ORB-SLAM2和ORB-SLAM3怎么跑出来&#xff0c;之前都有相关的保姆级的教程&#xff0c;下来给大家介绍一款evo工具&#xff0c;给科研加速&#xff01;&#xff01;&#xff01; 文章目录 1.下载evo2.生成轨迹3.evo别的功能使用 1.下载evo 输入命令下载 pip install -i https…

你真的会udf提权???数据库权限到系统权限 内网学习 mysql的udf提权操作 ??msf你会用了吗???

我们在已经取得了数据库的账号密码过后&#xff0c;我们要进一步进行提取的操作&#xff0c;我们mysql有4钟提权的操作。 udf提权(最常用的)mof提权启动项提权反弹shell提权操作 怎么获取密码操作&#xff1a; 怎么获取密码&#xff0c;通过sql注入获取这个大家都应该知道了&a…

百强韧劲,进击新局 2023年度中国医药工业百强系列榜单发布

2024年&#xff0c;经济工作坚持稳中求进、以进促稳、先立后破等工作要求。医药健康行业以不懈进取的“韧劲”&#xff0c;立身破局&#xff0c;迎变启新。通过创新和迭代应对不确定性&#xff0c;进化韧性力量&#xff0c;坚持高质量发展&#xff0c;把握新时代经济和社会给予…

零基础开始学习鸿蒙开发-读书app简单的设计与开发

目录 1.首页设计 2.发现页面的设计 3.设置页面的设计 4.导航页设计 5.总结&#xff1a; 6.最终的效果 1.首页设计 Entry Component export struct home {State message: string 首页build() {Row() {Column() {Text(this.message).fontSize(50).fontWeight(FontWeight.B…

基于线调频小波变换的非平稳信号分析方法(MATLAB)

信号处理领域学者为了改进小波变换在各时频区间能量聚集性不高的缺点&#xff0c;有学者在小波分析基础上引入调频算子构成了线性调频小波变换&#xff0c;线调频小波一方面继承了小波变换的理论完善性&#xff0c;另一方面用一个新的参数&#xff08;线调频参数&#xff09;刻…

构建高效业财一体化管理体系

构建高效业财一体化管理体系 业财一体化战略意义 提升决策质量 强化数据支撑&#xff1a;通过整合业务与财务数据&#xff0c;为决策提供准确、实时的信息基础&#xff0c;确保分析的深度与广度。促进业务与财务协同&#xff1a;打破信息孤岛&#xff0c;实现业务流程与财务管…

Django 定义模型执行迁移

1&#xff0c;创建应用 Test/app8 python manage.py startapp app8 2&#xff0c;注册应用 Test/Test/settings.py 3&#xff0c;配置路由 Test/Test/urls.py from django.contrib import admin from django.urls import path, includeurlpatterns [path(app8/, include(a…

Linux服务器上安装CUDA11.2和对应的cuDNN 8.4.0

一、检查 检查本机是否有CUDA工具包&#xff0c;输入nvcc -V: 如图所示&#xff0c;服务器上有CUDA&#xff0c;但版本为9.1.85&#xff0c;版本过低&#xff0c;因此博主要重装一个新的。 二、安装CUDA 1.查看服务器最高支持的CUDA版本 在命令行输入nvidia-smi查看显卡驱动…

Mining Engineering First Aid Riding

4个最主要的日常技能&#xff1a;Mining 采矿 Engineering 工程 First Aid 急救 Riding 骑术 4个最主要的日常技能