发新帖本帖赏金 200.00元(功能说明)我要提问
返回列表
[MM32生态]

带你掌握通过J-Link下载SPI FLASH的4种方式

[复制链接]
12945|17
手机看帖
扫描二维码
随时随地手机跟帖
xld0932|  楼主 | 2024-3-19 11:48 | 显示全部楼层 |阅读模式
本帖最后由 xld0932 于 2024-3-19 11:50 编辑

#申请原创#   @21小跑堂

做嵌入式UI设计的小伙伴,常常因为MCU内部存储资源限制,多数都是将显示资源存放在外部存储,有TF卡,也有SPI FLASH。对TF卡来说,显示资源更新操作方便,只需要把TF卡从板子上取下来,把显示资源拷贝进来,然后再插到开发板上就可以了,但TF卡成本贵呀,而且不断的插拔对于接口来说,也是一项考验。其次就是SPI FLASH,成本倒是便宜了不少,但更新显示资源是一件头疼的事,总不能SPI FLASH中的数据每变化一次,就要把SPI FLASH芯片从板子取下来,重新烧录一遍再焊上去?就算板子上预留了烧录点位,但操作起来还是很麻烦呀……SPI FLASH的烧录工具、MCU的调试工具等等,一堆工具、软件,操作也不省事。

有没有一种工具/办法可以解决SPI FALSH遇到的这些问题呢,降低成本、提升效率?同一个工具即可以做生产工具,也可以做调试工具;即可以离线烧录,也可以在线烧录?有!其实我们经常在用的J-LINK就具备这样的功能,具体怎么来操作/实现呢?下面来给大家分享一下以下几种方式:
  • J-Flash SPI实现SPI FLASH数据更新
  • 虚拟串口实现SPI FLASH数据更新
  • 下载算法实现SPI FLASH数据更新
  • J-Flash实现SPI FLASH数据更新

1、J-Flash SPI实现SPI FLASH数据更新
J-Link作为我们嵌入开发工具的一大调试利器,20PIN的接口功能很丰富,除了我们常用的JTAG、SWD调试接口功能外,还具备SPI、QSPI接口功能;我们可以通过在安装J-Link驱动时自动安装的J-Flash SPI软件,结合SPI、QSPI接口功能实现对SPI FLASH的离线烧录。

使用J-Link的SPI、QSPI接口功能下载SPI FLASH时,要么在硬件上需要预留SPI FLASH的下载引脚,要么可以通过专门的夹子治具夹住芯片进行烧录;J-Link有了SPI FLASH下载功能之后,就可以不用专门再去买SPI FLASH离线烧录工具了,对于开发人员,一个工具多个功能/用途,真的是很方便!首先我们还是先来了解一下J-Link对于SPI、QSPI接口功能的引脚定义吧,这样方便我们连接SPI FLASH,进行烧录。

J-Link SPI接口功能定义:
SPI (1).png SPI (2).png

J-Link QSPI接口功能定义:
SPI (15).png SPI (16).png

接下来我们使用J-Flash SPI软件来对SPI FLASH进行离线烧录,具体步骤如下:
1.1.准备硬件环境,我们准备了一个SPI FLASH模块,通过上述的J-Link SPI接口功能定义,使用杜邦线将SPI FLASH模块与J-Link连接起来,需要注意的是J-Link的VTref也需要供电哦
SPI (1).jpg

1.2.打开J-Flash SPI软件,如下图所示:
SPI (3).png

1.3.点击菜单栏Target->Connect,让J-Link连接SPI FLASH,确认连接是正确无误的:
SPI (4).png

1.4.等J-Link与SPI FLASH连接成功后,在左侧会显示SPI FLASH的相关信息:
SPI (5).png

1.5.点击菜单栏File->Open data file...打开一个准备好的烧录文件,并确认起始地址,我们默认是从0地址开始烧录哦:
SPI (6).png SPI (7).png

