如何制作符合自己设备的FLM下载算法
--------以I.MXRT1062 QSPI FLAH为例(串行qspi nor flash)
本文介绍一种基于i.mxrt1062的外挂flah的qspi nor flash下载算法FLM的一种方法,Flash 编程算法是一种用于擦除或下载应用程序到 Flash 设备的软件。设备系列包 (DFP) 通常包含预定义的 Flash 算法,用于对其中支持的设备进行编程。ARM:CMSIS 包中提供了用于创建新算法的模板。以下部分更详细地介绍了该过程。
前言
什么是FLM?FLM(FLASH Loader Middeware)即闪存加载中间件,一般来说FLM下载算法不需要我们制作,因为使用Keil MDK进行开发,我们需要安装packet软件包,而这软件包就包含了FLM下载算法。当我们在keil mdk中编译好了镜像(hex 、bin、srec、elf)文件需要下载到flash当中时,首先需要选择适合您开发板的FLM下载算法。
从上图可以看到我们可以选择的下载算法有都在列表里面了,但是我们要把程序下载进去需要选择适合自己的FLM文件,像开发STM32则直接选择就可以了,我们在安装软件包时候就已经将下载算法包含到Flash文件夹当中了。我们可以打开keil mdk的安装目录进行查看(路径为**…**/keil/arm/flash)文件夹下。
1、下载算法原理
我们使用下载工具(J-Link、ST-Link、DAP-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(目标单片机中)