一.前言
前面几篇文章写了一下关于cmake改造嵌入式编程的体验,点击查看
摆脱MDK,用cmake改造嵌入式软件开发体验
用IDE配置并运行cmake工程
读者反响不错,至今已有3个赞了,给了我很大的鼓舞(手动滑稽),今天就来分享一下嵌入式开发中最重要的环节,固件发布,毕竟,程序写的再溜,不出固件,老板拿什么卖钱嘛.
二.普通做法
首先说明一下一般大家做嵌入式发布程序的一般流程:程序猿在MDK编好固件之后,在工程目录的build的文件夹会产生编译出来的hex文件,如下图:
然后程序猿就把固件拷贝到一个新建的文件夹,重新命个名(因为mdk里生成的固件名称是固定的),一般是写上产品名称,版本号啊然后有一些特殊的定义也会加上(比如什么至尊版,加强版,普通版之类的区分同一产品的不同价位或者功能),再编写一个changlist或者版本记录,然后把版本记录也拷贝一份到文件夹内,然后再压缩发给测试人员或者生产负责人.
二.高级做法1.0
高级做法是编写一个脚本,比如python脚本或者bat(shell)脚本,在脚本里面执行一些拷贝或者某些系统命令,比如nrf52832带sdk的程序就需要把sdk合并到最终的hex文件里去.
比如这样:
这种方式比手动修改就要高级一些,我们称为高级1.0,但是这种做法有一个弊端,没法根据固件编译时的参数去定制固件的名称,倒是有一个方法,在build文件夹下有一个.dep文件可以利用python脚本去解析里面的特定字符,比如:
比如我需要知道固件编译的时候有没有定义TIANYI这个宏,可以这样写:
这样的话可以做到针对某个编译参数生成特定名称的固件,可以算是一个提高吧,用MDK的话就可以采取这种方式
三.高级方法2.0
高级做法2.0是高级做法1.0的升级版,前提是需要搭建好cmake的编译环境,闲话少说,上法宝:
这段代码是cmake里面添加编译期间定义的语句,结合前面的cmake环境搭建教程,结合语句按字面意思应该不难理解,首先我们定义了这么一些编译参数,然后我们把所有的编译参数获取出来,形成一个叫defs的数组(cmake语法里的数组类型,这里不用理解这个数组到底怎么存储的)
在cmake里面添加一个目标,叫做merge,名字可以随便取:
这段代码可以理解为:如果我需要产生一个merge的目标,就调用后面的cmd命令行语句,这个语句的作用是用python来启动一个脚本,启动脚本的时候会带上相应的参数,这个参数就是我们提取出来的defs,然后在python脚本里面把参数打印出来会是这样会是这样:
看到没有,刚刚我们定义的宏定义在python文件里面获取到了,但是有一点不太好,比如我们cmake文件里面SOFT_VER=809,在python里面也是这样子显示的,不利于我们把真实的数据提取出来,没事,python这么先进的语言,这还不是小事一桩,盘他:
如上图所示,我们花分把钟(专业人员,请勿羡慕)把解析函数写出来,形成一个字典(dict,python的一种数据结构):
字典是一种键值对的数据结构,可以理解成不用数字做下标的数组,但是python的类型很灵活,不理解的同学可以研究一下python
接下来的事就太简单了,我们只需要把字典的键和值分别取出来就行了,对于有的参数,没有值的说明咱们不需要他的值,看图:
这样的操作就把之前我们复杂的需要把.dep文件打开然后一行一行的找对应的宏有没有来的简便,而且效率也更高(不需要读文件了)
你以为到这就完事了,错,接下来我们还有合并固件,然后把所有相关文件压缩成压缩包,单独存储,而你只需要点一个按钮
比如我的工程里,除了要把hex生成出来,还要生成一个用于ota升级的固件包,同时编译的源hex也要保存,以方便追溯问题,请上眼:
对,就是这么短短的几行语句,你之前需要花好几分钟还不一定不出错的步骤,我们用几行脚本语句就实现了,接下来就是运行脚本了,怎么运行:
打开cmd命令行,输入:
`
cmake --build .build --target merge
`
就等着电脑帮你打包吧,如果还想简单一点,qtcreater,clion,包括mdk都有一个外部工具可以创建,创建完之后点击运行就ok了:
这是clion的
这是qtcreater的
这是mdk的
---------19-12-10更新-----------
四.高级版MDK版
最近发现MDK同样可以做到clion或者qtcreater同样的高级方法,共享如下,本质上还是运用python脚本,python真的可以做好多事情,配置如图:
很简单,就是调用python去执行merge.py脚本,那么如何实现获取编译的时候定义的各种参数呢,一种方法是上面讲的读取.dep文件,这是最简单高效的,另外附上高级用法:
1).高级获取宏定义方法一
mdk编译的时候有一个选项是misc_ctrl,如图,在这里加上一行:--via defines.opt
然后在工程目录下创建一个defines.opt文件,用文本编辑器打开,填入形如-DSOFTVERSION=101
-DUSE_UART
这样的宏定义就行,例如:
那这个文件起什么作用呢,用过MAKEFILE的童鞋可能反应过来了,因为makefile里面就是这样的语法,这个文件这样写的作用跟编译的时候指定的宏定义效果是一样的,比如这样:
同学们可以实验一下,用这个文件编译的话在程序里面定义-D之后的变量可以读取到,那既然有文件就好办了,用python解析一下文件不就形成了定义字典了吗,比如:
还是熟悉的正则,还是熟悉的py,换汤不换药,这里注意一下里面有个re.sub函数,这个函数是替换'""这样的字符的因为opt文件的语法原因直接定义某个字符串会导致程序无法正确识别,必须用''把""里面的内容括起来才能让程序识别成字符串.
后面的事情就清楚了,跟高级版3.0一样,固件打包神马的都一样,愿意研究的同学自行研究就行
2).高级获取宏定义方法二
这种方法要用到xml文件解析,我们都知道每个MDK项目都是由一个.uvprojx文件管理的(mdk四是.uvproj),我们尝试用文本编辑器看一下里面是什么:
喔吼吼,这不就是xml文件嘛,再看看里面有什么具体内容:
哦吼吼吼,怎么这么眼熟呢,原来,我们在编辑器上宏定义的东西都在这里,看这里:
理解不了的同学自己把宏定义改几个然后编译一下或者退出mdk然后重新打开就能看到效果了
嗯,接下来还不是py一把梭:
这段代码也很简单,无非就是打开xml文件,然后读取tag,attr和text,不熟悉XML文件的同学自行百度一下就知道了,比json难不到哪去,
利用python我们还是很容易的就获得了我们定义的宏定义,接下来的事就不用多说了,不过值得一提的是,如果在这种方法定义的宏包含字符串的话需要处理一下,比如:PRODUCT="S350N"
,不用像opt文件那样用单引号包起来,但是程序里面要使用的话得这样:
也不是很难理解,具体为什么我就不讲了,同学们照着我的葫芦能画出来瓢就行.
以上就是更新的内容,最近发现MDk也不是那么的烂,还是算比较灵活的,后面一篇搞几个MDK高级操作,列位看官请上眼.
五.总结
目前,关于固件生成的批量化就简单研究到这里了,如果同学们还有什么需求可以一起探讨.文章有错误在所难免,欢迎指正.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。