MD14 BootSwap与DisJTAG组合使用方案
-
版本:
YCT:3.2.0
MCU:YTM32B1MD141. BootSwap和 DisJTAG 原理
BootSwap 用于切换芯片下一次启动使用的 Flash Bank,DisJTAG 用于关闭 JTAG/SWD 调试连接。
CUSNVR:CUS_NVR(Customized NVR)是芯片中存放客户定制信息的非易失性 Flash 区域,断电后数据保留,也用于保存调试口禁用和、Flash 地址保护和BOOT_SWAP配置。
BootSwap:A/B面切换。 芯片把BOOT_SWAP配置保存在 CUS_NVR 中。程序调用BootSwap命令后会更改CUSNVR中的BOOT_SWAP字节段的内容**(A面是0xFF,B面是0x5A)**来实现A/B面的切换
DisJTAG:决定是否允许调试器连接。 芯片把调试禁用配置同样保存在 CUS_NVR 中。程序通过更改CUSNVR中的Disable JTAG字节段的内容**(禁用是0x5A,启用是0xFF)**来实现调试口的状态。
芯片差异说明: MD14 的 BootSwap 与 DisJTAG 配置均保存在 CUS_NVR 中,因此对该区域的擦写可能影响另一项配置。L 系列不支持 BootSwap;M 系列的其他芯片将 BootSwap 配置保存在 BOOTNVR 中,DisJTAG 配置保存在 CUSNVR 中,两项配置位于不同的存储区域,不存在因共用同一配置区域而产生的擦写影响。
下文的组合使用注意事项及处理方案仅针对 MD14 芯片。2. BootSwap 和 DisJTAG 组合使用注意事项
2.1 B→A 切换时会清除原调试配置
《YTM32B1MD1x_RM》“Embedded Flash Module (EFM) → Boot Swap Operation”说明:

即:如果 STS [BOOT_INFO] 为 1,在执行 BOOT_SWAP 命令后,CUS_NVR 将会被擦除,这意味着用户需要重新配置 Disable_JTAG/SSWD 以及地址保护(Address_Protection)。例如:当芯片所在程序位于B面且调试口的状态为禁用时,如果此时执行BootSwap命令此时CUSNVR中调试字节段上的内容0x5A(禁用调试口)会被擦除变成0xFF(启用调试口),调试口的状态就由禁用变成了启用
2.2 Disable Jtag可能误修改 Boot Swap
执行
CUSNVR写入操作之前需要执行CUSNVR擦除操作,但CUSNVR擦除以扇区为单位,代码中的FLASH_DRV_EraseNVR()会清空整个 1 KB CUS_NVR 扇区。如果擦除后只向其中的调试字段写入0x5A5A5A5A5A5A5A5A,其他原有配置就没有被保留下来。所以代码需要通过“先保存、再修改、最后写回”处理这个影响:
-
将整个 1 KB 扇区读到 RAM 中。
-
在 RAM 中只修改调试字段,保留其余内容。
-
擦除 CUS_NVR,再将修改后的整个扇区写回。
在读取、擦除和编程均成功的条件下,这个流程可以保留调用时的其他配置。
Enable_Jtag()采用相同流程,只是把调试字段改为全 1。这说明组合使用时要处理好共享区域的擦写,不能仅凭一次擦除操作推断 BootSwap 一定会被改变。3. BootSwap 和 DisJTAG 并行组合方案
程序执行流程介绍
程序主线如下:
Board_Init() → 初始化时钟、引脚、Flash 和 UART 日志 → 启用 GPIOB/GPIOC 中断 → 读取 BlockFlag(),打印当前 Bank 及 CTRL_OVRD → 进入无限循环: 根据 BlockFlag() 翻转绿灯或红灯,延时约 1 秒 → 处理 SwFlag:按当前 Bank 选择切换路径 → 处理 Jtag_log:执行共用的调试开关逻辑Board_Init()当前没有检查初始化调用的返回值。串口使用 LINFlexD2,115200、8 数据位、无校验、1 停止位。LED 每隔约 1 秒翻转一次,完整亮灭周期约为 2 秒;按键请求在主循环延时结束后处理。3.2 同一份代码如何判断当前启动面
BlockFlag()的定义为:#define BlockFlag() ((EFM->STS & EFM_STS_BOOT_INFO_MASK) >> EFM_STS_BOOT_INFO_SHIFT) #define ABANKFlag 0 #define BBANKFlag 1该宏读取
EFM->STS的BOOT_INFO位,将返回值与ABANKFlag、BBANKFlag比较。初始化、按键中断、Flash 驱动、NVR 缓冲区和调试开关函数均共用;运行时按当前状态选择 LED 和切换处理。BlockFlag() 返回值 应用状态约定 启动日志 LED 指示 SW2 切换方向 0,即 ABANKFlag当前为 A 面 code run in pflash0!PB5,绿色 LED A→B 1,即 BBANKFlag当前为 B 面 CODE RUN IN PFLASH1!PE8,红色 LED B→A 3.3 共用的按键中断与请求标志
SW2 为 PB2 上升沿中断,SW3 为 PC1 上升沿中断,Flash 操作均在主循环执行:

