版本:
YCT:3.2.0
MCU:YTM32B1MD14
1. 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”说明:
d05af363-c330-412d-a58e-b880e855f1b1-image.png
即:如果 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 操作均在主循环执行:
8c0889d8-cfc0-4635-9f1b-3bf131a2ec17-image.png
GPIOB_IRQHandler() 检查 PTB2 标志,设置 SwFlag=1 并清除引脚中断标志;GPIOC_IRQHandler() 对 PTC1 执行相同处理,设置 Jtag_log=1。Jtag_log 是调试开关请求标志,并非日志缓冲区。
3.4 共用的 Disable_Jtag 与 Enable_Jtag
3.4.1 Disable_Jtag:先读取整扇区,再修改并写回
当前禁用函数如下图所示:
021a06cd-c991-4927-acfa-ab507146d5ee-image.png
顺序
当前操作
作用
1
关闭全局中断
保护本次配置操作
2
写入 CUS_KEY=0x4DFF32
满足受保护 NVR 操作的 Key 条件
3
从 0x10000400 读取 0x400 字节
将当前整个 1 KB 扇区读入 CUS_NVR_data
4
修改数组索引 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 后整扇区写回
当前启用函数如下:
d4f58b9f-6df4-4bb9-be2a-6f6990abcb06-image.png
顺序
当前操作
作用
1
关闭全局中断
保护本次配置操作
2
写入 CUS_KEY=0x4DFF32
满足受保护 NVR 操作的 Key 条件
3
从 0x10000400 读取 0x400 字节
将当前整个 1 KB 扇区读入 CUS_NVR_data
4
修改数组索引 2、3
将偏移 0x08、0x0C 的两个 32 位字设为 0xFFFFFFFF(启用调试口操作)
5
擦除 0x10000400 扇区
为重新编程准备目标区域
6
从扇区起始地址编程 0x400 字节
将修改后的整个缓冲区写回
7
恢复全局中断
结束本次配置操作
在读取、擦除和编程均成功的条件下,函数可以保留CUSNVR中写保护和BootSwap配置
3.5 A 面处理:切换后直接复位,不重建调试禁用字段
435507b6-5c22-40b2-9146-eff17afa813b-image.png
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 面分支:
cb053016-e145-4553-b22e-b2c546435f53-image.png
当前执行顺序为:
调用 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,即 0
BlockFlag()==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 执行相同逻辑:
11a0f5a9-6418-44b2-9f85-1ba7d65e4b47-image.png
条件
当前操作
CTRL_OVRD==0x00
打印禁用信息,调用 Disable_Jtag(),打印 SUCCESS,然后软件复位
CTRL_OVRD==0x02
打印启用信息,调用 Enable_Jtag(),打印 SUCCESS,然后软件复位
其他值
本次调试开关请求不执行配置操作或复位
4.测试验证
4.1 将程序烧录到 PFlash0
4.1.1 加载当前工程编译产物
将当前工程编译生成的镜像加载到 J-Flash。当前程序按逻辑地址 0x00000000 链接,可将同一编译产物分别烧录到两个物理 Bank,运行时由 BlockFlag() 区分当前启动面。
8167a51b-8439-4786-85bf-6685e53c9b87-image.png
4.1.2 擦除并烧录 PFlash0
在初始调试连接可用、Bank 映射为默认状态时,先完成芯片擦除,再把当前镜像烧录到 PFlash0。擦除和编程命令以当前 J-Flash 版本及配置为准。
986c1f07-a7e6-4696-8b3a-4917c4c1b432-image.png
e18a293c-3979-4c46-bb22-776367b9dc5f-image.png
4.2 将同一程序烧录到 PFlash1
4.2.1 设置地址偏移
《YTM32B1MD1x_DS》说明 PFlash 总大小为 512 KB,由两个 256 KB Block 组成;当前 SDK 定义的主 Flash 地址范围为 0x00000000~0x0007FFFF。
使用同一份按零地址链接的镜像。在默认 Bank 映射下,将镜像的烧录地址整体偏移 0x00040000,使其放入 PFlash1。这里修改的是烧录位置,不是将代码重新链接到 0x00040000;已处于交换映射时需先核对当前物理 Bank 对应关系。
467557d4-1472-42e7-8465-87d857d3d08d-image.png
e8bf99e9-673d-487c-9bc8-d34fb74ade71-image.png
3a069f19-3290-4126-8743-b4a3bc04c1aa-image.png
4.2.2 烧录 PFlash1
完成 PFlash1 烧录,保留已经写入 PFlash0 的程序,避免在此步骤再次整片擦除。
f41d13d2-8a3d-4f7f-9d67-e50c5db3a796-image.png
4.3 重新上电与基本操作
BlockFlag()==0 时,程序按 A 面逻辑运行,PB5 绿灯约每 1 秒翻转一次。
BlockFlag()==1 时,同一程序按 B 面逻辑运行,PE8 红灯约每 1 秒翻转一次。
按 SW2 切换执行BootSwap操作,切换程序的启动面。
按 SW3 切换调试口的状态。
串口打印数据如下:
2230ae93-589b-4c89-936e-543dd7f47dc3-image.png
项目代码
project.zip