如何制作符合自己设备的FLM下载算法

如何制作符合自己设备的FLM下载算法

--------以I.MXRT1062 QSPI FLAH为例(串行qspi nor flash)

本文介绍一种基于i.mxrt1062的外挂flah的qspi nor flash下载算法FLM的一种方法,Flash 编程算法是一种用于擦除或下载应用程序到 Flash 设备的软件。设备系列包 (DFP) 通常包含预定义的 Flash 算法,用于对其中支持的设备进行编程。ARM:CMSIS 包中提供了用于创建新算法的模板。以下部分更详细地介绍了该过程。

前言

什么是FLMFLM(FLASH Loader Middeware)即闪存加载中间件,一般来说FLM下载算法不需要我们制作,因为使用Keil MDK进行开发,我们需要安装packet软件包,而这软件包就包含了FLM下载算法。当我们在keil mdk中编译好了镜像(hex 、bin、srec、elf)文件需要下载到flash当中时,首先需要选择适合您开发板的FLM下载算法。
选择FLM下载算法

从上图可以看到我们可以选择的下载算法有都在列表里面了,但是我们要把程序下载进去需要选择适合自己的FLM文件,像开发STM32则直接选择就可以了,我们在安装软件包时候就已经将下载算法包含到Flash文件夹当中了。我们可以打开keil mdk的安装目录进行查看(路径为**…**/keil/arm/flash)文件夹下。
在这里插入图片描述

1、下载算法原理

我们使用下载工具(J-LinkST-LinkDAP-Link)进行下载,其本质就是将FLM(Flash Loader Middeware)加载到RAM当中(可以理解为将一段操作FLASH的代码下载到RAM中然后通过这段程序将code下载到FLASH中),然后通过FLM作为媒介将程序下载到Flash当中。

加载流程

: 截自 <Programming External Flash used with STM32 Devices MDK Tutorial AN333, Autumn 2020, v1.0>

2、工程模板

笔者认为想要去快速入门某一样事情,最快的方式是先找历程demo,先把demo跑通然后再去深究其实现原理。这是尤为重要。回到本文的核心,怎么做?怎么找到模板?答案肯定是有的,除了在安装软件包附带的FLM下载算法之外还有keil 给我们留下的工程模板,这个模板的位置在keil5安录/ARM/Flash/_Template这个文件夹里面有给模板工程,我们对工程进行编译会发现会得到一个.FLM文件。

在这里插入图片描述

打开工程会发现工程目录只有简单的几个文件。

在这里插入图片描述

那么这两个文件有什么奥妙呢?接下来笔者为大家一一解密,解其全貌。

  • Abstract.txt

    这个文件里面写了笔记以及参考的网站。http://arm-software.github.io/CMSIS_5/Pack/html/flashAlgorithm.html

  • FlashPrag.c

    这个文件是和你的下载息息相关,大家可以去看看里面的函数,不难发现里面的函数是空函数,这部分需要读者自己去实现。

    这些函数分别是

    • init();

      • Init解释为初始化,初始化时钟、外设总线、Flash接口等等。
    • uinit();

      • Uint解释为解除初始化,解除初始化,用在IDE完成对程序的下载之后,解除FLM的操作。
    • EraseChip();

      • EraseChip解释为擦除整个芯片的意思,对于Flash而言,要想将程序下载到Flash里面,就要先擦除,Flash存储器的擦除操作是在块级别上进行的,意味着要擦除数据时,必须擦除整个块,而不能只擦除单个字节或位,擦除操作会将块中的所有位设置为逻辑“1”。
      • 在NOR Flash中,擦除操作会将位设置为高电平;在NAND Flash中,擦除操作会将位设置为低电平。在向Flash存储器写入数据之前,必须先擦除目标块,因为Flash只能将位从“1”改变为“0”,而不能从“0”改变为“1”。
    • EraseSector();

      EraseSector 解释为擦除扇区,在Flash中可以选择擦除的方式,比如:

      • 单元(Cell):Flash存储器的基本存储单元,用于存储一个位(bit)的信息。
      • 块(Block):由多个存储单元组成,是Flash擦除操作的基本单位。一个块通常包含32、64、128或更多个字节。
      • 面(Plane) 或 子系统(Sub-array):Flash设备可能包含多个块,这些块可以组织成面或子系统。
    • ProgramPage();

      ProgramPage(通俗讲就是将程序下载到Flash里面)通常是指在使用Flash存储器编程时,将数据写入Flash的一个操作过程。在嵌入式系统编程中,Flash编程通常涉及以下步骤:

      • 擦除(Erase):在写入数据之前,必须先擦除Flash块。这是因为Flash只能将位从1改变为0,而不能从0改变为1。擦除操作会将整个块设置为全1状态。
      • 编程(Program):编程操作是将数据写入已经被擦除的Flash块中。ProgramPage通常指的是将一页(Page)数据写入Flash存储器。
  • FlashDev.c

    这个文件是我们注册的设备结构体,和您的设备相关。

    /****************************************************************************
     * @file     FlashDev.c
     * @brief    Flash Device Description for New Device Flash
     * @version  V1.0.0
     * @date     10. January 2018
     ******************************************************************************/
    /*
     * Copyright (c) 2010-2018 Arm Limited. All rights reserved.
     *
     * SPDX-License-Identifier: Apache-2.0
     *
     * Licensed under the Apache License, Version 2.0 (the License); you may
     * not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     * www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an AS IS BASIS, WITHOUT
     * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
     
    #include "FlashOS.H"        // FlashOS Structures
    
    /* ZLG-MIMxRT1062 */
    struct FlashDevice const FlashDevice  =  {
       FLASH_DRV_VERS,               /* 设备版本 不能更改! */
       "MIMxRT1062_W25Q256",         /* 设备名称 */  
       EXTSPI,                       /* 设备类型 可以跳转到FlashOS.H去查看 相关的定义 */ 
       0x60000000,                   /* Flash设备shou地址 会将这个地址挂载到AHB总线上 */ 
       0x00800000,                   /* Flash设备的空间大小 这里是0x010000000 也就是16MB*/ 
       256,                          /* 编程页的大小 根据Flash的型号数据手册可以选择256 但是为了速度快一些可以选择 4096 */ 
       0,                            /* 保留位,但是这里不能修改必须默认为0 */ 
       0xFF,                         /* 擦除内存的初始内容 */ 
       100,                          /* 程序页面超时 100 毫秒 根据手册可以修改相应的时间 */ 
       3000,                         /* 擦除扇区超时 3000 毫秒 根据手册可以修改相应的时间*/ 
    
       0x001000, 0x000000,           /* 扇区大小 4KB*/ 
    
       SECTOR_END
    };
    

3、实战

1、基于I.MX RT1062的FLM下载算法

[!NOTE]

将模板工程复制到卓面(路径为:安装目录\Keil_v5\ARM\Flash_Template),取名为MIMxRT1062_xxxx(工程名称随意取)。除此之外我们还需要将从nxp官网的sdk下载到卓面。

1、将keil提供的模板复制到卓面中

2、从nxp官网下载官方evk的sdk

👍参考连接为:https://mcuxpresso.nxp.com/zh

进去之后选择开发板,选项,如在过程中提示需要登陆则先注册登录。

在这里插入图片描述

进去之后选择电路板/处理器,然后电机右下角的构建SDK,后面勾选全部工具链,最后点击下载即可。

在这里插入图片描述

点击构建sdk,最后点击下载即可。

在这里插入图片描述

下载完成之之后将SDK进行解压缩操作。

在这里插入图片描述

同步打开复制到卓面的工程模板

在这里插入图片描述

图上勾选的是从SDK中复制过来的文件夹,CMSIS、devices,里面有芯片的一些相关信息,复制进行主要是为了对芯片进行init的时候起到作用。其中的flexspi文件夹是因为板子使用flexspi操作flash,所以进行了分类。flash_ops文件夹是操作flash的具体函数。

做完这些之后我们开始点击工程,进到keil中进行代码的编写。

[!IMPORTANT]

接下来就是在keil中进行代码的编写了,这段内容比较重要,在文章的结尾会给示例demo连接

打开keil工程

在这里插入图片描述

