自己的脑袋自己保住:独立解决一次单片机开发板异常的过程。
由于时代的原因,现在的单片机课本大多是讲 μVision 4(包含于 MDK 4)的配置与运行。我们刚好要上单片机的实验课,配发的是 μVision 4.14——一个无法在 Windows 8/8.1 上运行的版本。为了尝试运行,我搜了一晚上的网页,最后终于痛下决心用 μVision 5(从配发的资料的关键字中可以知道,我们用的是 STM32F101/F103,所以我将这两个型号的包提前下载下来了),尽管在配发的资料中助教强调道:
不要用其他版本,以避免版本过高带来的问题。
(注:今天有同学不知在哪里弄到了 μVision 4.12,可以在 Windows 8/8.1 上运行。经过我配置,编译、连接、下载好像没什么大问题。我们使用的是 STM32F103 系列,4.12 在 Project Template 中并不包含此选项,不过在板子的数据库中包含,ARM/ST/STM32F1xx_EVAL
文件夹下有示例。习惯了 μVision 4 的同学可以用这个版本。)
既然上了 μVision 5,那就不要屈尊回到 μVision 4.12 上了。而且,包管理不是挺好用的么,还附带效果“对教材跟不上更新的嘲讽”。_(:з」∠)_
但是在实际使用上就出现了问题,主要是在设置界面上有差别。我们用的单片机是 STM32F103VC-T6。我之前也没试过单片机编程,不过大致了解一点整体流程,就开始尝试了。加电——加载示例工程——编译——下载!哎等一下,为什么会出错,说 No Algorithm found for: 08000000H-xxxxxxxxH
?上网搜了一下,大概是没有设置写入算法吧。遂照葫芦画瓢(我们用的是 JTAG 线,核心 Cortex M3,选的是 J-LINK/J-TRACE Cortex,传输速率设置正确、设备检测正常),设置 Debug
和 Utilities
(μVision 4 的位置有所不同,不过根据功能一致性,μVision 5 对应的位置在这里),在列表里选了一个 Device Type
为 512 KB ROM 的,标有 STM32F1 啥的。这次下载似乎很正常,但是板子没有出现预期的结果。此时正逢老师讲到重点原理,干脆找了一个同组的、装有 4.12 的同学,用那个 μVision 尝试下载。噩梦开始了。
下载结果:
Programming Failed (Cortex-M3)
Erase Failed
Flash Timeout
到了第三次之后就一直超时,连下载前的擦除(erase)都做不了了。再查资料,才发现:第一,之前选择的工程示例(Blinky
)是针对一个 ARM 架构的单片机的,但不是 STM32;第二,Device Type
不是随便选的,过大的 RAM/ROM 区域设置会造成写入异常。
好吧,换配发的工程模板(LED 点亮测试)。不过配置上好像都没什么变化,在 Programming Algorithm
处还是只有两个和 STM32 有关的设置,一个是 16 B 的,一个是 512 KB 的。STM32F103VC 配置是 256 KB ROM,48 KB RAM,明显这两个配置都不对。而且不管选择哪个,重新编译下载都提示超时。
助教发现了这个情况,问是不是设置上出了什么问题。我说应该不是(呃,小郄还对助教说“没事,他改的话他知道在做什么”,不过我毕竟是在高三前的暑假将某 Windows 的 SAM 直接删了导致后来发生化学组内部事件的家伙),助教认为是不是硬件问题,于是拿着到一个成功下载的组的计算机上尝试,依然 Flash Timeout
。此时,根本就无法重置,硬件的 RST 针脚输入(按下 Reset 键)不起作用,某个 LED 一直亮着,冷冷着看着按下又弹起的 Reset 键,连眨都不眨一下。(正常状态是 RST 则单片机全部数据引脚失效,中断执行,LED 肯定要灭,信号失去则开始初始化。)
到了这一步,助教也只好说:“你再试试吧,实在不行就换开发板。”(这个举动和物理实验中电光实验的那个实验老师很像,那个老师明明看到我的操作正确但是出不了结果,撂下一句话让我继续调——这明显是仪器问题,在前面做成功的同学走后我打乱他的仪器,3分钟调出了我这里30分钟调不出的现象。)我知道,潜台词就是“对不起,我也不知道是怎么回事,包袱还是给你吧”的意思。我还想起了这堂课开始讲过的,王教授(此次助教的导师,也是这批设备的购买者)“很严肃地说过,这些开发板不能弄坏,谁弄坏了要追究相关责任人”。没办法,自己的脑袋还是要自己来保住。
Google,百度。找到一篇帖子(转到一个 FAQ 存档),讲到如果在 Utilities
里选择 Erase Full Chip
而不是 Erase Section
(只擦除设置中的 ROM/RAM 段)的话,会造成软件设置的闪存写保护失效。于是选择菜单 Flash
->Erase
,强制擦除整个闪存。OK,现在每次能擦除了(Flash Timeout
和 Erase Failed
错误消失),剩下的是 Programming Failed (Cortex-M3)
。
这算是取得了突破性进展。同时,某次尝试设置编译目标,结果点进了项目选项中的 Device 窗口。由于这个工程是 μVision 4 的工程,所以 μVision 提示没有单片机相关的数据,需要安装 μVision 5 Legacy Support。按照界面上的提示转到网页安装,完成后发现在数据源里除了 μVision 5 的包之外,还多了 μVision 4 的单片机数据库。(所以这才叫 Legacy Support 嘛,才发现连数据源都变了,不兼容 μVision 4 了,所以要单列出一个 Legacy Support,应该是认为新时代的开发者应该放弃 μVision 4 数据库里的一些老单片机了吧。不过两个数据源……硬盘哭了。)现在能在 Device Type
里选择正确的设置了,STM32F1xx,256 KB。基地址是自动设置的,不用修改。
助教再过来看,一直提示的是 Programming Failed (Cortex-M3)
,这个他也不知道是怎么回事。我也说,既然擦除能成功,为什么会出现写入错误呢?助教再一次走了。于是我再一次单枪匹马向未知的敌人挑战。
根据网上的各种资料,Programming Failed
是一个 general error(和臭名昭著的 General Protection Fault 类似)。这不是开玩笑吗,引发这个错误的原因会有很多,我怎么知道是哪里出了问题?另外,越底层的东西越易碎玄妙(fragile and delicate),稍有不慎,满盘皆输。这就是为什么我讨厌写非常接近底层的东西——想想庞大的中断向量表(不是表本身,是其参考书),这还算好的,硬件哪怕是一根针脚就那么折了也得让人着急。
不过网上资料指出的错误分为四种:连接&连接线问题、算法分配问题、重置方式问题、锁死问题。尝试检查。
一、我们使用的是 JTAG 连接线,选项选 J-LINK 那个没错。JTAG 方式和 SW 方式都能成功连接设备(可用连接处显示可用设备)。自动测速(Auto Clk
)的时候传输频率能达到 10 MHz。这说明在连接上没问题,而且线的质量没问题,不用降低传输频率。
二、Device Type
选项没有问题。检查编译选项并重新编译,主要是两个 Start Address
(基地址)和 Size
(空间大小),都没有越界,保证在 Device Type
的详细信息的范围内。调试多组数据,都不行。
三、重置方式,帖子说如果 Auto 不行就选 System Rst Request(μVision 4),在某个帖子里讲到,对应的 μVision 5 选项是 Normal 和 Reset Pins。尝试过,还是不行。
四、锁死问题多发生在运行的程序占用了 JTAG 端口,导致 JTAG 连接失败。根据原理和教程,解决方法如下。
STM32 中是自带了BootLoader的,切换进BootLoader 中就可以了。具体的方法就是通过BOOT1、BOOT0两个跳线来选择启动模式。修改跳线将启动模式切换为上述的第二种方式:BOOT0=1,BOOT1=0。
断电,改跳线,重新上电,进入第二种方式:BOOT0=1,BOOT1=0,即STM32的BootLoader 模式。此时J-Link可以正常工作了,用J-Link 下载新的程序,下载成功。再断电,把跳线改回来第一种启动方式(BOOT0=0,BOOT1=0),重新上电,一切正常,问题解决。
问题是:第一,我能看到BOOT0针脚,看不到BOOT1针脚;第二,即使看到了,难道真的就是将BOOT0/BOOT1和裸露的 VCC 接起来吗;第三,这块板子启动后能显示自身的电压,应该是在某一段 ROM 中写入了程序的,应该不允许我用BOOT0/BOOT1法去捣鼓吧,第四,之前的操作(512 KB)设置了端口的概率很小。由于无法观察测试,这种方法就没法用了。
总之就这么折腾到了下课,别人都走了,我还在和这个问题较劲。到我收拾计算机和开发板的时候,下一堂课已经开始了,上课的是留学生,讲课的是一位操着口音(非洲原生口音?总之不是美音或英音)的黑人大叔,听课的看起来来自中东、非洲、南亚的都有。到此,在小郄的推波助澜下(“他独立调试能力很强的,让他带回去玩两天也许就好了”),助教给出的答复就是让我先试试,或许能好;实在不行打电话换开发板。(这是个啥解决方案嘛!要是我很会开挖掘机是不是就让我日铲万斤?)
回来之后继续调试,毕竟人头啊。偶然发现了这个帖子,里面提到了可能类似的情况:
调试一次之后如果停止调试,再启用调试就出现“Programming Failed!”错误
虽然里面的临时解决方案依然是BOOT0/BOOT1,但是提到了一个东西:J-Link ARM。想到在之前查找解决方案的时候曾经看到过一篇关于使用 J-Link ARM 下载编译结果的博文,想到既然 MDK 下载失败,是否能通过这款软件下载呢?(以前写 Veronica 的时候,由于对串口操作做了封装,因此专门写了一个串口调试程序发送/接受和编解码原始数据。这样的“双访问方式”思想是从各种兼具 GUI 和 CLI 的软件中学来的——例如 MySQL/phpMyAdmin、服务管理工具/sc、注册表编辑器/reg 等等。嗯后两个都是 Windows 上的,不要太计较了。)
继续照葫芦画瓢,根据所选用的单片机配置好 J-Link ARM。选择架构-选择固件/程序-设置基地址-烧录。在第二步,上面的博文用的是一个 bootloader 的例子(.bin 文件),我差点也选了一个 bootloader(ARM/STLink/stm32f10xLoader.bin
),后来仔细想想,再看了看后缀名过滤器,原来也能选编译好的程序(.hex)。再想到在 MDK 中编译时,除了设置输出代码清单、生成 MDK 用的编译结果(.axf)外,还生成了通用的程序(.hex)。至于为什么我认为 .hex 是可以写入单片机的程序,那是因为我以前在某些地方看到过用很老的软件往单片机里烧 .hex 的,而且也在老爸他们的一些写电路板的工程目录里见过大量的 .hex 文件,而且名称表明是用于执行用途的。(关于基地址,在选择了单片机型号之后,加载的程序文件左上角的文本框里就是该单片机默认的 ROM 基地址。由于只需要写一个程序,保留这个值不动就可以。)
总之选什么写的问题解决了,开始下载。战战兢兢,如临深渊,如履薄冰。按下 Auto
菜单项的那一瞬间,我也不能预测接下来会发生什么,能不能写成功。只见状态窗口开始滚动状态,第一个状态就是 Unsecuring flash
,这个操作花费了较多的时间,相比之下程序的传输是眨眼间的事。然后,就是一个 written successfully
。我按下电路板的 Reset,松手之后奇迹出现了——四个 LED 亮灭亮灭,这正是程序的内容。看来是真的成功了。我直接冲出宿舍门,大叫:“成了!”后来在 μVision 上测试,编译、下载一切正常。
后来打电话给助教,要说不用准备新的板子了。助教听了也惊讶了(毕竟一个大三的学生解决了一个研二都没辙的实际问题),不过更多的惊讶应该来自于当我说我是下午那个下载失败的学生时他说“啊,我差点忘了这事”。他问我是怎么做的,我就大致描述了一下。
事后再想想,最后的问题应该是闪存的写保护还挂在那里,要不然第一步解除保护的时候怎么会花了能感受到的时间呢。晚上再搜索的时候,新的关键词找到了一个文章拷贝,里面有几行:
问题已解决,可能是在jlink那里点了Unsecure chip,所以用ISP无法解锁了;
在RAM中运行
int main(void) { RCC_DeInit(); FLASH_Unlock(); FLASH_ReadOutProtection(DISABLE); }
问题解决了。
原理也是闪存的读写保护。
总之操作如下:
- 除了本体 MDK 5 外,还要安装 MDK 5 Legacy Support。
- 选择相应的单片机,添加对应的 Programming Algorithm。
- 必要的时候,进行全闪存擦除。
- 必要的时候,进行去读写保护。
- 可以正常下载程序了。
虽然好像就这么几步骤,不过我之前并没有真正编写、编译和下载单片机相关的代码或程序。因此此次解决这个风波,依赖的还是我的推理和知识储备。例如地址分布、.hex,同一个教室里连 C 语言都得重新给他们讲,甚至不知道二进制-十六进制转换的那些人,在不具备这些经验的情况下,即使是简单的问题也只好举手问“老师是不是点这个按钮就好了”——没错我不会开挖掘机,不过只是问老师也可以学开挖掘机,但是也只是会开挖掘机而已。更别提去独立解决一个(在正常使用条件下)不常见、难以确定来源、没有文档参考(毕竟现在的文档、教程基本上是 MDK 4)的错误了。上下级互惠关系更容易建立在这样的基础上,认知水平一致嘛。
我也许现在不会像那些研究生一样用领域软件写一些领域代码,不像一些学霸和老师和团委关系好成绩上佳(虽然这些确实是本事),不过在综合分析上还是能攻城略地的。所以当看到一些展示的时候我说“我大概知道组成了”的时候,旁边的人都认为我在装13,但是我很清楚我已经不和他们在一个层面了。
或者说,只是因为周围的人水?毕竟我也知道一些大神,面对他们我都不敢出声的。
彩蛋:Keil 的官网好像有点 bug。我记忆中的包索引地址是 http://www.keil.com/mdk5/pack/,不过访问此地址会造成过多跳转——310(net::ERR_TOO_MANY_REDIRECTS),在 www 和 www2 之间跳。