小猫爪:i.MX RT1050学习笔记15-FlexSPI-FLASH使用3-KEIL FLASH算法中的使用

小猫爪:i.MX RT1050学习笔记15-FlexSPI-FLASH使用3-KEIL FLASH算法中的使用

1 前言

在前面介绍了RT1050启动时会读取镜像的FCB数据对FlexSPI进行初始化,在下载代码进FLASH时,我们需要使用FLASH算法,那我们怎样在KEIL FLASH算法中对FlexSPI进行初始化呢,或者说怎样针对自己的FLASH设计自己的算法呢?

2 FLASH算法解析

首先去了解一下官方的源码做了什么,打开NXP RT1050的IAR FLASH算法源码(路径:C:\Keil_v5\ARM\Flash\MiMXRT105x_ATXP032)。

2.1 初始化Init

首先我们看看初始化函数,FLASH算法的FlashInit初始化函数源码如下:

int Init (unsigned long adr, unsigned long clk, unsigned long fnc) {
	// Initialize pins
	BOARD_InitPins();
	// Initialize clock
	BOARD_BootClockRUN();
	// Initialize FlexSPI controller for EcoXiP
	EcoXiP_init();
	//Unprotect all sectors in anticipation of erase/program operations
	EcoXiP_unprotect_all();
	//Switch to Octal-DDR mode
	EcoXiP_enter_octal_mode(true);
	return 0;
}

一览无余,首先对相关引脚和时钟的初始化,这些都跟FlexSPI无关,关于FlexSPI相关的初始化全部都在EcoXiP_init中,废话不多说上源码:

// Initialize FlexSPI controller for EcoXiP
void EcoXiP_init(void)
{
	uint32_t i;

   // Set flexspi source clock
    const clock_usb_pll_config_t g_ccmConfigUsbPll = {.loopDivider = 0U};

	// Reset flash
#if(FLASH_RESET_METHOD == METHOD_PIN_RESET)
	EcoXiP_reset();
#endif
#if(FLASH_RESET_METHOD == METHOD_JEDEC_RESET)
	EcoXiP_jreset();
#endif

	// Give it some time to settle down (using EcoXiP reset recovery time)
	for(i = 0; i < ECOXIP_RESET_RECOVEY_US; i++)
		delay();

	// Assign pins to FlexSPI and configure them 
	EcoXiP_config_flexspi_pins();

	// Point to FlexSPI base
	exip_flexspi = FLEXSPI;

	// Set up source clock
    CLOCK_InitUsb1Pll(&g_ccmConfigUsbPll);
	CLOCK_InitUsb1Pfd(kCLOCK_Pfd0, 480000*18/(FLEXSPI_SOURCE_CLOCK/1000)); /* Set PLL3 PFD0 clock */
    CLOCK_SetMux(kCLOCK_FlexspiMux, 0x3); /* Choose PLL3 PFD0 clock as FlexSPI source clock. */

	// Set up SCLK
	flexspi_root_clock = SCLK_FREQ;
	deviceconfig.flexspiRootClk = flexspi_root_clock;
    CLOCK_SetDiv(kCLOCK_FlexspiDiv, (FLEXSPI_SOURCE_CLOCK/flexspi_root_clock) - 1); /* Divide source clock the source/root ratio. */

    /* Get FLEXSPI default settings and configure the FlexSPI. */
    FLEXSPI_GetDefaultConfig(&flexspi_config);

	// Enable AHB prefetching
	flexspi_config.ahbConfig.enableAHBPrefetch = true;

	// To achieve high speeds - always use DQS
	flexspi_config.rxSampleClock = kFLEXSPI_ReadSampleClkExternalInputFromDqsPad;

	// Need to set the combination-enable option. This options combines 8 data lines
	// from FlexSPI channel A with 4 data lines from FlexSPI channel B to form an
	// 8-line bus for octal. On this SoC this is th eonly way to enable octal.
	flexspi_config.enableCombination = true;

    /* Init FlexSPI. */
    FLEXSPI_Init(exip_flexspi, &flexspi_config);

    /* Configure flash settings according to serial flash feature. */
    FLEXSPI_SetFlashConfig_Adesto(exip_flexspi, &deviceconfig, kFLEXSPI_PortA1);

    /* Update LUT table. */
    FLEXSPI_UpdateLUT_Adesto(exip_flexspi, 0, spi_lut, LUT_SIZE);

    /* Do software reset. */
    FLEXSPI_SoftwareReset(exip_flexspi);

	// Set current operation mode to SPI
	op_mode = SPI_MODE;

	// Initialize fields in the structure we will send to the FlexSPI driver.
	// These fields will always have the same values in our case.
	flashxfer.port = kFLEXSPI_PortA1;
	flashxfer.SeqNumber = 1;
}