接下来就是对以上的文件进行代码的编写和接口的编了,先进行编译确保工程可以编译通过

  • 对FlashDev.c进行编写

    /**************************************************************************//**
     * @file     FlashDev.c
     * @brief    Flash Device Description for New Device Flash
     * @version  V1.0.0
     * @date     10. January 2018
     ******************************************************************************/
    /*
     * Copyright (c) 2010-2018 Arm Limited. All rights reserved.
     *
     * SPDX-License-Identifier: Apache-2.0
     *
     * Licensed under the Apache License, Version 2.0 (the License); you may
     * not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     * www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an AS IS BASIS, WITHOUT
     * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
     
    #include "FlashOS.H"        // FlashOS Structures
    
    #if 0
    /* 模板可以作为参考的一个示例 */
    struct FlashDevice const FlashDevice  =  {
       FLASH_DRV_VERS,             // Driver Version, do not modify!
       "New Device 256kB Flash",   // Device Name 
       ONCHIP,                     // Device Type
       0x00000000,                 // Device Start Address
       0x00040000,                 // Device Size in Bytes (256kB)
       1024,                       // Programming Page Size
       0,                          // Reserved, must be 0
       0xFF,                       // Initial Content of Erased Memory
       100,                        // Program Page Timeout 100 mSec
       3000,                       // Erase Sector Timeout 3000 mSec
    
    // Specify Size and Address of Sectors
       0x002000, 0x000000,         // Sector Size  8kB (8 Sectors)
       0x010000, 0x010000,         // Sector Size 64kB (2 Sectors) 
       0x002000, 0x030000,         // Sector Size  8kB (8 Sectors)
       SECTOR_END
    };
    #else
    /* ZLG-MIMxRT1062 */
    struct FlashDevice const FlashDevice  =  {
       FLASH_DRV_VERS,               /* 设备版本 不能更改! */
       "MIMxRT1062_W25Q256",         /* 设备名称 */  
       EXTSPI,                       /* 设备类型 可以跳转到FlashOS.H去查看 相关的定义 */ 
       0x60000000,                   /* Flash设备shou地址 会将这个地址挂载到AHB总线上 */ 
       0x00800000,                   /* Flash设备的空间大小 这里是0x010000000 也就是16MB*/ 
       256,                          /* 编程页的大小 根据Flash的型号数据手册可以选择256 但是为了速度快一些可以选择 4096 */ 
       0,                            /* 保留位,但是这里不能修改必须默认为0 */ 
       0xFF,                         /* 擦除内存的初始内容 */ 
       100,                          /* 程序页面超时 100 毫秒 根据手册可以修改相应的时间 */ 
       3000,                         /* 擦除扇区超时 3000 毫秒 根据手册可以修改相应的时间*/ 
    
       0x001000, 0x000000,           /* 扇区大小 4KB*/ 
    
       SECTOR_END
    };
    
    #endif
    
    

    这里可以理解为是对设备进行注册,这些包含了flash信息,具体需要去看下载的Flash的数据手册应用手册

  • FlashPrag.c

    /**************************************************************************//**
     * @file     FlashPrg.c
     * @brief    Flash Programming Functions adapted for New Device Flash
     * @version  V1.0.0
     * @date     10. January 2018
     ******************************************************************************/
    /*
     * Copyright (c) 2010-2018 Arm Limited. All rights reserved.
     *
     * SPDX-License-Identifier: Apache-2.0
     *
     * Licensed under the Apache License, Version 2.0 (the License); you may
     * not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     * www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an AS IS BASIS, WITHOUT
     * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
     //CPU_MIMXRT1052DVL6B, MCUXPRESSO_SDK
    #include "FlashOS.H"        // FlashOS Structures
    #include "fsl_flexspi.h"
    #include "app.h" 
    extern status_t flexspi_nor_flash_erase_sector(FLEXSPI_Type *base, uint32_t address);
    extern status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t dstAddr, const uint32_t *src);
    extern status_t flexspi_nor_get_vendor_id(FLEXSPI_Type *base, uint8_t *vendorId);
    extern status_t flexspi_nor_enable_quad_mode(FLEXSPI_Type *base);
    extern status_t flexspi_nor_erase_chip(FLEXSPI_Type *base);
    extern void flexspi_nor_flash_init(FLEXSPI_Type *base);
    #define FLASH_BASE_ADR (0x60000000)
    
    #if 1
    /* 初始化Flash */
    int Init (unsigned long adr, unsigned long clk, unsigned long fnc) 
    {
        (void)adr;
        (void)clk;
        (void)fnc;
        flexspi_nor_flash_init(FLEXSPI);
        return (0); 
    }
    /* 解除初始化 */
    int UnInit (unsigned long fnc)
    {
        return (0); 
    }
    /* 擦除芯片 */
    int EraseChip (void) 
    {
        return (flexspi_nor_erase_chip(FLEXSPI)); 
    }
    /* 扇区擦除 */
    int EraseSector (unsigned long adr) 
    {
        return (flexspi_nor_flash_erase_sector(FLEXSPI, adr - FLASH_BASE_ADR)); 
    }
    /* 下载 */
    int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) 
    {
        return ( flexspi_nor_flash_page_program( FLEXSPI, adr - FLASH_BASE_ADR, (uint32_t*)buf)); 
    }
    
    #endif
    

    这个文件实际上就是对Flash的接口进行配置

  • flexspi.c

    /*
     * Copyright (c) 2016, Freescale Semiconductor, Inc.
     * Copyright 2016-2022, 2023 NXP
     * All rights reserved.
     *
     * SPDX-License-Identifier: BSD-3-Clause
     */
    
    #include "fsl_flexspi.h"
    
    /* Component ID definition, used by tools. */
    #ifndef FSL_COMPONENT_ID
    #define FSL_COMPONENT_ID "platform.drivers.flexspi"
    #endif
    
    /*******************************************************************************
     * Definitions
     ******************************************************************************/
    
    #define FREQ_1MHz             (1000000UL)
    #define FLEXSPI_DLLCR_DEFAULT (0x100UL)
    #define FLEXSPI_LUT_KEY_VAL   (0x5AF05AF0UL)
    
    enum
    {
        kFLEXSPI_DelayCellUnitMin = 75,  /* 75ps. */
        kFLEXSPI_DelayCellUnitMax = 225, /* 225ps. */
    };
    
    enum
    {
        kFLEXSPI_FlashASampleClockSlaveDelayLocked =
            FLEXSPI_STS2_ASLVLOCK_MASK, /* Flash A sample clock slave delay line locked. */
        kFLEXSPI_FlashASampleClockRefDelayLocked =
            FLEXSPI_STS2_AREFLOCK_MASK, /* Flash A sample clock reference delay line locked. */
    #if !((defined(FSL_FEATURE_FLEXSPI_HAS_NO_STS2_BSLVLOCK)) && (FSL_FEATURE_FLEXSPI_HAS_NO_STS2_BSLVLOCK))
        kFLEXSPI_FlashBSampleClockSlaveDelayLocked =
            FLEXSPI_STS2_BSLVLOCK_MASK, /* Flash B sample clock slave delay line locked. */
    #endif
    #if !((defined(FSL_FEATURE_FLEXSPI_HAS_NO_STS2_BREFLOCK)) && (FSL_FEATURE_FLEXSPI_HAS_NO_STS2_BREFLOCK))
        kFLEXSPI_FlashBSampleClockRefDelayLocked =
            FLEXSPI_STS2_BREFLOCK_MASK, /* Flash B sample clock reference delay line locked. */
    #endif
    };
    
    /*! @brief Common sets of flags used by the driver, _flexspi_flag_constants. */
    enum
    {
        /*! IRQ sources enabled by the non-blocking transactional API. */
        kIrqFlags = kFLEXSPI_IpTxFifoWatermarkEmptyFlag | kFLEXSPI_IpRxFifoWatermarkAvailableFlag |
                    kFLEXSPI_SequenceExecutionTimeoutFlag | kFLEXSPI_IpCommandSequenceErrorFlag |
                    kFLEXSPI_IpCommandGrantTimeoutFlag | kFLEXSPI_IpCommandExecutionDoneFlag,
    
        /*! Errors to check for. */
        kErrorFlags = kFLEXSPI_SequenceExecutionTimeoutFlag | kFLEXSPI_IpCommandSequenceErrorFlag |
                      kFLEXSPI_IpCommandGrantTimeoutFlag,
    };
    
    /* FLEXSPI transfer state, _flexspi_transfer_state. */
    enum
    {
        kFLEXSPI_Idle      = 0x0U, /*!< Transfer is done. */
        kFLEXSPI_BusyWrite = 0x1U, /*!< FLEXSPI is busy write transfer. */
        kFLEXSPI_BusyRead  = 0x2U, /*!< FLEXSPI is busy write transfer. */
    };
    
    /*! @brief Typedef for interrupt handler. */
    typedef void (*flexspi_isr_t)(FLEXSPI_Type *base, flexspi_handle_t *handle);
    
    /*******************************************************************************
     * Prototypes
     ******************************************************************************/
    static void FLEXSPI_Memset(void *src, uint8_t value, size_t length);
    
    /*!
     * @brief Calculate flash A/B sample clock DLL.
     *
     * @param base FLEXSPI base pointer.
     * @param config Flash configuration parameters.
     */
    static uint32_t FLEXSPI_CalculateDll(FLEXSPI_Type *base, flexspi_device_config_t *config);
    
    /*******************************************************************************
     * Variables
     ******************************************************************************/
    /*! @brief Pointers to flexspi bases for each instance. */
    static FLEXSPI_Type *const s_flexspiBases[] = FLEXSPI_BASE_PTRS;
    
    /*! @brief Pointers to flexspi IRQ number for each instance. */
    static const IRQn_Type s_flexspiIrqs[] = FLEXSPI_IRQS;
    
    #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
    /* Clock name array */
    static const clock_ip_name_t s_flexspiClock[] = FLEXSPI_CLOCKS;
    #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
    
    #if defined(FSL_DRIVER_TRANSFER_DOUBLE_WEAK_IRQ) && FSL_DRIVER_TRANSFER_DOUBLE_WEAK_IRQ
    /*! @brief Pointers to flexspi handles for each instance. */
    static flexspi_handle_t *s_flexspiHandle[ARRAY_SIZE(s_flexspiBases)];
    #endif
    
    #if defined(FSL_FEATURE_FLEXSPI_HAS_RESET) && FSL_FEATURE_FLEXSPI_HAS_RESET
    /*! @brief Pointers to FLEXSPI resets for each instance. */
    static const reset_ip_name_t s_flexspiResets[] = FLEXSPI_RSTS;
    #endif
    
    #if defined(FSL_DRIVER_TRANSFER_DOUBLE_WEAK_IRQ) && FSL_DRIVER_TRANSFER_DOUBLE_WEAK_IRQ
    /*! @brief Pointer to flexspi IRQ handler. */
    static flexspi_isr_t s_flexspiIsr;
    #endif
    /*******************************************************************************
     * Code
     ******************************************************************************/
    /* To avoid compiler opitimizing this API into memset() in library. */
    #if defined(__ICCARM__)
    #pragma optimize = none
    #endif /* defined(__ICCARM__) */
    static void FLEXSPI_Memset(void *src, uint8_t value, size_t length)
    {
        assert(src != NULL);
        uint8_t *p = (uint8_t *)src;
    
        for (uint32_t i = 0U; i < length; i++)
        {
            *p = value;
            p++;
        }
    }
    
    uint32_t FLEXSPI_GetInstance(FLEXSPI_Type *base)
    {
        uint32_t instance;
    
        /* Find the instance index from base address mappings. */
        for (instance = 0; instance < ARRAY_SIZE(s_flexspiBases); instance++)
        {
            if (s_flexspiBases[instance] == base)
            {
                break;
            }
        }
    
        assert(instance < ARRAY_SIZE(s_flexspiBases));
    
        return instance;
    }
    
    static uint32_t FLEXSPI_CalculateDll(FLEXSPI_Type *base, flexspi_device_config_t *config)
    {
        bool isUnifiedConfig = true;
        uint32_t flexspiDllValue;
        uint32_t dllValue;
        uint32_t temp;
    #if defined(FSL_FEATURE_FLEXSPI_DQS_DELAY_PS) && FSL_FEATURE_FLEXSPI_DQS_DELAY_PS
        uint32_t internalDqsDelayPs = FSL_FEATURE_FLEXSPI_DQS_DELAY_PS;
    #endif
        uint32_t rxSampleClock = (base->MCR0 & FLEXSPI_MCR0_RXCLKSRC_MASK) >> FLEXSPI_MCR0_RXCLKSRC_SHIFT;
        switch (rxSampleClock)
        {
            case (uint32_t)kFLEXSPI_ReadSampleClkLoopbackInternally:
            case (uint32_t)kFLEXSPI_ReadSampleClkLoopbackFromDqsPad:
            case (uint32_t)kFLEXSPI_ReadSampleClkLoopbackFromSckPad:
                isUnifiedConfig = true;
                break;
            case (uint32_t)kFLEXSPI_ReadSampleClkExternalInputFromDqsPad:
                if (config->isSck2Enabled)
                {
                    isUnifiedConfig = true;
                }
                else
                {
                    isUnifiedConfig = false;
                }
                break;
            default:
                assert(false);
                break;
        }
    
        if (isUnifiedConfig)
        {
            flexspiDllValue = FLEXSPI_DLLCR_DEFAULT; /* 1 fixed delay cells in DLL delay chain) */
        }
        else
        {
            if (config->flexspiRootClk >= 100U * FREQ_1MHz)
            {
    #if defined(FSL_FEATURE_FLEXSPI_DQS_DELAY_MIN) && FSL_FEATURE_FLEXSPI_DQS_DELAY_MIN
                /* DLLEN = 1, SLVDLYTARGET = 0x0, */
                flexspiDllValue = FLEXSPI_DLLCR_DLLEN(1) | FLEXSPI_DLLCR_SLVDLYTARGET(0x00);
    #else
                /* DLLEN = 1, SLVDLYTARGET = 0xF, */
                flexspiDllValue = FLEXSPI_DLLCR_DLLEN(1) | FLEXSPI_DLLCR_SLVDLYTARGET(0x0F);
    #endif
    #if (defined(FSL_FEATURE_FLEXSPI_HAS_REFPHASEGAP) && FSL_FEATURE_FLEXSPI_HAS_REFPHASEGAP)
                flexspiDllValue |= FLEXSPI_DLLCR_REFPHASEGAP(2U);
    #endif /* FSL_FEATURE_FLEXSPI_HAS_REFPHASEGAP */
            }
            else
            {
                temp     = (uint32_t)config->dataValidTime * 1000U; /* Convert data valid time in ns to ps. */
                dllValue = temp / (uint32_t)kFLEXSPI_DelayCellUnitMin;
                if (dllValue * (uint32_t)kFLEXSPI_DelayCellUnitMin < temp)
                {
                    dllValue++;
                }
                flexspiDllValue = FLEXSPI_DLLCR_OVRDEN(1) | FLEXSPI_DLLCR_OVRDVAL(dllValue);
            }
        }
        return flexspiDllValue;
    }
    
    status_t FLEXSPI_CheckAndClearError(FLEXSPI_Type *base, uint32_t status)
    {
        status_t result = kStatus_Success;
    
        /* Check for error. */
        status &= (uint32_t)kErrorFlags;
        if (0U != status)
        {
            /* Select the correct error code.. */
            if (0U != (status & (uint32_t)kFLEXSPI_SequenceExecutionTimeoutFlag))
            {
                result = kStatus_FLEXSPI_SequenceExecutionTimeout;
            }
            else if (0U != (status & (uint32_t)kFLEXSPI_IpCommandSequenceErrorFlag))
            {
                result = kStatus_FLEXSPI_IpCommandSequenceError;
            }
            else if (0U != (status & (uint32_t)kFLEXSPI_IpCommandGrantTimeoutFlag))
            {
                result = kStatus_FLEXSPI_IpCommandGrantTimeout;
            }
            else
            {
                assert(false);
            }
    
            /* Clear the flags. */
            FLEXSPI_ClearInterruptStatusFlags(base, status);
        }
    
        return result;
    }
    
    /*!
     * brief Initializes the FLEXSPI module and internal state.
     *
     * This function enables the clock for FLEXSPI and also configures the FLEXSPI with the
     * input configure parameters. Users should call this function before any FLEXSPI operations.
     *
     * param base FLEXSPI peripheral base address.
     * param config FLEXSPI configure structure.
     */
    void FLEXSPI_Init(FLEXSPI_Type *base, const flexspi_config_t *config)
    {
        uint32_t configValue = 0;
        uint8_t i            = 0;
    
    #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
        /* Enable the flexspi clock */
        (void)CLOCK_EnableClock(s_flexspiClock[FLEXSPI_GetInstance(base)]);
    #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
    
    #if defined(FSL_FEATURE_FLEXSPI_HAS_RESET) && FSL_FEATURE_FLEXSPI_HAS_RESET
        /* Reset the FLEXSPI module */
        RESET_PeripheralReset(s_flexspiResets[FLEXSPI_GetInstance(base)]);
    #endif
    
        /* Reset peripheral before configuring it. */
        base->MCR0 &= ~FLEXSPI_MCR0_MDIS_MASK;
        FLEXSPI_SoftwareReset(base);
    
        /* Configure MCR0 configuration items. */
        configValue = FLEXSPI_MCR0_RXCLKSRC(config->rxSampleClock) | FLEXSPI_MCR0_DOZEEN(config->enableDoze) |
                      FLEXSPI_MCR0_IPGRANTWAIT(config->ipGrantTimeoutCycle) |
                      FLEXSPI_MCR0_AHBGRANTWAIT(config->ahbConfig.ahbGrantTimeoutCycle) |
                      FLEXSPI_MCR0_SCKFREERUNEN(config->enableSckFreeRunning) |
                      FLEXSPI_MCR0_HSEN(config->enableHalfSpeedAccess) |
    #if !(defined(FSL_FEATURE_FLEXSPI_HAS_NO_MCR0_COMBINATIONEN) && FSL_FEATURE_FLEXSPI_HAS_NO_MCR0_COMBINATIONEN)
                      FLEXSPI_MCR0_COMBINATIONEN(config->enableCombination) |
    #endif
    #if !(defined(FSL_FEATURE_FLEXSPI_HAS_NO_MCR0_ATDFEN) && FSL_FEATURE_FLEXSPI_HAS_NO_MCR0_ATDFEN)
                      FLEXSPI_MCR0_ATDFEN(config->ahbConfig.enableAHBWriteIpTxFifo) |
    #endif
    #if !(defined(FSL_FEATURE_FLEXSPI_HAS_NO_MCR0_ARDFEN) && FSL_FEATURE_FLEXSPI_HAS_NO_MCR0_ARDFEN)
                      FLEXSPI_MCR0_ARDFEN(config->ahbConfig.enableAHBWriteIpRxFifo) |
    #endif
                      FLEXSPI_MCR0_MDIS_MASK;
        base->MCR0 = configValue;
    
        /* Configure MCR1 configurations. */
        configValue =
            FLEXSPI_MCR1_SEQWAIT(config->seqTimeoutCycle) | FLEXSPI_MCR1_AHBBUSWAIT(config->ahbConfig.ahbBusTimeoutCycle);
        base->MCR1 = configValue;
    
        /* Configure MCR2 configurations. */
        configValue = base->MCR2;
        configValue &= ~(FLEXSPI_MCR2_RESUMEWAIT_MASK |
    #if !(defined(FSL_FEATURE_FLEXSPI_HAS_NO_MCR2_SCKBDIFFOPT) && FSL_FEATURE_FLEXSPI_HAS_NO_MCR2_SCKBDIFFOPT)
                         FLEXSPI_MCR2_SCKBDIFFOPT_MASK |
    #endif
                         FLEXSPI_MCR2_SAMEDEVICEEN_MASK | FLEXSPI_MCR2_CLRAHBBUFOPT_MASK);
        configValue |= FLEXSPI_MCR2_RESUMEWAIT(config->ahbConfig.resumeWaitCycle) |
    #if defined(FSL_FEATURE_FLEXSPI_SUPPORT_SEPERATE_RXCLKSRC_PORTB) && FSL_FEATURE_FLEXSPI_SUPPORT_SEPERATE_RXCLKSRC_PORTB
                       FLEXSPI_MCR2_RXCLKSRC_B(config->rxSampleClockPortB) |
    #endif
    #if defined(FSL_FEATURE_FLEXSPI_SUPPORT_RXCLKSRC_DIFF) && FSL_FEATURE_FLEXSPI_SUPPORT_RXCLKSRC_DIFF
                       FLEXSPI_MCR2_RX_CLK_SRC_DIFF(config->rxSampleClockDiff) |
    #endif
    #if !(defined(FSL_FEATURE_FLEXSPI_HAS_NO_MCR2_SCKBDIFFOPT) && FSL_FEATURE_FLEXSPI_HAS_NO_MCR2_SCKBDIFFOPT)
                       FLEXSPI_MCR2_SCKBDIFFOPT(config->enableSckBDiffOpt) |
    #endif
                       FLEXSPI_MCR2_SAMEDEVICEEN(config->enableSameConfigForAll) |
                       FLEXSPI_MCR2_CLRAHBBUFOPT(config->ahbConfig.enableClearAHBBufferOpt);
    
        base->MCR2 = configValue;
    
        /* Configure AHB control items. */
        configValue = base->AHBCR;
        configValue &= ~(FLEXSPI_AHBCR_READADDROPT_MASK | FLEXSPI_AHBCR_PREFETCHEN_MASK | FLEXSPI_AHBCR_BUFFERABLEEN_MASK |
                         FLEXSPI_AHBCR_CACHABLEEN_MASK);
        configValue |= FLEXSPI_AHBCR_READADDROPT(config->ahbConfig.enableReadAddressOpt) |
                       FLEXSPI_AHBCR_PREFETCHEN(config->ahbConfig.enableAHBPrefetch) |
                       FLEXSPI_AHBCR_BUFFERABLEEN(config->ahbConfig.enableAHBBufferable) |
                       FLEXSPI_AHBCR_CACHABLEEN(config->ahbConfig.enableAHBCachable);
        base->AHBCR = configValue;
    
        /* Configure AHB rx buffers. */
        for (i = 0; i < (uint32_t)FSL_FEATURE_FLEXSPI_AHB_BUFFER_COUNT; i++)
        {
            configValue = base->AHBRXBUFCR0[i];
    
            configValue &= ~(FLEXSPI_AHBRXBUFCR0_PREFETCHEN_MASK | FLEXSPI_AHBRXBUFCR0_PRIORITY_MASK |
                             FLEXSPI_AHBRXBUFCR0_MSTRID_MASK | FLEXSPI_AHBRXBUFCR0_BUFSZ_MASK);
            configValue |= FLEXSPI_AHBRXBUFCR0_PREFETCHEN(config->ahbConfig.buffer[i].enablePrefetch) |
                           FLEXSPI_AHBRXBUFCR0_PRIORITY(config->ahbConfig.buffer[i].priority) |
                           FLEXSPI_AHBRXBUFCR0_MSTRID(config->ahbConfig.buffer[i].masterIndex) |
                           FLEXSPI_AHBRXBUFCR0_BUFSZ((uint32_t)config->ahbConfig.buffer[i].bufferSize / 8U);
            base->AHBRXBUFCR0[i] = configValue;
        }
    
        /* Configure IP Fifo watermarks. */
        base->IPRXFCR &= ~FLEXSPI_IPRXFCR_RXWMRK_MASK;
        base->IPRXFCR |= FLEXSPI_IPRXFCR_RXWMRK((uint32_t)config->rxWatermark / 8U - 1U);
        base->IPTXFCR &= ~FLEXSPI_IPTXFCR_TXWMRK_MASK;
        base->IPTXFCR |= FLEXSPI_IPTXFCR_TXWMRK((uint32_t)config->txWatermark / 8U - 1U);
    
        /* Reset flash size on all ports */
        for (i = 0; i < (uint32_t)kFLEXSPI_PortCount; i++)
        {
            base->FLSHCR0[i] = 0;
        }
    }
    
    /*!
     * brief Gets default settings for FLEXSPI.
     *
     * param config FLEXSPI configuration structure.
     */
    void FLEXSPI_GetDefaultConfig(flexspi_config_t *config)
    {
        /* Initializes the configure structure to zero. */
        FLEXSPI_Memset(config, 0, sizeof(*config));
    
        config->rxSampleClock        = kFLEXSPI_ReadSampleClkLoopbackInternally;
        config->enableSckFreeRunning = false;
    #if !(defined(FSL_FEATURE_FLEXSPI_HAS_NO_MCR0_COMBINATIONEN) && FSL_FEATURE_FLEXSPI_HAS_NO_MCR0_COMBINATIONEN)
        config->enableCombination = false;
    #endif
        config->enableDoze            = true;
        config->enableHalfSpeedAccess = false;
    #if !(defined(FSL_FEATURE_FLEXSPI_HAS_NO_MCR2_SCKBDIFFOPT) && FSL_FEATURE_FLEXSPI_HAS_NO_MCR2_SCKBDIFFOPT)
        config->enableSckBDiffOpt = false;
    #endif
        config->enableSameConfigForAll = false;
        config->seqTimeoutCycle        = 0xFFFFU;
        config->ipGrantTimeoutCycle    = 0xFFU;
        config->txWatermark            = 8;
        config->rxWatermark            = 8;
    #if !(defined(FSL_FEATURE_FLEXSPI_HAS_NO_MCR0_ATDFEN) && FSL_FEATURE_FLEXSPI_HAS_NO_MCR0_ATDFEN)
        config->ahbConfig.enableAHBWriteIpTxFifo = false;
    #endif
    #if !(defined(FSL_FEATURE_FLEXSPI_HAS_NO_MCR0_ARDFEN) && FSL_FEATURE_FLEXSPI_HAS_NO_MCR0_ARDFEN)
        config->ahbConfig.enableAHBWriteIpRxFifo = false;
    #endif
        config->ahbConfig.ahbGrantTimeoutCycle = 0xFFU;
        config->ahbConfig.ahbBusTimeoutCycle   = 0xFFFFU;
        config->ahbConfig.resumeWaitCycle      = 0x20U;
        FLEXSPI_Memset(config->ahbConfig.buffer, 0, sizeof(config->ahbConfig.buffer));
        /* Use invalid master ID 0xF and buffer size 0 for the first several buffers. */
        for (uint8_t i = 0; i < ((uint8_t)FSL_FEATURE_FLEXSPI_AHB_BUFFER_COUNT - 2U); i++)
        {
            config->ahbConfig.buffer[i].enablePrefetch = true; /* Default enable AHB prefetch. */
            config->ahbConfig.buffer[i].masterIndex = 0xFU; /* Invalid master index which is not used, so will never hit. */
            config->ahbConfig.buffer[i].bufferSize =
                0; /* Default buffer size 0 for buffer0 to buffer(FSL_FEATURE_FLEXSPI_AHB_BUFFER_COUNT - 3U)*/
        }
    
        for (uint8_t i = ((uint8_t)FSL_FEATURE_FLEXSPI_AHB_BUFFER_COUNT - 2U);
             i < (uint8_t)FSL_FEATURE_FLEXSPI_AHB_BUFFER_COUNT; i++)
        {
            config->ahbConfig.buffer[i].enablePrefetch = true; /* Default enable AHB prefetch. */
            config->ahbConfig.buffer[i].bufferSize     = 256U; /* Default buffer size 256 bytes. */
        }
        config->ahbConfig.enableClearAHBBufferOpt = false;
        config->ahbConfig.enableReadAddressOpt    = false;
        config->ahbConfig.enableAHBPrefetch       = false;
        config->ahbConfig.enableAHBBufferable     = false;
        config->ahbConfig.enableAHBCachable       = false;
    }
    
    /*!
     * brief Deinitializes the FLEXSPI module.
     *
     * Clears the FLEXSPI state and  FLEXSPI module registers.
     * param base FLEXSPI peripheral base address.
     */
    void FLEXSPI_Deinit(FLEXSPI_Type *base)
    {
        /* Reset peripheral. */
        FLEXSPI_SoftwareReset(base);
    }
    
    /*!
     * brief Update FLEXSPI DLL value depending on currently flexspi root clock.
     *
     * param base FLEXSPI peripheral base address.
     * param config Flash configuration parameters.
     * param port FLEXSPI Operation port.
     */
    void FLEXSPI_UpdateDllValue(FLEXSPI_Type *base, flexspi_device_config_t *config, flexspi_port_t port)
    {
        uint32_t configValue = 0;
        uint32_t statusValue = 0;
        uint8_t index        = (uint8_t)port >> 1U; /* PortA with index 0, PortB with index 1. */
    
        /* Wait for bus to be idle before changing flash configuration. */
        while (!FLEXSPI_GetBusIdleStatus(base))
        {
        }
    
        /* Configure DLL. */
        configValue        = FLEXSPI_CalculateDll(base, config);
        base->DLLCR[index] = configValue;
    
        /* Exit stop mode. */
        base->MCR0 &= ~FLEXSPI_MCR0_MDIS_MASK;
    
        /* According to ERR011377, need to delay at least 100 NOPs to ensure the DLL is locked. */
        if (index == 0U)
        {
            statusValue =
                ((uint32_t)kFLEXSPI_FlashASampleClockSlaveDelayLocked | (uint32_t)kFLEXSPI_FlashASampleClockRefDelayLocked);
        }
    #if !((defined(FSL_FEATURE_FLEXSPI_HAS_NO_STS2_BSLVLOCK)) && (FSL_FEATURE_FLEXSPI_HAS_NO_STS2_BSLVLOCK))
        else
        {
            statusValue =
                ((uint32_t)kFLEXSPI_FlashBSampleClockSlaveDelayLocked | (uint32_t)kFLEXSPI_FlashBSampleClockRefDelayLocked);
        }
    #endif
        if (0U != (configValue & FLEXSPI_DLLCR_DLLEN_MASK))
        {
    #if defined(FSL_FEATURE_FLEXSPI_HAS_ERRATA_051426) && (FSL_FEATURE_FLEXSPI_HAS_ERRATA_051426)
            if (config->isFroClockSource == false)
    #endif
            {
                /* Wait slave delay line locked and slave reference delay line locked. */
                while ((base->STS2 & statusValue) != statusValue)
                {
                }
            }
    
            /* Wait at least 100 NOPs*/
            for (uint8_t delay = 100U; delay > 0U; delay--)
            {
                __NOP();
            }
        }
    }
    
    /*!
     * brief Configures the connected device parameter.
     *
     * This function configures the connected device relevant parameters, such as the size, command, and so on.
     * The flash configuration value cannot have a default value. The user needs to configure it according to the
     * connected device.
     *
     * param base FLEXSPI peripheral base address.
     * param config Flash configuration parameters.
     * param port FLEXSPI Operation port.
     */
    void FLEXSPI_SetFlashConfig(FLEXSPI_Type *base, flexspi_device_config_t *config, flexspi_port_t port)
    {
        uint32_t configValue = 0;
        uint8_t index        = (uint8_t)port >> 1U; /* PortA with index 0, PortB with index 1. */
    
        /* Wait for bus to be idle before changing flash configuration. */
        while (!FLEXSPI_GetBusIdleStatus(base))
        {
        }
    
        /* Configure flash size and address shift. */
    #if defined(FSL_FEATURE_FLEXSPI_SUPPORT_ADDRESS_SHIFT) && (FSL_FEATURE_FLEXSPI_SUPPORT_ADDRESS_SHIFT)
        base->FLSHCR0[port] = config->flashSize | FLEXSPI_FLSHCR0_ADDRSHIFT(config->addressShift);
    #else
        base->FLSHCR0[port] = config->flashSize;
    #endif /* FSL_FEATURE_FLEXSPI_SUPPORT_ADDRESS_SHIFT */
    
        /* Configure flash parameters. */
        base->FLSHCR1[port] = FLEXSPI_FLSHCR1_CSINTERVAL(config->CSInterval) |
                              FLEXSPI_FLSHCR1_CSINTERVALUNIT(config->CSIntervalUnit) |
                              FLEXSPI_FLSHCR1_TCSH(config->CSHoldTime) | FLEXSPI_FLSHCR1_TCSS(config->CSSetupTime) |
                              FLEXSPI_FLSHCR1_CAS(config->columnspace) | FLEXSPI_FLSHCR1_WA(config->enableWordAddress);
    
        /* Configure AHB operation items. */
        configValue = base->FLSHCR2[port];
    
        configValue &= ~(FLEXSPI_FLSHCR2_AWRWAITUNIT_MASK | FLEXSPI_FLSHCR2_AWRWAIT_MASK | FLEXSPI_FLSHCR2_AWRSEQNUM_MASK |
                         FLEXSPI_FLSHCR2_AWRSEQID_MASK | FLEXSPI_FLSHCR2_ARDSEQNUM_MASK | FLEXSPI_FLSHCR2_ARDSEQID_MASK);
    
        configValue |=
            FLEXSPI_FLSHCR2_AWRWAITUNIT(config->AHBWriteWaitUnit) | FLEXSPI_FLSHCR2_AWRWAIT(config->AHBWriteWaitInterval);
    
        if (config->AWRSeqNumber > 0U)
        {
            configValue |= FLEXSPI_FLSHCR2_AWRSEQID((uint32_t)config->AWRSeqIndex) |
                           FLEXSPI_FLSHCR2_AWRSEQNUM((uint32_t)config->AWRSeqNumber - 1U);
        }
    
        if (config->ARDSeqNumber > 0U)
        {
            configValue |= FLEXSPI_FLSHCR2_ARDSEQID((uint32_t)config->ARDSeqIndex) |
                           FLEXSPI_FLSHCR2_ARDSEQNUM((uint32_t)config->ARDSeqNumber - 1U);
        }
    
        base->FLSHCR2[port] = configValue;
    
        /* Configure DLL. */
        FLEXSPI_UpdateDllValue(base, config, port);
    
        /* Step into stop mode. */
        base->MCR0 |= FLEXSPI_MCR0_MDIS_MASK;
    
        /* Configure write mask. */
        if (config->enableWriteMask)
        {
            base->FLSHCR4 &= ~FLEXSPI_FLSHCR4_WMOPT1_MASK;
        }
        else
        {
            base->FLSHCR4 |= FLEXSPI_FLSHCR4_WMOPT1_MASK;
        }
    
        if (index == 0U) /*PortA*/
        {
            base->FLSHCR4 &= ~FLEXSPI_FLSHCR4_WMENA_MASK;
            base->FLSHCR4 |= FLEXSPI_FLSHCR4_WMENA(config->enableWriteMask);
        }
    #if !((defined(FSL_FEATURE_FLEXSPI_HAS_NO_FLSHCR4_WMENB)) && (FSL_FEATURE_FLEXSPI_HAS_NO_FLSHCR4_WMENB))
        else
        {
            base->FLSHCR4 &= ~FLEXSPI_FLSHCR4_WMENB_MASK;
            base->FLSHCR4 |= FLEXSPI_FLSHCR4_WMENB(config->enableWriteMask);
        }
    #endif
    
        /* Exit stop mode. */
        base->MCR0 &= ~FLEXSPI_MCR0_MDIS_MASK;
    
        /* Wait for bus to be idle before use it access to external flash. */
        while (!FLEXSPI_GetBusIdleStatus(base))
        {
        }
    }
    
    /*! brief Updates the LUT table.
     *
     * param base FLEXSPI peripheral base address.
     * param index From which index start to update. It could be any index of the LUT table, which
     * also allows user to update command content inside a command. Each command consists of up to
     * 8 instructions and occupy 4*32-bit memory.
     * param cmd Command sequence array.
     * param count Number of sequences.
     */
    void FLEXSPI_UpdateLUT(FLEXSPI_Type *base, uint32_t index, const uint32_t *cmd, uint32_t count)
    {
        assert(index < 64U);
    
        uint32_t i = 0;
        volatile uint32_t *lutBase;
    
        /* Wait for bus to be idle before changing flash configuration. */
        while (!FLEXSPI_GetBusIdleStatus(base))
        {
        }
    
        /* Unlock LUT for update. */
    #if !((defined(FSL_FEATURE_FLEXSPI_LUTKEY_IS_RO)) && (FSL_FEATURE_FLEXSPI_LUTKEY_IS_RO))
        base->LUTKEY = FLEXSPI_LUT_KEY_VAL;
    #endif
        base->LUTCR = 0x02;
    
        lutBase = &base->LUT[index];
        for (i = 0; i < count; i++)
        {
            *lutBase++ = *cmd++;
        }
    
        /* Lock LUT. */
    #if !((defined(FSL_FEATURE_FLEXSPI_LUTKEY_IS_RO)) && (FSL_FEATURE_FLEXSPI_LUTKEY_IS_RO))
        base->LUTKEY = FLEXSPI_LUT_KEY_VAL;
    #endif
        base->LUTCR = 0x01;
    }
    
    /*! brief Update read sample clock source
     *
     * param base FLEXSPI peripheral base address.
     * param clockSource clockSource of type #flexspi_read_sample_clock_t
     */
    void FLEXSPI_UpdateRxSampleClock(FLEXSPI_Type *base, flexspi_read_sample_clock_t clockSource)
    {
        uint32_t mcr0Val;
    
        /* Wait for bus to be idle before changing flash configuration. */
        while (!FLEXSPI_GetBusIdleStatus(base))
        {
        }
    
        mcr0Val = base->MCR0;
        mcr0Val &= ~FLEXSPI_MCR0_RXCLKSRC_MASK;
        mcr0Val |= FLEXSPI_MCR0_RXCLKSRC(clockSource);
        base->MCR0 = mcr0Val;
    
        /* Reset peripheral. */
        FLEXSPI_SoftwareReset(base);
    }
    
    /*!
     * brief Sends a buffer of data bytes using blocking method.
     * note This function blocks via polling until all bytes have been sent.
     * param base FLEXSPI peripheral base address
     * param buffer The data bytes to send
     * param size The number of data bytes to send
     * retval kStatus_Success write success without error
     * retval kStatus_FLEXSPI_SequenceExecutionTimeout sequence execution timeout
     * retval kStatus_FLEXSPI_IpCommandSequenceError IP command sequence error detected
     * retval kStatus_FLEXSPI_IpCommandGrantTimeout IP command grant timeout detected
     */
    status_t FLEXSPI_WriteBlocking(FLEXSPI_Type *base, uint8_t *buffer, size_t size)
    {
        uint32_t txWatermark = ((base->IPTXFCR & FLEXSPI_IPTXFCR_TXWMRK_MASK) >> FLEXSPI_IPTXFCR_TXWMRK_SHIFT) + 1U;
        uint32_t status;
        status_t result = kStatus_Success;
        uint32_t i      = 0;
    
        /* Send data buffer */
        while (0U != size)
        {
            /* Wait until there is room in the fifo. This also checks for errors. */
            while (0U == ((status = base->INTR) & (uint32_t)kFLEXSPI_IpTxFifoWatermarkEmptyFlag))
            {
            }
    
            result = FLEXSPI_CheckAndClearError(base, status);
    
            if (kStatus_Success != result)
            {
                return result;
            }
    
            /* Write watermark level data into tx fifo . */
            if (size >= 8U * txWatermark)
            {
                for (i = 0U; i < 2U * txWatermark; i++)
                {
                    base->TFDR[i] = *(uint32_t *)(void *)buffer;
                    buffer += 4U;
                }
    
                size = size - 8U * txWatermark;
            }
            else
            {
                /* Write word aligned data into tx fifo. */
                for (i = 0U; i < (size / 4U); i++)
                {
                    base->TFDR[i] = *(uint32_t *)(void *)buffer;
                    buffer += 4U;
                }
    
                /* Adjust size by the amount processed. */
                size -= 4U * i;
    
                /* Write word un-aligned data into tx fifo. */
                if (0x00U != size)
                {
                    uint32_t tempVal = 0x00U;
    
                    for (uint32_t j = 0U; j < size; j++)
                    {
                        tempVal |= ((uint32_t)*buffer++ << (8U * j));
                    }
    
                    base->TFDR[i] = tempVal;
                }
    
                size = 0U;
            }
    
            /* Push a watermark level data into IP TX FIFO. */
            base->INTR = (uint32_t)kFLEXSPI_IpTxFifoWatermarkEmptyFlag;
        }
    
        return result;
    }
    
    /*!
     * brief Receives a buffer of data bytes using a blocking method.
     * note This function blocks via polling until all bytes have been sent.
     * param base FLEXSPI peripheral base address
     * param buffer The data bytes to send
     * param size The number of data bytes to receive
     * retval kStatus_Success read success without error
     * retval kStatus_FLEXSPI_SequenceExecutionTimeout sequence execution timeout
     * retval kStatus_FLEXSPI_IpCommandSequenceError IP command sequence error detected
     * retval kStatus_FLEXSPI_IpCommandGrantTimeout IP command grant timeout detected
     */
    status_t FLEXSPI_ReadBlocking(FLEXSPI_Type *base, uint8_t *buffer, size_t size)
    {
        uint32_t rxWatermark = ((base->IPRXFCR & FLEXSPI_IPRXFCR_RXWMRK_MASK) >> FLEXSPI_IPRXFCR_RXWMRK_SHIFT) + 1U;
        uint32_t status;
        status_t result = kStatus_Success;
        uint32_t i      = 0;
        bool isReturn   = false;
    
        /* Send data buffer */
        while (0U != size)
        {
            if (size >= 8U * rxWatermark)
            {
                /* Wait until there is room in the fifo. This also checks for errors. */
                while (0U == ((status = base->INTR) & (uint32_t)kFLEXSPI_IpRxFifoWatermarkAvailableFlag))
                {
                    result = FLEXSPI_CheckAndClearError(base, status);
    
                    if (kStatus_Success != result)
                    {
                        isReturn = true;
                        break;
                    }
                }
            }
            else
            {
                /* Wait fill level. This also checks for errors. */
                while (size > ((((base->IPRXFSTS) & FLEXSPI_IPRXFSTS_FILL_MASK) >> FLEXSPI_IPRXFSTS_FILL_SHIFT) * 8U))
                {
                    result = FLEXSPI_CheckAndClearError(base, base->INTR);
    
                    if (kStatus_Success != result)
                    {
                        isReturn = true;
                        break;
                    }
                }
            }
    
            if (isReturn)
            {
                break;
            }
    
            result = FLEXSPI_CheckAndClearError(base, base->INTR);
    
            if (kStatus_Success != result)
            {
                break;
            }
    
            /* Read watermark level data from rx fifo. */
            if (size >= 8U * rxWatermark)
            {
                for (i = 0U; i < 2U * rxWatermark; i++)
                {
                    *(uint32_t *)(void *)buffer = base->RFDR[i];
                    buffer += 4U;
                }
    
                size = size - 8U * rxWatermark;
            }
            else
            {
                /* Read word aligned data from rx fifo. */
                for (i = 0U; i < (size / 4U); i++)
                {
                    *(uint32_t *)(void *)buffer = base->RFDR[i];
                    buffer += 4U;
                }
    
                /* Adjust size by the amount processed. */
                size -= 4U * i;
    
                /* Read word un-aligned data from rx fifo. */
                if (0x00U != size)
                {
                    uint32_t tempVal = base->RFDR[i];
    
                    for (i = 0U; i < size; i++)
                    {
                        *buffer++ = ((uint8_t)(tempVal >> (8U * i)) & 0xFFU);
                    }
                }
    
                size = 0;
            }
    
            /* Pop out a watermark level datas from IP RX FIFO. */
            base->INTR = (uint32_t)kFLEXSPI_IpRxFifoWatermarkAvailableFlag;
        }
    
        return result;
    }
    
    /*!
     * brief Execute command to transfer a buffer data bytes using a blocking method.
     * param base FLEXSPI peripheral base address
     * param xfer pointer to the transfer structure.
     * retval kStatus_Success command transfer success without error
     * retval kStatus_FLEXSPI_SequenceExecutionTimeout sequence execution timeout
     * retval kStatus_FLEXSPI_IpCommandSequenceError IP command sequence error detected
     * retval kStatus_FLEXSPI_IpCommandGrantTimeout IP command grant timeout detected
     */
    status_t FLEXSPI_TransferBlocking(FLEXSPI_Type *base, flexspi_transfer_t *xfer)
    {
        uint32_t configValue = 0;
        status_t result      = kStatus_Success;
    
        /* Clear sequence pointer before sending data to external devices. */
        base->FLSHCR2[xfer->port] |= FLEXSPI_FLSHCR2_CLRINSTRPTR_MASK;
    
        /* Clear former pending status before start this transfer. */
        base->INTR = FLEXSPI_INTR_AHBCMDERR_MASK | FLEXSPI_INTR_IPCMDERR_MASK | FLEXSPI_INTR_AHBCMDGE_MASK |
                     FLEXSPI_INTR_IPCMDGE_MASK | FLEXSPI_INTR_IPCMDDONE_MASK;
    
        /* Configure base address. */
        base->IPCR0 = xfer->deviceAddress;
    
        /* Reset fifos. */
        base->IPTXFCR |= FLEXSPI_IPTXFCR_CLRIPTXF_MASK;
        base->IPRXFCR |= FLEXSPI_IPRXFCR_CLRIPRXF_MASK;
    
        /* Configure data size. */
        if ((xfer->cmdType == kFLEXSPI_Read) || (xfer->cmdType == kFLEXSPI_Write) || (xfer->cmdType == kFLEXSPI_Config))
        {
            configValue = FLEXSPI_IPCR1_IDATSZ(xfer->dataSize);
        }
    
        /* Configure sequence ID. */
        configValue |=
            FLEXSPI_IPCR1_ISEQID((uint32_t)xfer->seqIndex) | FLEXSPI_IPCR1_ISEQNUM((uint32_t)xfer->SeqNumber - 1U);
        base->IPCR1 = configValue;
    
        /* Start Transfer. */
        base->IPCMD |= FLEXSPI_IPCMD_TRG_MASK;
    
        if ((xfer->cmdType == kFLEXSPI_Write) || (xfer->cmdType == kFLEXSPI_Config))
        {
            result = FLEXSPI_WriteBlocking(base, (uint8_t *)xfer->data, xfer->dataSize);
        }
        else if (xfer->cmdType == kFLEXSPI_Read)
        {
            result = FLEXSPI_ReadBlocking(base, (uint8_t *)xfer->data, xfer->dataSize);
        }
        else
        {
            /* Empty else. */
        }
    
        /* Wait until the IP command execution finishes */
        while (0UL == (base->INTR & FLEXSPI_INTR_IPCMDDONE_MASK))
        {
        }
    
        /* Unless there is an error status already set, capture the latest one */
        if (result == kStatus_Success)
        {
            result = FLEXSPI_CheckAndClearError(base, base->INTR);
        }
    
        return result;
    }
    
    /*!
     * brief Initializes the FLEXSPI handle which is used in transactional functions.
     *
     * param base FLEXSPI peripheral base address.
     * param handle pointer to flexspi_handle_t structure to store the transfer state.
     * param callback pointer to user callback function.
     * param userData user parameter passed to the callback function.
     */
    void FLEXSPI_TransferCreateHandle(FLEXSPI_Type *base,
                                      flexspi_handle_t *handle,
                                      flexspi_transfer_callback_t callback,
                                      void *userData)
    {
        assert(NULL != handle);
    
        uint32_t instance = FLEXSPI_GetInstance(base);
    
        /* Zero handle. */
        (void)memset(handle, 0, sizeof(*handle));
    
        /* Set callback and userData. */
        handle->completionCallback = callback;
        handle->userData           = userData;
    
    #if defined(FSL_DRIVER_TRANSFER_DOUBLE_WEAK_IRQ) && FSL_DRIVER_TRANSFER_DOUBLE_WEAK_IRQ
        /* Save the context in global variables to support the double weak mechanism. */
        s_flexspiHandle[instance] = handle;
        s_flexspiIsr              = FLEXSPI_TransferHandleIRQ;
    #endif
    
        /* Enable NVIC interrupt. */
        (void)EnableIRQ(s_flexspiIrqs[instance]);
    }
    
    /*!
     * brief Performs a interrupt non-blocking transfer on the FLEXSPI bus.
     *
     * note Calling the API returns immediately after transfer initiates. The user needs
     * to call FLEXSPI_GetTransferCount to poll the transfer status to check whether
     * the transfer is finished. If the return status is not kStatus_FLEXSPI_Busy, the transfer
     * is finished. For FLEXSPI_Read, the dataSize should be multiple of rx watermark level, or
     * FLEXSPI could not read data properly.
     *
     * param base FLEXSPI peripheral base address.
     * param handle pointer to flexspi_handle_t structure which stores the transfer state.
     * param xfer pointer to flexspi_transfer_t structure.
     * retval kStatus_Success Successfully start the data transmission.
     * retval kStatus_FLEXSPI_Busy Previous transmission still not finished.
     */
    status_t FLEXSPI_TransferNonBlocking(FLEXSPI_Type *base, flexspi_handle_t *handle, flexspi_transfer_t *xfer)
    {
        uint32_t configValue = 0;
        status_t result      = kStatus_Success;
    
        assert(NULL != handle);
        assert(NULL != xfer);
    
        /* Check if the I2C bus is idle - if not return busy status. */
        if (handle->state != (uint32_t)kFLEXSPI_Idle)
        {
            result = kStatus_FLEXSPI_Busy;
        }
        else
        {
            handle->data              = (uint8_t *)xfer->data;
            handle->dataSize          = xfer->dataSize;
            handle->transferTotalSize = xfer->dataSize;
            handle->state = (xfer->cmdType == kFLEXSPI_Read) ? (uint32_t)kFLEXSPI_BusyRead : (uint32_t)kFLEXSPI_BusyWrite;
    
            /* Clear sequence pointer before sending data to external devices. */
            base->FLSHCR2[xfer->port] |= FLEXSPI_FLSHCR2_CLRINSTRPTR_MASK;
    
            /* Clear former pending status before start this transfer. */
            base->INTR = FLEXSPI_INTR_AHBCMDERR_MASK | FLEXSPI_INTR_IPCMDERR_MASK | FLEXSPI_INTR_AHBCMDGE_MASK |
                         FLEXSPI_INTR_IPCMDGE_MASK | FLEXSPI_INTR_IPCMDDONE_MASK;
    
            /* Configure base address. */
            base->IPCR0 = xfer->deviceAddress;
    
            /* Reset fifos. */
            base->IPTXFCR |= FLEXSPI_IPTXFCR_CLRIPTXF_MASK;
            base->IPRXFCR |= FLEXSPI_IPRXFCR_CLRIPRXF_MASK;
    
            /* Configure data size. */
            if ((xfer->cmdType == kFLEXSPI_Read) || (xfer->cmdType == kFLEXSPI_Write))
            {
                configValue = FLEXSPI_IPCR1_IDATSZ(xfer->dataSize);
            }
    
            /* Configure sequence ID. */
            configValue |=
                FLEXSPI_IPCR1_ISEQID((uint32_t)xfer->seqIndex) | FLEXSPI_IPCR1_ISEQNUM((uint32_t)xfer->SeqNumber - 1U);
            base->IPCR1 = configValue;
    
            /* Start Transfer. */
            base->IPCMD |= FLEXSPI_IPCMD_TRG_MASK;
    
            if (handle->state == (uint32_t)kFLEXSPI_BusyRead)
            {
                FLEXSPI_EnableInterrupts(base, (uint32_t)kFLEXSPI_IpRxFifoWatermarkAvailableFlag |
                                                   (uint32_t)kFLEXSPI_SequenceExecutionTimeoutFlag |
                                                   (uint32_t)kFLEXSPI_IpCommandSequenceErrorFlag |
                                                   (uint32_t)kFLEXSPI_IpCommandGrantTimeoutFlag |
                                                   (uint32_t)kFLEXSPI_IpCommandExecutionDoneFlag);
            }
            else
            {
                FLEXSPI_EnableInterrupts(
                    base, (uint32_t)kFLEXSPI_IpTxFifoWatermarkEmptyFlag | (uint32_t)kFLEXSPI_SequenceExecutionTimeoutFlag |
                              (uint32_t)kFLEXSPI_IpCommandSequenceErrorFlag | (uint32_t)kFLEXSPI_IpCommandGrantTimeoutFlag |
                              (uint32_t)kFLEXSPI_IpCommandExecutionDoneFlag);
            }
        }
    
        return result;
    }
    
    /*!
     * brief Gets the master transfer status during a interrupt non-blocking transfer.
     *
     * param base FLEXSPI peripheral base address.
     * param handle pointer to flexspi_handle_t structure which stores the transfer state.
     * param count Number of bytes transferred so far by the non-blocking transaction.
     * retval kStatus_InvalidArgument count is Invalid.
     * retval kStatus_Success Successfully return the count.
     */
    status_t FLEXSPI_TransferGetCount(FLEXSPI_Type *base, flexspi_handle_t *handle, size_t *count)
    {
        assert(NULL != handle);
    
        status_t result = kStatus_Success;
    
        if (handle->state == (uint32_t)kFLEXSPI_Idle)
        {
            result = kStatus_NoTransferInProgress;
        }
        else
        {
            *count = handle->transferTotalSize - handle->dataSize;
        }
    
        return result;
    }
    
    /*!
     * brief Aborts an interrupt non-blocking transfer early.
     *
     * note This API can be called at any time when an interrupt non-blocking transfer initiates
     * to abort the transfer early.
     *
     * param base FLEXSPI peripheral base address.
     * param handle pointer to flexspi_handle_t structure which stores the transfer state
     */
    void FLEXSPI_TransferAbort(FLEXSPI_Type *base, flexspi_handle_t *handle)
    {
        assert(NULL != handle);
    
        FLEXSPI_DisableInterrupts(base, (uint32_t)kIrqFlags);
        handle->state = (uint32_t)kFLEXSPI_Idle;
    }
    
    /*!
     * brief Master interrupt handler.
     *
     * param base FLEXSPI peripheral base address.
     * param handle pointer to flexspi_handle_t structure.
     */
    void FLEXSPI_TransferHandleIRQ(FLEXSPI_Type *base, flexspi_handle_t *handle)
    {
        uint32_t status;
        status_t result;
        uint32_t intEnableStatus;
        uint32_t txWatermark;
        uint32_t rxWatermark;
        uint32_t i = 0;
    
        status          = base->INTR;
        intEnableStatus = base->INTEN;
    
        /* Check if interrupt is enabled and status is alerted. */
        if ((status & intEnableStatus) != 0U)
        {
            result = FLEXSPI_CheckAndClearError(base, status);
    
            if ((result != kStatus_Success) && (handle->completionCallback != NULL))
            {
                FLEXSPI_TransferAbort(base, handle);
                if (NULL != handle->completionCallback)
                {
                    handle->completionCallback(base, handle, result, handle->userData);
                }
            }
            else
            {
                if ((0U != (status & (uint32_t)kFLEXSPI_IpRxFifoWatermarkAvailableFlag)) &&
                    (handle->state == (uint32_t)kFLEXSPI_BusyRead))
                {
                    rxWatermark = ((base->IPRXFCR & FLEXSPI_IPRXFCR_RXWMRK_MASK) >> FLEXSPI_IPRXFCR_RXWMRK_SHIFT) + 1U;
    
                    /* Read watermark level data from rx fifo . */
                    if (handle->dataSize >= 8U * rxWatermark)
                    {
                        /* Read watermark level data from rx fifo . */
                        for (i = 0U; i < 2U * rxWatermark; i++)
                        {
                            *(uint32_t *)(void *)handle->data = base->RFDR[i];
                            handle->data += 4U;
                        }
    
                        handle->dataSize = handle->dataSize - 8U * rxWatermark;
                    }
                    else
                    {
                        /* Read word aligned data from rx fifo. */
                        for (i = 0U; i < (handle->dataSize / 4U); i++)
                        {
                            *(uint32_t *)(void *)handle->data = base->RFDR[i];
                            handle->data += 4U;
                        }
    
                        /* Adjust size by the amount processed. */
                        handle->dataSize -= (size_t)4U * i;
    
                        /* Read word un-aligned data from rx fifo. */
                        if (0x00U != handle->dataSize)
                        {
                            uint32_t tempVal = base->RFDR[i];
    
                            for (i = 0U; i < handle->dataSize; i++)
                            {
                                *handle->data++ = ((uint8_t)(tempVal >> (8U * i)) & 0xFFU);
                            }
                        }
    
                        handle->dataSize = 0;
                    }
                    /* Pop out a watermark level data from IP RX FIFO. */
                    base->INTR = (uint32_t)kFLEXSPI_IpRxFifoWatermarkAvailableFlag;
                }
    
                if (0U != (status & (uint32_t)kFLEXSPI_IpCommandExecutionDoneFlag))
                {
                    base->INTR = (uint32_t)kFLEXSPI_IpCommandExecutionDoneFlag;
    
                    FLEXSPI_TransferAbort(base, handle);
    
                    if (NULL != handle->completionCallback)
                    {
                        handle->completionCallback(base, handle, kStatus_Success, handle->userData);
                    }
                }
    
                /* TX FIFO empty interrupt, push watermark level data into tx FIFO. */
                if ((0U != (status & (uint32_t)kFLEXSPI_IpTxFifoWatermarkEmptyFlag)) &&
                    (handle->state == (uint32_t)kFLEXSPI_BusyWrite))
                {
                    if (0U != handle->dataSize)
                    {
                        txWatermark = ((base->IPTXFCR & FLEXSPI_IPTXFCR_TXWMRK_MASK) >> FLEXSPI_IPTXFCR_TXWMRK_SHIFT) + 1U;
                        /* Write watermark level data into tx fifo . */
                        if (handle->dataSize >= 8U * txWatermark)
                        {
                            for (i = 0; i < 2U * txWatermark; i++)
                            {
                                base->TFDR[i] = *(uint32_t *)(void *)handle->data;
                                handle->data += 4U;
                            }
    
                            handle->dataSize = handle->dataSize - 8U * txWatermark;
                        }
                        else
                        {
                            /* Write word aligned data into tx fifo. */
                            for (i = 0U; i < (handle->dataSize / 4U); i++)
                            {
                                base->TFDR[i] = *(uint32_t *)(void *)handle->data;
                                handle->data += 4U;
                            }
    
                            /* Adjust size by the amount processed. */
                            handle->dataSize -= (size_t)4U * i;
    
                            /* Write word un-aligned data into tx fifo. */
                            if (0x00U != handle->dataSize)
                            {
                                uint32_t tempVal = 0x00U;
    
                                for (uint32_t j = 0U; j < handle->dataSize; j++)
                                {
                                    tempVal |= ((uint32_t)*handle->data++ << (8U * j));
                                }
    
                                base->TFDR[i] = tempVal;
                            }
    
                            handle->dataSize = 0;
                        }
    
                        /* Push a watermark level data into IP TX FIFO. */
                        base->INTR = (uint32_t)kFLEXSPI_IpTxFifoWatermarkEmptyFlag;
                    }
                }
                else
                {
                    /* Empty else */
                }
            }
        }
        else
        {
            /* Empty else */
        }
    }
    
    #if defined(FSL_DRIVER_TRANSFER_DOUBLE_WEAK_IRQ) && FSL_DRIVER_TRANSFER_DOUBLE_WEAK_IRQ
    #if defined(FLEXSPI)
    void FLEXSPI_DriverIRQHandler(void);
    void FLEXSPI_DriverIRQHandler(void)
    {
        s_flexspiIsr(FLEXSPI, s_flexspiHandle[0]);
        SDK_ISR_EXIT_BARRIER;
    }
    #endif
    
    #if defined(FLEXSPI0)
    void FLEXSPI0_DriverIRQHandler(void);
    void FLEXSPI0_DriverIRQHandler(void)
    {
        s_flexspiIsr(FLEXSPI0, s_flexspiHandle[0]);
        SDK_ISR_EXIT_BARRIER;
    }
    #endif
    #if defined(FLEXSPI1)
    void FLEXSPI1_DriverIRQHandler(void);
    void FLEXSPI1_DriverIRQHandler(void)
    {
        s_flexspiIsr(FLEXSPI1, s_flexspiHandle[1]);
        SDK_ISR_EXIT_BARRIER;
    }
    #endif
    #if defined(FLEXSPI2)
    void FLEXSPI2_DriverIRQHandler(void);
    void FLEXSPI2_DriverIRQHandler(void)
    {
        s_flexspiIsr(FLEXSPI2, s_flexspiHandle[2]);
        SDK_ISR_EXIT_BARRIER;
    }
    #endif
    
    #if defined(LSIO__FLEXSPI0)
    void LSIO_OCTASPI0_INT_DriverIRQHandler(void);
    void LSIO_OCTASPI0_INT_DriverIRQHandler(void)
    {
        s_flexspiIsr(LSIO__FLEXSPI0, s_flexspiHandle[0]);
        SDK_ISR_EXIT_BARRIER;
    }
    #endif
    #if defined(LSIO__FLEXSPI1)
    void LSIO_OCTASPI1_INT_DriverIRQHandler(void);
    void LSIO_OCTASPI1_INT_DriverIRQHandler(void)
    {
        s_flexspiIsr(LSIO__FLEXSPI1, s_flexspiHandle[1]);
        SDK_ISR_EXIT_BARRIER;
    }
    #endif
    
    #if defined(FSL_FEATURE_FLEXSPI_HAS_SHARED_IRQ0_IRQ1) && FSL_FEATURE_FLEXSPI_HAS_SHARED_IRQ0_IRQ1
    
    void FLEXSPI0_FLEXSPI1_DriverIRQHandler(void);
    void FLEXSPI0_FLEXSPI1_DriverIRQHandler(void)
    {
        /* If handle is registered, treat the transfer function is enabled. */
        if (NULL != s_flexspiHandle[0])
        {
            s_flexspiIsr(FLEXSPI0, s_flexspiHandle[0]);
        }
        if (NULL != s_flexspiHandle[1])
        {
            s_flexspiIsr(FLEXSPI1, s_flexspiHandle[1]);
        }
    }
    #endif
    
    #endif
    
    

    这个文件是芯片驱动flexspi的驱动。

  • flexspi_flash_nor_ops.c

    /*
     * Copyright (c) 2016, Freescale Semiconductor, Inc.
     * Copyright 2016-2022 NXP
     * All rights reserved.
     *
     *
     * SPDX-License-Identifier: BSD-3-Clause
     */
    
    #include "fsl_flexspi.h"
    #include "app.h"
    #include "fsl_iomuxc.h"
    #if (defined CACHE_MAINTAIN) && (CACHE_MAINTAIN == 1)
    #include "fsl_cache.h"
    #endif
    
    //#ifdef 0
    #if 0
    flexspi_device_config_t deviceconfig = {
        .flexspiRootClk       = 133000000,
        .flashSize            = FLASH_SIZE,
        .CSIntervalUnit       = kFLEXSPI_CsIntervalUnit1SckCycle,
        .CSInterval           = 2,
        .CSHoldTime           = 3,
        .CSSetupTime          = 3,
        .dataValidTime        = 0,
        .columnspace          = 0,
        .enableWordAddress    = 0,
        .AWRSeqIndex          = 0,
        .AWRSeqNumber         = 0,
        .ARDSeqIndex          = NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD,
        .ARDSeqNumber         = 1,
        .AHBWriteWaitUnit     = kFLEXSPI_AhbWriteWaitUnit2AhbCycle,
        .AHBWriteWaitInterval = 0,
    };
    
    const uint32_t customLUT[CUSTOM_LUT_LENGTH] = {
        /* Normal read mode -SDR */
        [4 * NOR_CMD_LUT_SEQ_IDX_READ_NORMAL] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x03, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
        [4 * NOR_CMD_LUT_SEQ_IDX_READ_NORMAL + 1] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),
    
        /* Fast read mode - SDR */
        [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x0B, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
        [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST + 1] = FLEXSPI_LUT_SEQ(
            kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_1PAD, 0x08, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04),
    
        /* Fast read quad mode - SDR */
        [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xEB, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_4PAD, 0x18),
        [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD + 1] = FLEXSPI_LUT_SEQ(
            kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_4PAD, 0x06, kFLEXSPI_Command_READ_SDR, kFLEXSPI_4PAD, 0x04),
    
        /* Write Enable */
        [4 * NOR_CMD_LUT_SEQ_IDX_WRITEENABLE] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x06, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),
    
        /* Erase Sector  */
        [4 * NOR_CMD_LUT_SEQ_IDX_ERASESECTOR] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x20, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
    
        /* Page Program - single mode */
        [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_SINGLE] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x02, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
        [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_SINGLE + 1] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),
    
        /* Page Program - quad mode */
        [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x32, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
        [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD + 1] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_4PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),
    
        /* Read ID */
        [4 * NOR_CMD_LUT_SEQ_IDX_READID] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x9F, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04),
    
        /* Enable Quad mode */
        [4 * NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x31, kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x02),
    
        /* Read status register */
        [4 * NOR_CMD_LUT_SEQ_IDX_READSTATUSREG] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x05, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04),
    
        /* Erase whole chip */
        [4 * NOR_CMD_LUT_SEQ_IDX_ERASECHIP] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xC7, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),
    };
    #endif
    /******************************************************************************/
    #if 1
    flexspi_device_config_t deviceconfig = {
        .flexspiRootClk       = 120000000,
        .flashSize            = FLASH_SIZE,
        .CSIntervalUnit       = kFLEXSPI_CsIntervalUnit1SckCycle,
        .CSInterval           = 2,
        .CSHoldTime           = 3,
        .CSSetupTime          = 3,
        .dataValidTime        = 0,
        .columnspace          = 0,
        .enableWordAddress    = 0,
        .AWRSeqIndex          = 0,
        .AWRSeqNumber         = 0,
        .ARDSeqIndex          = NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD,
        .ARDSeqNumber         = 1,
        .AHBWriteWaitUnit     = kFLEXSPI_AhbWriteWaitUnit2AhbCycle,
        .AHBWriteWaitInterval = 0,
    };
    
    const uint32_t customLUT[CUSTOM_LUT_LENGTH] = {
        /* Normal read mode -SDR */
        [4 * NOR_CMD_LUT_SEQ_IDX_READ_NORMAL] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x03, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
        [4 * NOR_CMD_LUT_SEQ_IDX_READ_NORMAL + 1] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),
    
        /* Fast read quad mode - SDR */
        [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xEB, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_4PAD, 0x18),
        [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD + 1] = FLEXSPI_LUT_SEQ(
            kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_4PAD, 0x06, kFLEXSPI_Command_READ_SDR, kFLEXSPI_4PAD, 0x04),
    
        /* Read extend parameters */
        [4 * NOR_CMD_LUT_SEQ_IDX_READSTATUS] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x81, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04),
    
        /* Write Enable */
        [4 * NOR_CMD_LUT_SEQ_IDX_WRITEENABLE] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x06, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),
    
        /* Erase Sector  */
        [4 * NOR_CMD_LUT_SEQ_IDX_ERASESECTOR] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x20, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
    
        /* Page Program - single mode */
        [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_SINGLE] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x02, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
        [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_SINGLE + 1] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),
    
        /* Page Program - quad mode */
        [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x32, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
        [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD + 1] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_4PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),
    
        /* Read ID */
        [4 * NOR_CMD_LUT_SEQ_IDX_READID] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x9F, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04),
    
        /* Enable Quad mode */
        [4 * NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x31, kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x04),
    
        /* Erase Block */
    //    [4 * NOR_CMD_LUT_SEQ_IDX_ERASEBLOCK] =
     //       FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xD8, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
    
        /* Read status register */
        [4 * NOR_CMD_LUT_SEQ_IDX_READSTATUSREG] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x05, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04),
    
        /* Erase whole chip */
        [4 * NOR_CMD_LUT_SEQ_IDX_ERASECHIP] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xC7, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),
    };
    
    
    #endif /* FLM */
    /*******************************************************************************
     * Definitions
     ******************************************************************************/
    
    /*******************************************************************************
     * Prototypes
     ******************************************************************************/
    
    /*******************************************************************************
     * Variables
     *****************************************************************************/
    extern flexspi_device_config_t deviceconfig;
    extern const uint32_t customLUT[CUSTOM_LUT_LENGTH];
    
    /*******************************************************************************
     * Code
     ******************************************************************************/
    #if (defined CACHE_MAINTAIN) && (CACHE_MAINTAIN == 1)
    void flexspi_nor_disable_cache(flexspi_cache_status_t *cacheStatus)
    {
    #if (defined __CORTEX_M) && (__CORTEX_M == 7U)
    #if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
        /* Disable D cache. */
        if (SCB_CCR_DC_Msk == (SCB_CCR_DC_Msk & SCB->CCR))
        {
            SCB_DisableDCache();
            cacheStatus->DCacheEnableFlag = true;
        }
    #endif /* __DCACHE_PRESENT */
    
    #if defined(__ICACHE_PRESENT) && (__ICACHE_PRESENT == 1U)
        /* Disable I cache. */
        if (SCB_CCR_IC_Msk == (SCB_CCR_IC_Msk & SCB->CCR))
        {
            SCB_DisableICache();
            cacheStatus->ICacheEnableFlag = true;
        }
    #endif /* __ICACHE_PRESENT */
    
    #elif (defined FSL_FEATURE_SOC_LMEM_COUNT) && (FSL_FEATURE_SOC_LMEM_COUNT != 0U)
        /* Disable code bus cache and system bus cache */
        if (LMEM_PCCCR_ENCACHE_MASK == (LMEM_PCCCR_ENCACHE_MASK & LMEM->PCCCR))
        {
            L1CACHE_DisableCodeCache();
            cacheStatus->codeCacheEnableFlag = true;
        }
        if (LMEM_PSCCR_ENCACHE_MASK == (LMEM_PSCCR_ENCACHE_MASK & LMEM->PSCCR))
        {
            L1CACHE_DisableSystemCache();
            cacheStatus->systemCacheEnableFlag = true;
        }
    
    #elif (defined FSL_FEATURE_SOC_CACHE64_CTRL_COUNT) && (FSL_FEATURE_SOC_CACHE64_CTRL_COUNT != 0U)
        /* Disable cache */
        CACHE64_DisableCache(EXAMPLE_CACHE);
        cacheStatus->CacheEnableFlag = true;
    #endif
    }
    
    void flexspi_nor_enable_cache(flexspi_cache_status_t cacheStatus)
    {
    #if (defined __CORTEX_M) && (__CORTEX_M == 7U)
    #if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
        if (cacheStatus.DCacheEnableFlag)
        {
            /* Enable D cache. */
            SCB_EnableDCache();
        }
    #endif /* __DCACHE_PRESENT */
    
    #if defined(__ICACHE_PRESENT) && (__ICACHE_PRESENT == 1U)
        if (cacheStatus.ICacheEnableFlag)
        {
            /* Enable I cache. */
            SCB_EnableICache();
        }
    #endif /* __ICACHE_PRESENT */
    
    #elif (defined FSL_FEATURE_SOC_LMEM_COUNT) && (FSL_FEATURE_SOC_LMEM_COUNT != 0U)
        if (cacheStatus.codeCacheEnableFlag)
        {
            /* Enable code cache. */
            L1CACHE_EnableCodeCache();
        }
    
        if (cacheStatus.systemCacheEnableFlag)
        {
            /* Enable system cache. */
            L1CACHE_EnableSystemCache();
        }
    
    #elif (defined FSL_FEATURE_SOC_CACHE64_CTRL_COUNT) && (FSL_FEATURE_SOC_CACHE64_CTRL_COUNT != 0U)
        if (cacheStatus.CacheEnableFlag)
        {
            /* Enable cache. */
            CACHE64_EnableCache(EXAMPLE_CACHE);
        }
    #endif
    }
    #endif
    
    status_t flexspi_nor_write_enable(FLEXSPI_Type *base, uint32_t baseAddr)
    {
        flexspi_transfer_t flashXfer;
        status_t status;
    
        /* Write enable */
        flashXfer.deviceAddress = baseAddr;
        flashXfer.port          = FLASH_PORT;
        flashXfer.cmdType       = kFLEXSPI_Command;
        flashXfer.SeqNumber     = 1;
        flashXfer.seqIndex      = NOR_CMD_LUT_SEQ_IDX_WRITEENABLE;
    
        status = FLEXSPI_TransferBlocking(base, &flashXfer);
    
        return status;
    }
    
    status_t flexspi_nor_wait_bus_busy(FLEXSPI_Type *base)
    {
        /* Wait status ready. */
        bool isBusy;
        uint32_t readValue;
        status_t status;
        flexspi_transfer_t flashXfer;
    
        flashXfer.deviceAddress = 0;
        flashXfer.port          = FLASH_PORT;
        flashXfer.cmdType       = kFLEXSPI_Read;
        flashXfer.SeqNumber     = 1;
        flashXfer.seqIndex      = NOR_CMD_LUT_SEQ_IDX_READSTATUSREG;
        flashXfer.data          = &readValue;
        flashXfer.dataSize      = 1;
    
        do
        {
            status = FLEXSPI_TransferBlocking(base, &flashXfer);
    
            if (status != kStatus_Success)
            {
                return status;
            }
            if (FLASH_BUSY_STATUS_POL)
            {
                if (readValue & (1U << FLASH_BUSY_STATUS_OFFSET))
                {
                    isBusy = true;
                }
                else
                {
                    isBusy = false;
                }
            }
            else
            {
                if (readValue & (1U << FLASH_BUSY_STATUS_OFFSET))
                {
                    isBusy = false;
                }
                else
                {
                    isBusy = true;
                }
            }
    
        } while (isBusy);
    
        return status;
    }
    
    status_t flexspi_nor_enable_quad_mode(FLEXSPI_Type *base)
    {
        flexspi_transfer_t flashXfer;
        status_t status;
    #if defined(FLASH_QUAD_ENABLE) && FLASH_QUAD_ENABLE
        uint32_t writeValue = FLASH_QUAD_ENABLE;
    #endif
    
    #if defined(CACHE_MAINTAIN) && CACHE_MAINTAIN
        flexspi_cache_status_t cacheStatus;
        flexspi_nor_disable_cache(&cacheStatus);
    #endif
    
        /* Write enable */
        status = flexspi_nor_write_enable(base, 0);
    
        if (status != kStatus_Success)
        {
            return status;
        }
    
        /* Enable quad mode. */
        flashXfer.deviceAddress = 0;
        flashXfer.port          = FLASH_PORT;
    #if defined(FLASH_QUAD_ENABLE) && FLASH_QUAD_ENABLE
        flashXfer.cmdType       = kFLEXSPI_Write;
        flashXfer.SeqNumber     = 1;
        flashXfer.seqIndex      = NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG;
        flashXfer.data          = &writeValue;
        flashXfer.dataSize      = writeValue <= 0xFFU ? 1 : 2;
    #endif
    #if defined(MT25Q_FLASH_QUAD_ENABLE) && MT25Q_FLASH_QUAD_ENABLE
        flashXfer.cmdType       = kFLEXSPI_Command;
        flashXfer.SeqNumber     = 1;
        flashXfer.seqIndex      = NOR_CMD_LUT_SEQ_IDX_ENABLEQUAD;
    #endif
        status = FLEXSPI_TransferBlocking(base, &flashXfer);
        if (status != kStatus_Success)
        {
            return status;
        }
    
        status = flexspi_nor_wait_bus_busy(base);
    
        /* Do software reset. */
        FLEXSPI_SoftwareReset(base);
    
    #if defined(CACHE_MAINTAIN) && CACHE_MAINTAIN
        flexspi_nor_enable_cache(cacheStatus);
    #endif
    
        return status;
    }
    
    #if defined(NOR_CMD_LUT_SEQ_IDX_SETREADPARAMETER) && NOR_CMD_LUT_SEQ_IDX_SETREADPARAMETER
    status_t flexspi_nor_set_read_parameter(
        FLEXSPI_Type *base, uint8_t burstLength, bool enableWrap, uint8_t dummyCycle, bool resetPinSelected)
    {
        flexspi_transfer_t flashXfer;
        status_t status;
        uint32_t readParameterRegVal = ((uint32_t)resetPinSelected << RESET_PIN_SELECTED_REG_SHIFT) |
                                       ((uint32_t)dummyCycle << DUMMY_CYCLES_REG_SHIFT) |
                                       ((uint32_t)enableWrap << WRAP_ENABLE_REG_SHIFT) |
                                       ((uint32_t)burstLength << BURST_LEGNTH_REG_SHIFT);
    
    #if defined(CACHE_MAINTAIN) && CACHE_MAINTAIN
        flexspi_cache_status_t cacheStatus;
        flexspi_nor_disable_cache(&cacheStatus);
    #endif
    
        /* Write enable */
        status = flexspi_nor_write_enable(base, 0);
    
        if (status != kStatus_Success)
        {
            return status;
        }
    
        flashXfer.deviceAddress = 0;
        flashXfer.port          = FLASH_PORT;
        flashXfer.cmdType       = kFLEXSPI_Write;
        flashXfer.SeqNumber     = 1;
        flashXfer.seqIndex      = NOR_CMD_LUT_SEQ_IDX_SETREADPARAMETER;
        flashXfer.data          = &readParameterRegVal;
        flashXfer.dataSize      = 1;
    
        status = FLEXSPI_TransferBlocking(base, &flashXfer);
        if (status != kStatus_Success)
        {
            return status;
        }
    
        status = flexspi_nor_wait_bus_busy(base);
    
        /* Do software reset. */
        FLEXSPI_SoftwareReset(base);
    
    #if defined(CACHE_MAINTAIN) && CACHE_MAINTAIN
        flexspi_nor_enable_cache(cacheStatus);
    #endif
    
        return status;
    }
    #endif
    
    status_t flexspi_nor_flash_erase_sector(FLEXSPI_Type *base, uint32_t address)
    {
        status_t status;
        flexspi_transfer_t flashXfer;
    
    #if defined(CACHE_MAINTAIN) && CACHE_MAINTAIN
        flexspi_cache_status_t cacheStatus;
        flexspi_nor_disable_cache(&cacheStatus);
    #endif
    
        /* Write enable */
        flashXfer.deviceAddress = address;
        flashXfer.port          = FLASH_PORT;
        flashXfer.cmdType       = kFLEXSPI_Command;
        flashXfer.SeqNumber     = 1;
        flashXfer.seqIndex      = NOR_CMD_LUT_SEQ_IDX_WRITEENABLE;
    
        status = FLEXSPI_TransferBlocking(base, &flashXfer);
    
        if (status != kStatus_Success)
        {
            return status;
        }
    
        flashXfer.deviceAddress = address;
        flashXfer.port          = FLASH_PORT;
        flashXfer.cmdType       = kFLEXSPI_Command;
        flashXfer.SeqNumber     = 1;
        flashXfer.seqIndex      = NOR_CMD_LUT_SEQ_IDX_ERASESECTOR;
        status                  = FLEXSPI_TransferBlocking(base, &flashXfer);
    
        if (status != kStatus_Success)
        {
            return status;
        }
    
        status = flexspi_nor_wait_bus_busy(base);
    
        /* Do software reset. */
        FLEXSPI_SoftwareReset(base);
    
    #if defined(CACHE_MAINTAIN) && CACHE_MAINTAIN
        flexspi_nor_enable_cache(cacheStatus);
    #endif
    
        return status;
    }
    
    status_t flexspi_nor_flash_read(FLEXSPI_Type *base, uint32_t dstAddr, const uint32_t *src, uint32_t length)
    {
        status_t status;
        flexspi_transfer_t flashXfer;
    
    #if defined(CACHE_MAINTAIN) && CACHE_MAINTAIN
        flexspi_cache_status_t cacheStatus;
        flexspi_nor_disable_cache(&cacheStatus);
    #endif
    
        /* Write enable */
        status = flexspi_nor_write_enable(base, dstAddr);
    
        if (status != kStatus_Success)
        {
            return status;
        }
    
        /* Prepare page program command */
        flashXfer.deviceAddress = dstAddr;
        flashXfer.port          = FLASH_PORT;
        flashXfer.cmdType       = kFLEXSPI_Read;
        flashXfer.SeqNumber     = 1;
        flashXfer.seqIndex      = NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD;
        flashXfer.data          = (uint32_t *)src;
        flashXfer.dataSize      = length;
        status                  = FLEXSPI_TransferBlocking(base, &flashXfer);
    
        if (status != kStatus_Success)
        {
            return status;
        }
    
        status = flexspi_nor_wait_bus_busy(base);
    
        /* Do software reset or clear AHB buffer directly. */
    #if defined(FSL_FEATURE_SOC_OTFAD_COUNT) && defined(FLEXSPI_AHBCR_CLRAHBRXBUF_MASK) && \
        defined(FLEXSPI_AHBCR_CLRAHBTXBUF_MASK)
        base->AHBCR |= FLEXSPI_AHBCR_CLRAHBRXBUF_MASK | FLEXSPI_AHBCR_CLRAHBTXBUF_MASK;
        base->AHBCR &= ~(FLEXSPI_AHBCR_CLRAHBRXBUF_MASK | FLEXSPI_AHBCR_CLRAHBTXBUF_MASK);
    #else
        FLEXSPI_SoftwareReset(base);
    #endif
    
    #if defined(CACHE_MAINTAIN) && CACHE_MAINTAIN
        flexspi_nor_enable_cache(cacheStatus);
    #endif
    
        return status;
    }
    
    status_t flexspi_nor_flash_program(FLEXSPI_Type *base, uint32_t dstAddr, const uint32_t *src, uint32_t length)
    {
        status_t status;
        flexspi_transfer_t flashXfer;
    
    #if defined(CACHE_MAINTAIN) && CACHE_MAINTAIN
        flexspi_cache_status_t cacheStatus;
        flexspi_nor_disable_cache(&cacheStatus);
    #endif
    
        /* Write enable */
        status = flexspi_nor_write_enable(base, dstAddr);
    
        if (status != kStatus_Success)
        {
            return status;
        }
    
        /* Prepare page program command */
        flashXfer.deviceAddress = dstAddr;
        flashXfer.port          = FLASH_PORT;
        flashXfer.cmdType       = kFLEXSPI_Write;
        flashXfer.SeqNumber     = 1;
        flashXfer.seqIndex      = NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD;
        flashXfer.data          = (uint32_t *)src;
        flashXfer.dataSize      = length;
        status                  = FLEXSPI_TransferBlocking(base, &flashXfer);
    
        if (status != kStatus_Success)
        {
            return status;
        }
    
        status = flexspi_nor_wait_bus_busy(base);
    
        /* Do software reset or clear AHB buffer directly. */
    #if defined(FSL_FEATURE_SOC_OTFAD_COUNT) && defined(FLEXSPI_AHBCR_CLRAHBRXBUF_MASK) && \
        defined(FLEXSPI_AHBCR_CLRAHBTXBUF_MASK)
        base->AHBCR |= FLEXSPI_AHBCR_CLRAHBRXBUF_MASK | FLEXSPI_AHBCR_CLRAHBTXBUF_MASK;
        base->AHBCR &= ~(FLEXSPI_AHBCR_CLRAHBRXBUF_MASK | FLEXSPI_AHBCR_CLRAHBTXBUF_MASK);
    #else
        FLEXSPI_SoftwareReset(base);
    #endif
    
    #if defined(CACHE_MAINTAIN) && CACHE_MAINTAIN
        flexspi_nor_enable_cache(cacheStatus);
    #endif
    
        return status;
    }
    
    status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t dstAddr, const uint32_t *src)
    {
        status_t status;
        flexspi_transfer_t flashXfer;
    
    #if defined(CACHE_MAINTAIN) && CACHE_MAINTAIN
        flexspi_cache_status_t cacheStatus;
        flexspi_nor_disable_cache(&cacheStatus);
    #endif
    
        /* To make sure external flash be in idle status, added wait for busy before program data for
            an external flash without RWW(read while write) attribute.*/
        status = flexspi_nor_wait_bus_busy(base);
    
        if (kStatus_Success != status)
        {
            return status;
        }
    
        /* Write enable. */
        status = flexspi_nor_write_enable(base, dstAddr);
    
        if (status != kStatus_Success)
        {
            return status;
        }
    
        /* Prepare page program command */
        flashXfer.deviceAddress = dstAddr;
        flashXfer.port          = FLASH_PORT;
        flashXfer.cmdType       = kFLEXSPI_Write;
        flashXfer.SeqNumber     = 1;
        flashXfer.seqIndex      = NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD;
        flashXfer.data          = (uint32_t *)src;
        flashXfer.dataSize      = FLASH_PAGE_SIZE;
        status                  = FLEXSPI_TransferBlocking(base, &flashXfer);
    
        if (status != kStatus_Success)
        {
            return status;
        }
    
        status = flexspi_nor_wait_bus_busy(base);
    
        /* Do software reset or clear AHB buffer directly. */
    #if defined(FSL_FEATURE_SOC_OTFAD_COUNT) && defined(FLEXSPI_AHBCR_CLRAHBRXBUF_MASK) && \
        defined(FLEXSPI_AHBCR_CLRAHBTXBUF_MASK)
        base->AHBCR |= FLEXSPI_AHBCR_CLRAHBRXBUF_MASK | FLEXSPI_AHBCR_CLRAHBTXBUF_MASK;
        base->AHBCR &= ~(FLEXSPI_AHBCR_CLRAHBRXBUF_MASK | FLEXSPI_AHBCR_CLRAHBTXBUF_MASK);
    #else
        FLEXSPI_SoftwareReset(base);
    #endif
    
    #if defined(CACHE_MAINTAIN) && CACHE_MAINTAIN
        flexspi_nor_enable_cache(cacheStatus);
    #endif
    
        return status;
    }
    
    status_t flexspi_nor_get_vendor_id(FLEXSPI_Type *base, uint8_t *vendorId)
    {
        uint32_t temp;
        flexspi_transfer_t flashXfer;
        flashXfer.deviceAddress = 0;
        flashXfer.port          = FLASH_PORT;
        flashXfer.cmdType       = kFLEXSPI_Read;
        flashXfer.SeqNumber     = 1;
        flashXfer.seqIndex      = NOR_CMD_LUT_SEQ_IDX_READID;
        flashXfer.data          = &temp;
        flashXfer.dataSize      = 1;
    
        status_t status = FLEXSPI_TransferBlocking(base, &flashXfer);
    
        *vendorId = temp;
    
        /* Do software reset or clear AHB buffer directly. */
    #if defined(FSL_FEATURE_SOC_OTFAD_COUNT) && defined(FLEXSPI_AHBCR_CLRAHBRXBUF_MASK) && \
        defined(FLEXSPI_AHBCR_CLRAHBTXBUF_MASK)
        base->AHBCR |= FLEXSPI_AHBCR_CLRAHBRXBUF_MASK | FLEXSPI_AHBCR_CLRAHBTXBUF_MASK;
        base->AHBCR &= ~(FLEXSPI_AHBCR_CLRAHBRXBUF_MASK | FLEXSPI_AHBCR_CLRAHBTXBUF_MASK);
    #else
        FLEXSPI_SoftwareReset(base);
    #endif
    
        return status;
    }
    
    status_t flexspi_nor_erase_chip(FLEXSPI_Type *base)
    {
        status_t status;
        flexspi_transfer_t flashXfer;
    
    #if defined(CACHE_MAINTAIN) && CACHE_MAINTAIN
        flexspi_cache_status_t cacheStatus;
        flexspi_nor_disable_cache(&cacheStatus);
    #endif
    
        /* Write enable */
        status = flexspi_nor_write_enable(base, 0);
    
        if (status != kStatus_Success)
        {
            return status;
        }
    
        flashXfer.deviceAddress = 0;
        flashXfer.port          = FLASH_PORT;
        flashXfer.cmdType       = kFLEXSPI_Command;
        flashXfer.SeqNumber     = 1;
        flashXfer.seqIndex      = NOR_CMD_LUT_SEQ_IDX_ERASECHIP;
    
        status = FLEXSPI_TransferBlocking(base, &flashXfer);
    
        if (status != kStatus_Success)
        {
            return status;
        }
    
        status = flexspi_nor_wait_bus_busy(base);
    
    #if defined(CACHE_MAINTAIN) && CACHE_MAINTAIN
        flexspi_nor_enable_cache(cacheStatus);
    #endif
    
        return status;
    }
    /* 配置flexspi的引脚 */
    void flexspi_config()
    {
    #ifdef FLM
    
        IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B1_05_FLEXSPIA_DQS,    1U); 
        IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B1_06_FLEXSPIA_SS0_B,  1U); 
        IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B1_07_FLEXSPIA_SCLK,   1U); 
        IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B1_08_FLEXSPIA_DATA00, 1U); 
        IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B1_09_FLEXSPIA_DATA01, 1U); 
        IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B1_10_FLEXSPIA_DATA02, 1U); 
        IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B1_11_FLEXSPIA_DATA03, 1U); 
    
        IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B1_05_FLEXSPIA_DQS, 	  0x10F1U); 
        IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B1_06_FLEXSPIA_SS0_B,  0x10F1U); 
        IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B1_07_FLEXSPIA_SCLK,   0x10F1U); 
        IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B1_08_FLEXSPIA_DATA00, 0x10F1U); 
        IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B1_09_FLEXSPIA_DATA01, 0x10F1U); 
        IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B1_10_FLEXSPIA_DATA02, 0x10F1U); 
        IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B1_11_FLEXSPIA_DATA03, 0x10F1U);     
    	
    #endif    
    
    }
    
    void flexspi_nor_flash_init(FLEXSPI_Type *base)
    {
        flexspi_config(); /* config flexspi */
        flexspi_config_t config;
        /* To store custom's LUT table in local. */
        uint32_t tempLUT[CUSTOM_LUT_LENGTH] = {0x00U};
    
    #if defined(CACHE_MAINTAIN) && CACHE_MAINTAIN
        flexspi_cache_status_t cacheStatus;
        flexspi_nor_disable_cache(&cacheStatus);
    #endif
    
        /* Copy LUT information from flash region into RAM region, because LUT update maybe corrupt read sequence(LUT[0])
         * and load wrong LUT table from FLASH region. */
        memcpy(tempLUT, customLUT, sizeof(tempLUT));
    
        flexspi_clock_init();
    
        /*Get FLEXSPI default settings and configure the flexspi. */
        FLEXSPI_GetDefaultConfig(&config);
    
        /*Set AHB buffer size for reading data through AHB bus. */
        config.ahbConfig.enableAHBPrefetch    = true;
        config.ahbConfig.enableAHBBufferable  = true;
        config.ahbConfig.enableReadAddressOpt = true;
        config.ahbConfig.enableAHBCachable    = true;
        config.rxSampleClock                  = EXAMPLE_FLEXSPI_RX_SAMPLE_CLOCK;
        FLEXSPI_Init(base, &config);
    
        /* Configure flash settings according to serial flash feature. */
        FLEXSPI_SetFlashConfig(base, &deviceconfig, FLASH_PORT);
    
        /* Update LUT table. */
        FLEXSPI_UpdateLUT(base, 0, tempLUT, CUSTOM_LUT_LENGTH);
    
        /* Do software reset. */
        FLEXSPI_SoftwareReset(base);
    
    #if defined(CACHE_MAINTAIN) && CACHE_MAINTAIN
        flexspi_nor_enable_cache(cacheStatus);
    #endif
    }
    
    

    这个文件很重要因为它是操作Flash的接口文件,上面的LUT表有相应的Flash命令:如写、擦除、复位,写使能、写使能等操作(对应命令需要去看相应的的flash数据手册)。

    const uint32_t customLUT[CUSTOM_LUT_LENGTH] = {
        /* Normal read mode -SDR */
        [4 * NOR_CMD_LUT_SEQ_IDX_READ_NORMAL] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x03, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
        [4 * NOR_CMD_LUT_SEQ_IDX_READ_NORMAL + 1] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),
    
        /* Fast read quad mode - SDR */
        [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xEB, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_4PAD, 0x18),
        [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD + 1] = FLEXSPI_LUT_SEQ(
            kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_4PAD, 0x06, kFLEXSPI_Command_READ_SDR, kFLEXSPI_4PAD, 0x04),
    
        /* Read extend parameters */
        [4 * NOR_CMD_LUT_SEQ_IDX_READSTATUS] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x81, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04),
    
        /* Write Enable */
        [4 * NOR_CMD_LUT_SEQ_IDX_WRITEENABLE] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x06, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),
    
        /* Erase Sector  */
        [4 * NOR_CMD_LUT_SEQ_IDX_ERASESECTOR] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x20, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
    
        /* Page Program - single mode */
        [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_SINGLE] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x02, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
        [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_SINGLE + 1] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),
    
        /* Page Program - quad mode */
        [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x32, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
        [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD + 1] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_4PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),
    
        /* Read ID */
        [4 * NOR_CMD_LUT_SEQ_IDX_READID] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x9F, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04),
    
        /* Enable Quad mode */
        [4 * NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x31, kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x04),
    
        /* Erase Block */
    //    [4 * NOR_CMD_LUT_SEQ_IDX_ERASEBLOCK] =
     //       FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xD8, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
    
        /* Read status register */
        [4 * NOR_CMD_LUT_SEQ_IDX_READSTATUSREG] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x05, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04),
    
        /* Erase whole chip */
        [4 * NOR_CMD_LUT_SEQ_IDX_ERASECHIP] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xC7, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),
    };
    
  • Fsl_colck.c

    /*
     * Copyright 2018 - 2021, 2024 NXP
     * All rights reserved.
     *
     * SPDX-License-Identifier: BSD-3-Clause
     */
    
    #include "fsl_clock.h"
    /* Component ID definition, used by tools. */
    #ifndef FSL_COMPONENT_ID
    #define FSL_COMPONENT_ID "platform.drivers.clock"
    #endif
    
    /*******************************************************************************
     * Definitions
     ******************************************************************************/
    /* To make full use of CM7 hardware FPU, use double instead of uint64_t in clock driver to
    achieve better performance, it is depend on the IDE Floating point settings, if double precision is selected
    in IDE, clock_64b_t will switch to double type automatically. only support IAR and MDK here */
    #if __FPU_USED
    
    #if (defined(__ICCARM__))
    
    #if (__ARMVFP__ >= __ARMFPV5__) && \
        (__ARM_FP == 0xE) /*0xe implies support for half, single and double precision operations*/
    typedef double clock_64b_t;
    #else
    typedef uint64_t clock_64b_t;
    #endif
    
    #elif (defined(__GNUC__))
    
    #if (__ARM_FP == 0xE) /*0xe implies support for half, single and double precision operations*/
    typedef double clock_64b_t;
    #else
    typedef uint64_t clock_64b_t;
    #endif
    
    #elif defined(__CC_ARM) || defined(__ARMCC_VERSION)
    
    #if defined __TARGET_FPU_FPV5_D16
    typedef double clock_64b_t;
    #else
    typedef uint64_t clock_64b_t;
    #endif
    
    #else
    typedef uint64_t clock_64b_t;
    #endif
    
    #else
    typedef uint64_t clock_64b_t;
    #endif
    
    /*******************************************************************************
     * Variables
     ******************************************************************************/
    
    /* External XTAL (OSC) clock frequency. */
    volatile uint32_t g_xtalFreq;
    /* External RTC XTAL clock frequency. */
    volatile uint32_t g_rtcXtalFreq;
    
    /*******************************************************************************
     * Prototypes
     ******************************************************************************/
    
    /*!
     * @brief Get the periph clock frequency.
     *
     * @return Periph clock frequency in Hz.
     */
    static uint32_t CLOCK_GetPeriphClkFreq(void);
    
    /*!
     * @brief Get the frequency of PLL USB1 software clock.
     *
     * @return The frequency of PLL USB1 software clock.
     */
    static uint32_t CLOCK_GetPllUsb1SWFreq(void);
    
    /*******************************************************************************
     * Code
     ******************************************************************************/
    
    static uint32_t CLOCK_GetPeriphClkFreq(void)
    {
        uint32_t freq;
    
        /* Periph_clk2_clk ---> Periph_clk */
        if ((CCM->CBCDR & CCM_CBCDR_PERIPH_CLK_SEL_MASK) != 0U)
        {
            switch (CCM->CBCMR & CCM_CBCMR_PERIPH_CLK2_SEL_MASK)
            {
                /* Pll3_sw_clk ---> Periph_clk2_clk ---> Periph_clk */
                case CCM_CBCMR_PERIPH_CLK2_SEL(0U):
                    freq = CLOCK_GetPllFreq(kCLOCK_PllUsb1);
                    break;
    
                /* Osc_clk ---> Periph_clk2_clk ---> Periph_clk */
                case CCM_CBCMR_PERIPH_CLK2_SEL(1U):
                    freq = CLOCK_GetOscFreq();
                    break;
    
                case CCM_CBCMR_PERIPH_CLK2_SEL(2U):
                    freq = CLOCK_GetPllFreq(kCLOCK_PllSys);
                    break;
    
                case CCM_CBCMR_PERIPH_CLK2_SEL(3U):
                default:
                    freq = 0U;
                    break;
            }
    
            freq /= (((CCM->CBCDR & CCM_CBCDR_PERIPH_CLK2_PODF_MASK) >> CCM_CBCDR_PERIPH_CLK2_PODF_SHIFT) + 1U);
        }
        /* Pre_Periph_clk ---> Periph_clk */
        else
        {
            switch (CCM->CBCMR & CCM_CBCMR_PRE_PERIPH_CLK_SEL_MASK)
            {
                /* PLL2 ---> Pre_Periph_clk ---> Periph_clk */
                case CCM_CBCMR_PRE_PERIPH_CLK_SEL(0U):
                    freq = CLOCK_GetPllFreq(kCLOCK_PllSys);
                    break;
    
                /* PLL2 PFD2 ---> Pre_Periph_clk ---> Periph_clk */
                case CCM_CBCMR_PRE_PERIPH_CLK_SEL(1U):
                    freq = CLOCK_GetSysPfdFreq(kCLOCK_Pfd2);
                    break;
    
                /* PLL2 PFD0 ---> Pre_Periph_clk ---> Periph_clk */
                case CCM_CBCMR_PRE_PERIPH_CLK_SEL(2U):
                    freq = CLOCK_GetSysPfdFreq(kCLOCK_Pfd0);
                    break;
    
                /* PLL1 divided(/2) ---> Pre_Periph_clk ---> Periph_clk */
                case CCM_CBCMR_PRE_PERIPH_CLK_SEL(3U):
                    freq = CLOCK_GetPllFreq(kCLOCK_PllArm) /
                           (((CCM->CACRR & CCM_CACRR_ARM_PODF_MASK) >> CCM_CACRR_ARM_PODF_SHIFT) + 1U);
                    break;
    
                default:
                    freq = 0U;
                    break;
            }
        }
    
        return freq;
    }
    
    static uint32_t CLOCK_GetPllUsb1SWFreq(void)
    {
        uint32_t freq;
    
        switch ((CCM->CCSR & CCM_CCSR_PLL3_SW_CLK_SEL_MASK) >> CCM_CCSR_PLL3_SW_CLK_SEL_SHIFT)
        {
            case 0:
            {
                freq = CLOCK_GetPllFreq(kCLOCK_PllUsb1);
                break;
            }
            case 1:
            {
                freq = 24000000UL;
                break;
            }
            default:
                freq = 0UL;
                break;
        }
    
        return freq;
    }
    
    /*!
     * brief Initialize the external 24MHz clock.
     *
     * This function supports two modes:
     * 1. Use external crystal oscillator.
     * 2. Bypass the external crystal oscillator, using input source clock directly.
     *
     * After this function, please call ref CLOCK_SetXtal0Freq to inform clock driver
     * the external clock frequency.
     *
     * param bypassXtalOsc Pass in true to bypass the external crystal oscillator.
     * note This device does not support bypass external crystal oscillator, so
     * the input parameter should always be false.
     */
    void CLOCK_InitExternalClk(bool bypassXtalOsc)
    {
        /* This device does not support bypass XTAL OSC. */
        assert(!bypassXtalOsc);
    
        CCM_ANALOG->MISC0_CLR = CCM_ANALOG_MISC0_XTAL_24M_PWD_MASK; /* Power up */
        while ((XTALOSC24M->LOWPWR_CTRL & XTALOSC24M_LOWPWR_CTRL_XTALOSC_PWRUP_STAT_MASK) == 0U)
        {
        }
        CCM_ANALOG->MISC0_SET = CCM_ANALOG_MISC0_OSC_XTALOK_EN_MASK; /* detect freq */
        while ((CCM_ANALOG->MISC0 & CCM_ANALOG_MISC0_OSC_XTALOK_MASK) == 0UL)
        {
        }
        CCM_ANALOG->MISC0_CLR = CCM_ANALOG_MISC0_OSC_XTALOK_EN_MASK;
    }
    
    /*!
     * brief Deinitialize the external 24MHz clock.
     *
     * This function disables the external 24MHz clock.
     *
     * After this function, please call ref CLOCK_SetXtal0Freq to set external clock
     * frequency to 0.
     */
    void CLOCK_DeinitExternalClk(void)
    {
        CCM_ANALOG->MISC0_SET = CCM_ANALOG_MISC0_XTAL_24M_PWD_MASK; /* Power down */
    }
    
    /*!
     * brief Switch the OSC.
     *
     * This function switches the OSC source for SoC.
     *
     * param osc   OSC source to switch to.
     */
    void CLOCK_SwitchOsc(clock_osc_t osc)
    {
        if (osc == kCLOCK_RcOsc)
        {
            XTALOSC24M->LOWPWR_CTRL_SET = XTALOSC24M_LOWPWR_CTRL_SET_OSC_SEL_MASK;
        }
        else
        {
            XTALOSC24M->LOWPWR_CTRL_CLR = XTALOSC24M_LOWPWR_CTRL_CLR_OSC_SEL_MASK;
        }
    }
    
    /*!
     * brief Initialize the RC oscillator 24MHz clock.
     */
    void CLOCK_InitRcOsc24M(void)
    {
        XTALOSC24M->LOWPWR_CTRL |= XTALOSC24M_LOWPWR_CTRL_RC_OSC_EN_MASK;
    }
    
    /*!
     * brief Power down the RCOSC 24M clock.
     */
    void CLOCK_DeinitRcOsc24M(void)
    {
        XTALOSC24M->LOWPWR_CTRL &= ~XTALOSC24M_LOWPWR_CTRL_RC_OSC_EN_MASK;
    }
    
    /*!
     * brief Gets the AHB clock frequency.
     *
     * return  The AHB clock frequency value in hertz.
     */
    uint32_t CLOCK_GetAhbFreq(void)
    {
        return CLOCK_GetPeriphClkFreq() / (((CCM->CBCDR & CCM_CBCDR_AHB_PODF_MASK) >> CCM_CBCDR_AHB_PODF_SHIFT) + 1U);
    }
    
    /*!
     * brief Gets the SEMC clock frequency.
     *
     * return  The SEMC clock frequency value in hertz.
     */
    uint32_t CLOCK_GetSemcFreq(void)
    {
        uint32_t freq;
    
        /* SEMC alternative clock ---> SEMC Clock */
        if ((CCM->CBCDR & CCM_CBCDR_SEMC_CLK_SEL_MASK) != 0U)
        {
            /* PLL3 PFD1 ---> SEMC alternative clock ---> SEMC Clock */
            if ((CCM->CBCDR & CCM_CBCDR_SEMC_ALT_CLK_SEL_MASK) != 0U)
            {
                freq = CLOCK_GetUsb1PfdFreq(kCLOCK_Pfd1);
            }
            /* PLL2 PFD2 ---> SEMC alternative clock ---> SEMC Clock */
            else
            {
                freq = CLOCK_GetSysPfdFreq(kCLOCK_Pfd2);
            }
        }
        /* Periph_clk ---> SEMC Clock */
        else
        {
            freq = CLOCK_GetPeriphClkFreq();
        }
    
        freq /= (((CCM->CBCDR & CCM_CBCDR_SEMC_PODF_MASK) >> CCM_CBCDR_SEMC_PODF_SHIFT) + 1U);
    
        return freq;
    }
    
    /*!
     * brief Gets the IPG clock frequency.
     *
     * return  The IPG clock frequency value in hertz.
     */
    uint32_t CLOCK_GetIpgFreq(void)
    {
        return CLOCK_GetAhbFreq() / (((CCM->CBCDR & CCM_CBCDR_IPG_PODF_MASK) >> CCM_CBCDR_IPG_PODF_SHIFT) + 1U);
    }
    
    /*!
     * brief Gets the PER clock frequency.
     *
     * return  The PER clock frequency value in hertz.
     */
    uint32_t CLOCK_GetPerClkFreq(void)
    {
        uint32_t freq;
    
        /* Osc_clk ---> PER Clock*/
        if ((CCM->CSCMR1 & CCM_CSCMR1_PERCLK_CLK_SEL_MASK) != 0U)
        {
            freq = CLOCK_GetOscFreq();
        }
        /* Periph_clk ---> AHB Clock ---> IPG Clock ---> PER Clock */
        else
        {
            freq = CLOCK_GetIpgFreq();
        }
    
        freq /= (((CCM->CSCMR1 & CCM_CSCMR1_PERCLK_PODF_MASK) >> CCM_CSCMR1_PERCLK_PODF_SHIFT) + 1U);
    
        return freq;
    }
    
    /*!
     * brief Gets the clock frequency for a specific clock name.
     *
     * This function checks the current clock configurations and then calculates
     * the clock frequency for a specific clock name defined in clock_name_t.
     *
     * param clockName Clock names defined in clock_name_t
     * return Clock frequency value in hertz
     */
    uint32_t CLOCK_GetFreq(clock_name_t name)
    {
        uint32_t freq;
    
        switch (name)
        {
            case kCLOCK_CpuClk:
            case kCLOCK_AhbClk:
                freq = CLOCK_GetAhbFreq();
                break;
    
            case kCLOCK_SemcClk:
                freq = CLOCK_GetSemcFreq();
                break;
    
            case kCLOCK_IpgClk:
                freq = CLOCK_GetIpgFreq();
                break;
    
            case kCLOCK_PerClk:
                freq = CLOCK_GetPerClkFreq();
                break;
    
            case kCLOCK_OscClk:
                freq = CLOCK_GetOscFreq();
                break;
            case kCLOCK_RtcClk:
                freq = CLOCK_GetRtcFreq();
                break;
            case kCLOCK_ArmPllClk:
                freq = CLOCK_GetPllFreq(kCLOCK_PllArm);
                break;
            case kCLOCK_Usb1PllClk:
                freq = CLOCK_GetPllFreq(kCLOCK_PllUsb1);
                break;
            case kCLOCK_Usb1PllPfd0Clk:
                freq = CLOCK_GetUsb1PfdFreq(kCLOCK_Pfd0);
                break;
            case kCLOCK_Usb1PllPfd1Clk:
                freq = CLOCK_GetUsb1PfdFreq(kCLOCK_Pfd1);
                break;
            case kCLOCK_Usb1PllPfd2Clk:
                freq = CLOCK_GetUsb1PfdFreq(kCLOCK_Pfd2);
                break;
            case kCLOCK_Usb1PllPfd3Clk:
                freq = CLOCK_GetUsb1PfdFreq(kCLOCK_Pfd3);
                break;
            case kCLOCK_Usb1SwClk:
                freq = CLOCK_GetPllUsb1SWFreq();
                break;
            case kCLOCK_Usb1Sw120MClk:
                freq = CLOCK_GetPllUsb1SWFreq() / 4UL;
                break;
            case kCLOCK_Usb1Sw60MClk:
                freq = CLOCK_GetPllUsb1SWFreq() / 8UL;
                break;
            case kCLOCK_Usb1Sw80MClk:
                freq = CLOCK_GetPllUsb1SWFreq() / 6UL;
                break;
            case kCLOCK_Usb2PllClk:
                freq = CLOCK_GetPllFreq(kCLOCK_PllUsb2);
                break;
            case kCLOCK_SysPllClk:
                freq = CLOCK_GetPllFreq(kCLOCK_PllSys);
                break;
            case kCLOCK_SysPllPfd0Clk:
                freq = CLOCK_GetSysPfdFreq(kCLOCK_Pfd0);
                break;
            case kCLOCK_SysPllPfd1Clk:
                freq = CLOCK_GetSysPfdFreq(kCLOCK_Pfd1);
                break;
            case kCLOCK_SysPllPfd2Clk:
                freq = CLOCK_GetSysPfdFreq(kCLOCK_Pfd2);
                break;
            case kCLOCK_SysPllPfd3Clk:
                freq = CLOCK_GetSysPfdFreq(kCLOCK_Pfd3);
                break;
            case kCLOCK_EnetPll0Clk:
                freq = CLOCK_GetPllFreq(kCLOCK_PllEnet);
                break;
            case kCLOCK_EnetPll1Clk:
                freq = CLOCK_GetPllFreq(kCLOCK_PllEnet2);
                break;
            case kCLOCK_EnetPll2Clk:
                freq = CLOCK_GetPllFreq(kCLOCK_PllEnet25M);
                break;
            case kCLOCK_AudioPllClk:
                freq = CLOCK_GetPllFreq(kCLOCK_PllAudio);
                break;
            case kCLOCK_VideoPllClk:
                freq = CLOCK_GetPllFreq(kCLOCK_PllVideo);
                break;
            default:
                freq = 0U;
                break;
        }
    
        return freq;
    }
    
    /*!
     * brief Gets the frequency of selected clock root.
     *
     * param clockRoot The clock root used to get the frequency, please refer to @ref clock_root_t.
     * return The frequency of selected clock root.
     */
    uint32_t CLOCK_GetClockRootFreq(clock_root_t clockRoot)
    {
        static const clock_name_t clockRootSourceArray[][6]  = CLOCK_ROOT_SOUCE;
        static const clock_mux_t clockRootMuxTupleArray[]    = CLOCK_ROOT_MUX_TUPLE;
        static const clock_div_t clockRootDivTupleArray[][2] = CLOCK_ROOT_DIV_TUPLE;
        uint32_t freq                                        = 0UL;
        clock_mux_t clockRootMuxTuple                        = clockRootMuxTupleArray[(uint8_t)clockRoot];
        clock_div_t clockRootPreDivTuple                     = clockRootDivTupleArray[(uint8_t)clockRoot][0];
        clock_div_t clockRootPostDivTuple                    = clockRootDivTupleArray[(uint8_t)clockRoot][1];
        uint32_t clockRootMuxValue = (CCM_TUPLE_REG(CCM, clockRootMuxTuple) & CCM_TUPLE_MASK(clockRootMuxTuple)) >>
                                     CCM_TUPLE_SHIFT(clockRootMuxTuple);
        clock_name_t clockSourceName;
    
        clockSourceName = clockRootSourceArray[(uint8_t)clockRoot][clockRootMuxValue];
    
        assert(clockSourceName != kCLOCK_NoneName);
    
        freq = CLOCK_GetFreq(clockSourceName);
    
        if (clockRootPreDivTuple != kCLOCK_NonePreDiv)
        {
            freq /= ((CCM_TUPLE_REG(CCM, clockRootPreDivTuple) & CCM_TUPLE_MASK(clockRootPreDivTuple)) >>
                     CCM_TUPLE_SHIFT(clockRootPreDivTuple)) +
                    1UL;
        }
    
        freq /= ((CCM_TUPLE_REG(CCM, clockRootPostDivTuple) & CCM_TUPLE_MASK(clockRootPostDivTuple)) >>
                 CCM_TUPLE_SHIFT(clockRootPostDivTuple)) +
                1UL;
    
        return freq;
    }
    
    /*! brief Enable USB HS clock.
     *
     * This function only enables the access to USB HS prepheral, upper layer
     * should first call the ref CLOCK_EnableUsbhs0PhyPllClock to enable the PHY
     * clock to use USB HS.
     *
     * param src  USB HS does not care about the clock source, here must be ref kCLOCK_UsbSrcUnused.
     * param freq USB HS does not care about the clock source, so this parameter is ignored.
     * retval true The clock is set successfully.
     * retval false The clock source is invalid to get proper USB HS clock.
     */
    bool CLOCK_EnableUsbhs0Clock(clock_usb_src_t src, uint32_t freq)
    {
        uint32_t i;
        CCM->CCGR6 |= CCM_CCGR6_CG0_MASK;
        USB1->USBCMD |= USBHS_USBCMD_RST_MASK;
    
        /* Add a delay between RST and RS so make sure there is a DP pullup sequence*/
        for (i = 0; i < 400000U; i++)
        {
            __ASM("nop");
        }
        PMU->REG_3P0 = (PMU->REG_3P0 & (~PMU_REG_3P0_OUTPUT_TRG_MASK)) |
                       (PMU_REG_3P0_OUTPUT_TRG(0x17) | PMU_REG_3P0_ENABLE_LINREG_MASK);
        return true;
    }
    
    /*! brief Enable USB HS clock.
     *
     * This function only enables the access to USB HS prepheral, upper layer
     * should first call the ref CLOCK_EnableUsbhs0PhyPllClock to enable the PHY
     * clock to use USB HS.
     *
     * param src  USB HS does not care about the clock source, here must be ref kCLOCK_UsbSrcUnused.
     * param freq USB HS does not care about the clock source, so this parameter is ignored.
     * retval true The clock is set successfully.
     * retval false The clock source is invalid to get proper USB HS clock.
     */
    bool CLOCK_EnableUsbhs1Clock(clock_usb_src_t src, uint32_t freq)
    {
        uint32_t i = 0;
        CCM->CCGR6 |= CCM_CCGR6_CG0_MASK;
        USB2->USBCMD |= USBHS_USBCMD_RST_MASK;
    
        /* Add a delay between RST and RS so make sure there is a DP pullup sequence*/
        for (i = 0; i < 400000U; i++)
        {
            __ASM("nop");
        }
        PMU->REG_3P0 = (PMU->REG_3P0 & (~PMU_REG_3P0_OUTPUT_TRG_MASK)) |
                       (PMU_REG_3P0_OUTPUT_TRG(0x17) | PMU_REG_3P0_ENABLE_LINREG_MASK);
        return true;
    }
    
    /*! brief Enable USB HS PHY PLL clock.
     *
     * This function enables the internal 480MHz USB PHY PLL clock.
     *
     * param src  USB HS PHY PLL clock source.
     * param freq The frequency specified by src.
     * retval true The clock is set successfully.
     * retval false The clock source is invalid to get proper USB HS clock.
     */
    bool CLOCK_EnableUsbhs0PhyPllClock(clock_usb_phy_src_t src, uint32_t freq)
    {
        static const clock_usb_pll_config_t g_ccmConfigUsbPll = {.loopDivider = 0U};
        if ((CCM_ANALOG->PLL_USB1 & CCM_ANALOG_PLL_USB1_ENABLE_MASK) != 0U)
        {
            CCM_ANALOG->PLL_USB1 |= CCM_ANALOG_PLL_USB1_EN_USB_CLKS_MASK;
        }
        else
        {
            CLOCK_InitUsb1Pll(&g_ccmConfigUsbPll);
        }
        USBPHY1->CTRL &= ~USBPHY_CTRL_SFTRST_MASK; /* release PHY from reset */
        USBPHY1->CTRL &= ~USBPHY_CTRL_CLKGATE_MASK;
    
        USBPHY1->PWD = 0;
        USBPHY1->CTRL |= USBPHY_CTRL_ENAUTOCLR_PHY_PWD_MASK | USBPHY_CTRL_ENAUTOCLR_CLKGATE_MASK |
                         USBPHY_CTRL_ENUTMILEVEL2_MASK | USBPHY_CTRL_ENUTMILEVEL3_MASK;
        return true;
    }
    
    /*! brief Disable USB HS PHY PLL clock.
     *
     * This function disables USB HS PHY PLL clock.
     */
    void CLOCK_DisableUsbhs0PhyPllClock(void)
    {
        CCM_ANALOG->PLL_USB1 &= ~CCM_ANALOG_PLL_USB1_EN_USB_CLKS_MASK;
        USBPHY1->CTRL |= USBPHY_CTRL_CLKGATE_MASK; /* Set to 1U to gate clocks */
    }
    
    /*!
     * brief Initialize the ARM PLL.
     *
     * This function initialize the ARM PLL with specific settings
     *
     * param config   configuration to set to PLL.
     */
    void CLOCK_InitArmPll(const clock_arm_pll_config_t *config)
    {
        /* Bypass PLL first */
        CCM_ANALOG->PLL_ARM = (CCM_ANALOG->PLL_ARM & (~CCM_ANALOG_PLL_ARM_BYPASS_CLK_SRC_MASK)) |
                              CCM_ANALOG_PLL_ARM_BYPASS_MASK | CCM_ANALOG_PLL_ARM_BYPASS_CLK_SRC(config->src);
    
        CCM_ANALOG->PLL_ARM =
            (CCM_ANALOG->PLL_ARM & (~(CCM_ANALOG_PLL_ARM_DIV_SELECT_MASK | CCM_ANALOG_PLL_ARM_POWERDOWN_MASK))) |
            CCM_ANALOG_PLL_ARM_ENABLE_MASK | CCM_ANALOG_PLL_ARM_DIV_SELECT(config->loopDivider);
    
        while ((CCM_ANALOG->PLL_ARM & CCM_ANALOG_PLL_ARM_LOCK_MASK) == 0UL)
        {
        }
    
        /* Disable Bypass */
        CCM_ANALOG->PLL_ARM &= ~CCM_ANALOG_PLL_ARM_BYPASS_MASK;
    }
    
    /*!
     * brief De-initialize the ARM PLL.
     */
    void CLOCK_DeinitArmPll(void)
    {
        CCM_ANALOG->PLL_ARM = CCM_ANALOG_PLL_ARM_POWERDOWN_MASK;
    }
    
    /*!
     * brief Initialize the System PLL.
     *
     * This function initializes the System PLL with specific settings
     *
     * param config Configuration to set to PLL.
     */
    void CLOCK_InitSysPll(const clock_sys_pll_config_t *config)
    {
        /* Bypass PLL first */
        CCM_ANALOG->PLL_SYS = (CCM_ANALOG->PLL_SYS & (~CCM_ANALOG_PLL_SYS_BYPASS_CLK_SRC_MASK)) |
                              CCM_ANALOG_PLL_SYS_BYPASS_MASK | CCM_ANALOG_PLL_SYS_BYPASS_CLK_SRC(config->src);
    
        CCM_ANALOG->PLL_SYS =
            (CCM_ANALOG->PLL_SYS & (~(CCM_ANALOG_PLL_SYS_DIV_SELECT_MASK | CCM_ANALOG_PLL_SYS_POWERDOWN_MASK))) |
            CCM_ANALOG_PLL_SYS_ENABLE_MASK | CCM_ANALOG_PLL_SYS_DIV_SELECT(config->loopDivider);
    
        /* Initialize the fractional mode */
        CCM_ANALOG->PLL_SYS_NUM   = CCM_ANALOG_PLL_SYS_NUM_A(config->numerator);
        CCM_ANALOG->PLL_SYS_DENOM = CCM_ANALOG_PLL_SYS_DENOM_B(config->denominator);
    
        /* Initialize the spread spectrum mode */
        CCM_ANALOG->PLL_SYS_SS = CCM_ANALOG_PLL_SYS_SS_STEP(config->ss_step) |
                                 CCM_ANALOG_PLL_SYS_SS_ENABLE(config->ss_enable) |
                                 CCM_ANALOG_PLL_SYS_SS_STOP(config->ss_stop);
    
        while ((CCM_ANALOG->PLL_SYS & CCM_ANALOG_PLL_SYS_LOCK_MASK) == 0UL)
        {
        }
    
        /* Disable Bypass */
        CCM_ANALOG->PLL_SYS &= ~CCM_ANALOG_PLL_SYS_BYPASS_MASK;
    }
    
    /*!
     * brief De-initialize the System PLL.
     */
    void CLOCK_DeinitSysPll(void)
    {
        CCM_ANALOG->PLL_SYS = CCM_ANALOG_PLL_SYS_POWERDOWN_MASK;
    }
    
    /*!
     * brief Initialize the USB1 PLL.
     *
     * This function initializes the USB1 PLL with specific settings
     *
     * param config Configuration to set to PLL.
     */
    void CLOCK_InitUsb1Pll(const clock_usb_pll_config_t *config)
    {
        /* Bypass PLL first */
        CCM_ANALOG->PLL_USB1 = (CCM_ANALOG->PLL_USB1 & (~CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_MASK)) |
                               CCM_ANALOG_PLL_USB1_BYPASS_MASK | CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC(config->src);
    
        CCM_ANALOG->PLL_USB1 = (CCM_ANALOG->PLL_USB1 & (~CCM_ANALOG_PLL_USB1_DIV_SELECT_MASK)) |
                               CCM_ANALOG_PLL_USB1_ENABLE_MASK | CCM_ANALOG_PLL_USB1_POWER_MASK |
                               CCM_ANALOG_PLL_USB1_EN_USB_CLKS_MASK | CCM_ANALOG_PLL_USB1_DIV_SELECT(config->loopDivider);
    
        while ((CCM_ANALOG->PLL_USB1 & CCM_ANALOG_PLL_USB1_LOCK_MASK) == 0UL)
        {
        }
    
        /* Disable Bypass */
        CCM_ANALOG->PLL_USB1 &= ~CCM_ANALOG_PLL_USB1_BYPASS_MASK;
    }
    
    /*!
     * brief Deinitialize the USB1 PLL.
     */
    void CLOCK_DeinitUsb1Pll(void)
    {
        CCM_ANALOG->PLL_USB1 = 0U;
    }
    
    /*!
     * brief Initialize the USB2 PLL.
     *
     * This function initializes the USB2 PLL with specific settings
     *
     * param config Configuration to set to PLL.
     */
    void CLOCK_InitUsb2Pll(const clock_usb_pll_config_t *config)
    {
        /* Bypass PLL first */
        CCM_ANALOG->PLL_USB2 = (CCM_ANALOG->PLL_USB2 & (~CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_MASK)) |
                               CCM_ANALOG_PLL_USB2_BYPASS_MASK | CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC(config->src);
    
        CCM_ANALOG->PLL_USB2 = (CCM_ANALOG->PLL_USB2 & (~CCM_ANALOG_PLL_USB2_DIV_SELECT_MASK)) |
                               CCM_ANALOG_PLL_USB2_ENABLE_MASK | CCM_ANALOG_PLL_USB2_POWER_MASK |
                               CCM_ANALOG_PLL_USB2_EN_USB_CLKS_MASK | CCM_ANALOG_PLL_USB2_DIV_SELECT(config->loopDivider);
    
        while ((CCM_ANALOG->PLL_USB2 & CCM_ANALOG_PLL_USB2_LOCK_MASK) == 0UL)
        {
        }
    
        /* Disable Bypass */
        CCM_ANALOG->PLL_USB2 &= ~CCM_ANALOG_PLL_USB2_BYPASS_MASK;
    }
    
    /*!
     * brief Deinitialize the USB2 PLL.
     */
    void CLOCK_DeinitUsb2Pll(void)
    {
        CCM_ANALOG->PLL_USB2 = 0U;
    }
    
    /*!
     * brief Initializes the Audio PLL.
     *
     * This function initializes the Audio PLL with specific settings
     *
     * param config Configuration to set to PLL.
     */
    void CLOCK_InitAudioPll(const clock_audio_pll_config_t *config)
    {
        uint32_t pllAudio;
        uint32_t misc2 = 0;
    
        /* Bypass PLL first */
        CCM_ANALOG->PLL_AUDIO = (CCM_ANALOG->PLL_AUDIO & (~CCM_ANALOG_PLL_AUDIO_BYPASS_CLK_SRC_MASK)) |
                                CCM_ANALOG_PLL_AUDIO_BYPASS_MASK | CCM_ANALOG_PLL_AUDIO_BYPASS_CLK_SRC(config->src);
    
        CCM_ANALOG->PLL_AUDIO_NUM   = CCM_ANALOG_PLL_AUDIO_NUM_A(config->numerator);
        CCM_ANALOG->PLL_AUDIO_DENOM = CCM_ANALOG_PLL_AUDIO_DENOM_B(config->denominator);
    
        /*
         * Set post divider:
         *
         * ------------------------------------------------------------------------
         * | config->postDivider | PLL_AUDIO[POST_DIV_SELECT]  | MISC2[AUDIO_DIV] |
         * ------------------------------------------------------------------------
         * |         1           |            2                |        0         |
         * ------------------------------------------------------------------------
         * |         2           |            1                |        0         |
         * ------------------------------------------------------------------------
         * |         4           |            2                |        3         |
         * ------------------------------------------------------------------------
         * |         8           |            1                |        3         |
         * ------------------------------------------------------------------------
         * |         16          |            0                |        3         |
         * ------------------------------------------------------------------------
         */
        pllAudio =
            (CCM_ANALOG->PLL_AUDIO & (~(CCM_ANALOG_PLL_AUDIO_DIV_SELECT_MASK | CCM_ANALOG_PLL_AUDIO_POWERDOWN_MASK))) |
            CCM_ANALOG_PLL_AUDIO_ENABLE_MASK | CCM_ANALOG_PLL_AUDIO_DIV_SELECT(config->loopDivider);
    
        switch (config->postDivider)
        {
            case 16:
                pllAudio |= CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT(0);
                misc2 = CCM_ANALOG_MISC2_AUDIO_DIV_MSB_MASK | CCM_ANALOG_MISC2_AUDIO_DIV_LSB_MASK;
                break;
    
            case 8:
                pllAudio |= CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT(1);
                misc2 = CCM_ANALOG_MISC2_AUDIO_DIV_MSB_MASK | CCM_ANALOG_MISC2_AUDIO_DIV_LSB_MASK;
                break;
    
            case 4:
                pllAudio |= CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT(2);
                misc2 = CCM_ANALOG_MISC2_AUDIO_DIV_MSB_MASK | CCM_ANALOG_MISC2_AUDIO_DIV_LSB_MASK;
                break;
    
            case 2:
                pllAudio |= CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT(1);
                break;
    
            default:
                pllAudio |= CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT(2);
                break;
        }
    
        CCM_ANALOG->MISC2 =
            (CCM_ANALOG->MISC2 & ~(CCM_ANALOG_MISC2_AUDIO_DIV_LSB_MASK | CCM_ANALOG_MISC2_AUDIO_DIV_MSB_MASK)) | misc2;
    
        CCM_ANALOG->PLL_AUDIO = pllAudio;
    
        while ((CCM_ANALOG->PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_LOCK_MASK) == 0UL)
        {
        }
    
        /* Disable Bypass */
        CCM_ANALOG->PLL_AUDIO &= ~CCM_ANALOG_PLL_AUDIO_BYPASS_MASK;
    }
    
    /*!
     * brief De-initialize the Audio PLL.
     */
    void CLOCK_DeinitAudioPll(void)
    {
        CCM_ANALOG->PLL_AUDIO = (uint32_t)CCM_ANALOG_PLL_AUDIO_POWERDOWN_MASK;
    }
    
    /*!
     * brief Initialize the video PLL.
     *
     * This function configures the Video PLL with specific settings
     *
     * param config   configuration to set to PLL.
     */
    void CLOCK_InitVideoPll(const clock_video_pll_config_t *config)
    {
        uint32_t pllVideo;
        uint32_t misc2 = 0;
    
        /* Bypass PLL first */
        CCM_ANALOG->PLL_VIDEO = (CCM_ANALOG->PLL_VIDEO & (~CCM_ANALOG_PLL_VIDEO_BYPASS_CLK_SRC_MASK)) |
                                CCM_ANALOG_PLL_VIDEO_BYPASS_MASK | CCM_ANALOG_PLL_VIDEO_BYPASS_CLK_SRC(config->src);
    
        CCM_ANALOG->PLL_VIDEO_NUM   = CCM_ANALOG_PLL_VIDEO_NUM_A(config->numerator);
        CCM_ANALOG->PLL_VIDEO_DENOM = CCM_ANALOG_PLL_VIDEO_DENOM_B(config->denominator);
    
        /*
         * Set post divider:
         *
         * ------------------------------------------------------------------------
         * | config->postDivider | PLL_VIDEO[POST_DIV_SELECT]  | MISC2[VIDEO_DIV] |
         * ------------------------------------------------------------------------
         * |         1           |            2                |        0         |
         * ------------------------------------------------------------------------
         * |         2           |            1                |        0         |
         * ------------------------------------------------------------------------
         * |         4           |            2                |        3         |
         * ------------------------------------------------------------------------
         * |         8           |            1                |        3         |
         * ------------------------------------------------------------------------
         * |         16          |            0                |        3         |
         * ------------------------------------------------------------------------
         */
        pllVideo =
            (CCM_ANALOG->PLL_VIDEO & (~(CCM_ANALOG_PLL_VIDEO_DIV_SELECT_MASK | CCM_ANALOG_PLL_VIDEO_POWERDOWN_MASK))) |
            CCM_ANALOG_PLL_VIDEO_ENABLE_MASK | CCM_ANALOG_PLL_VIDEO_DIV_SELECT(config->loopDivider);
    
        switch (config->postDivider)
        {
            case 16:
                pllVideo |= CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT(0);
                misc2 = CCM_ANALOG_MISC2_VIDEO_DIV(3);
                break;
    
            case 8:
                pllVideo |= CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT(1);
                misc2 = CCM_ANALOG_MISC2_VIDEO_DIV(3);
                break;
    
            case 4:
                pllVideo |= CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT(2);
                misc2 = CCM_ANALOG_MISC2_VIDEO_DIV(3);
                break;
    
            case 2:
                pllVideo |= CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT(1);
                break;
    
            default:
                pllVideo |= CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT(2);
                break;
        }
    
        CCM_ANALOG->MISC2 = (CCM_ANALOG->MISC2 & ~CCM_ANALOG_MISC2_VIDEO_DIV_MASK) | misc2;
    
        CCM_ANALOG->PLL_VIDEO = pllVideo;
    
        while ((CCM_ANALOG->PLL_VIDEO & CCM_ANALOG_PLL_VIDEO_LOCK_MASK) == 0UL)
        {
        }
    
        /* Disable Bypass */
        CCM_ANALOG->PLL_VIDEO &= ~CCM_ANALOG_PLL_VIDEO_BYPASS_MASK;
    }
    
    /*!
     * brief De-initialize the Video PLL.
     */
    void CLOCK_DeinitVideoPll(void)
    {
        CCM_ANALOG->PLL_VIDEO = CCM_ANALOG_PLL_VIDEO_POWERDOWN_MASK;
    }
    
    /*!
     * brief Initialize the ENET PLL.
     *
     * This function initializes the ENET PLL with specific settings.
     *
     * param config Configuration to set to PLL.
     */
    void CLOCK_InitEnetPll(const clock_enet_pll_config_t *config)
    {
        uint32_t enet_pll = CCM_ANALOG_PLL_ENET_DIV_SELECT(config->loopDivider) |
                            CCM_ANALOG_PLL_ENET_ENET2_DIV_SELECT(config->loopDivider1);
    
        CCM_ANALOG->PLL_ENET = (CCM_ANALOG->PLL_ENET & (~CCM_ANALOG_PLL_ENET_BYPASS_CLK_SRC_MASK)) |
                               CCM_ANALOG_PLL_ENET_BYPASS_MASK | CCM_ANALOG_PLL_ENET_BYPASS_CLK_SRC(config->src);
    
        if (config->enableClkOutput)
        {
            enet_pll |= CCM_ANALOG_PLL_ENET_ENABLE_MASK;
        }
    
        if (config->enableClkOutput1)
        {
            enet_pll |= CCM_ANALOG_PLL_ENET_ENET2_REF_EN_MASK;
        }
    
        if (config->enableClkOutput25M)
        {
            enet_pll |= CCM_ANALOG_PLL_ENET_ENET_25M_REF_EN_MASK;
        }
    
        CCM_ANALOG->PLL_ENET =
            (CCM_ANALOG->PLL_ENET & (~(CCM_ANALOG_PLL_ENET_DIV_SELECT_MASK | CCM_ANALOG_PLL_ENET_ENET2_DIV_SELECT_MASK |
                                       CCM_ANALOG_PLL_ENET_POWERDOWN_MASK))) |
            enet_pll;
    
        /* Wait for stable */
        while ((CCM_ANALOG->PLL_ENET & CCM_ANALOG_PLL_ENET_LOCK_MASK) == 0UL)
        {
        }
    
        /* Disable Bypass */
        CCM_ANALOG->PLL_ENET &= ~CCM_ANALOG_PLL_ENET_BYPASS_MASK;
    }
    
    /*!
     * brief Deinitialize the ENET PLL.
     *
     * This function disables the ENET PLL.
     */
    void CLOCK_DeinitEnetPll(void)
    {
        CCM_ANALOG->PLL_ENET = CCM_ANALOG_PLL_ENET_POWERDOWN_MASK;
    }
    
    /*!
     * brief Get current PLL output frequency.
     *
     * This function get current output frequency of specific PLL
     *
     * param pll   pll name to get frequency.
     * return The PLL output frequency in hertz.
     */
    uint32_t CLOCK_GetPllFreq(clock_pll_t pll)
    {
        uint32_t freq;
        uint32_t divSelect;
        clock_64b_t freqTmp;
    
        static const uint32_t enetRefClkFreq[] = {
            25000000U,  /* 25M */
            50000000U,  /* 50M */
            100000000U, /* 100M */
            125000000U  /* 125M */
        };
    
        /* check if PLL is enabled */
        if (!CLOCK_IsPllEnabled(CCM_ANALOG, pll))
        {
            return 0U;
        }
    
        /* get pll reference clock */
        freq = CLOCK_GetPllBypassRefClk(CCM_ANALOG, pll);
    
        /* check if pll is bypassed */
        if (CLOCK_IsPllBypassed(CCM_ANALOG, pll))
        {
            return freq;
        }
    
        switch (pll)
        {
            case kCLOCK_PllArm:
                freq = ((freq * ((CCM_ANALOG->PLL_ARM & CCM_ANALOG_PLL_ARM_DIV_SELECT_MASK) >>
                                 CCM_ANALOG_PLL_ARM_DIV_SELECT_SHIFT)) >>
                        1U);
                break;
            case kCLOCK_PllSys:
                /* PLL output frequency = Fref * (DIV_SELECT + NUM/DENOM). */
                freqTmp = ((clock_64b_t)freq * ((clock_64b_t)(CCM_ANALOG->PLL_SYS_NUM)));
                freqTmp /= ((clock_64b_t)(CCM_ANALOG->PLL_SYS_DENOM));
    
                if ((CCM_ANALOG->PLL_SYS & CCM_ANALOG_PLL_SYS_DIV_SELECT_MASK) != 0U)
                {
                    freq *= 22U;
                }
                else
                {
                    freq *= 20U;
                }
    
                freq += (uint32_t)freqTmp;
                break;
    
            case kCLOCK_PllUsb1:
                freq = (freq * (((CCM_ANALOG->PLL_USB1 & CCM_ANALOG_PLL_USB1_DIV_SELECT_MASK) != 0UL) ? 22U : 20U));
                break;
    
            case kCLOCK_PllAudio:
                /* PLL output frequency = Fref * (DIV_SELECT + NUM/DENOM). */
                divSelect =
                    (CCM_ANALOG->PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_DIV_SELECT_MASK) >> CCM_ANALOG_PLL_AUDIO_DIV_SELECT_SHIFT;
    
                freqTmp = ((clock_64b_t)freq * ((clock_64b_t)(CCM_ANALOG->PLL_AUDIO_NUM)));
                freqTmp /= ((clock_64b_t)(CCM_ANALOG->PLL_AUDIO_DENOM));
    
                freq = freq * divSelect + (uint32_t)freqTmp;
    
                /* AUDIO PLL output = PLL output frequency / POSTDIV. */
    
                /*
                 * Post divider:
                 *
                 * PLL_AUDIO[POST_DIV_SELECT]:
                 * 0x00: 4
                 * 0x01: 2
                 * 0x02: 1
                 *
                 * MISC2[AUDO_DIV]:
                 * 0x00: 1
                 * 0x01: 2
                 * 0x02: 1
                 * 0x03: 4
                 */
                switch (CCM_ANALOG->PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT_MASK)
                {
                    case CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT(0U):
                        freq = freq >> 2U;
                        break;
    
                    case CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT(1U):
                        freq = freq >> 1U;
                        break;
    
                    case CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT(2U):
                        freq = freq >> 0U;
                        break;
    
                    default:
                        assert(false);
                        break;
                }
    
                switch (CCM_ANALOG->MISC2 & (CCM_ANALOG_MISC2_AUDIO_DIV_MSB_MASK | CCM_ANALOG_MISC2_AUDIO_DIV_LSB_MASK))
                {
                    case CCM_ANALOG_MISC2_AUDIO_DIV_MSB(1) | CCM_ANALOG_MISC2_AUDIO_DIV_LSB(1):
                        freq >>= 2U;
                        break;
    
                    case CCM_ANALOG_MISC2_AUDIO_DIV_MSB(0) | CCM_ANALOG_MISC2_AUDIO_DIV_LSB(1):
                        freq >>= 1U;
                        break;
    
                    case CCM_ANALOG_MISC2_AUDIO_DIV_MSB(0) | CCM_ANALOG_MISC2_AUDIO_DIV_LSB(0):
                    case CCM_ANALOG_MISC2_AUDIO_DIV_MSB(1) | CCM_ANALOG_MISC2_AUDIO_DIV_LSB(0):
                        freq >>= 0U;
                        break;
    
                    default:
                        assert(false);
                        break;
                }
                break;
    
            case kCLOCK_PllVideo:
                /* PLL output frequency = Fref * (DIV_SELECT + NUM/DENOM). */
                divSelect =
                    (CCM_ANALOG->PLL_VIDEO & CCM_ANALOG_PLL_VIDEO_DIV_SELECT_MASK) >> CCM_ANALOG_PLL_VIDEO_DIV_SELECT_SHIFT;
    
                freqTmp = ((clock_64b_t)freq * ((clock_64b_t)(CCM_ANALOG->PLL_VIDEO_NUM)));
                freqTmp /= ((clock_64b_t)(CCM_ANALOG->PLL_VIDEO_DENOM));
                freq = freq * divSelect + (uint32_t)freqTmp;
    
                /* VIDEO PLL output = PLL output frequency / POSTDIV. */
    
                /*
                 * Post divider:
                 *
                 * PLL_VIDEO[POST_DIV_SELECT]:
                 * 0x00: 4
                 * 0x01: 2
                 * 0x02: 1
                 *
                 * MISC2[VIDEO_DIV]:
                 * 0x00: 1
                 * 0x01: 2
                 * 0x02: 1
                 * 0x03: 4
                 */
                switch (CCM_ANALOG->PLL_VIDEO & CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT_MASK)
                {
                    case CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT(0U):
                        freq = freq >> 2U;
                        break;
    
                    case CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT(1U):
                        freq = freq >> 1U;
                        break;
    
                    case CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT(2U):
                        freq = freq >> 0U;
                        break;
    
                    default:
                        assert(false);
                        break;
                }
    
                switch (CCM_ANALOG->MISC2 & CCM_ANALOG_MISC2_VIDEO_DIV_MASK)
                {
                    case CCM_ANALOG_MISC2_VIDEO_DIV(3U):
                        freq >>= 2U;
                        break;
    
                    case CCM_ANALOG_MISC2_VIDEO_DIV(1U):
                        freq >>= 1U;
                        break;
    
                    case CCM_ANALOG_MISC2_VIDEO_DIV(0U):
                    case CCM_ANALOG_MISC2_VIDEO_DIV(2U):
                        freq >>= 0U;
                        break;
    
                    default:
                        assert(false);
                        break;
                }
                break;
            case kCLOCK_PllEnet:
                divSelect =
                    (CCM_ANALOG->PLL_ENET & CCM_ANALOG_PLL_ENET_DIV_SELECT_MASK) >> CCM_ANALOG_PLL_ENET_DIV_SELECT_SHIFT;
                freq = enetRefClkFreq[divSelect];
                break;
    
            case kCLOCK_PllEnet2:
                divSelect = (CCM_ANALOG->PLL_ENET & CCM_ANALOG_PLL_ENET_ENET2_DIV_SELECT_MASK) >>
                            CCM_ANALOG_PLL_ENET_ENET2_DIV_SELECT_SHIFT;
                freq = enetRefClkFreq[divSelect];
                break;
    
            case kCLOCK_PllEnet25M:
                /* ref_enetpll1 if fixed at 25MHz. */
                freq = 25000000UL;
                break;
    
            case kCLOCK_PllUsb2:
                freq = (freq * (((CCM_ANALOG->PLL_USB2 & CCM_ANALOG_PLL_USB2_DIV_SELECT_MASK) != 0U) ? 22U : 20U));
                break;
            default:
                freq = 0U;
                break;
        }
    
        return freq;
    }
    
    /*!
     * brief Initialize the System PLL PFD.
     *
     * This function initializes the System PLL PFD. During new value setting,
     * the clock output is disabled to prevent glitch.
     *
     * param pfd Which PFD clock to enable.
     * param pfdFrac The PFD FRAC value.
     * note It is recommended that PFD settings are kept between 12-35.
     */
    void CLOCK_InitSysPfd(clock_pfd_t pfd, uint8_t pfdFrac)
    {
        uint32_t pfdIndex = (uint32_t)pfd;
        uint32_t pfd528;
    
        pfd528 = CCM_ANALOG->PFD_528 &
                 ~(((uint32_t)((uint32_t)CCM_ANALOG_PFD_528_PFD0_CLKGATE_MASK | CCM_ANALOG_PFD_528_PFD0_FRAC_MASK)
                    << (8UL * pfdIndex)));
    
        /* Disable the clock output first. */
        CCM_ANALOG->PFD_528 = pfd528 | ((uint32_t)CCM_ANALOG_PFD_528_PFD0_CLKGATE_MASK << (8UL * pfdIndex));
    
        /* Set the new value and enable output. */
        CCM_ANALOG->PFD_528 = pfd528 | (CCM_ANALOG_PFD_528_PFD0_FRAC(pfdFrac) << (8UL * pfdIndex));
    }
    
    /*!
     * brief De-initialize the System PLL PFD.
     *
     * This function disables the System PLL PFD.
     *
     * param pfd Which PFD clock to disable.
     */
    void CLOCK_DeinitSysPfd(clock_pfd_t pfd)
    {
        CCM_ANALOG->PFD_528 |= (uint32_t)CCM_ANALOG_PFD_528_PFD0_CLKGATE_MASK << (8U * (uint8_t)pfd);
    }
    
    /*!
     * brief Check if Sys PFD is enabled
     *
     * param pfd PFD control name
     * return PFD bypass status.
     *         - true: power on.
     *         - false: power off.
     */
    bool CLOCK_IsSysPfdEnabled(clock_pfd_t pfd)
    {
        return ((CCM_ANALOG->PFD_528 & (uint32_t)CCM_ANALOG_PFD_528_PFD0_CLKGATE_MASK << (8UL * (uint8_t)pfd)) == 0U);
    }
    
    /*!
     * brief Initialize the USB1 PLL PFD.
     *
     * This function initializes the USB1 PLL PFD. During new value setting,
     * the clock output is disabled to prevent glitch.
     *
     * param pfd Which PFD clock to enable.
     * param pfdFrac The PFD FRAC value.
     * note It is recommended that PFD settings are kept between 12-35.
     */
    void CLOCK_InitUsb1Pfd(clock_pfd_t pfd, uint8_t pfdFrac)
    {
        uint32_t pfdIndex = (uint32_t)pfd;
        uint32_t pfd480;
    
        pfd480 = CCM_ANALOG->PFD_480 &
                 ~(((uint32_t)((uint32_t)CCM_ANALOG_PFD_480_PFD0_CLKGATE_MASK | CCM_ANALOG_PFD_480_PFD0_FRAC_MASK)
                    << (8UL * pfdIndex)));
    
        /* Disable the clock output first. */
        CCM_ANALOG->PFD_480 = pfd480 | ((uint32_t)CCM_ANALOG_PFD_480_PFD0_CLKGATE_MASK << (8UL * pfdIndex));
    
        /* Set the new value and enable output. */
        CCM_ANALOG->PFD_480 = pfd480 | (CCM_ANALOG_PFD_480_PFD0_FRAC(pfdFrac) << (8UL * pfdIndex));
    }
    
    /*!
     * brief De-initialize the USB1 PLL PFD.
     *
     * This function disables the USB1 PLL PFD.
     *
     * param pfd Which PFD clock to disable.
     */
    void CLOCK_DeinitUsb1Pfd(clock_pfd_t pfd)
    {
        CCM_ANALOG->PFD_480 |= (uint32_t)CCM_ANALOG_PFD_480_PFD0_CLKGATE_MASK << (8UL * (uint8_t)pfd);
    }
    
    /*!
     * brief Check if Usb1 PFD is enabled
     *
     * param pfd PFD control name.
     * return PFD bypass status.
     *         - true: power on.
     *         - false: power off.
     */
    bool CLOCK_IsUsb1PfdEnabled(clock_pfd_t pfd)
    {
        return ((CCM_ANALOG->PFD_480 & (uint32_t)CCM_ANALOG_PFD_480_PFD0_CLKGATE_MASK << (8UL * (uint8_t)pfd)) == 0U);
    }
    
    /*!
     * brief Get current System PLL PFD output frequency.
     *
     * This function get current output frequency of specific System PLL PFD
     *
     * param pfd   pfd name to get frequency.
     * return The PFD output frequency in hertz.
     */
    uint32_t CLOCK_GetSysPfdFreq(clock_pfd_t pfd)
    {
        uint32_t freq = CLOCK_GetPllFreq(kCLOCK_PllSys);
        uint64_t tmp64 = (uint64_t)freq * 18UL;
    
        switch (pfd)
        {
            case kCLOCK_Pfd0:
                freq = (uint32_t)(tmp64 / (uint64_t)((CCM_ANALOG->PFD_528 & CCM_ANALOG_PFD_528_PFD0_FRAC_MASK) >> CCM_ANALOG_PFD_528_PFD0_FRAC_SHIFT));
                break;
    
            case kCLOCK_Pfd1:
                freq = (uint32_t)(tmp64 / (uint64_t)((CCM_ANALOG->PFD_528 & CCM_ANALOG_PFD_528_PFD1_FRAC_MASK) >> CCM_ANALOG_PFD_528_PFD1_FRAC_SHIFT));
                break;
    
            case kCLOCK_Pfd2:
                freq = (uint32_t)(tmp64 / (uint64_t)((CCM_ANALOG->PFD_528 & CCM_ANALOG_PFD_528_PFD2_FRAC_MASK) >> CCM_ANALOG_PFD_528_PFD2_FRAC_SHIFT));
                break;
    
            case kCLOCK_Pfd3:
                freq = (uint32_t)(tmp64 / (uint64_t)((CCM_ANALOG->PFD_528 & CCM_ANALOG_PFD_528_PFD3_FRAC_MASK) >> CCM_ANALOG_PFD_528_PFD3_FRAC_SHIFT));
                break;
    
            default:
                freq = 0U;
                break;
        }
    
        return freq;
    }
    
    /*!
     * brief Get current USB1 PLL PFD output frequency.
     *
     * This function get current output frequency of specific USB1 PLL PFD
     *
     * param pfd   pfd name to get frequency.
     * return The PFD output frequency in hertz.
     */
    uint32_t CLOCK_GetUsb1PfdFreq(clock_pfd_t pfd)
    {
        uint32_t freq = CLOCK_GetPllFreq(kCLOCK_PllUsb1);
        uint64_t tmp64 = (uint64_t)freq * 18UL;
    
        switch (pfd)
        {
            case kCLOCK_Pfd0:
                freq = (uint32_t)(tmp64 / (uint64_t)((CCM_ANALOG->PFD_480 & CCM_ANALOG_PFD_480_PFD0_FRAC_MASK) >> CCM_ANALOG_PFD_480_PFD0_FRAC_SHIFT));
                break;
    
            case kCLOCK_Pfd1:
                freq = (uint32_t)(tmp64 / (uint64_t)((CCM_ANALOG->PFD_480 & CCM_ANALOG_PFD_480_PFD1_FRAC_MASK) >> CCM_ANALOG_PFD_480_PFD1_FRAC_SHIFT));
                break;
    
            case kCLOCK_Pfd2:
                freq = (uint32_t)(tmp64 / (uint64_t)((CCM_ANALOG->PFD_480 & CCM_ANALOG_PFD_480_PFD2_FRAC_MASK) >> CCM_ANALOG_PFD_480_PFD2_FRAC_SHIFT));
                break;
    
            case kCLOCK_Pfd3:
                freq = (uint32_t)(tmp64 / (uint64_t)((CCM_ANALOG->PFD_480 & CCM_ANALOG_PFD_480_PFD3_FRAC_MASK) >> CCM_ANALOG_PFD_480_PFD3_FRAC_SHIFT));
                break;
    
            default:
                freq = 0U;
                break;
        }
    
        return freq;
    }
    
    /*! brief Enable USB HS PHY PLL clock.
     *
     * This function enables the internal 480MHz USB PHY PLL clock.
     *
     * param src  USB HS PHY PLL clock source.
     * param freq The frequency specified by src.
     * retval true The clock is set successfully.
     * retval false The clock source is invalid to get proper USB HS clock.
     */
    bool CLOCK_EnableUsbhs1PhyPllClock(clock_usb_phy_src_t src, uint32_t freq)
    {
        static const clock_usb_pll_config_t g_ccmConfigUsbPll = {.loopDivider = 0U};
        CLOCK_InitUsb2Pll(&g_ccmConfigUsbPll);
        USBPHY2->CTRL &= ~USBPHY_CTRL_SFTRST_MASK; /* release PHY from reset */
        USBPHY2->CTRL &= ~USBPHY_CTRL_CLKGATE_MASK;
    
        USBPHY2->PWD = 0;
        USBPHY2->CTRL |= USBPHY_CTRL_ENAUTOCLR_PHY_PWD_MASK | USBPHY_CTRL_ENAUTOCLR_CLKGATE_MASK |
                         USBPHY_CTRL_ENUTMILEVEL2_MASK | USBPHY_CTRL_ENUTMILEVEL3_MASK;
    
        return true;
    }
    
    /*! brief Disable USB HS PHY PLL clock.
     *
     * This function disables USB HS PHY PLL clock.
     */
    void CLOCK_DisableUsbhs1PhyPllClock(void)
    {
        CCM_ANALOG->PLL_USB2 &= ~CCM_ANALOG_PLL_USB2_EN_USB_CLKS_MASK;
        USBPHY2->CTRL |= USBPHY_CTRL_CLKGATE_MASK; /* Set to 1U to gate clocks */
    }
    
    /*!
     * brief Set the clock source and the divider of the clock output1.
     *
     * param selection The clock source to be output, please refer to clock_output1_selection_t.
     * param divider The divider of the output clock signal, please refer to clock_output_divider_t.
     */
    void CLOCK_SetClockOutput1(clock_output1_selection_t selection, clock_output_divider_t divider)
    {
        uint32_t tmp32;
    
        tmp32 = CCM->CCOSR;
        if (selection == kCLOCK_DisableClockOutput1)
        {
            tmp32 &= ~CCM_CCOSR_CLKO1_EN_MASK;
        }
        else
        {
            tmp32 |= CCM_CCOSR_CLKO1_EN_MASK;
            tmp32 &= ~(CCM_CCOSR_CLKO1_SEL_MASK | CCM_CCOSR_CLKO1_DIV_MASK);
            tmp32 |= CCM_CCOSR_CLKO1_SEL(selection) | CCM_CCOSR_CLKO1_DIV(divider);
        }
        CCM->CCOSR = tmp32;
    }
    
    /*!
     * brief Set the clock source and the divider of the clock output2.
     *
     * param selection The clock source to be output, please refer to clock_output2_selection_t.
     * param divider The divider of the output clock signal, please refer to clock_output_divider_t.
     */
    void CLOCK_SetClockOutput2(clock_output2_selection_t selection, clock_output_divider_t divider)
    {
        uint32_t tmp32;
    
        tmp32 = CCM->CCOSR;
        if (selection == kCLOCK_DisableClockOutput2)
        {
            tmp32 &= CCM_CCOSR_CLKO2_EN_MASK;
        }
        else
        {
            tmp32 |= CCM_CCOSR_CLKO2_EN_MASK;
            tmp32 &= ~(CCM_CCOSR_CLKO2_SEL_MASK | CCM_CCOSR_CLKO2_DIV_MASK);
            tmp32 |= CCM_CCOSR_CLKO2_SEL(selection) | CCM_CCOSR_CLKO2_DIV(divider);
        }
    
        CCM->CCOSR = tmp32;
    }
    
    /*!
     * brief Get the frequency of clock output1 clock signal.
     *
     * return The frequency of clock output1 clock signal.
     */
    uint32_t CLOCK_GetClockOutCLKO1Freq(void)
    {
        uint32_t freq = 0U;
        uint32_t tmp32;
    
        tmp32 = CCM->CCOSR;
    
        if ((tmp32 & CCM_CCOSR_CLKO1_EN_MASK) != 0UL)
        {
            switch ((tmp32 & CCM_CCOSR_CLKO1_SEL_MASK) >> CCM_CCOSR_CLKO1_SEL_SHIFT)
            {
                case (uint32_t)kCLOCK_OutputPllUsb1:
                    freq = CLOCK_GetPllFreq(kCLOCK_PllUsb1) / 2U;
                    break;
                case (uint32_t)kCLOCK_OutputPllSys:
                    freq = CLOCK_GetPllFreq(kCLOCK_PllSys) / 2U;
                    break;
                case (uint32_t)kCLOCK_OutputPllVideo:
                    freq = CLOCK_GetPllFreq(kCLOCK_PllVideo) / 2U;
                    break;
                case (uint32_t)kCLOCK_OutputSemcClk:
                    freq = CLOCK_GetSemcFreq();
                    break;
                case (uint32_t)kCLOCK_OutputLcdifPixClk:
                    freq = CLOCK_GetClockRootFreq(kCLOCK_LcdifClkRoot);
                    break;
                case (uint32_t)kCLOCK_OutputAhbClk:
                    freq = CLOCK_GetAhbFreq();
                    break;
                case (uint32_t)kCLOCK_OutputIpgClk:
                    freq = CLOCK_GetIpgFreq();
                    break;
                case (uint32_t)kCLOCK_OutputPerClk:
                    freq = CLOCK_GetPerClkFreq();
                    break;
                case (uint32_t)kCLOCK_OutputCkilSyncClk:
                    freq = CLOCK_GetRtcFreq();
                    break;
                case (uint32_t)kCLOCK_OutputPll4MainClk:
                    freq = CLOCK_GetPllFreq(kCLOCK_PllAudio);
                    break;
                default:
                    /* This branch should never be hit. */
                    break;
            }
    
            freq /= (((tmp32 & CCM_CCOSR_CLKO1_DIV_MASK) >> CCM_CCOSR_CLKO1_DIV_SHIFT) + 1U);
        }
        else
        {
            freq = 0UL;
        }
    
        return freq;
    }
    
    /*!
     * brief Get the frequency of clock output2 clock signal.
     *
     * return The frequency of clock output2 clock signal.
     */
    uint32_t CLOCK_GetClockOutClkO2Freq(void)
    {
        uint32_t freq = 0U;
        uint32_t tmp32;
    
        tmp32 = CCM->CCOSR;
    
        if ((tmp32 & CCM_CCOSR_CLKO2_EN_MASK) != 0UL)
        {
            switch ((tmp32 & CCM_CCOSR_CLKO2_SEL_MASK) >> CCM_CCOSR_CLKO2_SEL_SHIFT)
            {
                case (uint32_t)kCLOCK_OutputUsdhc1Clk:
                    freq = CLOCK_GetClockRootFreq(kCLOCK_Usdhc1ClkRoot);
                    break;
                case (uint32_t)kCLOCK_OutputLpi2cClk:
                    freq = CLOCK_GetClockRootFreq(kCLOCK_Lpi2cClkRoot);
                    break;
                case (uint32_t)kCLOCK_OutputCsiClk:
                    freq = CLOCK_GetClockRootFreq(kCLOCK_CsiClkRoot);
                    break;
                case (uint32_t)kCLOCK_OutputOscClk:
                    freq = CLOCK_GetOscFreq();
                    break;
                case (uint32_t)kCLOCK_OutputUsdhc2Clk:
                    freq = CLOCK_GetClockRootFreq(kCLOCK_Usdhc2ClkRoot);
                    break;
                case (uint32_t)kCLOCK_OutputSai1Clk:
                    freq = CLOCK_GetClockRootFreq(kCLOCK_Sai1ClkRoot);
                    break;
                case (uint32_t)kCLOCK_OutputSai2Clk:
                    freq = CLOCK_GetClockRootFreq(kCLOCK_Sai2ClkRoot);
                    break;
                case (uint32_t)kCLOCK_OutputSai3Clk:
                    freq = CLOCK_GetClockRootFreq(kCLOCK_Sai3ClkRoot);
                    break;
                case (uint32_t)kCLOCK_OutputCanClk:
                    freq = CLOCK_GetClockRootFreq(kCLOCK_CanClkRoot);
                    break;
                case (uint32_t)kCLOCK_OutputFlexspiClk:
                    freq = CLOCK_GetClockRootFreq(kCLOCK_FlexspiClkRoot);
                    break;
                case (uint32_t)kCLOCK_OutputUartClk:
                    freq = CLOCK_GetClockRootFreq(kCLOCK_UartClkRoot);
                    break;
                case (uint32_t)kCLOCK_OutputSpdif0Clk:
                    freq = CLOCK_GetClockRootFreq(kCLOCK_SpdifClkRoot);
                    break;
                default:
                    /* This branch should never be hit. */
                    break;
            }
    
            freq /= (((tmp32 & CCM_CCOSR_CLKO2_DIV_MASK) >> CCM_CCOSR_CLKO2_DIV_SHIFT) + 1U);
        }
        else
        {
            freq = 0UL;
        }
    
        return freq;
    }
    
    

    这个文件是这个芯片的时钟驱动,这里需要配置同样的时钟驱动,以确保可以让单片机正常运行。

    [!CAUTION]

    要引用正确的头文件路径,不然代码会编译不通过,这里需要重点注意

在这里插入图片描述

2、制作其它单片机FLM下载算法

  • 写好相应的接口
  • 对时钟,flash进行初始化
  • 可以对flash进行操作,如:读、写、擦、等
  • 对接flashprg文件中的接口即可

4、验证

打开keil工程目录,将生成的FLM文件复制到下载算法的Flash目录中,然后打开任意工程编译好之后选择生成的FLM算法进行下载,经测试发现可以正常将代码下载到Flash当中。

总结

本文主要介绍了如何制作符合自己设备的FLM下载算法,文中以nxp的 I.MX RT1062 的Qspi flash作为例子,最后成功将代码通过生成的FLM下载到trget(目标单片机中)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白今天也很酷

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值