1.6.点击菜单栏Target->Erase sectors,擦除SPI FLASH:
SPI (8).png SPI (9).png SPI (10).png SPI (11).png

1.7.点击菜单柆Target->Program & Verify,对SPI FLASH进行编程(下载数据),并对其进行校验:
SPI (12).png SPI (13).png SPI (14).png

1.8.上述是将擦除、编程、校验这些动作分开操作的,其实有更便捷的操作哦,直接点击菜单栏Targer->Auto即可。

2、虚拟串口实现SPI FLASH数据更新
J-Link硬件版本在9或更高的版本时,具有VCOM功能,我们可以J-Link Commander或者J-Link Configurator对J-Link的虚拟串口功能进行打开和关闭操作,默认是处于关闭状态的哦。需要注意的是,当使用JTAG接口功能时是不能使用VCOM功能的哈!首先我们还是先来了解一下J-Link对于VCOM接口功能的引脚定义吧……
VCOM (1).png VCOM (2).png

接下来我们通过不同的J-Link硬件版本来测试VCOM的功能:

2.1.通过J-Link Commander打开/关闭VCOM
连接J-Link后,打开J-Link Commander软件,输入“vcom enable”即为打开VCOM功能,输入“vcom disable”即为关闭VCOM功能,在操作完成后,需要重新连接J-Link,才可以正常使用VCOM功能哦
VCOM (8).png

2.2.通过J-Link Configurator打开/关闭VCOM
连接J-Link后,打开J-Link Configurator软件,此时在Connected via USB会显示当前连接的J-Link以及其状态:
VCOM (3).png

双击相应的J-Link后会弹出Configuration对话框,在Virtual COM-Port栏,通过选择Enable/Disable来打开或关闭VCOM功能,然后点击OK确认,同样也需要重新连接J-Link,才可以正常使用VCOM功能
VCOM (4).png VCOM (5).png

2.3.检查VCOM连接/识别状态
VCOM (6).png

2.4.VCOM通讯测试
通过将Rx和Tx引脚连接起来,建议回环测试,同时需要注意的是VTref引脚需要供电哦,要不然VCOM功能是不会工作的,所以为了测试我们可以将VTref与5V-Supply连接在一起进行通讯测试:
VCOM (7).png

那J-Link的虚拟串口跟我们的SPI FLASH数据更新有什么关系呢?关注我写过帖子的小伙伴,就会发现,在之前多篇分享中都用到了Xmodem通讯协议,基于串口通讯,通过上位机软件经过Xmodem协议,将数据传输到MCU,然后MCU解析Xmodem协议,提取有效的数据进行处理;所以J-Link的VCOM起到一个传输媒介的作用,通过MCU将接收到的数据写入到SPI FLASH来实现SPI FLASH数据的更新,具体的实现可以参考之前分享的帖子哦!

3、下载算法实现SPI FLASH数据更新
在我们硬件没有预留SPI FLASH烧录接口时,觉得通过引线的方式将烧录接口引出来不方便;又觉得通过MCU实现Xmodem协议太麻烦,或者说MCU的资源不够用;那通过下载算法来实现SPI FLASH的数据更新,确实是一个不错的选择!

通常提到下载算法,大家想到的就是对MCU的下载编程;其实通过MCU作为媒介,可以借助MCU的外设资源对挂载的外部其它器件进行数据传输/编程操作,比如说可以通过I2C接口实现对EEPROM的数据写入,可以通过SPI接口实现对SPI FLASH的数据写入等等。接下来我们就来讲解一下SPI FLASH下载算法实现的步骤:

3.1.我们使用MM32G0001开发板作为演示,官方的Mini-G0001开发板板载了一个8M-bit的SPI FLASH,便于我们进行验证
16.png

3.2.在实现SPI FLASH下载算法之前,我需要通过常规的应用来验证SPI的初始化、对SPI FLASH的读、写、擦除等操作,待这些功能验证OK了,然后将这些功能代码直接移植到下载算法中就可以了,此处的验证工程在后面的附件可以下载到哦