可以看到其初始化过程非常的露骨,还是那么一览无余,具体过程如下:
①初始化时钟
②初始化引脚
③初始化FlexSPI外设相关的寄存器
④初始化FLASH特性相关的寄存器
⑤初始化LUT表寄存器
⑤复位FlexSPI

非常简单,再看看初始化FlexSPI外设相关的寄存器的结构体:

typedef struct _flexspi_config
{
    flexspi_read_sample_clock_t rxSampleClock; /*!< Sample Clock source selection for Flash Reading. */
    bool enableSckFreeRunning;                 /*!< Enable/disable SCK output free-running. */
    bool enableCombination;                    /*!< Enable/disable combining PORT A and B Data Pins
                                               (SIOA[3:0] and SIOB[3:0]) to support Flash Octal mode. */
    bool enableDoze;                           /*!< Enable/disable doze mode support. */
    bool enableHalfSpeedAccess;                /*!< Enable/disable divide by 2 of the clock for half
                                                speed commands. */
    bool enableSckBDiffOpt;                    /*!< Enable/disable SCKB pad use as SCKA differential clock
                                                output, when enable, Port B flash access is not available. */
    bool enableSameConfigForAll;               /*!< Enable/disable same configuration for all connected devices
                                                when enabled, same configuration in FLASHA1CRx is applied to all. */
    uint16_t seqTimeoutCycle;                  /*!< Timeout wait cycle for command sequence execution,
                                               timeout after ahbGrantTimeoutCyle*1024 serial root clock cycles. */
    uint8_t ipGrantTimeoutCycle;               /*!< Timeout wait cycle for IP command grant, timeout after
                                                ipGrantTimeoutCycle*1024 AHB clock cycles. */
    uint8_t txWatermark;                       /*!< FLEXSPI IP transmit watermark value. */
    uint8_t rxWatermark;                       /*!< FLEXSPI receive watermark value. */
    struct
    {
        bool enableAHBWriteIpTxFifo;  /*!< Enable AHB bus write access to IP TX FIFO. */
        bool enableAHBWriteIpRxFifo;  /*!< Enable AHB bus write access to IP RX FIFO. */
        uint8_t ahbGrantTimeoutCycle; /*!< Timeout wait cycle for AHB command grant,
                                       timeout after ahbGrantTimeoutCyle*1024 AHB clock cycles. */
        uint16_t ahbBusTimeoutCycle;  /*!< Timeout wait cycle for AHB read/write access,
                                      timeout after ahbBusTimeoutCycle*1024 AHB clock cycles. */
        uint8_t resumeWaitCycle;      /*!< Wait cycle for idle state before suspended command sequence
                                       resume, timeout after ahbBusTimeoutCycle AHB clock cycles. */
        flexspi_ahbBuffer_config_t buffer[FSL_FEATURE_FLEXSPI_AHB_BUFFER_COUNT]; /*!< AHB buffer size. */
        bool enableClearAHBBufferOpt; /*!< Enable/disable automatically clean AHB RX Buffer and TX Buffer
                                       when FLEXSPI returns STOP mode ACK. */
        bool enableAHBPrefetch;       /*!< Enable/disable AHB read prefetch feature, when enabled, FLEXSPI
                                       will fetch more data than current AHB burst. */
        bool enableAHBBufferable;     /*!< Enable/disable AHB bufferable write access support, when enabled,
                                       FLEXSPI return before waiting for command excution finished. */
        bool enableAHBCachable;       /*!< Enable AHB bus cachable read access support. */
    } ahbConfig;
} flexspi_config_t;

再看看初始化FLASH特性相关的寄存器的结构体:

