0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

嵌入式設(shè)備系統(tǒng)日志記錄方法

jf_BxU6dNQb ? 來源:CSDN-JustCasper ? 2023-03-28 15:20 ? 次閱讀

嵌入式設(shè)備應用場景中,系統(tǒng)日志時??梢员O(jiān)控設(shè)備軟件的運行狀態(tài),及時記錄問題點以及關(guān)鍵信息,方便開發(fā)人員后期定位以及解決問題。本文將講述一種簡易的系統(tǒng)日志記錄方法,用于保存設(shè)備的系統(tǒng)日志,視具體嵌入式設(shè)備情況而定,可存儲在MCU內(nèi)部Flash、外部Flash、EEPROM等,本文采用外部Flash作為示例展開介紹。

思路分析 對于系統(tǒng)日志可以當成文件系統(tǒng),可以劃分為三個重要部分:目錄區(qū)、參數(shù)區(qū)、日志區(qū)。目錄區(qū):根據(jù)日期進行歸類,記錄當天的日志的存儲地址、日志索引、日志大小,通過目錄可以獲取整個日志文件的概況;參數(shù)區(qū):存儲記錄日志寫位置、目錄項個數(shù)、寫狀態(tài)等參數(shù);日志區(qū):這是我們主要的存儲區(qū),記錄系統(tǒng)的日志,支持環(huán)寫。這三個區(qū)域都需要占用部分內(nèi)存,可以自行分配大小。

實現(xiàn)的效果如下圖所示,設(shè)置通過指令可查詢到整個日志目錄區(qū)的概況。

查詢系統(tǒng)日志目錄:AT+CATALOG? LOG_ID: 存儲日志按日期分類,該ID用于查詢對應日期日志,從1開始計數(shù); LOG_DATE: 系統(tǒng)日志存儲日期; LOG_ADDR: 系統(tǒng)日志存儲外部FLASH地址; LOG_OFFSET: 系統(tǒng)日志存儲偏移量(各日期日志大小,單位:字節(jié))。

6c8fde44-cd38-11ed-bfe3-dac502259ad0.png

查詢指定日期系統(tǒng)日志:AT+CATALOG=

LOG_ID:在查詢系統(tǒng)日志目錄時獲取,當LOG_ID為0時,為查詢整個系統(tǒng)日志。

6c9f6fbc-cd38-11ed-bfe3-dac502259ad0.png

另外提供移除系統(tǒng)日志(清除日志目錄)指令:AT+RMLOG,后面將講述具體實現(xiàn)。

FLASH內(nèi)存劃分 FLASH內(nèi)存需要看具體設(shè)備進行合理劃分,目錄區(qū)、參數(shù)區(qū)與日志區(qū)實現(xiàn)環(huán)形存儲,延長擦寫壽命。

#defineFLASH_SECTOR_SIZE((uint32_t)0x001000)
#defineFLASH_BLOCK_32K_SIZE((uint32_t)0x008000)
#defineFLASH_BLOCK_64K_SIZE((uint32_t)0x010000)
#defineSECTOR_MASK(FLASH_SECTOR_SIZE-1)/*扇區(qū)掩碼------*/
#defineSECTOR_BASE(addr)(addr&(~SECTOR_MASK))/*扇區(qū)的基地址--*/
#defineSECTOR_OFFSET(addr)(addr&SECTOR_MASK)/*扇區(qū)內(nèi)的偏移--*/

#defineBLOCK_32K_BASE(addr)(addr&(~(FLASH_BLOCK_32K_SIZE)))
#defineBLOCK_64K_BASE(addr)(addr&(~(FLASH_BLOCK_64K_SIZE)))

typedefenum{
FLASH_BLOCK_4K=0,/**

Flash底層實現(xiàn)擦除、讀寫操作接口,由讀者自行實現(xiàn)。

flash_table_t*get_flash_table(flash_zone_ezone)
{
inti=0;
for(i=0;istart_address||address>flash_table_tmp->end_address)
return-1;

returnbsp_spi_flash_erase(address,block_type);
}

intflash_write(flash_zone_ezone,uint32_taddress,constuint8_t*data,uint32_tlength)
{
flash_table_t*flash_table_tmp=get_flash_table(zone);

if(flash_table_tmp==NULL)
return-1;

if((addressstart_address)||((address+length)>flash_table_tmp->end_address))
return-1;

returnbsp_spi_flash_buffer_write(address,(uint8_t*)data,length);
}

intflash_read(flash_zone_ezone,uint32_taddress,uint8_t*buffer,uint32_tlength)
{
flash_table_t*flash_table_tmp=get_flash_table(zone);

if(flash_table_tmp==NULL)
return-1;

if((addressstart_address)||((address+length)>flash_table_tmp->end_address))
return-1;

bsp_spi_flash_buffer_read(buffer,address,length);
return0;
}

參數(shù)與結(jié)構(gòu)體定義 日志數(shù)據(jù)存儲時間戳,便于問題定位,需要實現(xiàn)RTC接口調(diào)用。

typedefstruct{
uint16_tYear;/*年份:YYYY*/
uint8_tMonth;/*月份:MM*/
uint8_tDay;/*日:DD*/
uint8_tHour;/*小時:HH*/
uint8_tMinute;/*分鐘:MM*/
uint8_tSecond;/*秒:SS*/
}time_t;

