01、引言
在 STM32G0B1 的 FLASH_ACR 寄存器中有个 DBG_SWEN 位,当这个位的值为 0 时,设备就禁用调试能力,无法通过调试器连接到设备,反之则启用调试能力。下图是参考手册中的相关描述:
▲ 图1. RM0444_rev5 的 3.7.1 章节
从上图中可以看出,FLASH_ACR 寄存器的默认值为 0x00040600,即 DBG_SWEN 位的默认值为 1,也就是默认情况下是允许调试访问设备的。
02、问题描述
客户使用的是 STM32G0B1 的 I2C bootloader,bootloader 版本是 0x92,在执行完"Go"命令后使用 STM32CubeProgrammer 在"hot plug"模式下连接设备,但是连接失败,提示无法找到设备。
03、问题复现与分析
使用 NUCLEO-G0B1RE 板进行测试,bootloader 版本与客户的一致都是 0x92,通过配置选项字节将设备配置为从 system memoery 自举(也就是复位后进入片内 bootloader 中执行)。下图是 STM32G0B1 的参考手册中关于 boot modes 的描述,如下图所示 :
▲ 图2. RM0444_rev5 的 2.5 章节
如上图所示,在 boot modes 中显示有两种配置可从 system memory 自举,这里我使用的配置是"nBOOT1=0x1, nBOOT_SEL=0x1, nBOOT0=0x0",下图是通过 CubeProgrammer 配置选项字节的结果:
▲ 图3. 选项字节配置
配置完从 system memory 自举之后,我们可以看到"PC"指向 0x1FFFxxxx 的地址,这里就说明 MCU 已经在 bootloader 中运行了。
▲ 图4. 配置完选项字节后的 PC 值
然后将应用程序下载到设备中(我这里下载到 0x08000000 地址处),这一步主要是保证执行"Go"命令时可以跳转到可以运行的程序中。最后通过 I2C bootloader 引脚与 bootloader 主机端连接,然后执行"Go"命令。下图是 I2C bootloader 的"Go"命令在 host 端的流程图:
▲ 图5. AN4221_rev12 Go command: host side
下图是用逻辑分析仪抓取的"Go"命令的执行流程,跳转的地址是 0x08000000,可以 看出执行流程与文档中描述的一致,"Go"命令执行成功。
▲ 图6. 逻辑分析仪抓取到的 Go 命令执行流程
"Go"命令执行成功后,接着使用 CubeProgrammer 通过 SWD 接口连接设备。在测 试时发现,在"hot plug"模式时连接失败,但是在"normal"模式并且复位模式为 "Hardware reset"时可以连接成功,但是在非"hardware reset"时不能连接成功。
▲ 图7. 连接失败时的 log
下面是几种连接方式的说明:
▲ 图8. UM2237_Rev23 的 2.1.4 章节
从上图中的介绍我们可以看出,"hot-plug"模式在连接时不进行复位,而在 normal 模式会进行复位,并且复位方式可以自由选择。而"Software reset"和"Core reset"都需要通过设置设备的寄存器进行复位,所以首先需要通过调试接口连接到设备进而才能操作设备的寄存器,而"Hardware reset"则不需要,因为它是通过 nRST 引脚进行的复位。
也就是说,测试结果显示,在执行完 "Go"命令后只有执行了硬件复位后才能通过调试 接口成功连接到设备。而在硬件复位后,设备会根据 boot modes 的配置选择自举地址, 这里的 boot modes 配置的是从 system meomry 中自举(片内 bootloader 就在 system memory 中),所以在复位后设备还是进入了 bootloader 中。也就是只有在执 行"Go"命令之后,才会出现不能通过调试接口连接到设备的情况,复位后重新回到 bootloader 后是可以的。从“前言”中的描述中我们可以知道 DBG_SWEN 的默认值为 1,所以很可能是在通 过 I2C bootloader 执行完"Go"命令后,设备的 FLASH_ACR 寄存器的 DBG_SWEN 位被 设置为 0 了,所以我们无法通过调试器进行连接。
04、验证
4.1. 查看执行完 I2C bootloader 的"Go"命令后的 DBG_SWEN 的状态
在下载到设备的应用程序中添加打印 FLASH_ACR 寄存器的 DBG_SWEN 位的操作, 用来查看执行完 I2C bootloader 的"Go"命令后的 DBG_SWEN 的状态。
int main(void)
{
......
printf("---------------------------\r\n");
/* 1-Read DBG_SWEN */
printf("Read <- DBG_SWEN: %d\r\n", !!READ_BIT(FLASH->ACR, FLASH_ACR_DBG_SWEN)); ......
}
然后将程序下载到设备中,如果我们设置的 boot modes 从 main flash 中启动的话,我们可以看到 DBG_SWEN 位的值为 1,此时是可以通过调试接口连接到设备的。如下图所示:
▲ 图9. 从 main flash 中启动时的测试结果
如果是从 bootloader 启动,然后通过 I2C bootloader 的"Go"命令跳转到应用程序中执行的话。可以看到 DBG_SWEN 位的值为 0,在复位之前无法通过调试器连接到设备,也就是出现问题时的现象。如下图所示:
▲ 图10. 通过 I2C bootloader 的"Go"命令跳转到应用程序中执行的结果
所以从这里可以看出,执行完 I2C bootloader 的"Go"命令后,DBG_SWEN 位被设置 为 0,所以此时我们无法通过调试器连接到设备。但是这个位的默认值为 1,所以我们可 以通过复位来恢复其默认值,或者通过软件进行设置都可以将其设置为 1。
4.2. 通过软件将 DBG_SWEN 置位
HAL_FLASHEx_EnableDebugger()是 HAL 库中提供的将 DBG_SWEN 置位的函数, 下面是添加的代码。
int main(void)
{
......
printf("---------------------------\r\n");
/* 1-Read DBG_SWEN */
printf("1-Read <- DBG_SWEN: %d\r\n", !!READ_BIT(FLASH->ACR, FLASH_ACR_DBG_SWEN));
/* 2-Set DBG_SWEN */
HAL_FLASHEx_EnableDebugger();
printf("2-Enable debugger.\r\n");
/* 3-Read DBG_SWEN */
printf("3-Read <- DBG_SWEN: %d\r\n", !!READ_BIT(FLASH->ACR, FLASH_ACR_DBG_SWEN));
......
}
将固件下载到设备中,并且从 bootloader 启动,然后通过 I2C bootloader 的"Go"命 令跳转到应用程序中执行。可以看到刚开始时 DBG_SWEN 位为 0,然后将 DBG_SWEN位置位,通过回读 DBG_SWEN 位的结果可以看到已经成功置位了 DBG_DWEN,此时再 通过"hot plug"模式就可以连接到设备。下面是测试结果:
▲ 图11. 测试结果
通过上面两个测试我们可以看出,在 STM32G0B1x 的 0x92 版本的片内 bootloader 中,如果通过 I2C 接口执行"Go"命令跳转到应用程序时,DBG_SWEN 位将会被清零,此 时也就无法通过调试器连接到设备。如果想要恢复设备的调试能力的话,需要在应用程序 添加设置 DBG_SWEN 位为 1 的操作或者进行复位。
05、小结
在 STM32G0B1x 的 0x92 版本的 bootloader 中,通过 I2C 接口执行完"Go"命令跳转 到应用程序时,会将设备的 DBG_SWEN 位配置为 0,也就是禁用设备的调试能力。如果 我们想要恢复调试能力的话,可以在应用程序中添加设置 DBG_SWEN 位为 1 的操作。但 是其它型号或者其它版本的 bootloader 执行"Go"命令后,不一定会有这种设置,我们应 该具体应用具体分析,详细可以参考 AN2606,AN2606 是 STM32 bootloader 的说明 文档。
来源:STM32
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。