typedef struct _flexspi_device_config
{
    uint32_t flexspiRootClk;                         /*!< FLEXSPI serial root clock. */
    bool isSck2Enabled;                              /*!< FLEXSPI use SCK2. */
    uint32_t flashSize;                              /*!< Flash size in KByte. */
    flexspi_cs_interval_cycle_unit_t CSIntervalUnit; /*!< CS interval unit, 1 or 256 cycle. */
    uint16_t CSInterval;                             /*!< CS line assert interval, mutiply CS interval unit to
                                                      get the CS line assert interval cycles. */
    uint8_t CSHoldTime;                              /*!< CS line hold time. */
    uint8_t CSSetupTime;                             /*!< CS line setup time. */
    uint8_t dataValidTime;                           /*!< Data valid time for external device. */
    uint8_t columnspace;                             /*!< Column space size. */
    bool enableWordAddress;                          /*!< If enable word address.*/
    uint8_t AWRSeqIndex;                             /*!< Sequence ID for AHB write command. */
    uint8_t AWRSeqNumber;                            /*!< Sequence number for AHB write command. */
    uint8_t ARDSeqIndex;                             /*!< Sequence ID for AHB read command. */
    uint8_t ARDSeqNumber;                            /*!< Sequence number for AHB read command. */
    flexspi_ahb_write_wait_unit_t AHBWriteWaitUnit;  /*!< AHB write wait unit. */
    uint16_t AHBWriteWaitInterval;                   /*!< AHB write wait interval, mutiply AHB write interval
                                                      unit to get the AHB write wait cycles. */
    bool enableWriteMask;                            /*!< Enable/Disable FLEXSPI drive DQS pin as write mask
                                                      when writing to external device. */
} flexspi_device_config_t;

细心的小伙伴们可能发现了,这个结构体就是官网SDK包库的,还有这个结构体与FCB信息的结构体不一样,更加细心的小伙伴又发现了这个结构体与那个结构体大多数信息都是一样的,因为FCB是给bootROM用的,所以除了包括FlexSPI初始化的信息之外还包括了其他信息,感兴趣的小伙伴可以对比一下,找找不同。在这里我就不对结构体进行详细解释了(野火教程里对这两个结构体作了一个非常详细的解释)。

2.2 写操作

同样的,我们再来分析一下在KEIL中怎样去实现FLASH的写操作。直接找到写操作:

int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) {
	int stat;

	stat = EcoXiP_page_program(adr - FLASH_BASE_ADDR, sz, buf);
	return stat;;
}

在找到EcoXiP_page_program

int32_t EcoXiP_page_program(uint32_t address, uint32_t size, uint8_t * data)
{
	uint8_t is_busy;
	int32_t stat;
	status_t flexspi_status;

	stat = EcoXiP_write_enable();
	if(stat != 0)
		return 1;

	flashxfer.cmdType = kFLEXSPI_Write;
	flashxfer.seqIndex = LUT_SEQ_INDEX_PAGE_PROG;
    flashxfer.deviceAddress = address;
	flashxfer.data = (uint32_t *) data;
	flashxfer.dataSize = size;
	flexspi_status = FLEXSPI_TransferBlocking(exip_flexspi, &flashxfer);
    if (flexspi_status != kStatus_Success)
		return 1;

	do
	{
		stat = EcoXiP_read_busy_status(&is_busy);
		if(stat != 0)
			return 1;
	}
	while(is_busy);

	return 0;
}

一览无余啊,三个步骤,一个写操作总共调用了三个函数,先发送写使能指令(EcoXiP_write_enable),再开始写数据(FLEXSPI_TransferBlocking),最后发送读指令读取状态确认擦写完成(EcoXiP_read_busy_status),在这里我们就单独拿出读状态EcoXiP_read_busy_status来做个介绍。直接上代码:

// Read EcoXiP status register 1
uint32_t EcoXiP_read_busy_status(uint8_t *busy_status)
{
	uint32_t status_reg_1;
	status_t flexspi_status;

	flashxfer.cmdType = kFLEXSPI_Read;
	flashxfer.seqIndex = LUT_SEQ_INDEX_READ_STATUS_REG_BYTE1;
    flashxfer.deviceAddress = 0;
	flashxfer.data = &status_reg_1;
	flashxfer.dataSize = 1;
	flexspi_status = FLEXSPI_TransferBlocking(exip_flexspi, &flashxfer);
    if (flexspi_status != kStatus_Success)
		return 1;

	*busy_status = (status_reg_1 & BUSY_BIT);

	return 0; 
}

