ST官方提供了ST-LINK/V2-1调试器的原理图,PCB图及相关的固件更新工具(Stlink Utility或者ST-LINK firmware upgrade)当然也可以通过第三方的工具对调试器进行固件的升级操作(STLinkReflash)。但如果你在使用ST-LINK/V2-1的过程中不慎将原有的固件破环或者是MCU烧毁,使得ST-LINK/V2-1无法正常使用,这时候你就需要一个可用的bootloader进行修复。
- -实现一个简单的bootloader, 能够实现固件的下载及加载
经过进一个月的分析,不断尝试,自己写一个bootloader实现固件下载更新及加载也是可能的。
完整的固件包含如下这几个部分:
Flash size: 128KiB | ||||
bootloader: 15KiB | system parameter: 1KiB | firmware: 111KiB | manifest: 1KiB |
- -了解固件升级相关的通信协议
由于STLinkReflash命令在固件升级过程中,可以显示升级过程中发送及接收的数据的相关内容,并且结合OpenOCD源代码中关于ST-LINK通信相关的代码,可以很容易地猜测出固件升级的流程:
NOTE:
STLinkReflash工具在写入ST-LINK/V2-1的时候,没有去更新固件的配置信息(位于flash 0x08003c00 ~ 0x08003ffff)。这使得如果bootloader不包启配置信息(只包含flash前15KiB的数据),那么通过该工具写入的ST-LINK/V2-1固件是无法使用的(USB无法正常枚举),而必须先通过STLink Utility工具进行固件更新。
加密算法,通过分析STLinkReflash工具,我们知道固件在传送过程中,会进行加密,我们需要相关的加密算法来实现:
- - 官方原版bootloader
有了固件升级相关的通信协议及相关的参考文档,获取官方的bootloader也不是什么难事。
原以为有了ST-LINK/V2-1的固件并且知道固件的起始地址,就可以写一个简单的bootloader加载固件,可事实并非如此(加载JLink固件可以正常运行,但是对于ST-LINK/V2-1的固件,就没有那么走运了),还是需要分析调试bootloader代码,以发现其中的差异:
- - USB接口实现及其配置
ST-LINK/V2-1通过USB接口与PC机进行通信,在bootloader模式下接口的相关配置信息在linux/ubuntu下可通过lsusb命令查看:
$ lsusb -d 0483: -v Bus 001 Device 014: ID 0483:3748 STMicroelectronics ST-LINK/V2 Couldn't open device, some information will be missing Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 0 (Defined at Interface level) bDeviceSubClass 0 bDeviceProtocol 0 bMaxPacketSize0 64 idVendor 0x0483 STMicroelectronics idProduct 0x3748 ST-LINK/V2 bcdDevice 1.00 iManufacturer 1 iProduct 2 iSerial 3 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 39 bNumInterfaces 1 bConfigurationValue 1 iConfiguration 0 bmAttributes 0x80 (Bus Powered) MaxPower 100mA Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 3 bInterfaceClass 255 Vendor Specific Class bInterfaceSubClass 255 Vendor Specific Subclass bInterfaceProtocol 255 Vendor Specific Protocol iInterface 4 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x02 EP 2 OUT bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x83 EP 3 IN bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 0
有了这些相关信息,我们就可以完成USB接口配置的相关代码了。这里我们会用到EP 1 IN 与 EP 2 OUT而EP 3 IN在ST-LINK进行debug/trace的时候会用到。
- -基于ChibiOS/RT v16.1.2的实时系统实现的bootloader
1. 系统上电之后会检查固件是否完整,固件完整则跳转执行固件代码否则进入DFU模式:
/* * Application entry point. */ int __attribute__((noreturn)) main(void) { /* * System initializations. * - HAL initialization, this also initializes the configured device drivers * and performs the board-specific initializations. */ halInit(); do { uint32_t flashSize, magicValue; /* Check the firmware intergrity */ flashSize = (*(volatile uint32_t *)0x1FFFF7E0 & 0xffff) << 10; /* Magic value locate at the last 4 bytes in the flash */ magicValue = *(volatile uint32_t *)(flashSize - 4); if (magicValue != 0xa50027d3) { break; } /* Check power on reason */ if ((RCC->CSR & RCC_CSR_SFTRSTF) != 0 && BKP->DR1 != 0xfeed) { /* Software reset occurred */ break; } if (BKP->DR1 == 0xfeed) BKP->DR1 = 0x0000; /* Clear reset flag */ RCC->CSR |= RCC_CSR_RMVF; JumpToUserApp(0x08004000); } while (0); /* * System initializations. * - Kernel initialization, the main() function becomes a thread and the * RTOS is active. */ chSysInit(); // ... }
NOTE:
这里可以看到固件检查及跳转是在halInit()之后及chSysInit()之前。
2. 跳转时要重新设置线程工作模式: 在特权模式下运行并使用Main Stack:
void JumpToUserApp(uint32_t pAppAddr) {
volatile uint32_t *pMspAddr;
volatile uint32_t *pJmpAddr;
/* Get main stack address from the application vector table */
pMspAddr = (volatile uint32_t *)(pAppAddr + 0);
/* Get jump address from application vector table */
pJmpAddr = (volatile uint32_t *)(pAppAddr + 4);
/* Set stack pointer as in application's vector table */
__set_MSP(*pMspAddr);
/* Privileged and using main stack */
__set_CONTROL(0);
/* Jump to the new application */
(*(void (*)(void))*pJmpAddr)();
}
NOTE:
ChibiOS/RT初始化时,会使MCU工作在特权模式下,但使用的stack为thread stack。相关的代码在:os/common/ports/ARMCMx/compilers/GCC/crt0_v7m.s
/** * @brief Control special register initialization value. * @details The system is setup to run in privileged mode using the PSP * stack (dual stack mode). */ #if !defined(CRT0_CONTROL_INIT) || defined(__DOXYGEN__) #define CRT0_CONTROL_INIT (CONTROL_USE_PSP | \ CONTROL_MODE_PRIVILEGED) #endif // ... Reset_Handler: /* Interrupts are globally masked initially.*/ cpsid i /* PSP stack pointers initialization.*/ ldr r0, =__process_stack_end__ msr PSP, r0 #if CRT0_INIT_FPU == TRUE /* FPU FPCCR initialization.*/ movw r0, #CRT0_FPCCR_INIT & 0xFFFF movt r0, #CRT0_FPCCR_INIT >> 16 movw r1, #SCB_FPCCR & 0xFFFF movt r1, #SCB_FPCCR >> 16 str r0, [r1] dsb isb /* CPACR initialization.*/ movw r0, #CRT0_CPACR_INIT & 0xFFFF movt r0, #CRT0_CPACR_INIT >> 16 movw r1, #SCB_CPACR & 0xFFFF movt r1, #SCB_CPACR >> 16 str r0, [r1] dsb isb /* FPU FPSCR initially cleared.*/ mov r0, #0 vmsr FPSCR, r0 /* FPU FPDSCR initially cleared.*/ movw r1, #SCB_FPDSCR & 0xFFFF movt r1, #SCB_FPDSCR >> 16 str r0, [r1] /* Enforcing FPCA bit in the CONTROL register.*/ movs r0, #CRT0_CONTROL_INIT | CONTROL_FPCA #else movs r0, #CRT0_CONTROL_INIT #endif /* CONTROL register initialization as configured.*/ msr CONTROL, r0 isb // ...
- -相关源代码
这里提供部分代码供参考:
https://github.com/brobwind/chibios_bro_dbg_link_v2_1
- -预编译固件及己知问题
相关的固件可以从这里下载:2016_11_24_BRO-DBG-LINK-V2-1_BL-20161121.bin
己知问题:
- 下载ST-LINK/V2-1固件之后,无法再通过Stlink Utility,STLink firmware upgrade和STLinkReflash工具更新固件。
NOTE:
设置volume label可以通过ST-LINK firmware upgrade工具完成,label的最大长度为11字节。
ST-LINK firmware upgrade工具还可以指定固件类型:
- 相关的参考文档
- http://www.chibios.org/dokuwiki/doku.php
- http://www.st.com/zh/embedded-software/stsw-link007.html
- http://infocenter.arm.com/help/topic/com.arm.doc.dai0179b/ar01s02s06.html
Hi, 设置volume label可以通过ST-LINK firmware upgrade工具完成, 怎样设置?
1. volume name以”NUCLEO”, “EAL”, “DIS”, “NODE”开始: reserved for ST boards
2. volume name最长为11个字符,少于11个字符ST-LINK firmware upgrade工具会报错,超过会被截断:
$ java -jar ~/Downloads/stsw-link007/AllPlatforms/STLinkUpgrade.jar -volume brobwind -force_prog
libusb: warning [darwin_open] USBDeviceOpen: another process has device opened for exclusive access
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 8
at com.st.stlinkupgrade.core.e.a(SourceFile:191)
at com.st.stlinkupgrade.app.a.a(SourceFile:280)
at com.st.stlinkupgrade.app.b.a(SourceFile:76)
at com.st.stlinkupgrade.app.MainApp.main(SourceFile:16)
$ java -jar ~/Downloads/stsw-link007/AllPlatforms/STLinkUpgrade.jar -volume brobwind_com -force_prog
libusb: warning [darwin_open] USBDeviceOpen: another process has device opened for exclusive access
-volume NAME_OF_VOLUME: is limited to 11 characters and has been truncated
libusb: warning [darwin_open] USBDeviceOpen: another process has device opened for exclusive access
....................Upgrade is successful.
libusb: warning [darwin_open] USBDeviceOpen: another process has device opened for exclusive access
libusb: warning [darwin_open] USBDeviceOpen: another process has device opened for exclusive access
Version read: 2.28.16
你好 设置volume label可以通过ST-LINK firmware upgrade工具完成, 我看了你上面的回复 恕我愚钝 具体要怎么操作 可以告诉我吗 谢谢哦
== 请展开 4月11日的回复 ==
1
1
你好,请问具体来说,是怎样才可以用自己的bootloader驱动stlink的程序?是设置好USB,然后检查固件完整性后,就可以直接跳到0x8004000了?
我尝试弄一个bootloader,可以跳转到stlink或者black magic probe,这样就可以一个东西兼容STM8与CORTEX-M器件在LINUX上调试或者下载程序了。
请问可以给出USB部分的参考代码吗?
谢谢
对于使用自制bootloader加载st-link/v2-1固件,需要在bootloader的0x08000100开始的地方,写入0x15, 0x3c, 0xa5, 0x47这几个值。
gdb+openocd+ST-LINK/v2-1去调试USB相关的代码时会有性能瓶颈,但下载固件却是非常好用。
我看这几天要是有空就整一个出来。
用black magic probe调试并不需要开openocd,直接用gdb连接调试器提供的虚拟串口就可以了。就是下载程序麻烦点,需要弄个gdb的脚本
是的,black magic probe并不需要openocd,但我也没怎么用这东西。一般调试使用gdb+openocd+ST-LINK/V2-1还算稳定
对了,谢谢,我试试
还有个问题就是,升级ST-LINK的固件会比较麻烦。除非BOOTLOADER是ST-LINK兼容的
你可以试试做个ST-LINK兼容的bootloader, 加密、解密算法都在提供给你的参考代码里了。
由于ST-LINK性能不是太好,而且如果ST-LINK出现bug什么的,你也没有办法修改。
还是应该去尝试看看新的开源调试器,如BMP等
感谢。我是想用BMP来调试的,但是有些项目使用STM8,所以还是离不开ST-LINK
0x08000100这个地址应该是vector table里的,0x15, 0x3c, 0xa5, 0x47是不是一个中断服务的入口?我试了,在自己写的BOOTLOADER里,只做跳转到0x08004000,生成的bin用HEX编辑器把这四个数据写到便宜0x100处,然后把BIN写到0x08000000,还是无法启动STLINK,是不是还要做其他初始化设置?
这里提供代码给你参考(不一定能正常工作):https://github.com/brobwind/chibios_bro_dbg_link_v2_1
关于0x15, 0x3c, 0xa5, 0x47, ST-LINK/V2-1固件需要这几个值才能正常工作,ST-LINK/V2就没有这样的问题
即便是ST-LINK/V2-1固件能正常工作了,但是通过Stlink Utility工具进行升级操作的时候,还是会有问题(不能进入bootloader: 假设Stlink Utility执行升级命令的时候会重启进入bootloader或者是通过跳转进入bootloader)
非常感谢。原来这几个值是针对V2_1版本的。我因为要下载调试STM8,所以用了其他的版本,不过我也试过把那个版本在偏移0x100处的数值写到我自己的BOOTLOADER对应位置,还是无法启动ST-LINK的固件。我再研究研究。
我现在烧录了STLINK的固件,U盘显示盘符为“undifined”,我想让盘符显示自己产品的名称,能详细讲一下如何修改盘符吗?
请展开这条回复: https://www.brobwind.com/archives/1309#comment-215
我按照你的步骤,但是显示如下,怎么改?
C:\stsw-link007V2J28M18RC2\AllPlatforms>java -jar STLinkUpgrade.jar -volume Modular2_S -force_prog
Exception in thread “main” java.lang.ArrayIndexOutOfBoundsException: 10
at com.st.stlinkupgrade.core.e.a(SourceFile:186)
at com.st.stlinkupgrade.app.a.a(SourceFile:304)
at com.st.stlinkupgrade.app.b.a(SourceFile:84)
at com.st.stlinkupgrade.app.MainApp.main(SourceFile:16)
volume的长度必须大于10个字节,但程序会将其截断成11字节,也就是最长只能有11个字符
大神,你什么时候上线啊!我找您找得好辛苦啊!!
1