intbsp_rtc_get_time(time_t*date);

參數(shù)區(qū)應當保證數(shù)據(jù)的正確性,應加入?yún)?shù)校驗存儲,定義校驗結(jié)構(gòu)體。

#defineSYSTEM_LOG_MAGIC_PARAM0x87654321/*日志參數(shù)標識符*/
typedefstruct{
uint32_tmagic;/*參數(shù)標識符*/
uint16_tcrc;/*校驗值*/
uint16_tlen;/*參數(shù)長度*/
}single_sav_t;

參數(shù)區(qū)需記錄當前日志記錄的寫位置,以及目錄項個數(shù),還有日志區(qū)和目錄區(qū)環(huán)寫狀態(tài),并且存儲最新時間等等。

/*日志區(qū)參數(shù)*/
typedefstruct{
uint32_twrite_pos;/*寫位置*/
uint32_tcatalog_num;/*目錄項個數(shù)*/
uint8_tlog_cyclic_status;/*系統(tǒng)日志環(huán)形寫狀態(tài)*/
uint8_tcatalog_cyclic_status;/*日志目錄環(huán)形寫狀態(tài)*/
time_tlog_latest_time;/*存儲最新時間*/
}system_log_t;

/*目錄區(qū)參數(shù)*/
typedefstruct{
uint32_tlog_id;/*日志索引*/
uint32_tlog_addr;/*日志地址*/
uint32_tlog_offset;/*日志偏移大小,單位:字節(jié)*/
time_tlog_time;/*日志存儲時間*/
}system_catalog_t;

/*系統(tǒng)日志參數(shù)*/
typedefstruct{
single_sav_tcrc_val;
system_log_tsystem_log;
system_catalog_tsystem_catalog;
}sys_log_param_t;

typedefstruct{
uint8_tsystem_log_print_enable;/*系統(tǒng)日志打印使能*/
uint16_tsystem_log_print_id;/*打印指定id系統(tǒng)日志*/
uint32_tsystem_log_param_addr;/*當前日志寫地址*/
}sys_ram_t;

sys_ram_tSysRam;
sys_log_param_tSysLogParam;

sys_ram_t*gp_sys_ram=&SysRam;
sys_log_param_t*gp_sys_log=&SysLogParam;

實現(xiàn)接口說明 CRC校驗接口,可以自定義實現(xiàn)。

/*16位CRC校驗高位表*/
staticconstuint8_tauchCRCHi[]={
0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,
0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,
0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,
0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,
0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,
0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,
0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,
0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,

0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,
0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,
0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,
0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,
0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,
0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,
0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,
0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40
};

/*16位CRC校驗低位表*/
staticconstuint8_tauchCRCLo[]={
0x00,0xc0,0xc1,0x01,0xc3,0x03,0x02,0xc2,0xc6,0x06,0x07,0xc7,0x05,0xc5,0xc4,0x04,
0xcc,0x0c,0x0d,0xcd,0x0f,0xcf,0xce,0x0e,0x0a,0xca,0xcb,0x0b,0xc9,0x09,0x08,0xc8,
0xd8,0x18,0x19,0xd9,0x1b,0xdb,0xda,0x1a,0x1e,0xde,0xdf,0x1f,0xdd,0x1d,0x1c,0xdc,
0x14,0xd4,0xd5,0x15,0xd7,0x17,0x16,0xd6,0xd2,0x12,0x13,0xd3,0x11,0xd1,0xd0,0x10,
0xf0,0x30,0x31,0xf1,0x33,0xf3,0xf2,0x32,0x36,0xf6,0xf7,0x37,0xf5,0x35,0x34,0xf4,
0x3c,0xfc,0xfd,0x3d,0xff,0x3f,0x3e,0xfe,0xfa,0x3a,0x3b,0xfb,0x39,0xf9,0xf8,0x38,
0x28,0xe8,0xe9,0x29,0xeb,0x2b,0x2a,0xea,0xee,0x2e,0x2f,0xef,0x2d,0xed,0xec,0x2c,
0xe4,0x24,0x25,0xe5,0x27,0xe7,0xe6,0x26,0x22,0xe2,0xe3,0x23,0xe1,0x21,0x20,0xe0,

0xa0,0x60,0x61,0xa1,0x63,0xa3,0xa2,0x62,0x66,0xa6,0xa7,0x67,0xa5,0x65,0x64,0xa4,
0x6c,0xac,0xad,0x6d,0xaf,0x6f,0x6e,0xae,0xaa,0x6a,0x6b,0xab,0x69,0xa9,0xa8,0x68,
0x78,0xb8,0xb9,0x79,0xbb,0x7b,0x7a,0xba,0xbe,0x7e,0x7f,0xbf,0x7d,0xbd,0xbc,0x7c,
0xb4,0x74,0x75,0xb5,0x77,0xb7,0xb6,0x76,0x72,0xb2,0xb3,0x73,0xb1,0x71,0x70,0xb0,
0x50,0x90,0x91,0x51,0x93,0x53,0x52,0x92,0x96,0x56,0x57,0x97,0x55,0x95,0x94,0x54,
0x9c,0x5c,0x5d,0x9d,0x5f,0x9f,0x9e,0x5e,0x5a,0x9a,0x9b,0x5b,0x99,0x59,0x58,0x98,
0x88,0x48,0x49,0x89,0x4b,0x8b,0x8a,0x4a,0x4e,0x8e,0x8f,0x4f,0x8d,0x4d,0x4c,0x8c,
0x44,0x84,0x85,0x45,0x87,0x47,0x46,0x86,0x82,0x42,0x43,0x83,0x41,0x81,0x80,0x40
};