3.3.我们在Keil安装目录,或者是从CMSIS包中拷贝一份下载算法空工程,然后将刚刚验证好的SPI FLASH操作函数,根据下载算法的功能函数进行填充;需要注意的是下载算法的运行是通过Keil将相应的功能函数Load到SRAM运行的,SRAM其本身的存储空间正常来说是远远小于FLASH存储空间的,所以受SRAM存储空间大小限制,建议下载算法都通过寄存器的方式来实现!其次是对于MCU来说,有些应用是需要开户看门狗的,这是特别需要注意的地方,在下载算法中一定要对看门狗进行判断和处理,不然当SPI FLASH数据理大时,下载到中途MCU突然复位了,就会导致SPI FLASH下载失败或者异常了!!!

3.4.下载算法FlashDev.c配置
在FlashDevice结构体中,根据SPI FLASH的规格书定义了Page的大小、Sector的大小、SPI FLASH芯片存储空间大小,其次为了区分SPI FLASH和其它MCU外设,将SPI FLASH器件起始地址进行了自定义,对Device Name进行了自定义,如下所示:
struct FlashDevice const FlashDevice  =
{
    FLASH_DRV_VERS,                    // Driver Version, do not modify!
    "Mini-G0001_ZD25WQ80",             // Device Name
    EXTSPI,                            // Device Type
    0xC0000000,                        // Device Start Address
    0x00100000,                        // Device Size in Bytes (256kB)
    256,                               // 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
    0x001000, 0x000000,                // Sector Size  4kB (256 Sectors)
    SECTOR_END
};

3.5.下载算法Init函数
在Init函数中,我们根据Mini-G0001开发板硬件设计原理图,对SPI和GPIO的进行了配置
/*
*  Initialize Flash Programming Functions
*    Parameter:      adr:  Device Base Address
*                    clk:  Clock Frequency (Hz)
*                    fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)
*    Return Value:   0 - OK,  1 - Failed
*/
int Init(unsigned long adr, unsigned long clk, unsigned long fnc)
{
    /* Add your Code */
    RCC->AHBENR_b.mGPIOA  = 1;
    RCC->APB1ENR_b.mSPI1  = 1;

    SPI1->RXDNR = 0;

    SPI1->GCTL_b.MODE     = 1;         /* Master */
    SPI1->CCTL_b.LSBFE    = 0;         /* MSB */
    SPI1->CCTL_b.CPHA     = 1;
    SPI1->CCTL_b.CPOL     = 0;

    SPI1->GCTL_b.DW8_32   = 0;
    SPI1->CCTL_b.SPILEN   = 1;
    SPI1->EXTCTL_b.EXTLEN = 8;

    SPI1->SPBRG_b.SPBRG   = 2;

    SPI1->GCTL_b.NSS      = 0;

    SPI1->GCTL_b.TXEN     = 1;
    SPI1->GCTL_b.RXEN     = 1;

    /* PA15 : AF0, SPI1_NSS  */
    GPIOA->CRH_b.MODE15 = 1;
    GPIOA->CRH_b.CNF15  = 2;
    GPIOA->AFRH_b.AFR15 = 0;

    /* PA8  : AF0, SPI1_SCK  */
    GPIOA->CRH_b.MODE8  = 1;
    GPIOA->CRH_b.CNF8   = 2;
    GPIOA->AFRH_b.AFR8  = 0;

    /* PA9  : AF0, SPI1_MOSI */
    GPIOA->CRH_b.MODE9  = 1;
    GPIOA->CRH_b.CNF9   = 2;
    GPIOA->AFRH_b.AFR9  = 0;

    /* PA2  : AF3, SPI1_MISO */
    GPIOA->CRL_b.MODE2  = 0;
    GPIOA->CRL_b.CNF2   = 2;
    GPIOA->ODR_b.ODR2   = 1;
    GPIOA->AFRL_b.AFR2  = 3;

    SPI1->GCTL_b.SPIEN  = 1;

    SPI1->NSSR_b.NSS    = 1;

    // Test if IWDG is running (IWDG in HW mode)
    if ((FLASH->OBR & FLASH_OBR_WDG_SW_Msk) == 0)
    {
        IWDG->KR  = 0x5555;            // Enable write access to IWDG_PR and IWDG_RLR
        IWDG->PR  = 0x06;              // Set prescaler to 256
        IWDG->RLR = 4095;              // Set reload value to 4095
    }

    return (0);                        // Finished without Errors
}