GPIOB_IRQHandler()检查 PTB2 标志,设置SwFlag=1并清除引脚中断标志;GPIOC_IRQHandler()对 PTC1 执行相同处理,设置Jtag_log=1。Jtag_log是调试开关请求标志,并非日志缓冲区。3.4 共用的 Disable_Jtag 与 Enable_Jtag
3.4.1 Disable_Jtag:先读取整扇区,再修改并写回
当前禁用函数如下图所示:

顺序 当前操作 作用 1 关闭全局中断 保护本次配置操作 2 写入 CUS_KEY=0x4DFF32满足受保护 NVR 操作的 Key 条件 3 从 0x10000400读取0x400字节将当前整个 1 KB 扇区读入 CUS_NVR_data4 修改数组索引 2、3 将偏移 0x08、0x0C的两个 32 位字设为0x5A5A5A5A(禁用调试口操作)5 擦除 0x10000400扇区为重新编程准备目标区域 6 从扇区起始地址编程 0x400字节将修改后的整个缓冲区写回 7 恢复全局中断 结束本次配置操作 FEATURE_EFM_NVR_ARRAY_SECTOR_SIZE=0x400,缓冲区为volatile uint32_t CUS_NVR_data[256]。逻辑修改范围是调试字段的 8 字节,实际读取和编程范围均为整个 1 KB 扇区。在读取、擦除和编程均成功的条件下,函数可以保留CUSNVR中写保护和BootSwap配置
3.4.2 Enable_Jtag:将调试字段改为全 1 后整扇区写回
当前启用函数如下:

顺序 当前操作 作用 1 关闭全局中断 保护本次配置操作 2 写入 CUS_KEY=0x4DFF32满足受保护 NVR 操作的 Key 条件 3 从 0x10000400读取0x400字节将当前整个 1 KB 扇区读入 CUS_NVR_data4 修改数组索引 2、3 将偏移 0x08、0x0C的两个 32 位字设为0xFFFFFFFF(启用调试口操作)5 擦除 0x10000400扇区为重新编程准备目标区域 6 从扇区起始地址编程 0x400字节将修改后的整个缓冲区写回 7 恢复全局中断 结束本次配置操作 在读取、擦除和编程均成功的条件下,函数可以保留CUSNVR中写保护和BootSwap配置
3.5 A 面处理:切换后直接复位,不重建调试禁用字段

SW2 请求进入主循环后,先执行SwFlag=0。当BlockFlag()==ABANKFlag时,进入 A 面分支:当前执行顺序为:
-
调用
FLASH_DRV_BootSwap(FLASH_INST),其中FLASH_INST=0。 -
若返回
STATUS_SUCCESS,立即调用SystemSoftwareReset()。 -
若返回非成功,打印
Boot swap failed, status: ...。 -
失败路径随后仍会执行 SW2 处理末尾共用的
SystemSoftwareReset()。
A 面分支没有调用
Disable_Jtag(),切换后直接请求复位。 原因是正常 A→B 场景的切换前BOOT_INFO=0,不满足手册所述“BOOT_INFO=1时正常 Boot Swap 擦除 CUS_NVR”的条件,所以没有采用 B 面同样的正常切换补偿。3.6 B 面处理:切换后按条件重建调试禁用字段,再复位
当
BlockFlag()==BBANKFlag时,进入 B 面分支:

