|

我是老温,一名热爱学习的嵌入式工程师/ g5 k, t0 O: g0 _5 |& ~
关注我,一起变得更加优秀!
' x' U# u! P. ?9 I0 [1 a嵌入式软件工程师在发布固件之前,通常都需要先进行提测,提测过程中会不断发现和修复bug,期间会涉及到不断地编译固件和更新固件版本,那么问题来了:
& @5 o" o4 a x- S5 o6 y; v1 Y9 h0 z1 \0 j9 U5 @8 k
如何保证发布出去的bin文件是最终测试通过的版本?
, b2 |' p0 `: M! {
" Z. @3 j9 R* k! c一般的来讲,代码到了测试后期,master分支就不会频繁的提交了,并且提交也会更加谨慎。; L; I+ a9 J2 ]7 ~# p
但是人为操作总会出现纰漏,希望只要代码被重新编译过,那么bin文件就包含新的时间信息,而这个信息是可以从外部通信或printf来查看的。% U2 s* Q' e6 r5 o5 v- \
在嵌入式开发中,版本号一般的都是一个int变量或字符串变量。但是若修改了代码而没有改version变量或宏定义,那么从version上就看不出来文件的变化。* g A* k( ^3 k$ x& j
那么最终编译的版本到底是哪个版本,是否与测试的版本完全一致,这个问题尤为突出。$ a, w0 E- |, r* V/ J
目标文件中带有编译时间可以防止代码被改动过,只要代码被重新编译,那么就生成新的时间信息。! i( V6 x9 J% B9 V" h" V% z& ]
git能够记录文件修改信息,但是调试信息或工程配置等,很多文件都是ignore的,这些信息代表着最终的bin文件的运行环境。
0 L Y4 J8 \0 e* H) H& @某些复杂bug情况下,只有运行环境一致,仿真器才能attach到目标文件。: u# X' @# U) \& @5 }5 ]
! z2 x$ o8 \/ n1 C
如何获取时间?( ]/ E+ A* {4 e" g
8 m' V; {7 o3 M! U9 x8 q& \$ _0 R
2 H* r7 N# m+ h4 Y2 u3 l
这两个宏是日期和时间,格式如下。如果把这两个宏加入到代码,那么就得到了时间的字符串信息。; b. E# c& ?( }. Z
// Example of __DATE__ string: "Dec 27 2017". u: y1 I6 }$ k) ^ `- u
// Example of __TIME__ string: "15:06:19"1 v& n4 |: k( T0 P. z% s- I
const char *BuildInfo = "Version: " VERSION " " __DATE__ " " __TIME__;6 |/ v" j6 x8 W& l! q3 ?8 B
代码实现获取日期和时间的方法很多,比如: q; k- c! ~3 p3 [, g- Q& k2 B
unsigned int mk_Build_Date(void), V5 x+ V3 E" ~$ f$ D! l, F
{
! H; _9 Q% a$ B" m7 \ s" b int year = 0, month = 0, day = 0;
3 Q+ n& o* \! x' g- @( R int hour = 0, minute = 0, seconds = 0;1 ]( o' H( a3 K
char m[4] = {0};& M2 T* T1 n+ i% F
sscanf(__DATE__, "%3s %2d %4d", m, &day, &year);
8 v5 J% `( Z5 z( V3 h! }$ ] for (month = 0; month 12; month++)
2 M1 z6 F/ |9 A3 \+ B5 V; ` {
8 t0 K; u* p4 \. R; s% u if (strcmp(m, short_char_months[month]) == 0)
6 a2 @- e/ B2 i7 U5 K% e6 a {1 e. ]7 y7 e- T6 D$ r- u" Q$ n
break;
7 ?( N5 H: `# j T! O. o4 R }3 d. m F: R5 C
}( P1 F2 |3 M& m* N- L
sscanf(__TIME__, "%2d:%2d:%2d", &hour, &minute, &seconds);% _$ m X) ^$ v! t
#ifdef SHORT_DATA_CHAR__
, \* c; W5 V: b% N printf("[null] ** Build at: %04u-%02u-%02us %02u:%02u:%02u
6 r7 g) p2 v# d' G0 G0 P: W% B",
- \' Z4 P' X) Z1 ~- p/ w year, month, day,
a3 t. @ E6 u( O6 ~3 [/ V$ C% @ hour, minute,seconds);2 s, _+ {" n3 ]8 W
#else" \ u M* R2 J4 V9 J" c
printf("[null] ** Build at: %04u-%02u-%02u %02u:%02u:%02u, h$ P) }3 z. _* A' T# ~( u
",
6 d- |. l" z9 V: }9 Z- e4 E year, month, day,
/ U3 M1 l1 V x% N6 X! G/ k hour, minute,seconds);
3 P) d* {) u- S% A #endif
3 f" \- s4 x2 I* U4 A) ] DEBUG("buildDate: %s %s
4 d' s' J8 r5 ]/ F1 M", __DATE__, __TIME__);
- ]3 J$ S7 t9 _0 \, d return 0;# [$ y+ P6 L7 P) x& v
}
1 `- h. X9 j: b% {# N把上面的函数加入到代码中,就能获取工程编译的时间。
/ o' s4 h. O; Q: z5 b' m% o但是如果该代码所在的文件没有被修改,在非build-all情况下,编译器不会再次编译此文件,所以时间信息也就不会被更新。
' O; j8 w9 i8 V J* v( ~& l如果每次都使用re-build all,一来繁琐,二来也不能保证每次都会记得点击build all按钮,靠技术手段来保证每次build都更新时间信息才是正道。. Z. O- Z* _7 d/ g2 F% E
如何保证时间每次编译都更新?7 h; k' P$ A0 O5 w
% q; k/ P. p0 g! B. Z6 O+ _0 e3 Y( X* M" N! Y# `/ A
使用预编译指令,每次更新包含时间宏的文件或对应的链接文件。
/ ?- q$ l3 ^' A% j' W9 y$ V0 Z: }在IAR环境下,官方已经给出了解决的方法(Using pre-build actions for time stamping)。
4 v/ w4 r; [; {/ }) H ?! ^' g, hhttps://www.iar.com/support/tech-notes/ide/build-actions-pre-build-and-post-build/7 F; w, T: t6 {: e
方法1:修改文件的时间,引起编译器对文件进行重新编译。2 Z! M& Z5 s( d& K
cmd /c "touch /cygdrive/d/test.c"& n- P r, G1 S4 T5 \; L
方法虽好,可惜IAR用户大多数是Windows用户,包括我在内,touch是linux命令,必须Cywin环境。如果安装过这个环境的话,那就大功告成了。$ `! c5 G S, [8 F4 v1 _
Cygwin touch command! ?. t3 X3 \' C0 y
You can enter "cygwin-application.exe" on the pre- and post-build command lines, if the environment variable PATH includes the directory where the "cygwin-application.exe" is located.
. w9 L# E# @5 W' ^! l0 F! MYou can run the Cygwin command "touch" on the pre-build command line, but if you add a file path, for example "touch d:/test.c", the file path is not accepted by Cygwin.. O/ S" O# k) E
Cygwin expects the POSIX path /cygdrive/d/test.c so the resulting command line would be "touch /cygdrive/d/test.c", however this command cannot be executed directly on the pre- and post-build command. Instead you have to run indirectly using:9 G0 D) I0 c4 a p1 O# b$ s
cmd /c "touch /cygdrive/d/test.c"
# j i7 u5 }: kThe .bat file (located in project directory) alternative would look like:" [: M! y. }& M, |
Pre-build command line:0 m9 w5 P. j' R" ?, a
$PROJ_DIR$\pre-build.bat
" [ S% r! k. P! [2 z3 T$ ^( Q* uFile pre-build.bat:
* R% |& o% c- ~7 O$ y, I touch /cygdrive/d/test.c% J8 j5 G% X# V: A2 U. M/ t3 ?
方法2:修改文件对应的链接文件,触发编译器重新编译该文件,生成新的链接文件,那么就会生成新的带有时间信息的目标文件。3 ^1 D: x Q8 q! J
An alternative to the "touch" command is to have a pre-build action that deletes the object file, for example the Pre-build command line:6 U. f) w$ K% }/ L- W3 X( j: T# E
cmd /c "del "$OBJ_DIR$ est.o""2 o/ ]0 j" q: k3 ~) {& [% x
在pre-build中加入上面的命令,就会在编译前删除test.o文件。2 w2 ]" [' t& F3 `& w! y
在这种模式下,工程代码只要任何位置发生变化,代码重新编译,就会触发删除test.o,然后链接过程发现没有test.o文件,那么就会重新编译一次test.c,那么新的时间信息就会记录下来了。
, H# G/ d7 X5 ?虽有些曲线救国的味道,但还是很顺利的实现了目标。
8 L+ n% w$ E8 m+ P# n: b( F只要工程的任何地方有改动,生成新的目标文件,那么目标文件中就会带有最新的编译时间。
/ x( b, A( ~8 v- [( o方法3:直接告诉编译器每次重新编译某个文件更直接,MDK支持此功能。" h8 d# I& |" W z9 m7 s' U
时隔一年半再次来这里,发现当时自己简直是小白,还洋洋得意曲线救国,实际上舍近求远罢了。7 K& h q, [; l- z0 }; E' E
如果对工具多一些了解,万万是不会用上面的方法的,当然上面的方法也是通用想法,是通用型知识点,容易想到,也能达到目标。: v- q' `7 W8 f
新的方法,不需要写任何脚本,如果想让代码每次都编译更新DATA 和 TIME两个宏,那么让这个文件每次都编译一次就可以了,不需要删除它的obj文件然后让编译器找不到文件而触发重新编一次,其实直接告诉编译器每次重新编译更直接,MDK支持此功能。
. Z: F5 q$ U" J, }7 C, H% I5 N
sp3mtljg4dw64031166323.png
& |( K+ |) n3 Z K O
* i" ]5 j1 [9 F2 i0 ]. P
下面是测试的效果:
( S) V8 U0 u" j
- ~' B, W+ r' }* ?1 k
hpbfoxviqcd64031166423.png
6 J+ s k$ v5 y
7 K8 X8 k7 v. G( r其它资料:
x: L' A1 Z6 i% t& U& ^% u) Thttps://stackoverflow.com/questions/11697820/how-to-use-date-and-time-predefined-macros-in-as-two-integers-then-stri
" Q/ o% `2 K! z' K来源:Internet。
& X! i u7 L: p, z/ T-END-
2 A& _; x% @/ B9 G- h2 K; b往期推荐:点击图片即可跳转阅读
" [: l/ J7 p8 f) Q; w 3 T3 K2 E& ^; ~4 _3 z
. f/ j) P7 D; N4 | {' j, T3 S
1 o, s! t( {) p
- f% p! C; S D+ L' H: d2 u
f1fitjorx3c64031166523.jpg
7 p1 K9 b+ R+ c$ a* @' U" W
. q. M) X! V4 b* s' }$ s0 {
适用于嵌入式设备,用 C 语言编写的轻量级日志库!4 W% `9 @) O% X3 m# x
5 j/ o" ~% X! P8 N1 Z8 M+ [8 m
2 A' d/ b6 e# X6 w- k# l) z $ D/ T! k' l4 {% X/ l k% u3 C, c
. V# [- {5 @& C- D" |5 c1 P3 `
i253qyswdwp64031166623.jpg
' V& S/ a5 y" m+ ~
) \! o' o- d9 s% x
工业现场经常用到的 RS485 通信,原来它的收发机制是这样的。
: y# d( I1 f S8 u! \( ~
+ n. o+ y# u6 \. L4 b0 D, \; s' X6 `. ^ : i' o+ b8 x; E# J
( d3 b4 x6 G. D4 L# a
mtv4j3ojbbc64031166724.jpg
/ h+ }" Q% {- y. h) ^
3 c$ ?/ m" Y) M- R# V3 V
嵌入式应用程序开发,经常使用哪些数据结构?
( D' J4 ~% E* H3 @6 M* q 0 D$ ~; p0 [% ^0 h B( \
9 \( L0 N* k8 J5 b& v
2 @. @- N$ E, i$ [4 ? 我是老温,一名热爱学习的嵌入式工程师
* h' ?+ i6 F" d+ r+ c; j6 g9 `关注我,一起变得更加优秀!
% I$ v, |4 n+ Q* j. i/ w6 S
cpqctnk0ss064031166824.png
|
|