3.6.下载算法EraseSector函数
/*
*  Erase Sector in Flash Memory
*    Parameter:      adr:  Sector Address
*    Return Value:   0 - OK,  1 - Failed
*/
int EraseSector(unsigned long adr)
{
    uint8_t Status = 0;

    IWDG->KR = 0xAAAA;                  // Reload IWDG

    /* Add your Code */
    SPI1->NSSR_b.NSS = 0;
    SPIFLASH_RxTxData_Polling(0x06);
    SPI1->NSSR_b.NSS = 1;

    SPI1->NSSR_b.NSS = 0;

    SPIFLASH_RxTxData_Polling(0x20);
    SPIFLASH_RxTxData_Polling((uint8_t)(adr >> 16) & 0x000000FF);
    SPIFLASH_RxTxData_Polling((uint8_t)(adr >>  8) & 0x000000FF);
    SPIFLASH_RxTxData_Polling((uint8_t)(adr >>  0) & 0x000000FF);

    SPI1->NSSR_b.NSS = 1;

    do
    {
        SPI1->NSSR_b.NSS = 0;

        SPIFLASH_RxTxData_Polling(0x05);
        SPIFLASH_RxTxData_Polling(0xFF);

        Status = SPIFLASH_RxTxData_Polling(0xFF);

        SPI1->NSSR_b.NSS = 1;

        IWDG->KR = 0xAAAA;              // Reload IWDG
    }
    while (Status & 0x01);

    return (0);                         // Finished without Errors
}

3.7.下载算法ProgramPage函数
/*
*  Program Page in Flash Memory
*    Parameter:      adr:  Page Start Address
*                    sz:   Page Size
*                    buf:  Page Data
*    Return Value:   0 - OK,  1 - Failed
*/
int ProgramPage(unsigned long adr, unsigned long sz, unsigned char *buf)
{
    /* Add your Code */
    uint32_t i      = 0;
    uint8_t  Status = 0;

    IWDG->KR = 0xAAAA;                  // Reload IWDG

    SPI1->NSSR_b.NSS = 0;
    SPIFLASH_RxTxData_Polling(0x06);
    SPI1->NSSR_b.NSS = 1;

    SPI1->NSSR_b.NSS = 0;

    SPIFLASH_RxTxData_Polling(0x02);
    SPIFLASH_RxTxData_Polling((uint8_t)(adr >> 16) & 0x000000FF);
    SPIFLASH_RxTxData_Polling((uint8_t)(adr >>  8) & 0x000000FF);
    SPIFLASH_RxTxData_Polling((uint8_t)(adr >>  0) & 0x000000FF);

    for (i = 0; i < sz; i++)
    {
        SPIFLASH_RxTxData_Polling(buf[i]);
    }

    SPI1->NSSR_b.NSS = 1;

    do
    {
        SPI1->NSSR_b.NSS = 0;

        SPIFLASH_RxTxData_Polling(0x05);
        SPIFLASH_RxTxData_Polling(0xFF);

        Status = SPIFLASH_RxTxData_Polling(0xFF);

        SPI1->NSSR_b.NSS = 1;

        IWDG->KR = 0xAAAA;              // Reload IWDG
    }
    while (Status & 0x01);

    return (0);                         // Finished without Errors
}