/*實現(xiàn)crc功能函數(shù)*/
staticuint16_tCRC16(uint8_t*puchMsg,uint16_tusDataLen)
{
uint8_tuchCRCHi=0xff;
uint8_tuchCRCLo=0xff;
uint16_tuIndex;

while(usDataLen--){
uIndex=uchCRCHi^*(puchMsg++);
uchCRCHi=uchCRCLo^auchCRCHi[uIndex];
uchCRCLo=auchCRCLo[uIndex];
}

returnuchCRCHi<<8|uchCRCLo;
}

保存系統(tǒng)日志參數(shù),每實現(xiàn)寫日志操作后都需要保存當前的參數(shù)值,防止意外丟失。

voidsave_system_log_param(void)
{
uint32_ti=0;
uint32_taddr=0;
uint32_tremainbyte=0;
uint32_tstart_addr;
intlen=sizeof(sys_log_param_t);
uint8_t*pdata=(uint8_t*)&SysLogParam;
flash_table_t*flash_tmp=get_flash_table(FLASH_SYSLOG_PARA_ZONE);

/*校驗參數(shù)*/
gp_sys_log->crc_val.magic=SYSTEM_LOG_MAGIC_PARAM;
gp_sys_log->crc_val.len=sizeof(sys_log_param_t)-sizeof(single_sav_t);
gp_sys_log->crc_val.crc=CRC16(&pdata[sizeof(single_sav_t)],gp_sys_log->crc_val.len);

start_addr=gp_sys_ram->system_log_param_addr;
/*剩余內(nèi)存不夠?qū)?,則重新從起始地址開始寫,實現(xiàn)環(huán)形存儲功能*/
if((start_addr+len)>flash_tmp->end_address){
start_addr=flash_tmp->start_address;
}
gp_sys_ram->system_log_param_addr=start_addr+len;
/*首地址存儲,擦除整個系統(tǒng)日志參數(shù)存儲區(qū),如果劃分的內(nèi)存較大,可能出現(xiàn)第一次擦寫等待時間較長,
但實際應用嵌入式設(shè)備應該不會占用太多的內(nèi)存存儲系統(tǒng)日志,只當為輔助使用,有額外應用可自行實現(xiàn)*/
if(flash_tmp->start_address==start_addr){
/*for(i=flash_tmp->start_address;iend_address;i+=FLASH_SECTOR_SIZE)
flash_erase(FLASH_SYSLOG_PARA_ZONE,SECTOR_BASE(i),FLASH_BLOCK_4K);
*/
addr=flash_tmp->start_address;
do{
if((addr+FLASH_BLOCK_64K_SIZE)<=?flash_tmp->end_address){
flash_erase(FLASH_SYSLOG_PARA_ZONE,BLOCK_64K_BASE(i),FLASH_BLOCK_64K);
addr+=FLASH_BLOCK_64K_SIZE;
}elseif((addr+FLASH_BLOCK_32K_SIZE)<=?flash_tmp->end_address){
flash_erase(FLASH_SYSLOG_PARA_ZONE,BLOCK_32K_BASE(i),FLASH_BLOCK_32K);
addr+=FLASH_BLOCK_32K_SIZE;
}elseif((addr+FLASH_SECTOR_SIZE)<=?flash_tmp->end_address){
flash_erase(FLASH_SYSLOG_PARA_ZONE,SECTOR_BASE(i),FLASH_BLOCK_4K);
addr+=FLASH_SECTOR_SIZE;
}else{
break;
}
}while(addrend_address);
}

remainbyte=FLASH_SECTOR_SIZE-(start_addr%FLASH_SECTOR_SIZE);
if(remainbyte>len){
remainbyte=len;
}
while(1){
flash_write(FLASH_SYSLOG_PARA_ZONE,start_addr,pdata,remainbyte);
if(remainbyte==len){
break;
}else{
pdata+=remainbyte;
start_addr+=remainbyte;
len-=remainbyte;
remainbyte=(len>FLASH_SECTOR_SIZE)?FLASH_SECTOR_SIZE:len;
}
}
}

導入系統(tǒng)日志默認參數(shù)接口,初始化默認參數(shù)或者移除日志。

