|
我是老温,一名热爱学习的嵌入式工程师# F# H W# n+ c9 K5 j
关注我,一起变得更加优秀!) g7 j" k3 U& j( [
嵌入式软件工程师在发布固件之前,通常都需要先进行提测,提测过程中会不断发现和修复bug,期间会涉及到不断地编译固件和更新固件版本,那么问题来了:
+ v: D8 M, K( c" [2 n2 ^, ]" V/ {- [0 [, K
如何保证发布出去的bin文件是最终测试通过的版本?8 A( O2 C5 T# G5 W$ J
9 ?6 a" }# h# m+ E4 t, b) F* q- p一般的来讲,代码到了测试后期,master分支就不会频繁的提交了,并且提交也会更加谨慎。! C4 x3 j) `0 a, }; G8 a, g# f
但是人为操作总会出现纰漏,希望只要代码被重新编译过,那么bin文件就包含新的时间信息,而这个信息是可以从外部通信或printf来查看的。; A/ j! y( e) [" Q6 G$ ` @
在嵌入式开发中,版本号一般的都是一个int变量或字符串变量。但是若修改了代码而没有改version变量或宏定义,那么从version上就看不出来文件的变化。
2 ^" c% J7 u, ?3 ^那么最终编译的版本到底是哪个版本,是否与测试的版本完全一致,这个问题尤为突出。
/ I* K% L/ _0 G0 f$ Y7 b) }, T目标文件中带有编译时间可以防止代码被改动过,只要代码被重新编译,那么就生成新的时间信息。6 o" U! c8 L8 n
git能够记录文件修改信息,但是调试信息或工程配置等,很多文件都是ignore的,这些信息代表着最终的bin文件的运行环境。
" m$ c8 F" n- k [( S8 w+ }某些复杂bug情况下,只有运行环境一致,仿真器才能attach到目标文件。* K0 a: Z5 X; F
. ~# a, ?6 f! l; I* \3 i
如何获取时间?
6 e' C9 e/ v8 S, ?2 |
' m" {) \3 S4 J$ U5 H* b9 K4 F1 ~1 u" E6 O5 {, T, {
这两个宏是日期和时间,格式如下。如果把这两个宏加入到代码,那么就得到了时间的字符串信息。) w9 R% e1 Q5 U7 |" b
// Example of __DATE__ string: "Dec 27 2017": |3 y5 y* U0 J9 s" f+ e. U
// Example of __TIME__ string: "15:06:19"' p* M6 ~4 ~; c
const char *BuildInfo = "Version: " VERSION " " __DATE__ " " __TIME__;
' w' k2 I6 ^) f1 ?, \代码实现获取日期和时间的方法很多,比如:
( U7 W+ _5 A3 t& ~unsigned int mk_Build_Date(void)
9 f! }- _0 ^! P& l{2 M; h$ ^: `6 G* ]
int year = 0, month = 0, day = 0;
0 [" N$ b# {% N- |' O; p0 Z1 c int hour = 0, minute = 0, seconds = 0;; B. D! ?4 G' J; q
char m[4] = {0};1 {" w/ b$ E- r
sscanf(__DATE__, "%3s %2d %4d", m, &day, &year);. a$ K$ z, N' g0 [# x8 x
for (month = 0; month 12; month++)# `7 t3 S0 _2 u' Y
{% W2 ?/ O( Z" [" i! k+ M+ Q
if (strcmp(m, short_char_months[month]) == 0)
) g: Q7 u; f, n( L. ?0 a$ P {
/ U5 \! ^/ Y/ s/ d8 }# } N break;9 g+ r( ], ~3 s& Y3 ?
}+ t4 v+ N7 O8 p
}
F- {4 ]+ ?4 Q sscanf(__TIME__, "%2d:%2d:%2d", &hour, &minute, &seconds);5 E, H4 O6 z+ {
#ifdef SHORT_DATA_CHAR__2 K* H% _ L( u, o/ n
printf("[null] ** Build at: %04u-%02u-%02us %02u:%02u:%02u8 F/ y9 L- t5 A& x
",
6 b) Y# k: y7 p( Q3 y year, month, day,
/ f. ]( o# L, e% V' }- z7 [" S hour, minute,seconds);. \7 p6 W% J2 U4 L1 y
#else) e. h+ A) P. E4 t
printf("[null] ** Build at: %04u-%02u-%02u %02u:%02u:%02u# w- x$ i& W1 C, D
",
: R& r8 o0 ]* {3 `3 z+ j year, month, day,
" u9 y/ [) V! w" Y- e A T6 N hour, minute,seconds);# v$ L1 d& H+ x0 l4 \
#endif
" K! m4 }3 v3 n& f9 ~" Q DEBUG("buildDate: %s %s
' X8 p/ R; d' X! W; f( ^; k", __DATE__, __TIME__);$ t3 i* X, \% m L, q
return 0;% o6 O7 Y2 ^) q. h
}
2 d. \7 ~+ Y5 `/ j" u把上面的函数加入到代码中,就能获取工程编译的时间。3 M0 K0 B1 v. p! _! w
但是如果该代码所在的文件没有被修改,在非build-all情况下,编译器不会再次编译此文件,所以时间信息也就不会被更新。1 r1 M* g. f. _2 X& P* E, T; P1 I7 s
如果每次都使用re-build all,一来繁琐,二来也不能保证每次都会记得点击build all按钮,靠技术手段来保证每次build都更新时间信息才是正道。
0 g) T0 o! f, n" B' N如何保证时间每次编译都更新?1 [1 o0 o5 n, a$ l F
8 {, k( A# G* f+ I. l% I5 k5 h5 n% O. V
使用预编译指令,每次更新包含时间宏的文件或对应的链接文件。& b! L" G0 J8 j6 Z5 A6 S
在IAR环境下,官方已经给出了解决的方法(Using pre-build actions for time stamping)。
" B# s+ @ T# Yhttps://www.iar.com/support/tech-notes/ide/build-actions-pre-build-and-post-build/
- h9 w) z* W" _! u( O6 c0 q" e方法1:修改文件的时间,引起编译器对文件进行重新编译。
8 L) p; p# W; L4 bcmd /c "touch /cygdrive/d/test.c"# d: J, l" ^% n2 b
方法虽好,可惜IAR用户大多数是Windows用户,包括我在内,touch是linux命令,必须Cywin环境。如果安装过这个环境的话,那就大功告成了。6 b5 A2 d- C, s3 v. `( r9 g5 q4 h# {% f
Cygwin touch command
) ~4 v& d# ^" i4 cYou 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.
+ }3 n, q5 q" Q; y9 t: d( Z+ LYou 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.) M' P% l1 \( U9 Y& p. I0 e2 _
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:
! F# \. }' i: s( q! q" tcmd /c "touch /cygdrive/d/test.c"
4 `* N$ C2 _6 I) L% ~The .bat file (located in project directory) alternative would look like:% y0 j* N7 w+ J+ w7 U7 D# |
Pre-build command line:
( k' O6 K6 M% R' P $PROJ_DIR$\pre-build.bat
8 t! K1 k0 |6 a6 |6 a. s: lFile pre-build.bat:
+ U' z5 X1 {; ~ touch /cygdrive/d/test.c
4 H' E9 o, w: C, ]方法2:修改文件对应的链接文件,触发编译器重新编译该文件,生成新的链接文件,那么就会生成新的带有时间信息的目标文件。( Q+ W: c' r% C7 n0 s
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:
, S2 k0 l+ B' x2 n( |7 bcmd /c "del "$OBJ_DIR$ est.o""4 b4 E0 d9 S! D5 I7 w
在pre-build中加入上面的命令,就会在编译前删除test.o文件。
2 {( _" ^, l6 B3 L在这种模式下,工程代码只要任何位置发生变化,代码重新编译,就会触发删除test.o,然后链接过程发现没有test.o文件,那么就会重新编译一次test.c,那么新的时间信息就会记录下来了。8 V+ e: A4 s1 N% E o
虽有些曲线救国的味道,但还是很顺利的实现了目标。- V( E0 J2 p; t, K$ e; S2 k) @: u
只要工程的任何地方有改动,生成新的目标文件,那么目标文件中就会带有最新的编译时间。) j' N, B4 P' l7 B T
方法3:直接告诉编译器每次重新编译某个文件更直接,MDK支持此功能。
. x9 k6 F8 ^5 ]1 y8 b" S, D$ q时隔一年半再次来这里,发现当时自己简直是小白,还洋洋得意曲线救国,实际上舍近求远罢了。4 H; }) M7 Z. m6 ^& { @% s
如果对工具多一些了解,万万是不会用上面的方法的,当然上面的方法也是通用想法,是通用型知识点,容易想到,也能达到目标。
/ p0 v* f* m X新的方法,不需要写任何脚本,如果想让代码每次都编译更新DATA 和 TIME两个宏,那么让这个文件每次都编译一次就可以了,不需要删除它的obj文件然后让编译器找不到文件而触发重新编一次,其实直接告诉编译器每次重新编译更直接,MDK支持此功能。# {& ~- d- v5 A
sp3mtljg4dw64031166323.png
& ]6 `% k1 A9 T l& U7 {/ X- L3 o
7 ~ n K4 n# v& }* V4 I
下面是测试的效果:
$ R! U" ^1 ~% M/ z- {- m# y( }3 {& }1 m7 h A' }5 t
hpbfoxviqcd64031166423.png
2 h0 P$ O/ Q6 N8 y5 x! {" J+ t) S
$ J5 W+ m& @5 K! i0 C5 `其它资料:
$ H V. S/ L$ q4 ^0 x" W) ]; \https://stackoverflow.com/questions/11697820/how-to-use-date-and-time-predefined-macros-in-as-two-integers-then-stri: k/ w: w: V& n
来源:Internet。
0 J8 e" y( N: ^; ^7 y# o-END-- T! ?& @; H" d9 I
往期推荐:点击图片即可跳转阅读
& g1 f. D Q0 O0 K* z8 D
5 @ u3 B0 Y. W0 ^$ }, Q G
/ c$ {) m4 f G0 M* v
2 D- S7 @; `: {' |0 A' ]% O( N
; o+ J" S: Z7 H( Q
f1fitjorx3c64031166523.jpg
/ g' _4 |* M+ k$ O
8 z( L( L/ w* n2 l1 d5 Y0 K# L- a
适用于嵌入式设备,用 C 语言编写的轻量级日志库!0 a0 d o2 J- k' K
+ N$ r% y9 e1 K- T; n
5 x$ q% D! m- d& a0 A; z
+ H! W2 A {5 @1 m; M, p 5 A2 N1 f8 [" N: s4 r0 y% W# m
i253qyswdwp64031166623.jpg
# e$ [9 E! H z b& {+ j 0 t+ x" H& L+ S; ^/ z
工业现场经常用到的 RS485 通信,原来它的收发机制是这样的。, G0 T$ N! t6 @* `# Y3 @5 l
0 r3 W" L1 Y- b T, u 8 m+ }& ]. D: K
5 F! z+ C- E$ w1 H$ y( d+ r1 K6 ]* h
mtv4j3ojbbc64031166724.jpg
$ A6 B" T; I5 h9 q' @; J
4 E. R) l$ w' e8 H0 C4 c3 ^
嵌入式应用程序开发,经常使用哪些数据结构?
6 O1 L7 x$ t. O4 j8 w3 J. `
- J; V: J) Y; }# p G& O: s 6 ~7 B; V5 L0 q: c" N: n
; t2 m1 {$ R4 B3 g% Z; ^: g
我是老温,一名热爱学习的嵌入式工程师
; m5 W9 Q+ J" ]关注我,一起变得更加优秀!
7 d* l1 @- f1 s R1 E5 t0 r
cpqctnk0ss064031166824.png
|
|