|
我是老温,一名热爱学习的嵌入式工程师5 o# ^& A, H! w0 ~: O2 g) O9 ^
关注我,一起变得更加优秀! L+ [6 E1 N I4 J) H$ X& _
嵌入式软件工程师在发布固件之前,通常都需要先进行提测,提测过程中会不断发现和修复bug,期间会涉及到不断地编译固件和更新固件版本,那么问题来了:! s7 F& {6 P) g S2 q$ }
: r% E% v$ q* t
如何保证发布出去的bin文件是最终测试通过的版本?
; _% i6 K5 L2 {
( F0 Q9 W- I9 N1 x+ M一般的来讲,代码到了测试后期,master分支就不会频繁的提交了,并且提交也会更加谨慎。4 [/ ]+ e: B7 z) r7 E( f
但是人为操作总会出现纰漏,希望只要代码被重新编译过,那么bin文件就包含新的时间信息,而这个信息是可以从外部通信或printf来查看的。
, Z% N }6 e9 D在嵌入式开发中,版本号一般的都是一个int变量或字符串变量。但是若修改了代码而没有改version变量或宏定义,那么从version上就看不出来文件的变化。* X8 @( t2 U. z9 w4 P
那么最终编译的版本到底是哪个版本,是否与测试的版本完全一致,这个问题尤为突出。
% X8 ~1 G; J5 n0 O+ A目标文件中带有编译时间可以防止代码被改动过,只要代码被重新编译,那么就生成新的时间信息。
6 W/ U1 r$ e8 d2 p( O# }git能够记录文件修改信息,但是调试信息或工程配置等,很多文件都是ignore的,这些信息代表着最终的bin文件的运行环境。, `0 ~- y: V4 t+ g& }2 U8 d
某些复杂bug情况下,只有运行环境一致,仿真器才能attach到目标文件。
2 s" B' A" t4 ?% W( e4 J/ j Y+ ~+ k) p) Z- O' W; g3 [
如何获取时间?- X# b' ~: N* M: v
: H2 V/ |3 [2 q7 D! {0 i
/ w. J9 v8 w, B% m这两个宏是日期和时间,格式如下。如果把这两个宏加入到代码,那么就得到了时间的字符串信息。$ O7 G" {: o! O: l( L* E/ {- ^
// Example of __DATE__ string: "Dec 27 2017"
! U) L! q. y3 u' ^// Example of __TIME__ string: "15:06:19"
7 H4 o3 |) D- {3 `, oconst char *BuildInfo = "Version: " VERSION " " __DATE__ " " __TIME__;
. F0 y6 G6 b; q; M; b8 C+ z代码实现获取日期和时间的方法很多,比如:
' u! f5 N. X- s0 L" t" p: f! c' Y9 ~unsigned int mk_Build_Date(void)
) p7 k- `0 G* v) H{
2 B6 @4 |: X& R! \0 D int year = 0, month = 0, day = 0;9 d; P' c3 B6 X* G1 E- }
int hour = 0, minute = 0, seconds = 0;
0 X$ g1 K1 c& S8 \( t char m[4] = {0}; Q9 H( K' a" o5 B
sscanf(__DATE__, "%3s %2d %4d", m, &day, &year);
/ P7 b' L$ J( {4 \ for (month = 0; month 12; month++)
7 Y% p0 i* ]6 T7 l {
" D4 Y! F; H! @# `) V0 h; b if (strcmp(m, short_char_months[month]) == 0)
) S' z3 Z4 _* g( K: s! p {
* ^# a6 u2 A6 a9 K7 ]& U break;1 u& F2 S# o; f% y) U. {5 X
}4 T( j1 o2 T% k9 l f* T
}- t; V# G g; h* {: J3 `
sscanf(__TIME__, "%2d:%2d:%2d", &hour, &minute, &seconds);- k! @) n4 ?. p: A
#ifdef SHORT_DATA_CHAR__
3 C9 O% T4 [. f$ V printf("[null] ** Build at: %04u-%02u-%02us %02u:%02u:%02u
5 w3 ]1 c5 z! [",
. B$ Z* C/ j# f9 \5 {% g% _8 J year, month, day,8 t; d) X' J- S1 p) B/ f0 {
hour, minute,seconds);
8 {* ?0 R0 R% v S #else
% b3 [/ e" A. d P2 D3 C printf("[null] ** Build at: %04u-%02u-%02u %02u:%02u:%02u
! Y: Z, S5 l% b4 v# H; k"," C; Y" @; h* Y! G
year, month, day,9 d8 `4 o4 O8 a/ o# j3 c$ X
hour, minute,seconds);- W5 u4 Z2 |, b( y: r
#endif
1 {4 T9 }) ]+ ~: l+ n1 p DEBUG("buildDate: %s %s2 Q5 l2 V9 u/ P2 K0 K0 F4 m
", __DATE__, __TIME__);
5 r4 Q/ L* `, `2 B3 s return 0;5 _: v! ?6 J( B8 v
}
, e- R& q2 N2 L- N: }4 t8 f把上面的函数加入到代码中,就能获取工程编译的时间。% i' m. M U% Q$ |+ A, C1 I7 E" y* B
但是如果该代码所在的文件没有被修改,在非build-all情况下,编译器不会再次编译此文件,所以时间信息也就不会被更新。* ^7 c: u4 c6 H& R- E: x6 t
如果每次都使用re-build all,一来繁琐,二来也不能保证每次都会记得点击build all按钮,靠技术手段来保证每次build都更新时间信息才是正道。
( x5 v" h# B/ f9 H: y如何保证时间每次编译都更新?
# |' c" p0 x4 t7 ~: m& E' n$ M4 X* u7 Q s# U. s
$ k+ m$ J2 R" @) |$ ]: z使用预编译指令,每次更新包含时间宏的文件或对应的链接文件。
/ i+ d8 B* h6 z' \在IAR环境下,官方已经给出了解决的方法(Using pre-build actions for time stamping)。) I$ W5 P' B5 X$ h# E9 k
https://www.iar.com/support/tech-notes/ide/build-actions-pre-build-and-post-build/
: J `' ]7 r t2 B7 V: G方法1:修改文件的时间,引起编译器对文件进行重新编译。
/ |/ f6 g% h0 r" ?4 ccmd /c "touch /cygdrive/d/test.c") t+ a6 O) T4 }% ]$ d1 i
方法虽好,可惜IAR用户大多数是Windows用户,包括我在内,touch是linux命令,必须Cywin环境。如果安装过这个环境的话,那就大功告成了。, f/ v: W( q7 ~+ d% L
Cygwin touch command
4 u2 J2 i8 [! B9 N* _) L$ HYou 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.
! i g3 x; e8 r+ s, ?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.- b5 x, }, u7 V1 d+ R. V2 J
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:
. P3 `$ g# R- D" Z% u: Jcmd /c "touch /cygdrive/d/test.c") L& ?& ]; c7 `8 H
The .bat file (located in project directory) alternative would look like:
" B8 o; P* L G: ^0 ePre-build command line:9 h8 p& j+ }) T4 b
$PROJ_DIR$\pre-build.bat; z; f4 r& h4 ]8 T, v4 B
File pre-build.bat:5 I( z# d7 D5 M
touch /cygdrive/d/test.c
- Z D" Z; a; v方法2:修改文件对应的链接文件,触发编译器重新编译该文件,生成新的链接文件,那么就会生成新的带有时间信息的目标文件。$ k: O: }( ]; `5 |: A$ U
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:8 r& ^3 `: a# s; e6 a' ]2 S1 f3 P; Q
cmd /c "del "$OBJ_DIR$ est.o""
$ s* Q/ p/ W+ U0 D在pre-build中加入上面的命令,就会在编译前删除test.o文件。
- P$ ?+ L9 `' P- D; j在这种模式下,工程代码只要任何位置发生变化,代码重新编译,就会触发删除test.o,然后链接过程发现没有test.o文件,那么就会重新编译一次test.c,那么新的时间信息就会记录下来了。
/ D- m$ [% L& B9 z1 P3 W虽有些曲线救国的味道,但还是很顺利的实现了目标。
, |+ G+ t4 n: c7 k% q, M% Y只要工程的任何地方有改动,生成新的目标文件,那么目标文件中就会带有最新的编译时间。" E0 i$ E& L) [7 p$ W U! B8 K
方法3:直接告诉编译器每次重新编译某个文件更直接,MDK支持此功能。
$ I v, V# W+ L: {, F+ m! E) E; x时隔一年半再次来这里,发现当时自己简直是小白,还洋洋得意曲线救国,实际上舍近求远罢了。
! ?0 T1 ^6 X* y" v& I4 ]: J如果对工具多一些了解,万万是不会用上面的方法的,当然上面的方法也是通用想法,是通用型知识点,容易想到,也能达到目标。
' V5 `) T( c. M5 V. a) m5 S8 o) p新的方法,不需要写任何脚本,如果想让代码每次都编译更新DATA 和 TIME两个宏,那么让这个文件每次都编译一次就可以了,不需要删除它的obj文件然后让编译器找不到文件而触发重新编一次,其实直接告诉编译器每次重新编译更直接,MDK支持此功能。9 ?. J: g0 H- V6 G3 F, j, t
sp3mtljg4dw64031166323.png
: H% a" `+ T9 A* b
Z- @. l7 J: L, I' z4 t
下面是测试的效果:# B' ? n! e4 \' H( Z# Z
, { G) V3 z- \8 u6 K% W5 v
hpbfoxviqcd64031166423.png
1 D% Y! z9 X1 h; {. n. t5 ^2 |4 L: V" o
其它资料:; {9 G0 \; }0 S% L1 C) Y
https://stackoverflow.com/questions/11697820/how-to-use-date-and-time-predefined-macros-in-as-two-integers-then-stri
. m E( M6 a! C9 m% U来源:Internet。0 u, o/ I/ z) W) A2 P9 s
-END-! y4 ?) @5 t8 A
往期推荐:点击图片即可跳转阅读8 Z O* ?) I; @5 b1 \9 ` j/ p
5 ?& [/ k& p, X0 Y8 i8 X+ h
" i) P$ C2 B$ |# Q9 n : S; j* }; i A
3 z, g: A2 F+ j1 B/ o7 N# J! Z8 y
f1fitjorx3c64031166523.jpg
* T$ @6 f+ P7 E( E) l% A
' B& [, o9 Y( \0 e2 R. `8 | 适用于嵌入式设备,用 C 语言编写的轻量级日志库!. [; J) S1 s2 P, A1 O
* M7 V" X, B7 f4 y
: P+ @* B0 _) t7 N* c0 O
[3 }) L& g* c# |3 g- `* u # M2 Q- l& ?1 S1 ], {+ g
i253qyswdwp64031166623.jpg
/ n4 i$ F J% E; c
( {. | d( X! ]! n7 j1 c. i% q 工业现场经常用到的 RS485 通信,原来它的收发机制是这样的。
; Q6 H6 N6 w( B' r* r
, ?1 H6 ]0 z% T/ c5 F6 N ) a) q* t7 [" Y* g" ]
, H6 ?2 ^4 V# ~( ?# w+ F
mtv4j3ojbbc64031166724.jpg
, y& i8 a; v8 v
4 |; F4 d+ T1 C9 f
嵌入式应用程序开发,经常使用哪些数据结构?- L( B+ w Q h; l
$ C- O( f$ }& l- Y
g6 _# ?4 d0 L5 _' A/ J' E + C$ z2 Y$ A4 _0 C2 {
我是老温,一名热爱学习的嵌入式工程师
. [, R& ^9 f! `) w( @# ^关注我,一起变得更加优秀!( t: }( q, l8 ~2 Y& z2 _
cpqctnk0ss064031166824.png
|
|