voidload_system_log_default_param(void)
{
/*系統(tǒng)日志默認參數(shù)*/
/*目錄環(huán)寫狀態(tài)標志*/
gp_sys_log->system_log.catalog_cyclic_status=0x00;
/*目錄項個數(shù)*/
gp_sys_log->system_log.catalog_num=0;
/*日志環(huán)寫標志,1:環(huán)寫狀態(tài)*/
gp_sys_log->system_log.log_cyclic_status=0;
/*設(shè)置默認值,實際會重新從RTC獲取最新時間*/
gp_sys_log->system_log.log_latest_time.Year=2019;
gp_sys_log->system_log.log_latest_time.Month=5;
gp_sys_log->system_log.log_latest_time.Day=8;
gp_sys_log->system_log.log_latest_time.Hour=13;
gp_sys_log->system_log.log_latest_time.Minute=14;
gp_sys_log->system_log.log_latest_time.Second=10;
/*日志寫位置從0開始*/
gp_sys_log->system_log.write_pos=0;

gp_sys_log->system_catalog.log_addr=0;
gp_sys_log->system_catalog.log_id=0;
gp_sys_log->system_catalog.log_offset=0;
gp_sys_log->system_catalog.log_time.Year=2019;
gp_sys_log->system_catalog.log_time.Month=5;
gp_sys_log->system_catalog.log_time.Day=8;
gp_sys_log->system_catalog.log_time.Hour=12;
gp_sys_log->system_catalog.log_time.Minute=12;
gp_sys_log->system_catalog.log_time.Second=14;

gp_sys_log->crc_val.magic=SYSTEM_LOG_MAGIC_PARAM;

/*導入默認參數(shù)后進行保存*/
save_system_log_param();
}

設(shè)備開機或者復位都會進行導入系統(tǒng)日志參數(shù)操作,恢復日志讀寫參數(shù),參數(shù)區(qū)為頻繁讀寫操作區(qū)域,每一次寫操作都會進行一次偏移,有效的導入?yún)?shù)方法是從參數(shù)區(qū)結(jié)束地址到起始地址進行掃描,掃描不到合法的參數(shù)則會導入默認日志參數(shù)。

/*參數(shù)初始化,在終端啟動時調(diào)用*/
intload_system_log_param(void)
{
uint32_ti=0;
single_sav_tpsav;
uint32_tend_addr;
uint32_tinteral=sizeof(sys_log_param_t);
intdata_len=sizeof(sys_log_param_t)-sizeof(single_sav_t);
uint8_t*pram=(uint8_t*)&SysLogParam;
flash_table_t*flash_tmp=get_flash_table(FLASH_SYSLOG_PARA_ZONE);

end_addr=flash_tmp->end_address-(flash_tmp->end_address-flash_tmp->start_address)%interal;
for(i=end_addr-interal;i>flash_tmp->start_address;i-=interal){
flash_read(FLASH_SYSLOG_PARA_ZONE,i,(uint8_t*)&psav,sizeof(single_sav_t));
if((psav.magic==SYSTEM_LOG_MAGIC_PARAM)&&(psav.len==data_len)){
flash_read(FLASH_SYSLOG_PARA_ZONE,i+sizeof(single_sav_t),&pram[sizeof(single_sav_t)],data_len);
if(psav.crc!=CRC16(&pram[sizeof(single_sav_t)],data_len))
continue;
gp_sys_ram->system_log_param_addr=i;
log_info("LoadSystemLogParamAddr[0x%08x]!",gp_sys_ram->system_log_param_addr);
return0;
}
}

/*掃描不到合法的參數(shù),導入默認系統(tǒng)日志參數(shù)*/
load_system_log_default_param();
/*獲取日志寫地址*/
gp_sys_ram->system_log_param_addr=flash_tmp->start_address;
log_info("LoadSystemLogParamAddr(Default)[0x%08x]!",gp_sys_ram->system_log_param_addr);
return1;
}

讀寫系統(tǒng)日志目錄接口,讀寫指定日志索引目錄信息。實際實現(xiàn)會定義最新的目錄信息存儲在日志參數(shù)區(qū),當日期發(fā)生改變,則表示當前目錄信息已經(jīng)完結(jié),將最新的目錄信息錄入日志目錄區(qū)保存,最多每天寫入一次目錄區(qū)。

/*讀取日志目錄區(qū)指定日志索引目錄信息*/
intsystem_catalog_read(system_catalog_t*catalog,uint32_tid)
{
uint32_taddr;
intrlen=sizeof(system_catalog_t);
uint8_t*pbuf=(uint8_t*)catalog;
flash_table_t*flash_tmp=get_flash_table(FLASH_CATALOG_ZONE);

if(0==id)
return-1;
addr=flash_tmp->start_address+(rlen*(id-1));
if(addr>flash_tmp->end_address)
return-1;

returnflash_read(FLASH_CATALOG_ZONE,addr,pbuf,rlen);
}

/*寫日志目錄區(qū)目錄信息*/
intsystem_catalog_write(system_catalog_t*catalog,uint32_tid)
{
uint32_tstart_offset;
uint32_tstart_addr;
uint32_tstart_base;
uint32_tremainbyte;
intwlen=sizeof(system_catalog_t);
uint8_t*pdata=(uint8_t*)catalog;
flash_table_t*flash_tmp=get_flash_table(FLASH_CATALOG_ZONE);

if(0==id)return-1;
start_addr=flash_tmp->start_address+wlen*(id-1);
if((start_addr+wlen)>flash_tmp->end_address){
start_addr=flash_tmp->start_address;
}

/*本扇區(qū)剩余空間大小*/
remainbyte=FLASH_SECTOR_SIZE-(start_addr%FLASH_SECTOR_SIZE);
/*寫入數(shù)據(jù)長度小于本扇區(qū)剩余長度,直接寫入*/
if(remainbyte>wlen){
remainbyte=wlen;
}
/*寫目錄次數(shù)不會太頻繁,視具體情況改寫操作實現(xiàn)*/
while(1){
start_base=SECTOR_BASE(start_addr);
start_offset=SECTOR_OFFSET(start_addr);
flash_read(FLASH_CATALOG_ZONE,start_base,sector_buf,FLASH_SECTOR_SIZE);
flash_erase(FLASH_CATALOG_ZONE,start_base,FLASH_BLOCK_4K);
memcpy((char*)§or_buf[start_offset],pdata,remainbyte);
flash_write(FLASH_CATALOG_ZONE,start_base,sector_buf,FLASH_SECTOR_SIZE);
if(remainbyte==wlen){
break;
}else{
pdata+=remainbyte;
start_addr+=remainbyte;
wlen-=remainbyte;
remainbyte=(wlen>FLASH_SECTOR_SIZE)?FLASH_SECTOR_SIZE:wlen;
}
}

return0;
}

打印系統(tǒng)日志目錄區(qū)信息,可實現(xiàn)通過指令查詢到目錄區(qū)信息。