非常的简单,使用了SDK库里的发送数据结构体发起了一次IP command流程操作(具体流程可以参考我之前的文章《小猫爪:i.MX RT1050学习笔记12-FlexSPI简介》)。可以看出它调用了LUT表中序列LUT_SEQ_INDEX_READ_STATUS_REG_BYTE1的指令,找到这个序列:

	     // Read Status (byte 1)
		 [4] = (QINST_CMD << 10) | (PAD_1 << 8) | (EXIP_CMD_READ_STATUS_REG_BYTE1) |
				(QINST_READ << 26) | (PAD_1 << 24) | (1 << 16),

整理一下就是:

指令序号指令名称PAD数量OPCODE描述
1QINST_CMDPAD_10x05SDR模式1线模式向FLASH发送0x05(查芯片手册0x05为读状态寄存器第一位)
2QINST_READPAD_11SDR模式1线模式读取1个字节

说到这里,细心的小伙伴又问了,不是不满8个指令的序列需要在后面加STOP指令吗?因为STOP指令对应的LUT表值就是0,而数组初始值为0,所以每条指令后面默认都会有STOP指令,不加STOP也是可以的。

说到这里,大家可能对LUT表的使用已经非常熟悉了吧(注意:对LUT表其他指令的解析在另外两篇文章中也有)。

对于KEIL的FLASH算法,我们不要忘记了还要对FlashDevice的初始化,直接上源码:

struct FlashDevice const FlashDevice  =  {
   FLASH_DRV_VERS,             // Driver Version, do not modify!
   "MIMXRT105x EcoXiP Flash",  // Device Name 
   EXTSPI,		               // Device Type
   FLASH_BASE_ADDR,            // Device Start Address
   FLASH_SIZE,                 // Device Size in Bytes
   FLASH_PAGE_SIZE,            // 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
   FLASH_SECTORE_SIZE, 0x00000000, // Sector size and start offset of this sector group
   SECTOR_END
};

FlashDevice的初始化对于KEIL来说是非常重要的,需要根据实际FLASH情况对其进行修改。

根据以上的操作,我们就能针对FLASH编写自己的KEIL FLASH算法啦。FLASH算法编写完了之后,还有两处的设置不要随意篡改:

第一个就是FLASH空间大小,第二个则是FLASH算法下载的RAM位置及大小,如下图:

在这里插入图片描述

END

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
Keil,当出现"Flash Download failed - Target DLL has been cancelled"的错误提示时,可能是由于几个原因导致的。 首先,这个错误可能是由于Keil未能正确识别烧录工具引起的。解决这个问题的方法是打开工程配置窗口,然后点击Debug选项卡,在选择使用JLink后,点击"settings"。确保SN码自动填充,并且SWDI也能够自动填充。这样表示烧录工具被正确识别。然后再次尝试下载,这样应该能够成功解决问题。 另外,这个错误也可能是由于SWD引脚被占用或者被禁用导致的。在这种情况下,可以尝试按住reset按钮,然后点击download按钮,在程序运行到SWD引脚占用之前,松开reset按钮。这样可以抢占一段时间差,在程序运行前将新的程序烧录进去,从而解决问题。 总结起来,解决"Flash Download failed - Target DLL has been cancelled"错误的方法包括: 1. 确保Keil正确识别烧录工具,可以检查SN码和SWDI的填充情况。 2. 按住reset按钮并点击download按钮,抢占时间差,解决SWD引脚被占用或禁用的问题。 希望这些解决方法能帮助你成功解决问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Error: Flash Download failed - Target DLL has been cancelled](https://blog.csdn.net/Allen_Spring/article/details/130895372)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [keil报错:No Target connected+Error: Flash Download failed - Target DLL has been cancelled四种可能](https://blog.csdn.net/weixin_42105419/article/details/131342845)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小猫爪

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

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

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

打赏作者

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

抵扣说明:

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

余额充值