3.8.下载算法Verify函数
unsigned long Verify(unsigned long adr, unsigned long sz, unsigned char *buf)
{
    uint8_t  Data = 0;
    uint32_t i    = 0;

    IWDG->KR = 0xAAAA;                  // Reload IWDG

    SPI1->NSSR_b.NSS = 0;

    SPIFLASH_RxTxData_Polling(0x0B);
    SPIFLASH_RxTxData_Polling((uint8_t)(adr >> 16) & 0x000000FF);
    SPIFLASH_RxTxData_Polling((uint8_t)(adr >>  8) & 0x000000FF);
    SPIFLASH_RxTxData_Polling((uint8_t)(adr >>  0) & 0x000000FF);
    SPIFLASH_RxTxData_Polling(0xFF);

    for (i = 0; i < sz; i++)
    {
        Data = SPIFLASH_RxTxData_Polling(0xFF);

        if (Data != buf[i])
        {
            break;
        }
    }

    SPI1->NSSR_b.NSS = 1;

    return (adr + i);
}

这个函数的实现与否,取决于在下载时是否勾选Verify选项,如果需要勾选那就一定要实现Verify函数,否则会导致下载失败!
2.png

没有实现Verify函数,却勾选了Verify下载选项时,错误提示如下所示:
1.png

在这些下载算法函数都实现完成后,就可以通过编译来生成FLM下载算法文件了,我们将生成的下载算法文件拷贝到“C:\Keil_v5\ARM\Flash”目录下即可。接下来就是如何配置Keil的应用工程,通过这个下载算法,来更新SPI FLASH中的数据了……首先我们需要准备一下数据源,如下所示:
3.png

其中定义了这个数据源的起始存放地址,就是我们下载算法配置中的器件起始地址。对于Keil工程的配置我们有2种方式,看你们操作习惯可自行选择:
方式1:
在工程Option设置中的Target选项卡中,配置IROM2,Start为0xC0000000,Size为0x100000,这个配置与下载算法保持一致;在Linker选项卡中默认选择“Use Memory Layout from Target Dialog”:
5.png 8.png

右击spiflash_data.c配置文件选项,在Memory Assignment中将Code/Const指向IROM2即可:
6.png 7.png

方式2:
在工程Option设置中的Linker选项卡中不选择“Use Memory Layout from Target Dialog”:
10.png

在Scatter File一栏,点击Edit对.sct文件进行编辑,添加LR_IROM2段,如下所示:
11.png

3.9.在这些都配置完成后,将刚刚的下载算法添加到应用工程中,编译下载:
15.png

3.10.通过应用程序查看通过下载算法更新SPI FLASH中的数据内容:
4.png

4、J-Flash实现SPI FLASH数据更新
通过方式1结合硬件设计预留烧录接口,我们可以实现离线烧录SPI FLASH、通过方式2结合J-Link虚拟串口+程序设计,我们可以实现离线烧录SPI FLASH、通过方式3下载算法,我们可以实现在线烧录SPI FLASH;对于生产来说,都是离线烧录的方式,在线烧录只适用于开发调试阶段,更方便于开发人员。

那我们硬件上没有预留烧录接口、芯片资源也很紧张,不允许添加更多的程序,那能不能实现离线烧录呢?

当然可以,我们借助方式3的烧录算法,再结合J-Flash软件,可以实现通过MCU作为媒介,实现离线烧录SPI FLASH的功能,具体步骤如下:

首先我们需要将SPI FLASH设备及下载算法添加到J-Flash识别列表当中去,我们在“C:\Users\XXXXX\AppData\Roaming\SEGGER”文件夹中新建“JLinkDevices”文件夹,在“JLinkDevices”下再创建“ZD25WQ80”文件夹,这个文件夹就是我们Mini-G0001板载SPI FLASH的型号,接着在“ZD25WQ80”文件夹下将刚刚的下载算法文件添加进行来,然后再创建一个空的XML文件,如下所示:
12.png 13.png

在XML文件中添加如下信息,此时J-Flash添加一个自定义设备就完成啦:
14.png