intsystem_catalog_all_print(void)
{
inti=0;
system_catalog_tcatalog;

printf("SystemLogCommandInformation:
");
printf("QuerySpecifiesLog:AT+CATALOG=
");
printf("QueryAllLog:AT+CATALOG=<0>

");
printf("QueryAllSystemCatalog:
");
printf("LOG_IDLOG_DATELOG_ADDRLOG_OFFSET
");
for(i=0;isystem_log.catalog_num;i++){
/*當前最新目錄信息*/
if(i==(gp_sys_log->system_catalog.log_id-1)){
catalog=gp_sys_log->system_catalog;/*獲取當前最新目錄信息*/
}else{
system_catalog_read(&catalog,i+1);
}
printf("%d%04d-%02d-%02d0x%08X%d
",
catalog.log_id,catalog.log_time.Year,catalog.log_time.Month,catalog.log_time.Day,
catalog.log_addr,catalog.log_offset);
memset((char*)&catalog,0,sizeof(system_catalog_t));
}
return0;
}

讀取指定日志目錄索引信息接口,可指定日志索引或者讀取全部日志數(shù)據(jù)。

intsystem_log_task(intargc)
{
intrlen=0;
uint32_toffset,start_addr,end_addr;
system_catalog_tcatalog;
flash_table_t*flash_tmp=get_flash_table(FLASH_SYSLOG_ZONE);

if(0==gp_sys_ram->system_log_print_enable)
return1;

gp_sys_ram->system_log_print_enable=0x00;
if(gp_sys_ram->system_log_print_id==ALL_LOG_PRINT){
/*log回環(huán)寫標志,打印整個LOG存儲區(qū)*/
if(0x01==gp_sys_log->system_log.log_cyclic_status){
start_addr=flash_tmp->start_address;
end_addr=flash_tmp->end_address;
offset=end_addr-start_addr;
}else{
start_addr=flash_tmp->start_address;
end_addr=start_addr+gp_sys_log->system_log.write_pos;
offset=gp_sys_log->system_log.write_pos;
}
}else{/*讀取指定ID日志*/
if(gp_sys_ram->system_log_print_id==gp_sys_log->system_catalog.log_id){
catalog=gp_sys_log->system_catalog;
}else{
system_catalog_read(&catalog,gp_sys_ram->system_log_print_id);
}
start_addr=catalog.log_addr;
offset=catalog.log_offset;
}

if(0==offset)
return1;

while(1){
rlen=(offset>512)?512:offset;
system_log_read(sector_buf,start_addr,rlen);
HAL_Delay(80);
/*目錄信息通過調(diào)式串口打印*/
bsp_debug_send(sector_buf,rlen);
start_addr+=rlen;
offset-=rlen;
if(0==offset)
break;
}
return0;
}

存儲系統(tǒng)日志接口,實現(xiàn)更新存儲日期,當寫位置為扇區(qū)地址,則擦除一個扇區(qū)作為存儲日志,這樣避免每寫一次就擦除一次。

intsystem_log_write(uint8_t*wbuf,intwlen)
{
uint32_tstart_addr;
uint8_t*pdata=wbuf;
uint32_tremainbyte;
intsystem_catalog_max_id;
flash_table_t*flash_tmp=get_flash_table(FLASH_SYSLOG_ZONE);

/*計算目錄區(qū)的最大存儲目錄項個數(shù)*/
system_catalog_max_id=((flash_tmp->end_address-flash_tmp->start_address)/sizeof(system_catalog_t));
start_addr=flash_tmp->start_address+gp_sys_log->system_log.write_pos;
/*存儲數(shù)據(jù)地址大于規(guī)劃內(nèi)存地址范圍處理*/
if((start_addr+wlen)>flash_tmp->end_address){
start_addr=flash_tmp->start_address;
/*寫位置偏移量重置*/
gp_sys_log->system_log.write_pos=0;
/*LOG回環(huán)存儲標志置位*/
gp_sys_log->system_log.log_cyclic_status=0x01;
}
/*寫位置偏移*/
gp_sys_log->system_log.write_pos+=wlen;

if((gp_sys_log->system_log.log_latest_time.Year!=gp_sys_log->system_catalog.log_time.Year)||
(gp_sys_log->system_log.log_latest_time.Month!=gp_sys_log->system_catalog.log_time.Month)||
(gp_sys_log->system_log.log_latest_time.Day!=gp_sys_log->system_catalog.log_time.Day)){

/*日期改變,記錄目錄信息,當log_id為0,則不寫入*/
system_catalog_write(&gp_sys_log->system_catalog,gp_sys_log->system_catalog.log_id);
/*記錄存儲日期*/
gp_sys_log->system_catalog.log_time=gp_sys_log->system_log.log_latest_time;

if((gp_sys_log->system_catalog.log_id+1)>=system_catalog_max_id){
gp_sys_log->system_log.catalog_num=system_catalog_max_id;/*目錄循環(huán)寫,目錄數(shù)應為最大*/
gp_sys_log->system_log.catalog_cyclic_status=1;/*目錄回環(huán)寫標志*/
}else{
if(0==gp_sys_log->system_log.catalog_cyclic_status){
/*獲取目錄數(shù)*/
gp_sys_log->system_log.catalog_num=gp_sys_log->system_catalog.log_id+1;
}
}

/*存儲最新目錄項信息*/
gp_sys_log->system_catalog.log_id=(gp_sys_log->system_catalog.log_id+1)%system_catalog_max_id;
gp_sys_log->system_catalog.log_addr=start_addr;
gp_sys_log->system_catalog.log_offset=wlen;
}else{
gp_sys_log->system_catalog.log_offset+=wlen;
}

/*寫位置為存儲起始地址并且不為扇區(qū)首地址*/
if((flash_tmp->start_address==start_addr)&&(SECTOR_OFFSET(flash_tmp->start_address))){
flash_read(FLASH_SYSLOG_ZONE,SECTOR_BASE(start_addr),sector_buf,FLASH_SECTOR_SIZE);
flash_erase(FLASH_SYSLOG_ZONE,SECTOR_BASE(start_addr),FLASH_BLOCK_4K);
/*將扇區(qū)頭部至起始地址區(qū)間的數(shù)據(jù)回寫*/
flash_write(FLASH_SYSLOG_ZONE,SECTOR_BASE(start_addr),§or_buf[0],SECTOR_OFFSET(start_addr));
}
/*寫位置為扇區(qū)首地址,則擦除一個扇區(qū)的存儲區(qū)*/
if(0==SECTOR_OFFSET(start_addr)){
flash_erase(FLASH_SYSLOG_ZONE,SECTOR_BASE(start_addr),FLASH_BLOCK_4K);
}

/*本扇區(qū)剩余空間大小*/
remainbyte=FLASH_SECTOR_SIZE-(start_addr%FLASH_SECTOR_SIZE);
/*寫入數(shù)據(jù)長度小于本扇區(qū)剩余長度,直接寫入*/
if(remainbyte>wlen){
remainbyte=wlen;
}
while(1){
flash_write(FLASH_SYSLOG_ZONE,start_addr,pdata,remainbyte);
if(remainbyte==wlen){
break;
}else{
pdata+=remainbyte;
start_addr+=remainbyte;
wlen-=remainbyte;
remainbyte=(wlen>FLASH_SECTOR_SIZE)?FLASH_SECTOR_SIZE:wlen;
/*扇區(qū)首地址則擦除整個扇區(qū),該扇區(qū)數(shù)據(jù)不保存*/
if(0==SECTOR_OFFSET(start_addr)){
flash_erase(FLASH_SYSLOG_ZONE,SECTOR_BASE(start_addr),FLASH_BLOCK_4K);
}
}
}

/*環(huán)形存儲參數(shù)*/
save_system_log_param();
return0;
}
系統(tǒng)調(diào)試對接 為了更好記錄系統(tǒng)日志,將應用調(diào)試等級結(jié)合一塊,實現(xiàn)記錄錯誤調(diào)試信息以及需要保存的關(guān)鍵信息。定義的調(diào)試等級有:關(guān)閉調(diào)試等級、錯誤調(diào)試等級、警告調(diào)試等級、關(guān)鍵調(diào)試等級、debug調(diào)試等級,而LOG_RECORD_LEVEL將主動保存日志并輸出信息,LOG_ERROR_LEVEL會存儲對應的日志信息,但需要根據(jù)應用調(diào)試等級輸出信息。設(shè)置與讀取應用調(diào)試等級由讀者自行定義。
#defineLOG_CLOSE_LEVEL0x00/*關(guān)閉調(diào)試信息*/
#defineLOG_ERROR_LEVEL0x01/*錯誤調(diào)試信息*/
#defineLOG_WARN_LEVEL0x02/*警告調(diào)試信息*/
#defineLOG_INFO_LEVEL0x03/*關(guān)鍵調(diào)試信息*/
#defineLOG_DEBUG_LEVEL0x04/*debug調(diào)試信息*/
#defineLOG_RECORD_LEVEL0x10/*保存日志并輸出信息*/
#defineLOG_PRINT_LEVEL0xff

#defineSET_LOG_LEVEL(LEVEL)(gp_sys_param->system_print_level=LEVEL)
#defineGET_LOG_LEVEL()(gp_sys_param->system_print_level)

#definelog_debug(fmt,args...)log_format(LOG_DEBUG_LEVEL,fmt,##args)
#definelog_info(fmt,args...)log_format(LOG_INFO_LEVEL,fmt,##args)
#definelog_warn(fmt,args...)log_format(LOG_WARN_LEVEL,fmt,##args)
#definelog_error(fmt,args...)log_format(LOG_ERROR_LEVEL,fmt,##args)
#definelog_record(fmt,args...)log_format(LOG_RECORD_LEVEL,fmt,##args)
#defineprintf(fmt,args...)log_format(LOG_PRINT_LEVEL,fmt,##args)

typedefstruct{
intlevel;
char*fmt_str;
}system_print_fmt_t;

system_print_fmt_tsystem_print_fmt_list[]={
{.level=LOG_ERROR_LEVEL,.fmt_str=":"},
{.level=LOG_WARN_LEVEL,.fmt_str=":"},
{.level=LOG_INFO_LEVEL,.fmt_str=":"},
{.level=LOG_DEBUG_LEVEL,.fmt_str=":"},
{.level=LOG_RECORD_LEVEL,.fmt_str=":"},
};

intlog_format(uint8_tlevel,constchar*fmt,...)
{
#defineTIME_PREFIX_SIZE(21)
#definePRINT_MAX_SIZE(1024+TIME_PREFIX_SIZE)

va_listargs;
intnum=0,i=0,fmt_index=0;
intfmt_str_len=0,ret=-1;
intfile_str_len=0,line_str_len=0;
charline_buf[20]={0};
staticcharbuf[PRINT_MAX_SIZE];
staticQueueHandle_tsem=NULL;
time_ttime={0};

/*針對os系統(tǒng)*/
if(NULL==sem){
sem=xSemaphoreCreateCounting(1,1);/*alwaysthinkofsuccess*/
}

xSemaphoreTake(sem,portMAX_DELAY);

ret=-1;
fmt_str_len=0;
if(level!=LOG_PRINT_LEVEL){
if((GET_LOG_LEVEL()SYSTEM_PRINT_FMT_LIST_MAX){
gotoexit_end;
}

fmt_str_len=strlen(system_print_fmt_list[fmt_index].fmt_str);
strncpy((char*)&buf[TIME_PREFIX_SIZE],system_print_fmt_list[fmt_index].fmt_str,fmt_str_len);
}

va_start(args,fmt);
num=vsnprintf((char*)&buf[fmt_str_len+TIME_PREFIX_SIZE],PRINT_MAX_SIZE-fmt_str_len-TIME_PREFIX_SIZE-2,fmt,args);
va_end(args);

if(num<=?0)?{
????????goto?exit_end;
????}

????if?(level?!=?LOG_PRINT_LEVEL)?{
????????num?+=?fmt_str_len;
????????buf[num?+?TIME_PREFIX_SIZE]?=?'
';
????????buf[num?+?TIME_PREFIX_SIZE?+?1]?=?'
';
????????num?+=?2;
????}

????if?((GET_LOG_LEVEL()?system_log.log_latest_time=time;
system_log_write((uint8_t*)buf,num+TIME_PREFIX_SIZE);
}

exit_end:
xSemaphoreGive(sem);
returnret;
}

結(jié)語 本文提供的一種簡易嵌入式設(shè)備系統(tǒng)日志記錄方法,代碼量不多,實現(xiàn)簡單,針對不同的設(shè)備需要合理規(guī)劃內(nèi)存使用,根據(jù)軟件運行狀態(tài),合適加入調(diào)試信息并保存對應的日志信息,方便開發(fā)人員了解系統(tǒng)或軟件運行狀況,協(xié)助開發(fā)分析數(shù)據(jù)資源從而更好完善系統(tǒng),提高定位以及解決問題的效果。

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 嵌入式
    +關(guān)注

    關(guān)注

    5082

    文章

    19126

    瀏覽量

    305184
  • FlaSh
    +關(guān)注

    關(guān)注

    10

    文章

    1635

    瀏覽量

    148023
  • 存儲
    +關(guān)注

    關(guān)注

    13

    文章

    4314

    瀏覽量

    85846
  • 嵌入式設(shè)備
    +關(guān)注

    關(guān)注

    0

    文章

    110

    瀏覽量

    16963
  • 日志
    +關(guān)注

    關(guān)注

    0

    文章

    138

    瀏覽量

    10642

原文標題:嵌入式設(shè)備系統(tǒng)日志記錄方法

文章出處:【微信號:混說Linux,微信公眾號:混說Linux】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    簡易的嵌入式系統(tǒng)日志記錄方法

    嵌入式設(shè)備中,很多場景都需要記錄日志,特別是單片機這種存儲資源有限的環(huán)境下,就需要一種輕量級的存儲方法。
    發(fā)表于 10-31 14:17 ?1199次閱讀

    一種簡易的嵌入式設(shè)備系統(tǒng)日志記錄方法

    嵌入式設(shè)備應用場景中,系統(tǒng)日志時??梢员O(jiān)控設(shè)備軟件的運行狀態(tài),及時記錄問題點以及關(guān)鍵信息,方便
    發(fā)表于 11-10 11:43 ?1165次閱讀

    嵌入式文件系統(tǒng)μC/FS的日志使用

    盡管在PC領(lǐng)域NTFS已經(jīng)取代了FAT,但FAT文件系統(tǒng)仍然是嵌入式開發(fā)的首選。除了為嵌入式應用程序提供與PC(因為Windows繼續(xù)支持FAT)的無縫交互,對于電源不穩(wěn)定的設(shè)備開發(fā)者
    發(fā)表于 09-19 16:41

    嵌入式系統(tǒng)日志相關(guān)資料分享

    嵌入式系統(tǒng)日志void wdbg_printf(const char * format, ... ) { va_list args; s32 len; if(g_trace_handle
    發(fā)表于 10-27 09:21

    如何去實現(xiàn)嵌入式linux設(shè)備中應用運行日志

    嵌入式linux設(shè)備中應用運行日志的實現(xiàn)最近在做一個項目時,需要記錄設(shè)備運行中情況,以方便對故障進行跟蹤定位,完善.所以決定采用
    發(fā)表于 11-04 08:24

    嵌入式設(shè)備的網(wǎng)絡化方法研究

    嵌入式設(shè)備的網(wǎng)絡化方法研究-Study on Networked Method for Embedded Devices 摘要網(wǎng)絡化的嵌入式設(shè)備
    發(fā)表于 02-09 10:37 ?21次下載

    嵌入式系統(tǒng)的設(shè)計方法

    嵌入式系統(tǒng)的設(shè)計方法嵌入式系統(tǒng)的C程序設(shè)計開始講,一步步深入。
    發(fā)表于 03-28 09:45 ?38次下載

    基于虛擬存儲的嵌入式存儲系統(tǒng)的設(shè)計方法

    基于虛擬存儲的嵌入式存儲系統(tǒng)的設(shè)計方法   1、引言   嵌入式系統(tǒng)嵌入式硬件
    發(fā)表于 11-05 16:10 ?773次閱讀
    基于虛擬存儲的<b class='flag-5'>嵌入式</b>存儲<b class='flag-5'>系統(tǒng)</b>的設(shè)計<b class='flag-5'>方法</b>

    基于嵌入式系統(tǒng)的汽車行駛記錄儀的設(shè)計

     基于嵌入式系統(tǒng)的汽車行駛記錄儀的設(shè)計  引言   汽車行駛記錄儀是能夠記錄和再現(xiàn)汽車行駛狀態(tài)的一種數(shù)字
    發(fā)表于 01-12 11:10 ?1714次閱讀
    基于<b class='flag-5'>嵌入式</b><b class='flag-5'>系統(tǒng)</b>的汽車行駛<b class='flag-5'>記錄</b>儀的設(shè)計

    [學習嵌入式]嵌入式系統(tǒng)學習方法,輕松入門嵌入式

    [學習嵌入式]嵌入式系統(tǒng)學習方法,輕松入門嵌入式。
    發(fā)表于 03-28 15:29 ?79次下載

    嵌入式系統(tǒng)原理、設(shè)計與應用

    嵌入式操作系統(tǒng)的原理和開發(fā)方法,而是講述嵌入式系統(tǒng)的通用技術(shù)和基本原理、嵌入式產(chǎn)品的開發(fā)
    發(fā)表于 04-25 17:07 ?2次下載

    嵌入式系統(tǒng)死鎖檢測方法

    棘手。死鎖是并發(fā)缺陷的典型問題,有時會導致整個嵌入式系統(tǒng)陷入癱瘓,嚴重影響嵌入式系統(tǒng)的穩(wěn)定性、可靠性。由于死鎖難以再現(xiàn)和修正,如何有效檢測死鎖成為
    發(fā)表于 01-31 10:27 ?0次下載
    <b class='flag-5'>嵌入式</b><b class='flag-5'>系統(tǒng)</b>死鎖檢測<b class='flag-5'>方法</b>

    嵌入式系統(tǒng)日志

    嵌入式系統(tǒng)日志void wdbg_printf(const char * format, ... ) { va_list args; s32 len
    發(fā)表于 10-20 16:21 ?8次下載
    <b class='flag-5'>嵌入式</b><b class='flag-5'>系統(tǒng)</b><b class='flag-5'>日志</b>

    嵌入式linux設(shè)備中應用運行日志的實現(xiàn)

    嵌入式linux設(shè)備中應用運行日志的實現(xiàn)? ? ? 最近在做一個項目時,需要記錄設(shè)備運行中情況,以方便對故障進行跟蹤定位,完善.所以決定采用
    發(fā)表于 11-01 17:22 ?8次下載
    <b class='flag-5'>嵌入式</b>linux<b class='flag-5'>設(shè)備</b>中應用運行<b class='flag-5'>日志</b>的實現(xiàn)

    基于嵌入式系統(tǒng)的行車記錄儀的設(shè)計

    電子發(fā)燒友網(wǎng)站提供《基于嵌入式系統(tǒng)的行車記錄儀的設(shè)計.doc》資料免費下載
    發(fā)表于 10-13 09:20 ?1次下載
    基于<b class='flag-5'>嵌入式</b><b class='flag-5'>系統(tǒng)</b>的行車<b class='flag-5'>記錄</b>儀的設(shè)計