通常嵌入式开发中,调试用的都是基于搭载了xx芯片的模组搞得开发板(后续简称MCU),给它烧录固件一般都是在电脑上(后续简称PC)使用串口进行下载烧写。所以通常的开发调试步骤都是,先打开一个串口工具看看调试信息,当需要下载固件时,就关闭这个串口工具,然后使用下载工具打开这个串口进行固件下载烧写,当固件烧写完成之后,再关闭固件下载工具,再次打开串口工具查看调试信息。 ---> 这样我们不难看出,研发人员需要不停的切换串口工具,虽然操作不难,但是步骤重复繁琐。
有次我在对接客户程序时,发现客户提供了一个使用调试器烧写固件的工具,用它下载固件就不用进行串口工具切换,感觉十分的方便。后来发现RT-Thread提供的studio集成环境也是提供了类似的工具(如给stm32下载固件时点一下下载就烧写进去了),感觉这好傻瓜化啊,真特么好用。
于是我就对这种烧写的技术原理产生了探究的冲动。我以前也接触过keil、Iar等工具,我知道像这种用调试进行烧写时需要先编写flash烧写驱动,所以各家拿调试器下载固件的套路应该基本上都是一样的。起先我在linux时使用gdb调试程序,后来切换到嵌入式后发现使用调试器(jtag)也可以使用gdb调试,于是我瞬间就明白了这种烧写套路的原理。
先讲讲这种烧写模型:
这里面pc1和pc2可能是同一台电脑,也有可能是两台不同的电脑(常见的pc1是自己电脑,pc2是编译服i务器);
mcu和pc的连接通常就是使用调试器进行连接的(常见的如jlink、stlink、dap、cklink、icemini等);
debug server各家实现都不一样甚至名字都不一样(如openocd),有些甚至就是开源项目openocd的包装改名(如csky debug server、iceman等);
再讲讲这种烧写步骤:
为了通俗好懂,我以gdb操作为例说明怎么完成这种烧写。
1. 先编写一个加载在mcu上ram上运行的elf程序,这个程序里通常定义了几个固定名称的变量和函数,流程都是根据某个变量的取值进行下一步操作的;
2. 使用gdb加载elf程序到mcu上(load指令);
3. 对elf程序某个固定函数设置断点(b fun指令),也有可能是elf程序固定产生一个软件断点(软中断汇编指令);
4. 让elf程序run起来(c指令);
5. 等待预定的断点产生,查询elf那几个固定的变量名称以便检查某个流程执行结果是否成功(p var指令);
6. 改变某些固定变量的值(set var=xx指令),这一步是为了控制流程或传输数据(如将真正的固件bin文件逐渐填入这些变量地址中);
7. 重复步骤5和6;
8. 当真正的固件bin传输完成之后,继续用步骤6中的步骤设置流程变量触发elf程序结束烧写流程(如复位mcu或直接从rom启动);
如何实现这种操作?
根据我的模型图,先在电脑上连接一个mcu,然后起个debug serve让它连接到mcu上,然后就写一段在pc上执行的程序,这个pc程序使用tcp连接debug server,然后使用rsp协议(GDB Remote Serial Protocol)控制debug server:加载mcu的elf程序--->传输真正的固件bin数据到mcu并控制elf程序完成烧写。
更多功能
进一步给他增加了把bin带入默认参数、自动拉起debug server、可以选择使用带界面的程序等等等。
可以命令行使用--->
可以带界面使用--->
windows下
ubuntu下
结束
写到最后,我提供编译好的程序供喜欢的朋友研究。
flash_program_v3.5 for windows.zip <--- 在windows10下测试可用
flash_program_v3.5 for linux.zip <--- 在ubuntu20.04下测试可用
所有这套程序是我用c语言实现的,也提供源码给感兴趣的朋友研究。
除了pC侧程序,针对具体的mcu核,还需要使用者根据例子实现mcu 侧的汇编启动代码和的flash操作接口。
em_flash_program_v1.0.7z <--- 包含了mcu测的代码示例
flash_program_v3.5 source.7z <--- 仅包含了PC侧代码
分享看到两篇好文章: