|
我是老溫,一名熱愛學(xué)習(xí)的嵌入式工程師" D+ [2 o& s* J/ w
關(guān)注我,一起變得更加優(yōu)秀! 1在嵌入式設(shè)備應(yīng)用場(chǎng)景中,系統(tǒng)日志時(shí)?梢员O(jiān)控設(shè)備軟件的運(yùn)行狀態(tài),及時(shí)記錄問題點(diǎn)以及關(guān)鍵信息,方便開發(fā)人員后期定位以及解決問題。本文將講述一種簡(jiǎn)易的系統(tǒng)日志記錄方法,用于保存設(shè)備的系統(tǒng)日志,視具體嵌入式設(shè)備情況而定,可存儲(chǔ)在 MCU 內(nèi)部Flash、外部 Flash、EEPROM等,本文采用外部 Flash 作為示例展開介紹。1 j [. I. \$ c4 V* h! y% Q
思路分析對(duì)于系統(tǒng)日志可以當(dāng)成文件系統(tǒng),可以劃分為三個(gè)重要部分:目錄區(qū)、參數(shù)區(qū)、日志區(qū)。
! t) W, k* l7 ~6 ^2 a9 r* z5 R0 P目錄區(qū):根據(jù)日期進(jìn)行歸類,記錄當(dāng)天的日志的存儲(chǔ)地址、日志索引、日志大小,通過目錄可以獲取整個(gè)日志文件的概況;
! S$ }* ~) S% d; C8 k參數(shù)區(qū):存儲(chǔ)記錄日志寫位置、目錄項(xiàng)個(gè)數(shù)、寫狀態(tài)等參數(shù);, P I4 X) E" f6 [& u
日志區(qū):這是我們主要的存儲(chǔ)區(qū),記錄系統(tǒng)的日志,支持環(huán)寫。# Q# H" W* I% O: [
這三個(gè)區(qū)域都需要占用部分內(nèi)存,可以自行分配大小。5 r; i) t4 ^/ G, {( n. k* I
實(shí)現(xiàn)的效果如下圖所示,設(shè)置通過指令可查詢到整個(gè)日志目錄區(qū)的概況。查詢系統(tǒng)日志目錄:AT+CATALOG?
; E" M* n0 J. u; s2 s* z. mLOG_ID: 存儲(chǔ)日志按日期分類,該ID用于查詢對(duì)應(yīng)日期日志,從1開始計(jì)數(shù);
! v( H; {, i1 |) QLOG_DATE: 系統(tǒng)日志存儲(chǔ)日期;7 s$ h! w0 t9 c# B6 n! C/ P& a& l
LOG_ADDR: 系統(tǒng)日志存儲(chǔ)外部FLASH地址;
6 M3 s2 A. q! l8 p) VLOG_OFFSET: 系統(tǒng)日志存儲(chǔ)偏移量(各日期日志大小,單位:字節(jié))。6 q& _3 s7 A; |0 ^
iejtqngseu4640722125.png (8.1 KB, 下載次數(shù): 0)
下載附件
保存到相冊(cè)
iejtqngseu4640722125.png
前天 23:19 上傳
) d+ y% R% f% l4 }( S
J7 H" S+ ]! r: q- I5 T( e: g查詢指定日期系統(tǒng)日志:AT+CATALOG=
! g6 o' k" o9 f7 YLOG_ID:在查詢系統(tǒng)日志目錄時(shí)獲取,當(dāng)LOG_ID為 0 時(shí),為查詢整個(gè)系統(tǒng)日志。
" q) U% W: q& q
dblt2xzfh2m640722225.png (4.5 KB, 下載次數(shù): 0)
下載附件
保存到相冊(cè)
dblt2xzfh2m640722225.png
前天 23:19 上傳
9 C! n) @6 ], J* p$ {
: F# P& J$ `$ q l$ l另外提供移除系統(tǒng)日志(清除日志目錄)指令:AT+RMLOG,后面將講述具體實(shí)現(xiàn)。
) C$ q L H4 e5 x
) f! \0 C' z3 K7 E+ Y+ A; U3 g$ k1 gFLASH內(nèi)存劃分; }3 b+ z- p) N5 ?
FLASH內(nèi)存需要看具體設(shè)備進(jìn)行合理劃分,目錄區(qū)、參數(shù)區(qū)與日志區(qū)實(shí)現(xiàn)環(huán)形存儲(chǔ),延長(zhǎng)擦寫壽命。. @% e* \' N0 C5 B K/ m- Z
#define FLASH_SECTOR_SIZE ((uint32_t)0x001000)$ m$ D9 H5 |: E' @% g) o
#define FLASH_BLOCK_32K_SIZE ((uint32_t)0x008000)6 Y( T6 k; f7 N
#define FLASH_BLOCK_64K_SIZE ((uint32_t)0x010000)6 g/ h# D$ e+ X/ I4 v/ c
#define SECTOR_MASK (FLASH_SECTOR_SIZE - 1) /*扇區(qū)掩碼 ------*/
: ~/ F1 j- M8 V7 D' W" P#define SECTOR_BASE(addr) (addr & (~SECTOR_MASK)) /*扇區(qū)的基地址 --*/9 q9 \3 b! X) N N: k `
#define SECTOR_OFFSET(addr) (addr & SECTOR_MASK) /*扇區(qū)內(nèi)的偏移 --*/( x; P1 M# a; i: v ?' A8 ?
#define BLOCK_32K_BASE(addr) (addr & (~(FLASH_BLOCK_32K_SIZE)))
v1 m K* ^9 Z#define BLOCK_64K_BASE(addr) (addr & (~(FLASH_BLOCK_64K_SIZE)))
0 _' ] j- q R: T# X! utypedef enum {' O2 E2 t9 `. l$ u6 j
FLASH_BLOCK_4K = 0, /**/ a) ~& J" }! r1 c! c* z
FLASH_BLOCK_32K = 1, /**% {2 O( I% x9 u: {; U; B
FLASH_BLOCK_64K = 2 /**, v/ X- x( F! D$ U
}flash_block_t;6 E! z' N2 d) F4 ?
/* flash 空間索引 */( y- _+ z3 E' O0 b
typedef enum{
) |; S# f. C9 I" \ FLASH_CATALOG_ZONE = 0,
0 _" ?: h- W" n ~& v FLASH_SYSLOG_PARA_ZONE,9 y" M- o& j/ |
FLASH_SYSLOG_ZONE,+ N1 Q! e, K& Q/ W2 t
FLASH_ZONEX,
1 x4 c1 z. J7 x# ]2 E3 ~}flash_zone_e;
4 H, q. c; G& K6 z8 Y5 Z2 Htypedef struct{4 z% W( t4 } U, R+ S; |
flash_zone_e zone;
" \; y+ s0 h% h, \) `- u3 S uint32_t start_address;9 ^4 K+ w' P7 z: [( X5 t& A& {0 r
uint32_t end_address;
4 s( t7 r/ r1 M8 d* R}flash_table_t;
/ j1 l% R, j4 B6 t! S0 d, y/* 地址劃分 */
0 h3 \: ]5 k2 ?+ `9 @static const flash_table_t flash_table[] = {7 W8 U, U! G: O( o; y4 B' |3 m
{ .zone = FLASH_CATALOG_ZONE, .start_address = 0x03200000, .end_address = 0x032FFFFF},
" ]; |+ u0 v4 {4 O+ q1 ~9 o* b { .zone = FLASH_SYSLOG_PARA_ZONE, .start_address = 0x03300000, .end_address = 0x033FFFFF},
6 V A7 w8 g* R( w8 m. V { .zone = FLASH_SYSLOG_ZONE, .start_address = 0x03400000, .end_address = 0x03FFFFFF}, P* G; b" H* a. ]& k D' h
};Flash底層實(shí)現(xiàn)擦除、讀寫操作接口,由讀者自行實(shí)現(xiàn)。0 N) L; k: C9 r" ?- [
flash_table_t *get_flash_table(flash_zone_e zone)# y* [. H& Y P0 h' [
{/ K' f& {5 V4 q* S/ H
int i = 0;
$ U, Y6 ~' l3 |2 _' P0 h for (i = 0; i if (zone == flash_table.zone) ' z2 w. K( U: i. X. C6 n
return (flash_table_t *)&flash_table;
" R9 l7 D# `7 A- `! e( m }
) g* p& [9 ?% U' b3 y1 o/ Y- i I - ?8 M/ e2 `2 I9 }2 c( E
return NULL;
" b8 Z" {* V' l$ e: @7 t( q}
8 ` G' ^0 {) D' eint flash_erase(flash_zone_e zone, uint32_t address, flash_block_t block_type)
3 W5 {. Y4 y Z" v- z s{ O! O( _" R. a. ^
flash_table_t *flash_table_tmp = get_flash_table(zone);
2 i: Y/ m: x% J3 A1 S+ E 2 m8 i) i" d8 h4 a8 R0 {" M+ i
if (flash_table_tmp == NULL)0 l- H( C, X; R2 K
return -1;
; e o8 _/ h& E* S4 d9 M+ A
; i1 z8 q' x; B if (address start_address ||address > flash_table_tmp->end_address) ; B- _% l+ I. N
return -1;
7 i) D/ j0 z+ U# ~ k: E, t1 ` return bsp_spi_flash_erase(address, block_type);2 N( ]' \8 T) {& u! K
}
; [* c, O% _3 \) W, L! d# hint flash_write(flash_zone_e zone, uint32_t address, const uint8_t*data, uint32_t length)
9 c9 W" H8 G, A+ r{
9 ]6 L. c/ M7 u0 }& v( u7 ? flash_table_t *flash_table_tmp = get_flash_table(zone);
7 n+ g* S/ r& ] : K. }$ t; P* }* [6 g5 m
if (flash_table_tmp == NULL)
" u" S7 G) V/ L4 ]* Y return -1;
5 Y+ R h4 p, c; z if ((address start_address) ||((address + length) > flash_table_tmp->end_address)). x. @8 k7 a Y+ g6 U. N: T, G
return -1;
: ?, t2 ?7 w2 E; D3 z return bsp_spi_flash_buffer_write(address, (uint8_t *)data, length);+ o* z" f0 d9 S$ K
}
l4 X' i: H( W3 y Vint flash_read(flash_zone_e zone, uint32_t address, uint8_t*buffer, uint32_t length)( N, O& K! r7 l) w( r
{* D0 E# O6 D. y( \; B
flash_table_t *flash_table_tmp = get_flash_table(zone); @7 N# t5 Z* ?+ r. u
3 j% J6 _- j/ J8 f+ i9 \- ] if (flash_table_tmp == NULL): @/ O I, K/ b/ `1 I; I
return -1;
7 e" U: o2 y* n2 o if ((address start_address) ||((address + length) > flash_table_tmp->end_address))
) d5 X6 y$ t) h! e) \* a: h& _3 @ return -1;
1 W/ s" \: B+ d) F( p7 r
: a2 J4 L# T- D+ K3 v bsp_spi_flash_buffer_read(buffer, address, length);
9 s( a5 U* z" l3 |+ e return 0;- {& J& M% P5 M+ r
}
, h$ r4 Y% j. p Q+ u參數(shù)與結(jié)構(gòu)體定義
; W& x' e- y6 i+ O9 |3 p/ s日志數(shù)據(jù)存儲(chǔ)時(shí)間戳,便于問題定位,需要實(shí)現(xiàn)RTC接口調(diào)用。
, c r- p* K5 Z% u9 G0 |typedef struct {
2 ^8 v$ g7 V, u5 F uint16_t Year; /* 年份:YYYY */( m% G6 x* s1 S6 {: D" b
uint8_t Month; /* 月份:MM */
( ?: ^& k& s: K Y6 X5 { uint8_t Day; /* 日:DD */5 Z: `/ q# M, K4 i k
uint8_t Hour; /* 小時(shí):HH */
/ j3 K7 ^: ]7 g, u4 d9 Z- ] uint8_t Minute; /* 分鐘:MM */( y |8 g/ I9 q: N# f1 w
uint8_t Second; /* 秒:SS */! n8 ]: S; |0 I0 w5 j$ x
}time_t;
' G" J# \% A% _2 h& jint bsp_rtc_get_time(time_t *date);參數(shù)區(qū)應(yīng)當(dāng)保證數(shù)據(jù)的正確性,應(yīng)加入?yún)?shù)校驗(yàn)存儲(chǔ),定義校驗(yàn)結(jié)構(gòu)體。#define SYSTEM_LOG_MAGIC_PARAM 0x87654321 /* 日志參數(shù)標(biāo)識(shí)符 */
5 |* U) ^4 X: v4 ]' atypedef struct {. Q2 D5 T; }6 `% k! s0 k% s2 s
uint32_t magic; /* 參數(shù)標(biāo)識(shí)符 */9 u2 t+ {. m. e. X+ b4 _% N
uint16_t crc; /* 校驗(yàn)值 */
. @9 {- w+ [0 U3 X. f, T uint16_t len; /* 參數(shù)長(zhǎng)度 */- ~0 Y6 V8 Q/ n1 ?7 N
} single_sav_t;參數(shù)區(qū)需記錄當(dāng)前日志記錄的寫位置,以及目錄項(xiàng)個(gè)數(shù),還有日志區(qū)和目錄區(qū)環(huán)寫狀態(tài),并且存儲(chǔ)最新時(shí)間等等。! ]8 R1 U5 n! m- I& @ ]
/* 日志區(qū)參數(shù) */8 P3 g& _- y3 ~3 {- K' _
typedef struct {
: I5 X1 O# B. q6 ` uint32_t write_pos; /* 寫位置 */
; j; p# `& U8 J: X* U4 W uint32_t catalog_num; /* 目錄項(xiàng)個(gè)數(shù) */8 z) x0 o7 l9 m' H6 T
uint8_t log_cyclic_status; /* 系統(tǒng)日志環(huán)形寫狀態(tài) */
2 ^& G% Y6 ?% s& y uint8_t catalog_cyclic_status; /* 日志目錄環(huán)形寫狀態(tài) */
3 T$ }3 p& n* a6 O+ i3 J time_t log_latest_time; /* 存儲(chǔ)最新時(shí)間 */5 T6 _- X' n9 Z& w- I. O7 J
}system_log_t;
' h n+ U4 ?6 W H/* 目錄區(qū)參數(shù) */
8 Y( N: p4 Y# n `4 K+ btypedef struct {
( N1 ?' u6 i( U7 m+ z2 V uint32_t log_id; /* 日志索引 */
8 A; g" b1 j, r- d: r uint32_t log_addr; /* 日志地址 */
+ I1 n# g" r8 ]9 n: p$ s uint32_t log_offset; /* 日志偏移大小,單位:字節(jié) */
2 N, k$ U* N/ G1 E. J7 Z time_t log_time; /* 日志存儲(chǔ)時(shí)間 */
5 `0 l2 n2 [$ {; ^& v}system_catalog_t;, f; z) |( a8 \6 V
/* 系統(tǒng)日志參數(shù) */# k2 G3 [: \( j( M8 E* V5 Y1 L
typedef struct {0 x* x( P: q. A
single_sav_t crc_val;4 f! W% I4 B) Z# n# O
system_log_t system_log;; _+ Y4 F2 f% }! ]: D
system_catalog_t system_catalog;
* q3 e+ J+ @# D& p# N}sys_log_param_t;
* ?: l) X$ s4 Q' ^% l9 K* V) }3 jtypedef struct {/ S q2 F' P4 M' N9 v; K2 H
uint8_t system_log_print_enable; /* 系統(tǒng)日志打印使能 */
$ E) k+ h: B$ f) B0 b6 d: {/ g uint16_t system_log_print_id; /* 打印指定id系統(tǒng)日志 */
- F; o3 Y( C: P7 _! f uint32_t system_log_param_addr; /* 當(dāng)前日志寫地址 */- ?* _) D8 X) }2 A; l2 U
} sys_ram_t;
% g& z4 S7 }/ A5 P$ ~) ] C5 ^/ Gsys_ram_t SysRam;
* D: R) ~! B% I$ Z* i/ Y- C/ {sys_log_param_t SysLogParam;
- E: I9 x3 n( c% @. j: G0 Ysys_ram_t *gp_sys_ram = &SysRam;
4 |, f( T) x1 s2 n, ksys_log_param_t *gp_sys_log = &SysLogParam;實(shí)現(xiàn)接口說(shuō)明CRC校驗(yàn)接口,可以自定義實(shí)現(xiàn)。/* 16位CRC校驗(yàn)高位表 */6 z9 \0 z! O" F& c, G5 L! t
static const uint8_t auchCRCHi[]={
1 k; A7 e9 R& W! W! n* G/ I# O0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,
6 V" V: |: F. z [& t5 _& V0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,: y) w. z. q( U4 U, W
0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,
; n- x9 N0 Q& }$ B7 W0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,
; D6 l* B4 M7 l0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,
2 Z$ T% o Z5 F, W3 Y( Q0 Y0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,
. n" N/ K/ X8 t' \5 S+ s0 [0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,
/ I; ^9 A) r- j& A" n& K% o1 n0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,& {' Q. a4 B/ g7 g) q
0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,
% L* e& x9 l+ Z7 H& \0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,; U$ {& v9 I7 p: Y8 y5 h0 f0 `
0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,/ o% u7 G0 o+ X% t" C* p% I$ |
0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,
2 }5 Y' U- G+ ~" d0 o& e0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,! K. |, _3 }# W
0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,
# m& a9 t' Y, l* j" X0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,
9 g7 A5 T% ?5 N" J8 E, _5 [% a8 a0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40# I& J+ q ?; x0 {! C; R
};
! Z8 a$ h+ f7 t4 Q3 t" {3 _9 z2 o/* 16位CRC校驗(yàn)低位表 */% v G8 \$ r/ b% P g
static const uint8_t auchCRCLo[]={
{* k+ k( x. a( x" s# L1 B0x00,0xc0,0xc1,0x01,0xc3,0x03,0x02,0xc2,0xc6,0x06,0x07,0xc7,0x05,0xc5,0xc4,0x04,
# _! W& R( [- o+ }- i0xcc,0x0c,0x0d,0xcd,0x0f,0xcf,0xce,0x0e,0x0a,0xca,0xcb,0x0b,0xc9,0x09,0x08,0xc8,
! o, A# B4 U" I; V0xd8,0x18,0x19,0xd9,0x1b,0xdb,0xda,0x1a,0x1e,0xde,0xdf,0x1f,0xdd,0x1d,0x1c,0xdc,- W' [! Y P1 e' v" ^
0x14,0xd4,0xd5,0x15,0xd7,0x17,0x16,0xd6,0xd2,0x12,0x13,0xd3,0x11,0xd1,0xd0,0x10,0 m2 N+ A6 a( P! u7 M5 u: C( H% u" G
0xf0,0x30,0x31,0xf1,0x33,0xf3,0xf2,0x32,0x36,0xf6,0xf7,0x37,0xf5,0x35,0x34,0xf4,0 L1 n# d6 g# K9 a" Z; v4 [
0x3c,0xfc,0xfd,0x3d,0xff,0x3f,0x3e,0xfe,0xfa,0x3a,0x3b,0xfb,0x39,0xf9,0xf8,0x38,3 [- B# P) G L
0x28,0xe8,0xe9,0x29,0xeb,0x2b,0x2a,0xea,0xee,0x2e,0x2f,0xef,0x2d,0xed,0xec,0x2c,- Z' F1 s/ q9 p/ C9 r0 d: N
0xe4,0x24,0x25,0xe5,0x27,0xe7,0xe6,0x26,0x22,0xe2,0xe3,0x23,0xe1,0x21,0x20,0xe0," K: {" Q4 @0 u7 b
0xa0,0x60,0x61,0xa1,0x63,0xa3,0xa2,0x62,0x66,0xa6,0xa7,0x67,0xa5,0x65,0x64,0xa4,, K+ E% c6 Q* u1 I# c' O
0x6c,0xac,0xad,0x6d,0xaf,0x6f,0x6e,0xae,0xaa,0x6a,0x6b,0xab,0x69,0xa9,0xa8,0x68,
1 m& n! D+ ~( c; ]1 t2 o0x78,0xb8,0xb9,0x79,0xbb,0x7b,0x7a,0xba,0xbe,0x7e,0x7f,0xbf,0x7d,0xbd,0xbc,0x7c,
1 Y+ b5 W' n7 C# N. _& n0xb4,0x74,0x75,0xb5,0x77,0xb7,0xb6,0x76,0x72,0xb2,0xb3,0x73,0xb1,0x71,0x70,0xb0, ]' _$ w1 U( `
0x50,0x90,0x91,0x51,0x93,0x53,0x52,0x92,0x96,0x56,0x57,0x97,0x55,0x95,0x94,0x54,
0 u2 V( a1 Y' O) _+ Z0x9c,0x5c,0x5d,0x9d,0x5f,0x9f,0x9e,0x5e,0x5a,0x9a,0x9b,0x5b,0x99,0x59,0x58,0x98,
# {! t% E' }0 e3 F# H0x88,0x48,0x49,0x89,0x4b,0x8b,0x8a,0x4a,0x4e,0x8e,0x8f,0x4f,0x8d,0x4d,0x4c,0x8c,
! S, U; m( T$ I* e/ e# O, g; O# ]9 r5 \0x44,0x84,0x85,0x45,0x87,0x47,0x46,0x86,0x82,0x42,0x43,0x83,0x41,0x81,0x80,0x40! Y% [% S) t8 r5 I6 J( C
};. f' J! H, t6 l" k0 c5 q% c
/* 實(shí)現(xiàn)crc功能函數(shù) */ k0 h$ \) [/ D2 d3 l. m" G0 }+ \9 ]
static uint16_t CRC16(uint8_t* puchMsg, uint16_t usDataLen)
" O8 e1 i: ?( z{4 [" V6 i+ G/ W2 m
uint8_t uchCRCHi=0xff;
" x) B# M0 u9 Y. v0 S" m uint8_t uchCRCLo=0xff; U+ x, i* z7 d' y
uint16_t uIndex;
1 L9 C7 H# J( |. Q" e+ J " K# Z" x! P% @: ~% }
while(usDataLen--) {
/ T; f$ r* J( q( @# f3 X8 f uIndex=uchCRCHi^*(puchMsg++);
: [% k7 E% M- ~ G5 M- n uchCRCHi=uchCRCLo^auchCRCHi[uIndex];: j9 F: q+ G# K! u. C# L% z
uchCRCLo=auchCRCLo[uIndex];% I$ u7 t5 ?* ?/ `% i" h
}. q9 l3 W. N- f! K3 ?
" Z! K; Y, }; t" h8 }( }" G return uchCRCHi8|uchCRCLo;
! y* T) L! l5 C; P}保存系統(tǒng)日志參數(shù),每實(shí)現(xiàn)寫日志操作后都需要保存當(dāng)前的參數(shù)值,防止意外丟失。( ]; J/ t% U" B5 j5 J8 R/ F! h
void save_system_log_param(void)7 x% a0 ~; s# M& n/ T5 l
{8 K+ O9 r% a$ X7 |# F% m
uint32_t i = 0;
# P& n( w* y' y) Z) D4 S uint32_t addr = 0;! u! u7 M/ A2 F6 ^7 d
uint32_t remainbyte = 0;2 `5 P/ y, p+ Z3 [; N
uint32_t start_addr;
( d/ N2 H$ A( W int len = sizeof(sys_log_param_t);
! D; q% y0 M+ g5 f2 Q uint8_t *pdata = (uint8_t *)&SysLogParam;8 d9 G+ R \, a4 \2 R' s! J
flash_table_t *flash_tmp = get_flash_table(FLASH_SYSLOG_PARA_ZONE);" p0 ?5 S' i( |, S1 K( z5 {
~7 T; z5 Z; r" F' f /* 校驗(yàn)參數(shù) */
6 t+ j w+ H( l gp_sys_log->crc_val.magic = SYSTEM_LOG_MAGIC_PARAM;9 S4 f( }5 T2 ^
gp_sys_log->crc_val.len = sizeof(sys_log_param_t) - sizeof(single_sav_t);: F$ M- O9 d; d. h4 N9 }4 l9 |3 C8 s
gp_sys_log->crc_val.crc = CRC16(&pdata[sizeof(single_sav_t)], gp_sys_log->crc_val.len);" @, I4 c8 i n, T( R
start_addr = gp_sys_ram->system_log_param_addr;5 d8 b( s! g4 [' M8 ]1 U" E
/* 剩余內(nèi)存不夠?qū),則重新從起始地址開始寫,實(shí)現(xiàn)環(huán)形存儲(chǔ)功能 */
6 b* X' o% \3 B8 G! K; I/ k if ((start_addr + len) > flash_tmp->end_address) { $ t7 b1 S- C$ m* F$ [+ h
start_addr = flash_tmp->start_address;
/ }& F3 N1 l4 H) p. l }$ a7 `5 P8 z% J4 r
gp_sys_ram->system_log_param_addr = start_addr + len;
) f: E. \& y0 [ /* 首地址存儲(chǔ),擦除整個(gè)系統(tǒng)日志參數(shù)存儲(chǔ)區(qū),如果劃分的內(nèi)存較大,可能出現(xiàn)第一次擦寫等待時(shí)間較長(zhǎng),
% O, s+ M" h N 但實(shí)際應(yīng)用嵌入式設(shè)備應(yīng)該不會(huì)占用太多的內(nèi)存存儲(chǔ)系統(tǒng)日志,只當(dāng)為輔助使用,有額外應(yīng)用可自行實(shí)現(xiàn) */
8 m: f1 r; q: `2 y( F if (flash_tmp->start_address == start_addr) {5 w* Z' R# b* x7 {4 s* ^- y- M
/*for (i = flash_tmp->start_address; i end_address; i+= FLASH_SECTOR_SIZE)
, C' P2 q# k8 U( b9 { flash_erase(FLASH_SYSLOG_PARA_ZONE, SECTOR_BASE(i), FLASH_BLOCK_4K);
9 ]; E1 b" r4 e" j */
1 K) P4 Y. n0 q. A& f addr = flash_tmp->start_address;9 E5 y( I% D' u5 p! ]6 U5 S: @
do {
; [+ V& j# |% G if ((addr + FLASH_BLOCK_64K_SIZE) end_address) {
5 R, y) w$ p: o9 T/ F. u8 w' o flash_erase(FLASH_SYSLOG_PARA_ZONE, BLOCK_64K_BASE(i), FLASH_BLOCK_64K);
! ]# N; t# o% h& s! W8 B: Q1 D% s- \ addr += FLASH_BLOCK_64K_SIZE;
9 H5 D& V7 S& i6 { } else if ((addr + FLASH_BLOCK_32K_SIZE) end_address) {
4 v8 o: b) ]' c& ` flash_erase(FLASH_SYSLOG_PARA_ZONE, BLOCK_32K_BASE(i), FLASH_BLOCK_32K);
5 e& ], a; E: Y) G T, Q addr += FLASH_BLOCK_32K_SIZE;# u5 y m* f* e2 T. ?1 h2 }5 ~
} else if ((addr + FLASH_SECTOR_SIZE) end_address) {
1 x$ n) ~8 ^' S5 @& R$ Y' | flash_erase(FLASH_SYSLOG_PARA_ZONE, SECTOR_BASE(i), FLASH_BLOCK_4K);* v( }, D4 f- M) F
addr += FLASH_SECTOR_SIZE;; P0 k' ?/ J0 K6 ?/ ?+ a
} else {
- C; \" a) o+ c$ P break;
: M9 A( w; q/ t4 X }
! G4 Y0 g3 O* T; H7 [- [$ I } while (addr end_address);
* ~& j1 d; I T1 H$ N5 l0 I }
' \7 X5 D3 @( M( G2 e remainbyte = FLASH_SECTOR_SIZE - (start_addr % FLASH_SECTOR_SIZE);
; } A3 S) j/ ~7 ?! S: b if (remainbyte > len) {: m$ L5 p0 ~4 s4 T( Q+ a
remainbyte = len;4 i8 h" `; v6 c5 L( Q9 w/ h
}+ f: ]8 h/ b/ v& ?0 N
while (1) {3 c" d7 Z6 |2 |9 ?6 B
flash_write(FLASH_SYSLOG_PARA_ZONE, start_addr, pdata, remainbyte);8 I% S4 I7 R0 w1 L$ f! D6 M7 q$ p
if (remainbyte == len) {
$ Q- X6 p) Z9 S: v1 @4 t! V. x) X break;; G& s! Z) h: b3 Q" T: x1 `. G
} else {4 k S3 X; u6 n. t1 ^0 M$ `
pdata += remainbyte;' h1 x- F* u4 G" S! U+ g2 s
start_addr += remainbyte;+ |% i# ?/ \" h# i! K5 a
len -= remainbyte;
6 J+ M1 n: v4 j remainbyte = (len > FLASH_SECTOR_SIZE) ? FLASH_SECTOR_SIZE : len;9 B7 K G) ~" N
}
8 k: E) e8 p5 \ }
$ L& I$ g6 T, m; ^: g7 I}導(dǎo)入系統(tǒng)日志默認(rèn)參數(shù)接口,初始化默認(rèn)參數(shù)或者移除日志。6 O7 ^2 \, ]" h# s4 v$ w* N
void load_system_log_default_param(void)
2 N6 \* n7 B* o: y3 p. f{6 E+ P% |7 D, A6 ]7 z
/* 系統(tǒng)日志默認(rèn)參數(shù) */
0 w0 s& N9 X0 [* @. {/ V /* 目錄環(huán)寫狀態(tài)標(biāo)志 */' f4 M$ S( Z" ^1 a5 H
gp_sys_log->system_log.catalog_cyclic_status = 0x00;
6 c7 L) T$ a. p /* 目錄項(xiàng)個(gè)數(shù) */
& H' _; O: g6 q9 ]/ y/ D gp_sys_log->system_log.catalog_num = 0;
. j3 g# y) W7 {, B* n6 k; u! v6 s /* 日志環(huán)寫標(biāo)志 , 1:環(huán)寫狀態(tài) */
/ u* k: Q q. w0 u' U; C gp_sys_log->system_log.log_cyclic_status = 0;8 h. Z; y% |$ P. {& [8 T, E
/* 設(shè)置默認(rèn)值,實(shí)際會(huì)重新從RTC獲取最新時(shí)間 */' h; r3 L( g; m& c
gp_sys_log->system_log.log_latest_time.Year = 2019;# F6 v2 ^# X+ q5 t1 ?8 R
gp_sys_log->system_log.log_latest_time.Month = 5;, D% w" Y) p V, l
gp_sys_log->system_log.log_latest_time.Day = 8;; P1 m- l9 U/ X# x- ?
gp_sys_log->system_log.log_latest_time.Hour = 13;
1 i( X( a% m% M5 i gp_sys_log->system_log.log_latest_time.Minute = 14;/ s/ c: f* z7 o4 r/ F! _7 R
gp_sys_log->system_log.log_latest_time.Second = 10;
; q- n, m. C( C" r /* 日志寫位置從0開始 */
. @. @+ q. H4 z gp_sys_log->system_log.write_pos = 0;/ m; N* x, o! K. w+ z
6 L7 a: _( i+ d& u M$ r9 H( g9 X
gp_sys_log->system_catalog.log_addr = 0;
6 ~# k( o0 @0 c0 `$ ? gp_sys_log->system_catalog.log_id = 0;/ c- o/ i+ d8 X
gp_sys_log->system_catalog.log_offset = 0;
& `1 b+ i @3 _7 ^ i& o gp_sys_log->system_catalog.log_time.Year = 2019; J# n/ w R, {0 k. i" i6 M
gp_sys_log->system_catalog.log_time.Month = 5;
t9 v% ^9 [3 P5 i( @5 ~8 a gp_sys_log->system_catalog.log_time.Day = 8;
6 S. m% N& }! N0 h1 M; M: G$ V4 \ gp_sys_log->system_catalog.log_time.Hour = 12;
/ O# E9 K: _2 E' G gp_sys_log->system_catalog.log_time.Minute = 12;
+ E- e# t: {6 _* d* S gp_sys_log->system_catalog.log_time.Second = 14;
: m0 t4 G$ O0 j6 b. K3 | 3 O" q6 c4 _# `7 d+ T# d6 W
gp_sys_log->crc_val.magic = SYSTEM_LOG_MAGIC_PARAM;
/ U- M# @) \: e4 P7 A0 m7 y' n /* 導(dǎo)入默認(rèn)參數(shù)后進(jìn)行保存 */
5 ~. s' {: d3 U b0 o save_system_log_param();
: E- j! d; g! e }6 N1 p}" J, \. `; T5 n4 Q# R& z0 z/ O7 ]
設(shè)備開機(jī)或者復(fù)位都會(huì)進(jìn)行導(dǎo)入系統(tǒng)日志參數(shù)操作,恢復(fù)日志讀寫參數(shù),參數(shù)區(qū)為頻繁讀寫操作區(qū)域,每一次寫操作都會(huì)進(jìn)行一次偏移,有效的導(dǎo)入?yún)?shù)方法是從參數(shù)區(qū)結(jié)束地址到起始地址進(jìn)行掃描,掃描不到合法的參數(shù)則會(huì)導(dǎo)入默認(rèn)日志參數(shù)。/* 參數(shù)初始化,在終端啟動(dòng)時(shí)調(diào)用 */
' ]2 h) O* t2 e% C5 |& Tint load_system_log_param(void)
( h( o8 {# X" a{8 g: t5 U" \" K5 o, h: H3 k5 Z
uint32_t i = 0;
# w5 `9 L! k6 a, t# P8 @) q2 t' q! L single_sav_t psav;/ z# [, t. t( p/ y* ?
uint32_t end_addr;
; B' g: w S! n0 c9 B uint32_t interal = sizeof(sys_log_param_t);' ?9 ^/ M* S; t6 O1 u7 Z& r
int data_len = sizeof(sys_log_param_t) - sizeof(single_sav_t);/ d: b; f% F( C' x# [0 t9 D& F% r
uint8_t *pram = (uint8_t *)&SysLogParam;
3 G3 M( H% s( [/ {5 W! r flash_table_t *flash_tmp = get_flash_table(FLASH_SYSLOG_PARA_ZONE);
/ H5 t3 |; ]4 A) E9 Z9 Z. f
- x4 p8 w& k4 q ] end_addr =flash_tmp->end_address - (flash_tmp->end_address - flash_tmp->start_address) % interal;
: A! p9 V( u# c8 Z' e0 N1 q* [- z for (i = end_addr - interal; i > flash_tmp->start_address; i -= interal) {
" q- e$ F( p/ ? a5 | flash_read(FLASH_SYSLOG_PARA_ZONE, i, (uint8_t *)&psav, sizeof(single_sav_t));
R% O s8 j6 u/ q" e if ((psav.magic == SYSTEM_LOG_MAGIC_PARAM) && (psav.len ==data_len)) {
2 D* k9 F1 _3 A- c% X+ j" z! P flash_read(FLASH_SYSLOG_PARA_ZONE, i + sizeof(single_sav_t), &pram[sizeof(single_sav_t)], data_len);- m6 f7 E2 j5 A( Q, P8 a H8 [# b" Z5 o
if (psav.crc != CRC16(&pram[sizeof(single_sav_t)], data_len)) 3 `& m* Z8 O9 B7 M0 e- }
continue; c3 \1 v3 X+ e, V6 q5 W2 D" Z
gp_sys_ram->system_log_param_addr = i;- ]2 S9 ^* Q& J* F6 {1 k" `9 _
log_info("Load System Log Param Addr[0x%08x]!", gp_sys_ram->system_log_param_addr);2 k' M& l# w. r
return 0;: _1 Z) f/ {: s e( M5 d7 y
}0 h+ c2 m. J8 O& b1 K0 h/ M7 T
}3 C% W g- R: B. g
1 j( O, S5 S7 m. R( } /* 掃描不到合法的參數(shù),導(dǎo)入默認(rèn)系統(tǒng)日志參數(shù) */5 v% Y' ^( N9 x
load_system_log_default_param();7 J Q% M- T% ?4 [7 w$ M. M' a" f
/* 獲取日志寫地址 */9 L" ^! }. b) @4 R0 Y! d! |9 {3 E
gp_sys_ram->system_log_param_addr = flash_tmp->start_address;
7 t( @3 }* g" \! m9 d3 h log_info("Load System Log Param Addr(Default)[0x%08x]!", gp_sys_ram->system_log_param_addr);
+ P' [1 ?; E' V) }1 w return 1;
4 V# X3 Q& |1 x7 ^, t7 y. X- x}
9 z1 o3 H( [2 }0 K& O讀寫系統(tǒng)日志目錄接口,讀寫指定日志索引目錄信息。實(shí)際實(shí)現(xiàn)會(huì)定義最新的目錄信息存儲(chǔ)在日志參數(shù)區(qū),當(dāng)日期發(fā)生改變,則表示當(dāng)前目錄信息已經(jīng)完結(jié),將最新的目錄信息錄入日志目錄區(qū)保存,最多每天寫入一次目錄區(qū)。/* 讀取日志目錄區(qū)指定日志索引目錄信息 */
, @& w9 f2 R/ O3 V' R# ?int system_catalog_read(system_catalog_t *catalog, uint32_t id)7 b5 C/ h1 g& J3 g9 d. F
{$ I3 o% \5 |3 l5 N2 x" `$ @
uint32_t addr;9 ?' | y5 X& ~0 s
int rlen = sizeof(system_catalog_t);
' o' m2 w) X' c6 I5 j( {6 `9 Q uint8_t *pbuf = (uint8_t *)catalog;
8 R2 F$ Q* k: o3 \ flash_table_t *flash_tmp = get_flash_table(FLASH_CATALOG_ZONE);
; y( `2 c: o ?* S( f% D, O* O2 \( ] if (0 == id)
; X8 y6 {+ i2 q' I return -1;
" _# h' h( O% B2 G5 k addr = flash_tmp->start_address + (rlen * (id - 1));) r4 n- d0 I# F \- `* Y. C! {
if (addr > flash_tmp->end_address) 9 [, v4 |) D* i' Y/ C
return -1;
6 P3 G& g2 {( E3 V" d
, K) o6 k) R9 W return flash_read(FLASH_CATALOG_ZONE, addr, pbuf, rlen);
: ~( W5 E) o( s& F! ]0 s' L}
7 ~) d0 T. A; g% V/* 寫日志目錄區(qū)目錄信息 */3 X9 Z# J c9 }
int system_catalog_write(system_catalog_t *catalog, uint32_t id)
. W1 x4 y2 U( _4 y! x8 G' x{
% |: b* D1 k5 z1 z3 t4 |- v uint32_t start_offset;+ d9 u; w9 f* ~1 w
uint32_t start_addr;+ N3 j4 Y9 I% i9 o
uint32_t start_base;
& P! q5 u2 D$ C$ P" N9 P uint32_t remainbyte;5 @2 V8 @3 k3 W: W
int wlen = sizeof(system_catalog_t);; s/ n& X' j6 ^
uint8_t *pdata = (uint8_t *)catalog;
4 l3 I6 o5 C! X6 J flash_table_t *flash_tmp = get_flash_table(FLASH_CATALOG_ZONE);( `, A- G" A8 t- z3 G
. x( e z8 m# [6 K: M5 h5 o/ g1 L if (0 == id) return -1;
$ y1 x+ _+ c8 n* m2 K0 \ start_addr = flash_tmp->start_address + wlen * (id - 1);6 X" ^! a0 A0 B) }5 g$ A
if ((start_addr + wlen) > flash_tmp->end_address) {2 a" |& e8 K$ M8 b8 g5 b% |
start_addr = flash_tmp->start_address;$ H4 a! R& \' C K" O
}
$ t0 f4 u9 o8 V; D1 `3 h0 b ' [6 r' j7 z( [
/* 本扇區(qū)剩余空間大小 */' Y; S; l; y: `+ H
remainbyte = FLASH_SECTOR_SIZE - (start_addr % FLASH_SECTOR_SIZE);" V/ g) G# e, ^% B" j6 w0 G. i
/* 寫入數(shù)據(jù)長(zhǎng)度小于本扇區(qū)剩余長(zhǎng)度,直接寫入 */
- H5 L0 f* `; D/ o if (remainbyte > wlen) {
" j2 F! b2 |% y8 A9 v* o remainbyte = wlen;
5 t: B7 e3 G* u& |" R0 N( [8 N' K }
; K9 t, \! a9 Q' K/ u /* 寫目錄次數(shù)不會(huì)太頻繁,視具體情況改寫操作實(shí)現(xiàn) */; d* T4 R5 r0 E. `4 h
while (1) {
+ a/ m3 F7 S8 I! u2 e start_base = SECTOR_BASE(start_addr);: }; v2 O3 F% b( h/ ^- F
start_offset = SECTOR_OFFSET(start_addr); e0 `' \% @/ Y# P4 N% L
flash_read(FLASH_CATALOG_ZONE, start_base, sector_buf, FLASH_SECTOR_SIZE);
& f% v$ m: ]. K. O+ }) d3 M' A flash_erase(FLASH_CATALOG_ZONE, start_base, FLASH_BLOCK_4K);6 N0 {4 ]) C4 C2 V
memcpy((char *)§or_buf[start_offset], pdata, remainbyte);& s8 o# I7 c" ]) q& c- S8 U
flash_write(FLASH_CATALOG_ZONE, start_base, sector_buf, FLASH_SECTOR_SIZE);) R& m! D/ R" E) I8 ]- e G
if (remainbyte == wlen) {- d! g9 }. S/ q2 Y. t- Z5 ~/ u4 s: o6 w0 m
break;3 B8 j* Q% p" m) X) n5 `( Y
} else {
2 Q* e ?' |; u% H6 F9 d pdata += remainbyte;
$ q) g. R/ e% C' b9 w8 ? start_addr += remainbyte;! k9 b5 k. J8 F; o) j
wlen -= remainbyte;5 v4 D9 O/ H) U! o% }- T
remainbyte = (wlen > FLASH_SECTOR_SIZE) ? FLASH_SECTOR_SIZE : wlen;, Z- K6 }8 v# I) v
}
3 }, p3 D1 x+ r! q+ R; v/ D }" C6 D- X" x; }2 Y2 z/ o6 a
) U3 t A x+ G) d. x6 b7 V3 Z/ E
return 0;
7 g8 j& c! G, G6 N; F) X* R}: d3 E* s/ ? @: B2 k, f
打印系統(tǒng)日志目錄區(qū)信息,可實(shí)現(xiàn)通過指令查詢到目錄區(qū)信息。int system_catalog_all_print(void)
- g8 L/ _4 D( i o. j' B8 O% o6 E5 Y& S{
7 l7 U3 ~0 \: I/ D$ u int i = 0;5 X; D( f( [3 g4 K1 N# Q" m
system_catalog_t catalog;/ g' G4 Y. Z' }. {* \6 p
printf("System Log Command Information:\r/ f ]4 p% e! T. k" q& U
");! Z7 ^# B8 j4 {8 `; W
printf("Query Specifies Log : AT+CATALOG=\r
) o T. z* k) ^ A# J% u0 v% S");
* S; P/ G- |# w1 a5 Z$ P printf("Query All Log : AT+CATALOG=\r' \' D7 w" L7 X% t* B
\r4 Q2 w- {" Z0 v6 r4 M# n
");+ r, t5 f5 F3 i2 ]/ ?. x& X
printf("Query All System Catalog:\r
3 q. X. s6 Z: ]! x4 Z");
O( J# c! \4 ~2 t- V printf("LOG_ID LOG_DATE LOG_ADDR LOG_OFFSET \r
* V: C! ? i1 ` f k9 d");! R8 D( x$ r3 x6 ~1 w
for (i = 0; i system_log.catalog_num; i++) {
" |" \* |: h' J( w% i) f /* 當(dāng)前最新目錄信息 */ 0 n5 @9 q9 z4 O# ~
if (i == (gp_sys_log->system_catalog.log_id - 1)) {
2 s* f l: e2 |% U catalog = gp_sys_log->system_catalog; /* 獲取當(dāng)前最新目錄信息 */
3 s( j5 F# y. e! d: F9 r } else {4 n. {# G3 H- ~6 R/ b
system_catalog_read(&catalog, i + 1);- X: g& \( e w" D
}% t9 o" W* S% `* w8 L( K
printf("%d %04d-%02d-%02d 0x%08X %d \r
4 w' B+ D# i0 c" G! g- i",
5 l& _' m' v& d: Q. N catalog.log_id, catalog.log_time.Year, catalog.log_time.Month, catalog.log_time.Day, 7 o# _$ e- x$ T6 b
catalog.log_addr, catalog.log_offset);
' p, S8 S7 e* w2 N. T6 ` memset((char *)&catalog, 0, sizeof(system_catalog_t));
$ v: R( k3 s5 Y% d }8 v9 Z( ^ n r; }
return 0;# v! w+ j# F! L& h; g5 Z" V
}
k Y+ M4 i+ R- F3 T! Y讀取指定日志目錄索引信息接口,可指定日志索引或者讀取全部日志數(shù)據(jù)。int system_log_task(int argc); Q! r } _7 H
{, }) m4 x( G. o. s9 e
int rlen = 0;. j% ~. _9 k: z; u( L
uint32_t offset, start_addr, end_addr;+ ]# h4 g/ a0 |& k
system_catalog_t catalog;0 _3 Z% s- x; t& R8 h/ f6 Q" z
flash_table_t *flash_tmp =get_flash_table(FLASH_SYSLOG_ZONE);
, }6 g. V9 C6 z& X/ j2 y ' ^, R, w2 ~0 R& _+ t( ]* Y# O% H
if (0 == gp_sys_ram->system_log_print_enable)
) i( M* ]5 q7 w; D7 F% b* e R return 1;
$ t1 D1 D) |- k3 z gp_sys_ram->system_log_print_enable = 0x00;5 D' `, e+ A0 I) J
if (gp_sys_ram->system_log_print_id == ALL_LOG_PRINT) {
7 ~- \% {5 x W U- t /* log回環(huán)寫標(biāo)志,打印整個(gè)LOG存儲(chǔ)區(qū) */, x5 ~# ~. v( f
if (0x01 == gp_sys_log->system_log.log_cyclic_status) {
- T3 J8 e- a* V% F/ } start_addr = flash_tmp->start_address;
2 u5 C' W r& k( c end_addr = flash_tmp->end_address;, H9 u4 }9 J) ~
offset = end_addr - start_addr;
) |9 q# @# ?) Z } else {
# P: D/ l9 y5 ]5 \5 z0 v* Y start_addr = flash_tmp->start_address;
# A; R8 a* b6 r# l end_addr = start_addr + gp_sys_log->system_log.write_pos;
0 P0 ?1 o C4 ~6 @ offset = gp_sys_log->system_log.write_pos;( s: T! i+ C$ Z
}" @5 I$ _; P B, I5 V D7 {- e" F/ ~
} else { /* 讀取指定ID日志 */# p( H6 _2 l4 D2 C
if (gp_sys_ram->system_log_print_id == gp_sys_log->system_catalog.log_id) {; ? {% |( f1 m
catalog = gp_sys_log->system_catalog;
7 p) I2 N( I8 Q } else {% L7 ^8 g* W$ ]
system_catalog_read(&catalog, gp_sys_ram->system_log_print_id);5 c( z( w1 \, G1 P9 t
}
7 T$ a' d' X! M) S. j: U A) |7 N8 p' m start_addr = catalog.log_addr;! k* j, {4 p$ ]1 ^* @0 ]
offset = catalog.log_offset;( q4 i- K) H7 T' g: y3 U% [. \% S
}
) s. m1 l$ n7 E6 y* e4 ?6 ] if (0 == offset)
. n; X) ^' v. d4 R/ T# j return 1;
+ c! ^$ X2 L8 R. Y" B1 ^ while (1) {# l/ f/ Y% }1 q' K2 }7 r
rlen = (offset > 512) ? 512 : offset;
7 z3 ?; V; B( g ?; ~4 l) e( s- ?7 V$ c$ ?! ? system_log_read(sector_buf, start_addr, rlen);
+ v. w9 v$ }% x! O' ]% L+ { HAL_Delay(80);
/ j6 W1 _! s, _ /* 目錄信息通過調(diào)式串口打印 */& J5 D6 P5 v% n
bsp_debug_send(sector_buf, rlen);. J; i6 j4 c+ W8 i- g
start_addr += rlen;1 w/ x* i$ b. Y$ v
offset -= rlen;3 E% U8 X3 n* ` w1 J; e' I
if (0 == offset)
$ D# _2 U/ i! ?) ^( Q3 E break;
) d: y! _; ^* A, q5 o5 g }" @8 m2 g' T( e+ D
return 0;3 \5 ]: t/ Y9 `- ^" c' S K' J
}5 `$ `/ T7 o! i0 K: x# V" W
存儲(chǔ)系統(tǒng)日志接口,實(shí)現(xiàn)更新存儲(chǔ)日期,當(dāng)寫位置為扇區(qū)地址,則擦除一個(gè)扇區(qū)作為存儲(chǔ)日志,這樣避免每寫一次就擦除一次。int system_log_write(uint8_t *wbuf, int wlen)8 A3 x1 \8 O! v" I, D5 ?- o: p
{
* @* q7 ]* X+ s& t2 d0 ? uint32_t start_addr;
/ A4 \3 i4 m% d uint8_t *pdata = wbuf;' B2 W2 ~7 J0 Z
uint32_t remainbyte;
0 }$ y4 l) `0 [ int system_catalog_max_id;
* i: D+ F+ Z; K4 ]% S flash_table_t *flash_tmp =get_flash_table(FLASH_SYSLOG_ZONE);
/ S2 m% L N' P# Q7 Y
O8 ^% _* Q1 h# R; L( F* ^/ ]( W /* 計(jì)算目錄區(qū)的最大存儲(chǔ)目錄項(xiàng)個(gè)數(shù) */- F( Q! I* q/ d* F" Q
system_catalog_max_id = ((flash_tmp->end_address - flash_tmp->start_address) / sizeof(system_catalog_t));
( Y4 \* C1 W# {" r1 _7 S start_addr = flash_tmp->start_address + gp_sys_log->system_log.write_pos;
! I! r7 k2 E% D. Z /* 存儲(chǔ)數(shù)據(jù)地址大于規(guī)劃內(nèi)存地址范圍處理 */
5 l' } i; ~8 ~ if ((start_addr + wlen) > flash_tmp->end_address) { v t1 c$ ?5 W" _. O
start_addr = flash_tmp->start_address;
0 a0 R$ X5 L, [5 Q) u /* 寫位置偏移量重置 */
7 P3 i! U U9 l) j, K, K D* B f gp_sys_log->system_log.write_pos = 0;8 `( y0 G8 l$ Z/ p2 \1 h% [
/* LOG回環(huán)存儲(chǔ)標(biāo)志置位 */
8 J3 [: y: E( k9 n( J gp_sys_log->system_log.log_cyclic_status = 0x01;
0 P" @! l. f: r' w }
& p: f$ [! U) N+ u9 R" W9 c- E( \ /* 寫位置偏移 */$ e) Q$ ] q: k
gp_sys_log->system_log.write_pos += wlen; 3 I0 k8 {0 E! o% h) r# p+ d
if ((gp_sys_log->system_log.log_latest_time.Year != gp_sys_log->system_catalog.log_time.Year) ||
+ s3 z) Q* y$ s4 U) \9 c% ^ (gp_sys_log->system_log.log_latest_time.Month != gp_sys_log->system_catalog.log_time.Month) ||: V$ V( C1 a8 ]8 T2 I6 \
(gp_sys_log->system_log.log_latest_time.Day != gp_sys_log->system_catalog.log_time.Day)) {0 L4 @2 t8 V5 b8 ]4 ?4 w. J# z
/* 日期改變,記錄目錄信息,當(dāng)log_id為0,則不寫入 */
1 ^$ f' A( i; q" {7 ?+ J system_catalog_write(&gp_sys_log->system_catalog, gp_sys_log->system_catalog.log_id); n- @1 B, B% j0 N# {) w' @1 D
/* 記錄存儲(chǔ)日期 */+ a( ~; ~& A+ M# s
gp_sys_log->system_catalog.log_time = gp_sys_log->system_log.log_latest_time;
" e* ^5 k" p8 N. [$ D* }; I( e : K3 ^5 v$ c2 q/ n
if ((gp_sys_log->system_catalog.log_id + 1) >= system_catalog_max_id) {
- O8 S) o+ m0 T3 K) s) h( X gp_sys_log->system_log.catalog_num = system_catalog_max_id; /* 目錄循環(huán)寫,目錄數(shù)應(yīng)為最大 */
, B& e0 r9 J9 [- ] gp_sys_log->system_log.catalog_cyclic_status = 1; /* 目錄回環(huán)寫標(biāo)志 */
p9 ^# y" F5 s9 k } else {; X- b& S4 B( `) H9 T) q# T
if (0 == gp_sys_log->system_log.catalog_cyclic_status) {! k3 s1 A' l$ x$ w" X
/* 獲取目錄數(shù) */- C- ?# T1 _: J; a
gp_sys_log->system_log.catalog_num = gp_sys_log->system_catalog.log_id + 1;
7 u( s2 D8 R8 p7 Z* |: Q }
g$ e7 ^$ R5 U }
: A1 l; K; J& O, F4 S" L
0 J$ B# Z! n& N! _ /* 存儲(chǔ)最新目錄項(xiàng)信息 */6 Y: z, P: N: p0 ~: \, M, v n" d8 @6 H
gp_sys_log->system_catalog.log_id = (gp_sys_log->system_catalog.log_id + 1) % system_catalog_max_id;2 J* Y; `) ?! _1 Y2 t8 {. ] ~ R
gp_sys_log->system_catalog.log_addr = start_addr;1 C& A! X% N" H& G, I1 a
gp_sys_log->system_catalog.log_offset = wlen; 5 |& _8 s$ d7 r
} else {$ [/ {( J* j+ n
gp_sys_log->system_catalog.log_offset += wlen;
3 H( c9 u( T, f }. T; X7 u; a+ ~. {5 Q
" d/ M# s7 M6 N# [4 D/ O /* 寫位置為存儲(chǔ)起始地址并且不為扇區(qū)首地址 *// a7 e% g! `, N8 N
if ((flash_tmp->start_address == start_addr) && (SECTOR_OFFSET(flash_tmp->start_address))){/ [1 e2 `/ w! J8 c
flash_read(FLASH_SYSLOG_ZONE, SECTOR_BASE(start_addr), sector_buf, FLASH_SECTOR_SIZE);2 U$ S# c! g' L' q# D1 {0 [; O! G
flash_erase(FLASH_SYSLOG_ZONE, SECTOR_BASE(start_addr), FLASH_BLOCK_4K);: p8 ~1 X& _ k
/* 將扇區(qū)頭部至起始地址區(qū)間的數(shù)據(jù)回寫 */- Z9 B5 g, g( F% a
flash_write(FLASH_SYSLOG_ZONE, SECTOR_BASE(start_addr), §or_buf[0], SECTOR_OFFSET(start_addr));
- z. J, A* |7 f0 K: U3 p3 F }
- o% N5 `; A; h+ u, v# I J5 m /* 寫位置為扇區(qū)首地址,則擦除一個(gè)扇區(qū)的存儲(chǔ)區(qū) */
# c5 ?& ~5 N1 `6 V) V1 | if (0 == SECTOR_OFFSET(start_addr)) {
' g7 @4 K$ c: @/ ~6 } flash_erase(FLASH_SYSLOG_ZONE, SECTOR_BASE(start_addr), FLASH_BLOCK_4K);8 L" [, t( S, N! ~ J; l& A; R
}
$ c+ Y! `" R. x; s6 O4 H /* 本扇區(qū)剩余空間大小 */8 [% \7 e% `8 s4 k
remainbyte = FLASH_SECTOR_SIZE - (start_addr % FLASH_SECTOR_SIZE);
" A; |0 |7 s8 L+ O: ]9 ? /* 寫入數(shù)據(jù)長(zhǎng)度小于本扇區(qū)剩余長(zhǎng)度,直接寫入 */6 W4 b$ E) x3 Z" T9 d: j3 T* Z9 P
if (remainbyte > wlen) {2 ^5 w* k# b& s& ?7 d
remainbyte = wlen;
8 x* H6 R; J. ~" |: Y }
+ e- X: E, R6 r. Z& G while (1) {4 z+ W/ D0 O# S6 w" \2 W9 J
flash_write(FLASH_SYSLOG_ZONE, start_addr, pdata, remainbyte);/ p. P1 S3 r* f! f
if (remainbyte == wlen) {! J! B( G; S5 b( W- j
break;9 X* o& ^( c) {/ m9 b, D
} else {
3 Q' b V3 X9 O# H+ q$ r; w pdata += remainbyte;
0 G0 Y, W* ?4 U; F( i start_addr += remainbyte;! l9 [* [0 W$ n1 Y1 ]+ a
wlen -= remainbyte;
1 z9 q1 v5 s' D3 E6 r+ R remainbyte = (wlen > FLASH_SECTOR_SIZE) ? FLASH_SECTOR_SIZE : wlen;1 h0 E0 C, F9 n- y& V9 E& J
/* 扇區(qū)首地址則擦除整個(gè)扇區(qū),該扇區(qū)數(shù)據(jù)不保存 */
1 S. a2 V' |$ J; r/ U3 k& v2 k9 V if (0 == SECTOR_OFFSET(start_addr)) {- \. \2 Q. e1 y/ F
flash_erase(FLASH_SYSLOG_ZONE, SECTOR_BASE(start_addr), FLASH_BLOCK_4K);
" @) W: u0 O; u) c4 I9 e) @: \ }
; R: }1 C( \6 n* ^ }
' |1 P+ p( S. T }9 _4 q/ Q8 x: H/ W0 q0 J, F
/* 環(huán)形存儲(chǔ)參數(shù) */
! s2 e' @3 A4 ]& d' R1 f- _ save_system_log_param();
6 {! q3 }1 C) t, r3 M6 y return 0;2 m- i3 O& ~$ a& q# n
} 系統(tǒng)調(diào)試對(duì)接3 r) n3 Z i0 z0 h, t) d
為了更好記錄系統(tǒng)日志,將應(yīng)用調(diào)試等級(jí)結(jié)合一塊,實(shí)現(xiàn)記錄錯(cuò)誤調(diào)試信息以及需要保存的關(guān)鍵信息。定義的調(diào)試等級(jí)有:關(guān)閉調(diào)試等級(jí)、錯(cuò)誤調(diào)試等級(jí)、警告調(diào)試等級(jí)、關(guān)鍵調(diào)試等級(jí)、debug調(diào)試等級(jí)。而 LOG_RECORD_LEVEL 將主動(dòng)保存日志并輸出信息,LOG_ERROR_LEVEL 會(huì)存儲(chǔ)對(duì)應(yīng)的日志信息,但需要根據(jù)應(yīng)用調(diào)試等級(jí)輸出信息。設(shè)置與讀取應(yīng)用調(diào)試等級(jí)由讀者自行定義。
0 f% M G/ Y' O/ i# g6 b* P( A9 O#define LOG_CLOSE_LEVEL 0x00 /* 關(guān)閉調(diào)試信息 */
" g- Y) `. j$ A$ w ~ i#define LOG_ERROR_LEVEL 0x01 /* 錯(cuò)誤調(diào)試信息 */
5 o* [" j" F! S* [4 m3 Z+ C! c3 u3 T) U#define LOG_WARN_LEVEL 0x02 /* 警告調(diào)試信息 */
4 h4 A9 a3 _: x% k#define LOG_INFO_LEVEL 0x03 /* 關(guān)鍵調(diào)試信息 */
& j( r1 @* V x1 F4 |" u9 i#define LOG_DEBUG_LEVEL 0x04 /* debug調(diào)試信息 */& v7 c& q% k: e- {
#define LOG_RECORD_LEVEL 0x10 /* 保存日志并輸出信息 */
/ T! q( c, `2 j#define LOG_PRINT_LEVEL 0xff ]' C3 t9 v* `2 a" Z
#define SET_LOG_LEVEL(LEVEL) (gp_sys_param->system_print_level = LEVEL)
8 `( }0 [' ^/ p; o5 K7 v#define GET_LOG_LEVEL() (gp_sys_param->system_print_level)
4 W+ f' u, K& k" j. k#define log_debug(fmt, args...) log_format(LOG_DEBUG_LEVEL, fmt, ##args) r" i& E0 h7 F2 L
#define log_info(fmt, args...) log_format(LOG_INFO_LEVEL, fmt, ##args)
$ E# j8 u. _ x# q! H+ E- t#define log_warn(fmt, args...) log_format(LOG_WARN_LEVEL, fmt, ##args)
4 {6 d# w3 @! m4 j# o3 }#define log_error(fmt, args...) log_format(LOG_ERROR_LEVEL, fmt, ##args)$ h% P$ P* h6 W0 `8 [2 K% @; `
#define log_record(fmt, args...) log_format(LOG_RECORD_LEVEL, fmt, ##args)
7 E& m" h$ U% Z2 F#define printf(fmt, args...) log_format(LOG_PRINT_LEVEL, fmt, ##args)
: t9 q) Y J u" mtypedef struct {6 t4 H9 |- i3 K* d% y B
int level;/ {1 f1 L( h A0 G4 f# {9 d5 J9 U
char *fmt_str; _. [+ f0 h7 `& _1 E- }' j9 v
}system_print_fmt_t;5 P: f U' ^0 F0 C0 b6 R5 g
system_print_fmt_t system_print_fmt_list[] = {
1 ?0 }1 J% B1 M( V1 i; q$ K4 L% @$ \ { .level = LOG_ERROR_LEVEL, .fmt_str = ":"},
# f: l9 e' B: Z7 z, M( m { .level = LOG_WARN_LEVEL, .fmt_str = ":"},5 I" k8 B$ L& C3 j* L+ l: I
{ .level = LOG_INFO_LEVEL, .fmt_str = ":"},& e. w' W I# D1 R; F% s# d& C
{ .level = LOG_DEBUG_LEVEL, .fmt_str = ":"},
/ }+ [6 W: s$ Y. E; s, G { .level = LOG_RECORD_LEVEL, .fmt_str = ":"},
# t1 |1 L* ?/ |};
; X: L: f) f) H5 o7 Lint log_format(uint8_t level, const char *fmt, ...)
# b9 ]* L7 ^! ^0 d4 a{7 Z5 G( F' g# L$ H
#define TIME_PREFIX_SIZE (21): G, y+ I2 @7 k! r0 i2 @3 E
#define PRINT_MAX_SIZE (1024 + TIME_PREFIX_SIZE)
2 q& N4 c8 l0 |& m3 e9 n/ a/ T; f 3 t3 p: p2 b- q* I4 S0 n! l F
va_list args;' a+ D: |* ^0 B+ A1 T
int num = 0, i = 0, fmt_index = 0;4 x# d) S# u$ b4 \/ r- n8 o
int fmt_str_len = 0, ret = -1;
+ @' C+ G2 L: H; C9 |9 H int file_str_len = 0, line_str_len = 0;/ i) ? k0 R V5 V; l& P8 a4 t- p
char line_buf[20] = {0};
1 b* I2 A* _+ V7 w static char buf[PRINT_MAX_SIZE];$ u; m- ]5 x5 e! v: H
static QueueHandle_t sem = NULL;* Q2 z* q* E3 J1 |8 O" t/ f
time_t time = {0};/ o! P+ g9 h7 c& O! u5 M8 h
+ S% j1 k/ I$ s
/* 針對(duì)os系統(tǒng) */% H8 i& e, l" \1 Z
if (NULL == sem) {
8 T( g) c, b8 ~! { sem = xSemaphoreCreateCounting(1, 1); /* always think of success */
& N6 q' E- A/ j# H4 { }; b! j8 ^1 U+ o _1 H N
( Q6 r0 l \5 _
xSemaphoreTake(sem, portMAX_DELAY);
# [* ^2 P5 v. f$ u8 h/ |5 { ret = -1;6 W- y1 s! n4 Z# M5 J
fmt_str_len = 0;# V# I7 O; h9 V! p9 m2 G
if (level != LOG_PRINT_LEVEL) {
9 p o F' ~7 C+ H1 h0 t+ B7 Y; ? if ((GET_LOG_LEVEL() goto exit_end;5 d( k) k" k- o G# C$ K
for (i = 0; i if (level == system_print_fmt_list.level) { t) h& o, B5 A1 v
fmt_index = i;
% h& S( s* ~/ p, e# R) p6 ?( x break;' l+ M2 m" W1 H3 Q" s$ C0 V
}+ O3 |3 e2 {: t! _
}
, u" q( w7 K8 P a; {" F) w if (i > SYSTEM_PRINT_FMT_LIST_MAX) {
. F! X: [ W* q1 q+ f goto exit_end;1 C& m" Z* |* T- |1 {0 m' a
}8 S) @3 ^. h8 E2 n
fmt_str_len = strlen(system_print_fmt_list[fmt_index].fmt_str);1 T% m; S! u6 G5 p0 }0 S
strncpy((char *)&buf[TIME_PREFIX_SIZE], system_print_fmt_list[fmt_index].fmt_str, fmt_str_len);/ X0 X0 u; S4 w% d5 i8 S$ ] |
}
8 _2 I# m8 F0 q- U# p! W va_start(args, fmt);
: ] s( \# q A* q7 p! L num = vsnprintf((char *)&buf[fmt_str_len + TIME_PREFIX_SIZE], PRINT_MAX_SIZE - fmt_str_len - TIME_PREFIX_SIZE - 2, fmt, args);: N2 B1 b8 V# I5 {. J4 ~
va_end(args);; ]" k# @9 u, S' C/ {
if (num 0) {2 g& i6 Y" e5 I& J
goto exit_end;
+ d m% V. P5 }* L3 r( ` }5 o& }3 c5 w' w1 ~
if (level != LOG_PRINT_LEVEL) {3 r) o. S/ }& s3 _! G+ {
num += fmt_str_len;
) {+ j! Q' B8 u1 c/ } buf[num + TIME_PREFIX_SIZE] = '\r';
9 ~! z. n3 S, e( d* S buf[num + TIME_PREFIX_SIZE + 1] = '
- O0 Q0 S2 w3 U4 X- y';6 ^$ k; O O5 u* V1 u3 Z
num += 2;
9 D% [7 y U# G/ ] }0 \- C5 y* Q% l" A
if ((GET_LOG_LEVEL() //do nothing
5 B. }7 K. b$ z, U% ?+ } R } else {. m' n" i& P# Q% z7 M' J
ret = bsp_debug_send((uint8_t*)&buf[TIME_PREFIX_SIZE], num); : c# S, V& ?+ E ]" M
} C z$ s& `" J8 o
if ((LOG_ERROR_LEVEL == level) || (LOG_RECORD_LEVEL == level)) {
* p2 y+ B) H9 H1 ]1 W& i; f+ q bsp_rtc_get_time(&time);! ` e2 @! q) A T$ O' x% Y
sprintf(&buf[0], "[%04d-%02d-%02d %02d:%02d:%02d",* Z) s* |5 Z* `0 n' b" D
time.Year, time.Month, time.Day,time.Hour, time.Minute, time.Second);6 \+ m- ~& c7 C# f s2 a
buf[TIME_PREFIX_SIZE - 1] = ']';
6 X t1 |, @: s) N5 n gp_sys_log->system_log.log_latest_time = time;
! K, T, j# [; u2 L+ h. w system_log_write((uint8_t *)buf, num + TIME_PREFIX_SIZE);& S# o0 K* A6 z2 B& U, f3 x- w
}
9 d; y" r) l5 j# J- lexit_end:
. j( L* v |" j5 s$ u: F xSemaphoreGive(sem);2 q" l) i+ ^ k" c' S: |
return ret;/ c, L1 n1 g1 I$ A/ m3 l" w/ k1 l
}結(jié)語(yǔ)4 {6 _; W5 a+ ^* m0 a" s* r: t( Q4 F
本文提供的一種簡(jiǎn)易嵌入式設(shè)備系統(tǒng)日志記錄方法,代碼量不多,實(shí)現(xiàn)簡(jiǎn)單,針對(duì)不同的設(shè)備需要合理規(guī)劃內(nèi)存使用。根據(jù)軟件運(yùn)行狀態(tài),加入調(diào)試信息并保存對(duì)應(yīng)的日志信息,方便開發(fā)人員了解系統(tǒng)或軟件運(yùn)行狀況,協(xié)助開發(fā)分析數(shù)據(jù)資源從而更好完善系統(tǒng),提高定位以及解決問題的效果。文章來(lái)源于網(wǎng)絡(luò),排版:一起學(xué)嵌入式: r& Y7 k) Q! F
6 A9 Q: r2 s8 Q6 R& o5 o" N
-END-% Y0 C/ U# v2 g# M b
往期推薦:點(diǎn)擊圖片即可跳轉(zhuǎn)閱讀) r# L0 b7 i; y' q
! S: u7 h' v$ U
, `* m/ h: j/ z0 A
# ? x( y/ y+ d, V
& H A* [- K& }( x6 N
9 Q+ ?2 v$ W C+ {5 K6 H: a3 O
5tt1joomon0640722325.jpg (64.55 KB, 下載次數(shù): 0)
下載附件
保存到相冊(cè)
5tt1joomon0640722325.jpg
前天 23:19 上傳
0 \5 v! H8 j& O+ } U1 }. q
) D* R; e1 d1 H' Y 掌握這些嵌入式 C/C++ 開發(fā)神器,迅速提升開發(fā)效率!
/ U9 M3 B1 }8 ]0 y6 Q+ u : `+ V& l) U" }9 [% _
/ n9 n7 o5 \4 _9 C- `- Z3 D! X
( X- t2 m5 t8 i; R. C& \
: |- y; W, @# F9 A. `
0 Y. i& p8 w) @9 v2 \# Q
/ d/ }& O9 T" O2 ]' ^# N. k3 C
7 I; G' Z( C0 H" O
% d7 S `9 S: q) s. h- ^3 P" k" [
# f5 h; `0 c2 d! g* ]/ l( {
: E* o) D- u4 \9 `
go1ab3lg5db640722425.jpg (121.43 KB, 下載次數(shù): 0)
下載附件
保存到相冊(cè)
go1ab3lg5db640722425.jpg
前天 23:19 上傳
8 o7 t6 v4 {& _ 6 B4 t8 J1 [0 g; i' v# ~, a
用嵌入式 C 語(yǔ)言,設(shè)計(jì)一種垃圾內(nèi)存回收機(jī)制。" G4 T5 X; v7 a0 w! N3 i4 l
2 T! u" p/ ]" J # \3 _: M5 M+ p) g0 J2 z
5 w7 i% S1 U, F; _9 J' n
* A- M1 P7 g( |5 ~! Z: d; }
) E2 E- n$ h6 z* J
" z7 d: K, x; n, m
. ]. O+ r7 c# ^$ C2 R0 [2 u
3 b# A" h3 y' @; ?( l' O, R
% c" _; k! F" ~
) c' W: ~! Z9 I+ Y# V
sqm0kevbkt4640722525.jpg (102.87 KB, 下載次數(shù): 0)
下載附件
保存到相冊(cè)
sqm0kevbkt4640722525.jpg
前天 23:19 上傳
6 |" s) x7 K8 @; P+ v$ t
( D: y& h3 v- Y/ z$ } 工程師的藝術(shù),完美治愈強(qiáng)迫癥!+ M5 l9 s3 T9 q- |/ v2 ?& y
( h. p' a+ j( S& ^& ~4 o
" f2 j% C- y5 }: Z
2 N$ z2 r' ]4 O- }
6 L9 p/ y3 N5 A$ g5 v, k% A * {. i0 ?, M% o" C$ l1 d" I
我是老溫,一名熱愛學(xué)習(xí)的嵌入式工程師2 _0 M1 f0 v4 ? d3 j$ q# v
關(guān)注我,一起變得更加優(yōu)秀!
! L, N' B" _# j* y( x" [/ _& y/ |
dihtz2qydzp640722625.png (665.93 KB, 下載次數(shù): 0)
下載附件
保存到相冊(cè)
dihtz2qydzp640722625.png
前天 23:19 上傳
|
|