點擊左上方藍色“混說Linux”,選擇“設為星標”

在嵌入式設備應用場景中,系統日志時??梢员O控設備軟件的運行狀態,及時記錄問題點以及關鍵信息,方便開發人員后期定位以及解決問題。本文將講述一種簡易的系統日志記錄方法,用于保存設備的系統日志,視具體嵌入式設備情況而定,可存儲在MCU內部Flash、外部Flash、EEPROM等,本文采用外部Flash作為示例展開介紹。
?
實現的效果如下圖所示,設置通過指令可查詢到整個日志目錄區的概況。

查詢指定日期系統日志:AT+CATALOG=
LOG_ID:在查詢系統日志目錄時獲取,當LOG_ID為0時,為查詢整個系統日志。

另外提供移除系統日志(清除日志目錄)指令:AT+RMLOG,后面將講述具體實現。
?
#define?FLASH_SECTOR_SIZE???((uint32_t)0x001000)
#define?FLASH_BLOCK_32K_SIZE??((uint32_t)0x008000)
#define?FLASH_BLOCK_64K_SIZE??((uint32_t)0x010000)
#define?SECTOR_MASK?????????????(FLASH_SECTOR_SIZE?-?1)?????????/*扇區掩碼?------*/
#define?SECTOR_BASE(addr)???????(addr?&?(~SECTOR_MASK))????????/*扇區的基地址?--*/
#define?SECTOR_OFFSET(addr)?????(addr?&?SECTOR_MASK)???????????/*扇區內的偏移?--*/
#define?BLOCK_32K_BASE(addr)??(addr?&?(~(FLASH_BLOCK_32K_SIZE)))
#define?BLOCK_64K_BASE(addr)??(addr?&?(~(FLASH_BLOCK_64K_SIZE)))
typedef?enum?{
????FLASH_BLOCK_4K??=?0,??????????/**
????FLASH_BLOCK_32K?=?1,??????????/**
????FLASH_BLOCK_64K?=?2???????????/**
}flash_block_t;
/*?flash?空間索引?*/
typedef?enum{
????FLASH_CATALOG_ZONE?=?0,
????FLASH_SYSLOG_PARA_ZONE,
????FLASH_SYSLOG_ZONE,
????FLASH_ZONEX,
}flash_zone_e;
typedef?struct{
????flash_zone_e?zone;
????uint32_t?start_address;
????uint32_t?end_address;
}flash_table_t;
/*?地址劃分?*/
static?const?flash_table_t?flash_table[]?=?{
??{?.zone?=?FLASH_CATALOG_ZONE,???????.start_address?=?0x03200000,?.end_address?=?0x032FFFFF},??
??{?.zone?=?FLASH_SYSLOG_PARA_ZONE,???.start_address?=?0x03300000,?.end_address?=?0x033FFFFF},??
??{?.zone?=?FLASH_SYSLOG_ZONE,????????.start_address?=?0x03400000,?.end_address?=?0x03FFFFFF},??
};Flash底層實現擦除、讀寫操作接口,由讀者自行實現。
flash_table_t?*get_flash_table(flash_zone_e?zone)
{
??int?i?=?0;
??for?(i?=?0;?i?????if?(zone?==?flash_table[i].zone)?
??????return?(flash_table_t?*)&flash_table[i];
??}
??
??return?NULL;??
}
int?flash_erase(flash_zone_e?zone,?uint32_t?address,?flash_block_t?block_type)
{
??flash_table_t?*flash_table_tmp?=?get_flash_table(zone);
??
??if?(flash_table_tmp?==?NULL)
????return?-1;
????
??if?(address?start_address?||address?>?flash_table_tmp->end_address)?
????return?-1;
??return?bsp_spi_flash_erase(address,?block_type);
}
int?flash_write(flash_zone_e?zone,?uint32_t?address,?const?uint8_t*data,?uint32_t?length)
{
??flash_table_t?*flash_table_tmp?=?get_flash_table(zone);
?????
??if?(flash_table_tmp?==?NULL)
?????return?-1;
??if?((address?start_address)?||((address?+?length)?>?flash_table_tmp->end_address))
?????return?-1;
??return?bsp_spi_flash_buffer_write(address,?(uint8_t?*)data,?length);
}
int?flash_read(flash_zone_e?zone,?uint32_t?address,?uint8_t*buffer,?uint32_t?length)
{
??flash_table_t?*flash_table_tmp?=?get_flash_table(zone);
??
??if?(flash_table_tmp?==?NULL)
????return?-1;
??if?((address?start_address)?||((address?+?length)?>?flash_table_tmp->end_address))
????return?-1;
????
??bsp_spi_flash_buffer_read(buffer,?address,?length);
??return?0;
}?
typedef?struct?{
????uint16_t??Year;??/*?年份:YYYY?*/
????uint8_t??Month;??/*?月份:MM?*/
????uint8_t??Day;??/*?日:DD?*/
????uint8_t?????Hour;??/*?小時:HH?*/
????uint8_t?????Minute;??/*?分鐘:MM?*/
????uint8_t??Second;??/*?秒:SS?*/
}time_t;???
int?bsp_rtc_get_time(time_t?*date);參數區應當保證數據的正確性,應加入參數校驗存儲,定義校驗結構體。
#define?SYSTEM_LOG_MAGIC_PARAM??0x87654321?/*?日志參數標識符?*/
typedef?struct?{
????uint32_t?magic;??/*?參數標識符?*/
????uint16_t?crc;??/*?校驗值?*/
????uint16_t?len;??/*?參數長度?*/
}?single_sav_t;參數區需記錄當前日志記錄的寫位置,以及目錄項個數,還有日志區和目錄區環寫狀態,并且存儲最新時間等等。
/*?日志區參數?*/
typedef?struct?{
????uint32_t???write_pos;????????????/*?寫位置?*/
????uint32_t???catalog_num;???????????/*?目錄項個數?*/
????uint8_t????log_cyclic_status;???/*?系統日志環形寫狀態?*/???
????uint8_t????catalog_cyclic_status;?/*?日志目錄環形寫狀態?*/
????time_t?????log_latest_time;????/*?存儲最新時間?*/
}system_log_t;
/*?目錄區參數?*/
typedef?struct?{
????uint32_t?log_id;????/*?日志索引?*/?
????uint32_t?log_addr;????/*?日志地址?*/
????uint32_t?log_offset;??/*?日志偏移大小,單位:字節?*/
????time_t???log_time;????/*?日志存儲時間?*/
}system_catalog_t;
/*?系統日志參數?*/
typedef?struct?{
????single_sav_t?crc_val;
????system_log_t?system_log;
????system_catalog_t?system_catalog;
}sys_log_param_t;
typedef?struct?{
????uint8_t?system_log_print_enable;?/*?系統日志打印使能?*/
????uint16_t?system_log_print_id;????/*?打印指定id系統日志?*/
????uint32_t?system_log_param_addr;??/*?當前日志寫地址?*/
}?sys_ram_t;
sys_ram_t?SysRam;
sys_log_param_t?SysLogParam;
sys_ram_t?*gp_sys_ram?=?&SysRam;
sys_log_param_t?*gp_sys_log?=?&SysLogParam;?
/*?16位CRC校驗高位表?*/
static?const?uint8_t?auchCRCHi[]={
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校驗低位表?*/
static?const?uint8_t?auchCRCLo[]={
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
};
/*?實現crc功能函數?*/
static?uint16_t?CRC16(uint8_t*?puchMsg,?uint16_t?usDataLen)
{
????uint8_t?uchCRCHi=0xff;
????uint8_t?uchCRCLo=0xff;
????uint16_t?uIndex;
????
????while(usDataLen--)?{
????????uIndex=uchCRCHi^*(puchMsg++);
????????uchCRCHi=uchCRCLo^auchCRCHi[uIndex];
????????uchCRCLo=auchCRCLo[uIndex];
????}
????
????return?uchCRCHi<<8|uchCRCLo;
}保存系統日志參數,每實現寫日志操作后都需要保存當前的參數值,防止意外丟失。
void?save_system_log_param(void)
{
????uint32_t?i?=?0;
????uint32_t?addr?=?0;
????uint32_t?remainbyte?=?0;
????uint32_t?start_addr;
????int?len?=?sizeof(sys_log_param_t);
????uint8_t?*pdata?=?(uint8_t?*)&SysLogParam;
????flash_table_t?*flash_tmp?=?get_flash_table(FLASH_SYSLOG_PARA_ZONE);
????
????/*?校驗參數?*/
????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;
????/*?剩余內存不夠寫,則重新從起始地址開始寫,實現環形存儲功能??*/
????if?((start_addr?+?len)?>?flash_tmp->end_address)?{?
????????start_addr?=?flash_tmp->start_address;
????}
????gp_sys_ram->system_log_param_addr?=?start_addr?+?len;
????/*?首地址存儲,擦除整個系統日志參數存儲區,如果劃分的內存較大,可能出現第一次擦寫等待時間較長,
???????但實際應用嵌入式設備應該不會占用太多的內存存儲系統日志,只當為輔助使用,有額外應用可自行實現?*/
????if?(flash_tmp->start_address?==?start_addr)?{
????????/*for?(i?=?flash_tmp->start_address;?i?end_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;
????????????}?else?if?((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;
????????????}?else?if?((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?(addr?end_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;
????????}
????}
}導入系統日志默認參數接口,初始化默認參數或者移除日志。
void?load_system_log_default_param(void)
{
????/*?系統日志默認參數?*/
????/*?目錄環寫狀態標志?*/
????gp_sys_log->system_log.catalog_cyclic_status?=?0x00;
????/*?目錄項個數?*/
????gp_sys_log->system_log.catalog_num?=?0;
????/*?日志環寫標志?,?1:環寫狀態?*/
????gp_sys_log->system_log.log_cyclic_status?=?0;
????/*?設置默認值,實際會重新從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;
????/*?導入默認參數后進行保存?*/
????save_system_log_param();
}設備開機或者復位都會進行導入系統日志參數操作,恢復日志讀寫參數,參數區為頻繁讀寫操作區域,每一次寫操作都會進行一次偏移,有效的導入參數方法是從參數區結束地址到起始地址進行掃描,掃描不到合法的參數則會導入默認日志參數。
/*?參數初始化,在終端啟動時調用?*/
int?load_system_log_param(void)
{
????uint32_t?i?=?0;
????single_sav_t?psav;
????uint32_t?end_addr;
????uint32_t?interal?=?sizeof(sys_log_param_t);
????int?data_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("Load?System?Log?Param?Addr[0x%08x]!",?gp_sys_ram->system_log_param_addr);
????????????return?0;
????????}
????}
????
????/*?掃描不到合法的參數,導入默認系統日志參數?*/
????load_system_log_default_param();
????/*?獲取日志寫地址?*/
????gp_sys_ram->system_log_param_addr?=?flash_tmp->start_address;
????log_info("Load?System?Log?Param?Addr(Default)[0x%08x]!",?gp_sys_ram->system_log_param_addr);
????return?1;
}讀寫系統日志目錄接口,讀寫指定日志索引目錄信息。實際實現會定義最新的目錄信息存儲在日志參數區,當日期發生改變,則表示當前目錄信息已經完結,將最新的目錄信息錄入日志目錄區保存,最多每天寫入一次目錄區。
/*?讀取日志目錄區指定日志索引目錄信息?*/
int?system_catalog_read(system_catalog_t?*catalog,?uint32_t?id)
{
????uint32_t?addr;
????int?rlen?=?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;
????????
????return?flash_read(FLASH_CATALOG_ZONE,?addr,?pbuf,?rlen);
}
/*?寫日志目錄區目錄信息?*/
int?system_catalog_write(system_catalog_t?*catalog,?uint32_t?id)
{
????uint32_t?start_offset;
????uint32_t?start_addr;
????uint32_t?start_base;
????uint32_t?remainbyte;
????int?wlen?=?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;
????}
????
????/*?本扇區剩余空間大小?*/
????remainbyte?=?FLASH_SECTOR_SIZE?-?(start_addr?%?FLASH_SECTOR_SIZE);
????/*?寫入數據長度小于本扇區剩余長度,直接寫入?*/
????if?(remainbyte?>?wlen)?{
????????remainbyte?=?wlen;
????}
????/*?寫目錄次數不會太頻繁,視具體情況改寫操作實現?*/
????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;
????????}
????}
????
????return?0;
}打印系統日志目錄區信息,可實現通過指令查詢到目錄區信息。
int?system_catalog_all_print(void)
{
????int?i?=?0;
????system_catalog_t?catalog;
????printf("System?Log?Command?Information:\r\n");
????printf("Query?Specifies?Log?:?AT+CATALOG=\r\n" );
????printf("Query?All?Log?:?AT+CATALOG=<0>\r\n\r\n" );
????printf("Query?All?System?Catalog:\r\n");
????printf("LOG_ID??LOG_DATE??LOG_ADDR??LOG_OFFSET?\r\n");
????for?(i?=?0;?i?system_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-%02d??0x%08X??%d?\r\n",?
????????????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));
????}
????return?0;
}讀取指定日志目錄索引信息接口,可指定日志索引或者讀取全部日志數據。
int?system_log_task(int?argc)
{
????int?rlen?=?0;
????uint32_t?offset,?start_addr,?end_addr;
????system_catalog_t?catalog;
????flash_table_t?*flash_tmp?=get_flash_table(FLASH_SYSLOG_ZONE);
????
????if?(0?==?gp_sys_ram->system_log_print_enable)?
????????return?1;
????gp_sys_ram->system_log_print_enable?=?0x00;
????if?(gp_sys_ram->system_log_print_id?==?ALL_LOG_PRINT)?{
????????/*?log回環寫標志,打印整個LOG存儲區?*/
????????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)
????????return?1;
????while?(1)?{
????????rlen?=?(offset?>?512)???512?:?offset;
????????system_log_read(sector_buf,?start_addr,?rlen);
????????HAL_Delay(80);
????????/*?目錄信息通過調式串口打印?*/
????????bsp_debug_send(sector_buf,?rlen);
????????start_addr?+=?rlen;
????????offset?-=?rlen;
????????if?(0?==?offset)?
????????????break;
????}
????return?0;
}存儲系統日志接口,實現更新存儲日期,當寫位置為扇區地址,則擦除一個扇區作為存儲日志,這樣避免每寫一次就擦除一次。
int?system_log_write(uint8_t?*wbuf,?int?wlen)
{
????uint32_t?start_addr;
????uint8_t?*pdata?=?wbuf;
????uint32_t?remainbyte;
????int?system_catalog_max_id;
????flash_table_t?*flash_tmp?=get_flash_table(FLASH_SYSLOG_ZONE);
????
????/*?計算目錄區的最大存儲目錄項個數?*/
????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;
????/*?存儲數據地址大于規劃內存地址范圍處理?*/
????if?((start_addr?+?wlen)?>?flash_tmp->end_address)?{?
????????start_addr?=?flash_tmp->start_address;
????????/*?寫位置偏移量重置?*/
????????gp_sys_log->system_log.write_pos?=?0;
????????/*?LOG回環存儲標志置位?*/
????????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;?/*?目錄循環寫,目錄數應為最大?*/
????????????gp_sys_log->system_log.catalog_cyclic_status?=?1;?/*?目錄回環寫標志?*/
????????}?else?{
????????????if?(0?==?gp_sys_log->system_log.catalog_cyclic_status)?{
????????????????/*?獲取目錄數?*/
????????????????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;?
????}
????
????/*?寫位置為存儲起始地址并且不為扇區首地址?*/
????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);
????????/*?將扇區頭部至起始地址區間的數據回寫?*/
????????flash_write(FLASH_SYSLOG_ZONE,?SECTOR_BASE(start_addr),?§or_buf[0],?SECTOR_OFFSET(start_addr));?
????}
????/*?寫位置為扇區首地址,則擦除一個扇區的存儲區????*/
????if?(0?==?SECTOR_OFFSET(start_addr))?{
????????flash_erase(FLASH_SYSLOG_ZONE,?SECTOR_BASE(start_addr),?FLASH_BLOCK_4K);
????}
????/*?本扇區剩余空間大小?*/
????remainbyte?=?FLASH_SECTOR_SIZE?-?(start_addr?%?FLASH_SECTOR_SIZE);
????/*?寫入數據長度小于本扇區剩余長度,直接寫入?*/
????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;
????????????/*?扇區首地址則擦除整個扇區,該扇區數據不保存?*/
????????????if?(0?==?SECTOR_OFFSET(start_addr))?{
????????????????flash_erase(FLASH_SYSLOG_ZONE,?SECTOR_BASE(start_addr),?FLASH_BLOCK_4K);
????????????}
????????}
????}
????/*?環形存儲參數?*/
????save_system_log_param();
????return?0;
}#define?LOG_CLOSE_LEVEL????0x00?/*?關閉調試信息?*/
#define?LOG_ERROR_LEVEL????0x01?/*?錯誤調試信息?*/
#define?LOG_WARN_LEVEL????0x02?/*?警告調試信息?*/
#define?LOG_INFO_LEVEL????0x03?/*?關鍵調試信息?*/
#define?LOG_DEBUG_LEVEL????0x04?/*?debug調試信息?*/
#define?LOG_RECORD_LEVEL???0x10?/*?保存日志并輸出信息?*/?
#define?LOG_PRINT_LEVEL????0xff
#define?SET_LOG_LEVEL(LEVEL)??(gp_sys_param->system_print_level?=?LEVEL)
#define?GET_LOG_LEVEL()????(gp_sys_param->system_print_level)
#define?log_debug(fmt,?args...)??log_format(LOG_DEBUG_LEVEL,?fmt,?##args)
#define?log_info(fmt,?args...)??log_format(LOG_INFO_LEVEL,?fmt,?##args)
#define?log_warn(fmt,?args...)??log_format(LOG_WARN_LEVEL,?fmt,?##args)
#define?log_error(fmt,?args...)??log_format(LOG_ERROR_LEVEL,?fmt,?##args)
#define?log_record(fmt,?args...)?log_format(LOG_RECORD_LEVEL,?fmt,?##args)
#define?printf(fmt,?args...)??log_format(LOG_PRINT_LEVEL,?fmt,?##args)
typedef?struct?{
????int?level;
????char?*fmt_str;
}system_print_fmt_t;
system_print_fmt_t?system_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?=?":" },
};
int?log_format(uint8_t?level,?const?char?*fmt,?...)
{
????#define?TIME_PREFIX_SIZE?(21)
????#define?PRINT_MAX_SIZE??(1024?+?TIME_PREFIX_SIZE)
????
????va_list?args;
????int?num?=?0,?i?=?0,?fmt_index?=?0;
????int?fmt_str_len?=?0,?ret?=?-1;
????int?file_str_len?=?0,?line_str_len?=?0;
????char?line_buf[20]?=?{0};
????static?char?buf[PRINT_MAX_SIZE];
????static?QueueHandle_t?sem?=?NULL;
????time_t?time?=?{0};
????
????/*?針對os系統?*/
????if?(NULL?==?sem)?{
??????????sem?=?xSemaphoreCreateCounting(1,?1);?/*?always?think?of?success?*/
????}
????
????xSemaphoreTake(sem,?portMAX_DELAY);
????ret?=?-1;
????fmt_str_len?=?0;
????if?(level?!=?LOG_PRINT_LEVEL)?{
????????if?((GET_LOG_LEVEL()?????????????goto?exit_end;
????????for?(i?=?0;?i?????????????if?(level?==?system_print_fmt_list[i].level)?{
????????????????fmt_index?=?i;
????????????????break;
????????????}
????????}
????????if?(i?>?SYSTEM_PRINT_FMT_LIST_MAX)?{
????????????goto?exit_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]?=?'\r';
????????buf[num?+?TIME_PREFIX_SIZE?+?1]?=?'\n';
????????num?+=?2;
????}
????if?((GET_LOG_LEVEL()?????????//do?nothing
????}?else?{
????????ret?=?bsp_debug_send((uint8_t*)&buf[TIME_PREFIX_SIZE],?num);?
????}
????if?((LOG_ERROR_LEVEL?==?level)?||?(LOG_RECORD_LEVEL?==?level))?{
????????bsp_rtc_get_time(&time);
????????sprintf(&buf[0],?"[%04d-%02d-%02d?%02d:%02d:%02d",
????????????time.Year,?time.Month,?time.Day,time.Hour,?time.Minute,?time.Second);
????????buf[TIME_PREFIX_SIZE?-?1]?=?']';
????????gp_sys_log->system_log.log_latest_time?=?time;
????????system_log_write((uint8_t?*)buf,?num?+?TIME_PREFIX_SIZE);
????}?
exit_end:
????xSemaphoreGive(sem);
????return?ret;
}?
原文:http://t.csdn.cn/gFDSG
本文系網絡轉載,版權歸原作者所有,如有侵權,請聯系刪除。

往期推薦