4.1.我们打开J-Flash软件
FLASH (1).png

4.2.在Create New Project窗口中,点击Target device后面的点点点,选择我们刚刚添加的新设备后,勾选Flash banks中的0xC0000000一栏,如下所示:
FLASH (2).png FLASH (3).png FLASH (4).png

4.3.打开我们的测试烧录文件,起始设置默认为0xC0000000,如下所示:
FLASH (5).png FLASH (6).png

4.4.点击菜单栏Target->Connect,连接J-Link:
FLASH (7).png FLASH (8).png

4.5.点击菜单柆Target->Manual Programming->Erase Sectors,擦除SPI FLASH:
FLASH (9).png FLASH (10).png FLASH (11).png FLASH (12).png

在擦除完成后,通过程序读取SPI FLASH中的部分内容,都已经被清除成0xFF数值了:
FLASH (13).png

4.6.点击菜单柆Target->Manual Programming->Program,对SPI FLASH进行编程:
FLASH (14).png FLASH (15).png FLASH (16).png FLASH (17).png

4.7.我们通过代码读取SPI FLASH中的内容进行比较,看数据是否正确写入,我们选取了0x00005200这个偏移地址进行比对:
FLASH (20).png FLASH (19).png

5、附件
Mini-G0001原理图: Mini-G0001_SCH.PDF (1.2 MB)

使用特权

评论回复

打赏榜单

21小跑堂 打赏了 200.00 元 2024-03-19
理由:恭喜通过原创审核~期待您跟多的原创作品~

评论
21小跑堂 2024-3-19 16:57 回复TA
超级贴心,巨详细的J-Link下载SPI FLASH的4种方式。J-Link下载SPI FLASH 有困难,一篇文章教你搞定! 
sf116| | 2024-3-19 13:59 | 显示全部楼层
干货满满

使用特权

评论回复
springvirus| | 2024-3-19 17:24 | 显示全部楼层
楼主威武!!非常牛叉!!

使用特权

评论回复
xld0932|  楼主 | 2024-3-19 17:57 | 显示全部楼层

使用特权

评论回复
简单happy| | 2024-3-19 21:58 | 显示全部楼层
收藏了,明天继续学习

使用特权

评论回复
[鑫森淼焱垚]| | 2024-3-20 09:31 | 显示全部楼层
干货满满,谢谢楼主

使用特权

评论回复
00750| | 2024-3-20 10:34 | 显示全部楼层
这个实用

使用特权

评论回复
zjsx8192| | 2024-3-20 19:11 | 显示全部楼层
jlink flash标记

使用特权

评论回复
zhuhaiyang| | 2024-3-21 16:22 | 显示全部楼层
干货满满,

使用特权

评论回复
comeeeg| | 2024-3-23 17:03 | 显示全部楼层
哥你牛B啊!B得真牛~服了

使用特权

评论回复
AProgrammer| | 2024-3-25 13:51 | 显示全部楼层
太牛了!

使用特权

评论回复
chenqianqian| | 2024-3-25 21:29 | 显示全部楼层
JLINK的SPI接口是怎么实现的?

使用特权

评论回复
WoodData| | 2024-3-26 11:13 | 显示全部楼层
非常实用啊

使用特权

评论回复
lanjackg2003| | 2024-4-1 09:30 | 显示全部楼层
可以可以

使用特权

评论回复
三色马| | 2024-4-9 10:36 | 显示全部楼层
楼主厉害

使用特权

评论回复
三色马| | 2024-4-9 10:37 | 显示全部楼层
楼主厉害

使用特权

评论回复
chenqianqian| | 2024-4-9 20:16 | 显示全部楼层
这个是JLINK的标准功能,只是很少这样用。

使用特权

评论回复
发新帖 本帖赏金 200.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:上海灵动微电子股份有限公司资深现场应用工程师
简介:诚信·承诺·创新·合作

67

主题

2992

帖子

29

粉丝