<table id="km2im"></table>
  • <bdo id="km2im"><center id="km2im"></center></bdo>
    • 自動秒收錄
    • 軟件:1973
    • 資訊:56264|
    • 收錄網站:185720|

    IT精英團

    嵌入式系統登錄的簡單方法

    嵌入式系統登錄的簡單方法

    瀏覽次數:
    評論次數:
    編輯: 溫瑜
    信息來源: ITPUB
    更新日期: 2022-05-24 21:25:20
    摘要

    來源|我姓梁很多場景都需要記錄日志,在嵌入式系統中,特別是單片機這種存儲資源有限的環境下,就需要一種輕量級的存儲方法。系統日志在嵌入式設備應用場景中,系統日志時??梢员O控設備軟件的運行狀態,及時記錄

    • 正文開始
    • 相關閱讀
    • 推薦作品

    來源|我姓梁

    很多場景需要記錄日志。在嵌入式系統中,特別是在單片機等存儲資源有限的環境中,需要一種輕量級的存儲方法。

    系統日志

    在嵌入式設備的應用場景中,系統日志往往可以監控設備軟件的運行狀態,及時記錄問題點和關鍵信息,方便開發者后期定位和解決問題。

    本文將介紹一種簡單的系統日志記錄方法,用于保存設備的系統日志。根據具體的嵌入式設備,可以存儲在MCU的內部Flash、外部Flash、EEPROM等。本文以外置Flash為例進行介紹。思路分析系統日志可以看作是一個文件系統,可以分為三個重要部分:目錄區、參數區和日志區。目錄:按照日期分類,記錄當天日志的存儲地址、日志索引、日志大小,通過目錄得到整個日志文件的概況;

    參數區:存儲日志的寫入位置、目錄條目數、寫入狀態等參數;

    日志區:這是我們的主存儲區,記錄系統的日志,支持循環寫入。這三個區域都需要占用一部分內存,所以可以自己分配大小。

    實現效果如下圖所示,通過設置指令可以查詢整個日志目錄區的大致情況。查詢系統日志目錄:在目錄?LOG_ID:存儲按日期分類的日志。該ID用于查詢相應日期的日志,從1開始計數;LOG_DATE:系統日志存儲日期;LOG_ADDR:系統日志存儲外部閃存地址;LOG_OFFSET:系統日志存儲偏移量(每個日期的日志大小,單位:字節)。

    查詢系統日志目錄時獲取指定日期的系統日志:atcatalog=LOG_IDlog _ id3360,當log _ id為0時,查詢整個系統日志。

    此外,還提供了一個移除系統日志(清除日志目錄)的指令:AT RMLOG,具體實現將在后面描述。FLASH內存劃分的閃存需要根據具體設備合理劃分,目錄區、參數區、日志區可以環形存放,延長擦寫壽命。

    # Define FLASH _ SECTOR _ SIZE((uint 32 _ t)0x 001000)# Define FLASH _ BLOCK _ 32K _ SIZE((uint 32 _ t)0x 008000)# Define FLASH _ BLOCK _ 64K _ SIZE((uint 32 _ t)0x 010000)# Define SECTOR _ MASK(FLASH _ SECTOR _ SIZE-1)/* SECTOR MASK-*/# Define SECTOR _ base(addr(~ SECTOR _ MASK))/*扇區的基址-*/# Define SECTOR _ Offset(Offset

    de>FLASH_BLOCK_64K = 2 /**< flash erase block size 64k */}flash_block_t;
    /* flash 空間索引 */typedef enum{FLASH_CATALOG_ZONE = ,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 = ;for (i = ; i < flash_zone_count; 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 < flash_table_tmp->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 < flash_table_tmp->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 < flash_table_tmp->start_address) ||((address + length) > flash_table_tmp->end_address))return -1;
    bsp_spi_flash_buffer_read(buffer, address, length);return ;}

    參數與結構體定義日志數據存儲時間戳,便于問題定位,需要實現RTC接口調用。
    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;

    實現接口說明CRC校驗接口,可以自定義實現。

    /* 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 = ;uint32_t addr = ;uint32_t remainbyte = ;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 < flash_tmp->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 < flash_tmp->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 = ;/* 日志環寫標志 , 1:環寫狀態 */gp_sys_log->system_log.log_cyclic_status = ;/* 設置默認值,實際會重新從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 = ;
    gp_sys_log->system_catalog.log_addr = ;gp_sys_log->system_catalog.log_id = ;gp_sys_log->system_catalog.log_offset = ;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 = ;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[x%08x]!", gp_sys_ram->system_log_param_addr);return ;}}
    /* 掃描不到合法的參數,導入默認系統日志參數 */load_system_log_default_param();/* 獲取日志寫地址 */gp_sys_ram->system_log_param_addr = flash_tmp->start_address;log_info("Load System Log Param Addr(Default)[x%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 ( == 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 ( == 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 *)&sector_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 ;}

    打印系統日志目錄區信息,可實現通過指令查詢到目錄區信息。
    int system_catalog_all_print(void){int i = ;system_catalog_t catalog;
    printf("System Log Command Information:\r\n");printf("Query Specifies Log : AT+CATALOG=<LOG_ID><CR><LF>\r\n");printf("Query All Log : AT+CATALOG=<><CR><LF>\r\n\r\n");printf("Query All System Catalog:\r\n");printf("LOG_ID LOG_DATE LOG_ADDR LOG_OFFSET \r\n");for (i = ; i < gp_sys_log->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, , sizeof(system_catalog_t));}return ;}

    讀取指定日志目錄索引信息接口,可指定日志索引或者讀取全部日志數據。
    int system_log_task(int argc){int rlen = ;uint32_t offset, start_addr, end_addr;system_catalog_t catalog;flash_table_t *flash_tmp =get_flash_table(FLASH_SYSLOG_ZONE);
    if ( == 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 ( == 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 ( == offset) break;}return ;}

    存儲系統日志接口,實現更新存儲日期,當寫位置為扇區地址,則擦除一個扇區作為存儲日志,這樣避免每寫一次就擦除一次。
    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 = ;/* 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 ( == 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), &sector_buf[], SECTOR_OFFSET(start_addr)); }/* 寫位置為扇區首地址,則擦除一個扇區的存儲區 */if ( == 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 ( == SECTOR_OFFSET(start_addr)) {flash_erase(FLASH_SYSLOG_ZONE, SECTOR_BASE(start_addr), FLASH_BLOCK_4K);}}}
    /* 環形存儲參數 */save_system_log_param();return ;}

    系統調試對接為了更好記錄系統日志,將應用調試等級結合一塊,實現記錄錯誤調試信息以及需要保存的關鍵信息。定義的調試等級有:關閉調試等級、錯誤調試等級、警告調試等級、關鍵調試等級、debug調試等級,而LOG_RECORD_LEVEL將主動保存日志并輸出信息,LOG_ERROR_LEVEL會存儲對應的日志信息,但需要根據應用調試等級輸出信息。設置與讀取應用調試等級由讀者自行定義。
    #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 = "<error>:"},{ .level = LOG_WARN_LEVEL, .fmt_str = "<warn>:"},{ .level = LOG_INFO_LEVEL, .fmt_str = "<info>:"},{ .level = LOG_DEBUG_LEVEL, .fmt_str = "<debug>:"},{ .level = LOG_RECORD_LEVEL, .fmt_str = "<record>:"},};
    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 = , i = , fmt_index = ;int fmt_str_len = , ret = -1;int file_str_len = , line_str_len = ;char line_buf[20] = {};static char buf[PRINT_MAX_SIZE];static QueueHandle_t sem = NULL;time_t time = {};
    /* 針對os系統 */if (NULL == sem) {sem = xSemaphoreCreateCounting(1, 1); /* always think of success */}
    xSemaphoreTake(sem, portMAX_DELAY);
    ret = -1;fmt_str_len = ;if (level != LOG_PRINT_LEVEL) {if ((GET_LOG_LEVEL() < level) && (level != LOG_RECORD_LEVEL) && (level != LOG_ERROR_LEVEL))goto exit_end;
    for (i = ; i < SYSTEM_PRINT_FMT_LIST_MAX; 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 <= ) {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() < level) && (level == LOG_ERROR_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[], "[%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;}

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

    來源地址:

    https://blog.csdn.net/LiaRonBob/article/details/102766871

    聲明:本文素材來源網絡,版權歸原作者所有。如涉及作品版權問題,請與我聯系刪除。


    ------------ END ------------



    標簽:日志 目錄 參數
    全網最全面的云原生存儲OpenEBS用戶指南
    ? 上一篇 2022-05-24
    發表評論 共有條評論
    用戶名: 密碼:
    驗證碼: 匿名發表
    • 運維數字化轉型的幾點思考
      1閱讀 0條評論 個贊
      這個月底要召開一個企業運維數字化轉型的研討會,昨天一個合作伙伴和我討論一些研討會的話題問題。數字化轉型是近期十分熱門的話題,各大國企央企也把數字化轉型作為未來幾年的工作重點。不過針對于IT運維,數字化……
    • Spring Boot的表現太差了 我教你幾招輕松搞定
      1閱讀 0條評論 個贊
      文章……
    • SQL中左連接左表合并去重實用技巧
      0閱讀 0條評論 個贊
      zyc88.blog.csdn.net/article/details/83002882建表:CREATETABLE`table1`(`id`int(11)NOTNULLAUTO_INCREMENT,……
    • 我用Java在幾分鐘內處理了30億條數據.
      2閱讀 0條評論 個贊
      來源:https://c1n.cn/GM8hb目錄場景說明模擬數據場景分析讀取數據處理數據遇到的問題場景說明現有一個10G文件的數據,里面包含了18-70之間的整數,分別表示18-70歲的……
    • Python極簡編碼規范
      1閱讀 0條評論 個贊
      本文是閱讀《PythonCodingRule》之后總結的最為精華及簡單的編碼規范,根據每個人不同喜好有些地方會有不同的選擇,我只是做了對自己來說最簡單易行的選擇,僅供大家參考。1、重要原則a.保持……
    • Linux的10個最危險的命令
      0閱讀 0條評論 個贊
      rm-rf命令該命令可能導致不可恢復的系統崩壞。>rm-rf/#強制刪除根目錄下所有東西。>rm-rf*#強制刪除當前目錄的所有文件。>rm-rf.#強制刪除當前文件夾及其子文件夾。執行rm-r……
    • Linux預定任務調度(crontab) 好實用!
      0閱讀 0條評論 個贊
      概述crontab命令用于設置周期性被執行的指令。該命令從標準輸入設備讀取指令,并將其存放于“crontab”文件中,以供之后讀取和執行??梢允褂胏rontab定時處理離線任務,比如每天凌晨2點更新數……
    • 為什么NodeJS是構建微服務的最佳選擇?
      4閱讀 0條評論 個贊
      作者|RonFybish譯者|Sambodhi策劃|閆園園什么是微服務微服務是一種應用架構,它將每個應用功能都放在自己的服務中,與其他服務隔離。這些服務是松散耦合的,可獨立部署。這種架構……
    • 納尼?數據也是立法的嗎?
      1閱讀 0條評論 個贊
      編輯:彭文華來源:大數據架構師(ID:bigdata_arch)彭友們好,我是老彭。最近忙瘋了,天天給客戶寫方案,都沒時間寫文章了。趁著五一假期,跟彭友們嘮嘮新鮮事兒。今天這個,真的是顛覆我的認知,……
    • 可以解決80%問題的故障排除思路
      1閱讀 0條評論 個贊
      在講解事件、故障處理思路前,先講一個故障場景(以呼叫中心系統作為一例子):業務人員反映呼叫中心系統運行緩慢,部份電話在自助語言環節系統處理超時,話務轉人工座席,人工座席出現爆線情況。運維人員開始忙活了……
    • PostgreSQL并行框架分析
      3閱讀 0條評論 個贊
      作者簡介施博文,目前就職于騰訊云PG團隊概覽PostgreSQL并行框架提供了一系列方便的函數,支持在插件或內核中直接調用相關函數,啟動若干個后臺進程進行并行操作。目前,PG的并行框架主要用來……
    • Java“年度加密漏洞”修復 網友:更多堅持Java 8的理由
      9閱讀 0條評論 個贊
      文|Travis出品|OSC開源社區(ID:oschina2013)甲骨文于昨日推送了安全更新修復了一個漏洞,該漏洞允許攻擊者偽造某些種類的SSL證書和握手、雙因素認證信息,以及由一系列廣……
    • 用Ansible實現MySQL的備份、操作和維護
      0閱讀 0條評論 個贊
      作者簡介曹杰,中國結算上海分公司高級經理,從事系統運維管理工作。本文以容器形式部署了開源自動化運維工具Ansible,基于自帶的MySQL管理模塊編排了playbook配置文件,最終實現M……
    • MYSQL VS POLARDB唯一索引死鎖及應用設計
      1閱讀 0條評論 個贊
      #issue68021MySQLuniquecheck問題-知乎(zhihu.com)事情的開始是這樣的,最近和阿里云密切聯系,也成為他們的大客戶,(我們當然是大客戶,BIGBIG……
    • epoll這個Linux高性能服務的本質真的不簡單
      1閱讀 0條評論 個贊
      設想一個場景:有100萬用戶同時與一個進程保持著TCP連接,而每一時刻只有幾十個或幾百個TCP連接是活躍的(接收TCP包),也就是說在每一時刻進程只需要處理這100萬連接中的一小部分連接。那么,如何才……
    • 大數據平臺核心架構圖解 推薦收藏!
      1閱讀 0條評論 個贊
      我們先來看看這張圖,這是某公司使用的大數據平臺架構圖,大部分公司應該都差不多:從這張大數據的整體架構圖上看來,大數據的核心層應該是:數據采集層、數據存儲與分析層、數據共享層、數據應用層,可能叫法有所不……
    • 本文將帶您了解kubernetes的架構和組件!
      1閱讀 0條評論 個贊
      kubernetes架構目標kubernetes是生產級的,用于跨主機部署,擴展,管理和組合應用程序容器的基礎設施。kubernetes不僅僅是“容器編排”,他更加主要的解決方向是消除協調計算資源,網……
    • 服務器端高并發分布式架構的演進之路
      1閱讀 0條評論 個贊
      1.概述本文以淘寶作為例子,介紹從一百個到千萬級并發情況下服務端的架構的演進過程。同時列舉出每個演進階段會遇到的相關技術,讓大家對架構的演進有一個整體的認知。文章最后匯總了一些架構設計的原則。特別說……
    • 說說春云的全鏈路灰度發布方案~
      1閱讀 0條評論 個贊
      以下文章來源于公眾號-碼猿技術專欄,作者不才陳某大家好實際生產中如有需求變更,并不會直接更新線上服務,最通常的做法便是:切出線上的小部分流量進行體驗測試,經過測試后無問題則全面的上線。這樣做的好處也是……
    • 干貨:10個聚類算法的完整Python操作實例
      0閱讀 0條評論 個贊
      來源:海豚數據科學實驗室本文約7000字,建議閱讀14分鐘本文將介紹一篇關于聚類的文章,10種聚類介紹和Python代碼。聚類或聚類分析是無監督學習問題。它通常被用作數據分析技術,用于發現數據中的有趣……
    最近發布資訊
    更多
    国产H视频在线播放,国产毛多水多的老女人,国产成人午夜福利电影在线播放
    <table id="km2im"></table>
  • <bdo id="km2im"><center id="km2im"></center></bdo>