|
我是老温,一名热爱学习的嵌入式工程师
5 c- ~: K2 x: `5 o关注我,一起变得更加优秀!5 Q% d/ V6 i3 i- ^
嵌入式软件工程师在发布固件之前,通常都需要先进行提测,提测过程中会不断发现和修复bug,期间会涉及到不断地编译固件和更新固件版本,那么问题来了:/ x# _' s( Z3 a) {, v
( r( f$ U6 ?* c5 e7 ^如何保证发布出去的bin文件是最终测试通过的版本?
- x+ c7 u! c$ y. L) i7 {, J7 ?+ f" l d: m. ]
一般的来讲,代码到了测试后期,master分支就不会频繁的提交了,并且提交也会更加谨慎。
2 g; }9 L; [5 b# Z e' G1 S但是人为操作总会出现纰漏,希望只要代码被重新编译过,那么bin文件就包含新的时间信息,而这个信息是可以从外部通信或printf来查看的。
0 r- m4 L9 X5 c, n4 V: p在嵌入式开发中,版本号一般的都是一个int变量或字符串变量。但是若修改了代码而没有改version变量或宏定义,那么从version上就看不出来文件的变化。
& c; M3 n7 Y; } u" d; W7 D那么最终编译的版本到底是哪个版本,是否与测试的版本完全一致,这个问题尤为突出。
8 U2 [6 j2 G5 e; X3 \+ \+ ` i' T目标文件中带有编译时间可以防止代码被改动过,只要代码被重新编译,那么就生成新的时间信息。
6 ^/ X+ e! g8 o" Hgit能够记录文件修改信息,但是调试信息或工程配置等,很多文件都是ignore的,这些信息代表着最终的bin文件的运行环境。
% v; {' ]: b" i( Z某些复杂bug情况下,只有运行环境一致,仿真器才能attach到目标文件。. S; d; h9 C9 _3 J
( C. _# U, p3 r7 p& {8 {
如何获取时间?* R1 S# [. p V0 p# e
9 r- y4 i5 X$ Q3 x
, z8 {7 ~8 x. |这两个宏是日期和时间,格式如下。如果把这两个宏加入到代码,那么就得到了时间的字符串信息。, K, w+ E3 ^: e* Y3 c5 H
// Example of __DATE__ string: "Dec 27 2017"
5 Q' Q. J0 u$ _- T6 [; W// Example of __TIME__ string: "15:06:19"" e. G& }4 R7 ?0 X
const char *BuildInfo = "Version: " VERSION " " __DATE__ " " __TIME__;4 `$ \6 G1 K( X, X
代码实现获取日期和时间的方法很多,比如:
* F3 n" x3 y. _unsigned int mk_Build_Date(void)
9 H4 g4 M$ y: {{
* S r& g2 d8 _ int year = 0, month = 0, day = 0;
+ u3 j4 h z0 @) `# D- ~ int hour = 0, minute = 0, seconds = 0;1 U( `0 E5 N/ t `5 Q) v
char m[4] = {0};
+ K& o) e- l5 |- O sscanf(__DATE__, "%3s %2d %4d", m, &day, &year);% O9 ^- p6 Z4 y! {4 f: A
for (month = 0; month 12; month++)1 I6 } ~" {0 f8 y3 T
{' q9 g5 C& Y9 T' u# ?3 o
if (strcmp(m, short_char_months[month]) == 0)
& j8 ? Z5 V' g0 v; d; d {
9 q. H0 l' w1 [3 r6 c) _+ B& b break;
1 {8 ~. p0 f; f; A9 V }
: S# |5 s( L- g( o }/ `8 ]4 L( R6 g8 _7 s
sscanf(__TIME__, "%2d:%2d:%2d", &hour, &minute, &seconds);
0 S+ y2 d+ `$ T% S% C. j; M' g+ M) Q #ifdef SHORT_DATA_CHAR__6 _7 ~; b7 [# { @/ g' H5 Q) I
printf("[null] ** Build at: %04u-%02u-%02us %02u:%02u:%02u
m& }: D f A8 F0 F3 m",
) |. Y5 T1 l, T/ x year, month, day,/ L7 s& S, \0 }3 }
hour, minute,seconds);
' u. G8 W4 v& u8 `; R2 z7 ` #else
, p" s. E1 E3 r, V0 E printf("[null] ** Build at: %04u-%02u-%02u %02u:%02u:%02u
1 k# N, h/ ]3 x/ ~; b: @ [",
4 i, v8 F" T, N- K year, month, day,( M O* K1 E) p' A' ]+ v
hour, minute,seconds);) D$ w, T3 P2 q: f& a8 u( z
#endif
, X0 f/ `& }( F% p; @- I9 [( E DEBUG("buildDate: %s %s( R& t" o8 t/ Z0 ~2 L
", __DATE__, __TIME__);
& g1 L, U+ q1 @; t; q return 0;: \! K$ I' L0 }( N$ [; k0 G0 _
}
1 U$ _5 {9 @, _4 `. \把上面的函数加入到代码中,就能获取工程编译的时间。$ [0 x0 x9 X" \" D. ]. R2 U6 l O9 D
但是如果该代码所在的文件没有被修改,在非build-all情况下,编译器不会再次编译此文件,所以时间信息也就不会被更新。
5 H" V! M4 E1 R. j; B/ K如果每次都使用re-build all,一来繁琐,二来也不能保证每次都会记得点击build all按钮,靠技术手段来保证每次build都更新时间信息才是正道。
$ m9 w( `4 G' d* `: P- K. `如何保证时间每次编译都更新?
& K: M, i/ e' @! W* j2 c0 ]/ N3 q" k& Y9 ]4 E0 {, \* q- ^
3 y1 [3 e' |8 u' r
使用预编译指令,每次更新包含时间宏的文件或对应的链接文件。
1 c& y& h, m H1 n1 M在IAR环境下,官方已经给出了解决的方法(Using pre-build actions for time stamping)。8 p) x7 I! g1 R% |) V V
https://www.iar.com/support/tech-notes/ide/build-actions-pre-build-and-post-build/4 l1 W; ~" M& B
方法1:修改文件的时间,引起编译器对文件进行重新编译。4 E* o& v4 Z" v _. Q8 N
cmd /c "touch /cygdrive/d/test.c"' h0 u0 h5 o* n
方法虽好,可惜IAR用户大多数是Windows用户,包括我在内,touch是linux命令,必须Cywin环境。如果安装过这个环境的话,那就大功告成了。, v) a2 W$ x1 U0 d; X k! R% g. W- O
Cygwin touch command
; _# u0 T( U" R* k$ z6 I' A* q# SYou 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.& A+ A+ l2 O: K. C
You 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.$ \: \9 I) j; `/ O
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:
& i+ S- y" K, g4 Zcmd /c "touch /cygdrive/d/test.c": x T- ]. T9 h) H
The .bat file (located in project directory) alternative would look like:8 c4 _! E. D% }( _& B+ L. v5 f
Pre-build command line:8 l' ?8 \: c5 E
$PROJ_DIR$\pre-build.bat
( l! B$ T! V( G+ nFile pre-build.bat:+ y# ~* \ I, z" m& ], ~: |. M
touch /cygdrive/d/test.c/ \, }. c' b, ?: O6 |0 Y
方法2:修改文件对应的链接文件,触发编译器重新编译该文件,生成新的链接文件,那么就会生成新的带有时间信息的目标文件。
' s& _0 J5 Q) l" T4 ?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:
/ Q3 q. A9 k$ q* A0 x4 Tcmd /c "del "$OBJ_DIR$ est.o""
7 H& |( D7 F7 |在pre-build中加入上面的命令,就会在编译前删除test.o文件。# E+ L( z+ d1 W/ Q8 z2 Q
在这种模式下,工程代码只要任何位置发生变化,代码重新编译,就会触发删除test.o,然后链接过程发现没有test.o文件,那么就会重新编译一次test.c,那么新的时间信息就会记录下来了。7 y9 m4 p9 A% \: [2 Z
虽有些曲线救国的味道,但还是很顺利的实现了目标。) A j8 A3 G P& }. R
只要工程的任何地方有改动,生成新的目标文件,那么目标文件中就会带有最新的编译时间。
8 `- m! v4 B* c7 N0 ]$ t* Q+ I9 g方法3:直接告诉编译器每次重新编译某个文件更直接,MDK支持此功能。
/ [+ p4 F, m* A3 W/ |时隔一年半再次来这里,发现当时自己简直是小白,还洋洋得意曲线救国,实际上舍近求远罢了。
( @! H1 H" q/ d- Q' s3 P如果对工具多一些了解,万万是不会用上面的方法的,当然上面的方法也是通用想法,是通用型知识点,容易想到,也能达到目标。. D7 M/ d7 n. J" z. A7 s# D
新的方法,不需要写任何脚本,如果想让代码每次都编译更新DATA 和 TIME两个宏,那么让这个文件每次都编译一次就可以了,不需要删除它的obj文件然后让编译器找不到文件而触发重新编一次,其实直接告诉编译器每次重新编译更直接,MDK支持此功能。- D' P3 }" c0 `$ i3 L @! w
sp3mtljg4dw64031166323.png
0 J/ V0 |) a" K* f. P
1 y; z, z8 c5 i1 K g1 w$ D* D+ e* `下面是测试的效果:
# S7 D$ P1 k; ~9 x5 x6 d: P6 B" j5 `: t$ e# {9 k
hpbfoxviqcd64031166423.png
, d/ J- |: A7 d- h2 B) K
8 X) C/ p0 P' x; J9 I: i2 X1 V4 ?
其它资料:/ |7 g. A6 Q5 u
https://stackoverflow.com/questions/11697820/how-to-use-date-and-time-predefined-macros-in-as-two-integers-then-stri% v; @$ Y& |3 |; I2 j% O; p3 o
来源:Internet。8 ^: m% p1 L( [3 z
-END-: P; d d- I- R1 H2 E3 U; j% J
往期推荐:点击图片即可跳转阅读
; v' j! H1 g( [: p, m
2 C% y" _, Q. i: }, Q# K+ M7 t% S
& D4 ?7 p% d1 D4 j/ o, D ( U9 T9 y$ c' W: x& D# z* ?8 `
9 B% g' \# Y" y8 q! Y# A
f1fitjorx3c64031166523.jpg
0 R! ^7 \/ A$ N. c& O! l
E% M9 a4 E' _% Q2 D, z- p/ T( |; W1 t
适用于嵌入式设备,用 C 语言编写的轻量级日志库!
* A3 z+ u! k Z- v5 u( j) U: [5 |
9 l: _* e+ B$ }9 P1 A
8 G- N% h3 u: f' F6 R/ h0 v( L
3 n1 D; `1 C6 K$ ]( F 0 [7 K7 h, i' |: H$ w9 r
i253qyswdwp64031166623.jpg
8 n- U+ l# z% ?. z& Q6 q6 C1 C9 V* ?
! Z6 T1 ~2 J2 u* b( c 工业现场经常用到的 RS485 通信,原来它的收发机制是这样的。5 O0 B' c) \5 q3 }. {) \2 c( a2 D
4 X+ [# ?+ N4 E3 ]; d * I' w- I# ]# {5 p8 t5 r
- I) L7 n8 u7 g
mtv4j3ojbbc64031166724.jpg
" U# D7 d" x B U6 K5 W2 u ~5 M9 i
q8 X' K ^8 h- c+ i
嵌入式应用程序开发,经常使用哪些数据结构?
1 s/ z: b- N, T. Z + F( ?0 s r. s9 `4 k8 r. |
& N/ R) R$ C/ f( c9 g
) l( g- e. T. m: @, p# }9 P 我是老温,一名热爱学习的嵌入式工程师0 R- G! p: x7 _# w9 }
关注我,一起变得更加优秀!
4 }- K! V" I: C! R9 T
cpqctnk0ss064031166824.png
|
|