当前执行顺序为:-
调用
FLASH_DRV_BootSwap(0)。 -
切换命令返回后,判断
EFM->CTRL_OVRD==0x02。 -
条件成立时,调用共用的
Disable_Jtag(),重新写入调试禁用配置。 -
执行 SW2 处理末尾共用的
SystemSoftwareReset()。
B 面比 A 面多了一步“切换后、复位前重建调试禁用配置”。 这是因为 B→A 的切换前
BOOT_INFO=1,正常 Boot Swap 本身就会擦除 CUS_NVR。原禁用字段一旦被清除,直接复位就不能依靠原 NVR 值保持禁用状态,因此需要在切换命令结束后重新写入。调用顺序是关键:
A 面:BlockFlag() == 0 → Boot Swap → 请求软件复位 B 面:BlockFlag() == 1 → Boot Swap(该状态下会擦除 CUS_NVR) → CTRL_OVRD == 0x02 时调用 Disable_Jtag() 读取切换后的整个扇区 修改调试禁用字段 擦除并整扇区写回 → 请求软件复位如果先写禁用值,再执行会擦除 CUS_NVR 的切换命令,刚写入的值仍可能被清除。因此,补偿必须放在切换之后、复位之前。若切换返回时
CTRL_OVRD不等于0x02,当前 B 面分支跳过补偿,直接请求复位。3.7 两条切换路径的差异汇总
对比项 A 面分支 B 面分支 入口条件 BlockFlag()==ABANKFlag,即 0BlockFlag()==BBANKFlag,即 1预期切换方向 A→B B→A 调用接口 FLASH_DRV_BootSwap(``0``)FLASH_DRV_BootSwap(0)执行BootSwap命令下的 CUS_NVR 擦除条件 不满足 BOOT_INFO=1条件满足该条件,会擦除 切换后调试禁用补偿<br> 不调用 CTRL_OVRD==0x02时调用Disable_Jtag()复位前操作 直接复位;失败时先打印错误 先判断并按条件补偿,再复位 两条路径共用请求标志、NVR 缓冲区、调试开关函数及 Flash 驱动。主要区别来自切换前的硬件 Bank 状态,以及 B→A 擦除 CUS_NVR 后增加的补偿操作。
3.7.1 补偿能恢复什么
Disable_Jtag()会保留调用时读到的其他字段,但 B 面补偿中的读取发生在 Boot Swap 之后。若切换前的地址保护等配置已经被擦除,函数无法从切换后的 NVR 恢复其原值。当前实现重建的是调试禁用字段,没有在切换前备份并恢复全部 CUS_NVR 配置。此外,补偿依据切换后的整个
CTRL_OVRD值是否恰好为0x02,没有提前保存调试策略,也没有只按 bit 1 掩码判断。3.8 SW3 触发的调试开关:A/B 面共用相同流程
SW3 处理没有使用
BlockFlag()分支,而是清除Jtag_log后,直接依据CTRL_OVRD执行相同逻辑:

条件 当前操作 CTRL_OVRD==0x00打印禁用信息,调用 Disable_Jtag(),打印 SUCCESS,然后软件复位CTRL_OVRD==0x02打印启用信息,调用 Enable_Jtag(),打印 SUCCESS,然后软件复位其他值 本次调试开关请求不执行配置操作或复位 4.测试验证
4.1 将程序烧录到 PFlash0
4.1.1 加载当前工程编译产物
将当前工程编译生成的镜像加载到 J-Flash。当前程序按逻辑地址
0x00000000链接,可将同一编译产物分别烧录到两个物理 Bank,运行时由BlockFlag()区分当前启动面。

4.1.2 擦除并烧录 PFlash0
在初始调试连接可用、Bank 映射为默认状态时,先完成芯片擦除,再把当前镜像烧录到 PFlash0。擦除和编程命令以当前 J-Flash 版本及配置为准。


4.2 将同一程序烧录到 PFlash1
4.2.1 设置地址偏移
《YTM32B1MD1x_DS》说明 PFlash 总大小为 512 KB,由两个 256 KB Block 组成;当前 SDK 定义的主 Flash 地址范围为
0x00000000~0x0007FFFF。使用同一份按零地址链接的镜像。在默认 Bank 映射下,将镜像的烧录地址整体偏移
0x00040000,使其放入 PFlash1。这里修改的是烧录位置,不是将代码重新链接到0x00040000;已处于交换映射时需先核对当前物理 Bank 对应关系。



4.2.2 烧录 PFlash1
完成 PFlash1 烧录,保留已经写入 PFlash0 的程序,避免在此步骤再次整片擦除。

4.3 重新上电与基本操作
-
BlockFlag()==0时,程序按 A 面逻辑运行,PB5 绿灯约每 1 秒翻转一次。 -
BlockFlag()==1时,同一程序按 B 面逻辑运行,PE8 红灯约每 1 秒翻转一次。 -
按 SW2 切换执行BootSwap操作,切换程序的启动面。
-
按 SW3 切换调试口的状态。
串口打印数据如下:

项目代码
-
快速上手云途开发生态
发帖前请查看
帮助改进和优化YT CONFIG TOOL,有机会抽取YTM32B1ME0 EVB哦...