嵌入式设备系统日志记录方法|天天时讯
点击左上方蓝色“混说Linux”,选择“设为星标”第一时间看干货文章 1 在嵌入式设备应用场景中,系统日志时常可以监控设备软件的运行状态
面包芯语 2023-03-28 13:10:27
点击左上方蓝色“混说Linux”,选择“设为星标”
(资料图)
在嵌入式设备应用场景中,系统日志时常可以监控设备软件的运行状态,及时记录问题点以及关键信息,方便开发人员后期定位以及解决问题。本文将讲述一种简易的系统日志记录方法,用于保存设备的系统日志,视具体嵌入式设备情况而定,可存储在MCU内部Flash、外部Flash、EEPROM等,本文采用外部Flash作为示例展开介绍。
实现的效果如下图所示,设置通过指令可查询到整个日志目录区的概况。
查询指定日期系统日志:AT+CATALOG=
LOG_ID:在查询系统日志目录时获取,当LOG_ID为0时,为查询整个系统日志。
另外提供移除系统日志(清除日志目录)指令:AT+RMLOG,后面将讲述具体实现。
#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)/*扇区掩码------*/#defineSECTOR_BASE(addr)(addr&(~SECTOR_MASK))/*扇区的基地址--*/#defineSECTOR_OFFSET(addr)(addr&SECTOR_MASK)/*扇区内的偏移--*/#defineBLOCK_32K_BASE(addr)(addr&(~(FLASH_BLOCK_32K_SIZE)))#defineBLOCK_64K_BASE(addr)(addr&(~(FLASH_BLOCK_64K_SIZE)))typedefenum{FLASH_BLOCK_4K=0,/**
Flash底层实现擦除、读写操作接口,由读者自行实现。
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;}
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);
参数区应当保证数据的正确性,应加入参数校验存储,定义校验结构体。
#defineSYSTEM_LOG_MAGIC_PARAM0x87654321/*日志参数标识符*/typedefstruct{uint32_tmagic;/*参数标识符*/uint16_tcrc;/*校验值*/uint16_tlen;/*参数长度*/}single_sav_t;
参数区需记录当前日志记录的写位置,以及目录项个数,还有日志区和目录区环写状态,并且存储最新时间等等。
/*日志区参数*/typedefstruct{uint32_twrite_pos;/*写位置*/uint32_tcatalog_num;/*目录项个数*/uint8_tlog_cyclic_status;/*系统日志环形写状态*/uint8_tcatalog_cyclic_status;/*日志目录环形写状态*/time_tlog_latest_time;/*存储最新时间*/}system_log_t;/*目录区参数*/typedefstruct{uint32_tlog_id;/*日志索引*/uint32_tlog_addr;/*日志地址*/uint32_tlog_offset;/*日志偏移大小,单位:字节*/time_tlog_time;/*日志存储时间*/}system_catalog_t;/*系统日志参数*/typedefstruct{single_sav_tcrc_val;system_log_tsystem_log;system_catalog_tsystem_catalog;}sys_log_param_t;typedefstruct{uint8_tsystem_log_print_enable;/*系统日志打印使能*/uint16_tsystem_log_print_id;/*打印指定id系统日志*/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;
/*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};/*实现crc功能函数*/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;}
保存系统日志参数,每实现写日志操作后都需要保存当前的参数值,防止意外丢失。
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);/*校验参数*/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;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;}}}
导入系统日志默认参数接口,初始化默认参数或者移除日志。
voidload_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();}
设备开机或者复位都会进行导入系统日志参数操作,恢复日志读写参数,参数区为频繁读写操作区域,每一次写操作都会进行一次偏移,有效的导入参数方法是从参数区结束地址到起始地址进行扫描,扫描不到合法的参数则会导入默认日志参数。
/*参数初始化,在终端启动时调用*/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;}}/*扫描不到合法的参数,导入默认系统日志参数*/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;}
读写系统日志目录接口,读写指定日志索引目录信息。实际实现会定义最新的目录信息存储在日志参数区,当日期发生改变,则表示当前目录信息已经完结,将最新的目录信息录入日志目录区保存,最多每天写入一次目录区。
/*读取日志目录区指定日志索引目录信息*/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);}/*写日志目录区目录信息*/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;}/*本扇区剩余空间大小*/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;}}return0;}
打印系统日志目录区信息,可实现通过指令查询到目录区信息。
intsystem_catalog_all_print(void){inti=0;system_catalog_tcatalog;printf("SystemLogCommandInformation:\r\n");printf("QuerySpecifiesLog:AT+CATALOG=\r\n" );printf("QueryAllLog:AT+CATALOG=<0>\r\n\r\n" );printf("QueryAllSystemCatalog:\r\n");printf("LOG_IDLOG_DATELOG_ADDRLOG_OFFSET\r\n");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\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));}return0;}
读取指定日志目录索引信息接口,可指定日志索引或者读取全部日志数据。
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回环写标志,打印整个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)return1;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;}return0;}
存储系统日志接口,实现更新存储日期,当写位置为扇区地址,则擦除一个扇区作为存储日志,这样避免每写一次就擦除一次。
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);/*计算目录区的最大存储目录项个数*/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();return0;}
#defineLOG_CLOSE_LEVEL0x00/*关闭调试信息*/#defineLOG_ERROR_LEVEL0x01/*错误调试信息*/#defineLOG_WARN_LEVEL0x02/*警告调试信息*/#defineLOG_INFO_LEVEL0x03/*关键调试信息*/#defineLOG_DEBUG_LEVEL0x04/*debug调试信息*/#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系统*/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){gotoexit_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()system_log.log_latest_time=time;system_log_write((uint8_t*)buf,num+TIME_PREFIX_SIZE);}exit_end:xSemaphoreGive(sem);returnret;}
原文:http://t.csdn.cn/gFDSG
本文系网络转载,版权归原作者所有,如有侵权,请联系删除。
点击左上方蓝色“混说Linux”,选择“设为星标”第一时间看干货文章 1 在嵌入式设备应用场景中,系统日志时常可以监控设备软件的运行状态
即食燕窝就是打开即可食用的燕窝,常见的为罐头装置,打开瓶盖即可食用,现在工作节奏快,大多数人压根没时间买干燕窝回家慢慢浸
取住房公积金需要什么,取住房公积金很多人还不知道,现在让我们一起来看看吧!1 首先需要提供本人真实身份证及其复印件。此外,我需要根据支
创业板股最新融资余额为2033 19亿元,环比增加32 74亿元,46只股融资余额环比增长超10%,融资余额环比降幅超10%的有18只。证券时报·数据宝统
红色的纸张在指间翻飞,不一会儿,一朵雍容的牡丹便在畅杨杨手中“绽放”。作为第25届河南省五四青年奖章获得者、省级非遗项目孟津剪纸的代表
有了数字化乡村系统,西裴村找到了开启乡村振兴之门的“金钥匙”,蹚出强村富民路。就这样,西裴村集体经济收入从2020年的10万元,到2021年的4
1、内盘外盘,股市术语。2、内盘常用S,取英sell[sel]卖出的首字母S表示,外盘用B,取英文buy[baɪ]
挖贝网3月27日,博迅生物(836504)近日发布2022年年度报告,报告期内公司实现营业收入142,679,394 19元,同比下滑10 51%;归属于挂牌公
曾被宣布“野外灭绝”!好消息传来---3月26日,记者从在四川宜宾市江安县开展的“长江鲟野化繁殖实验”中了解到,此前被宣布“野外灭绝”的长
百奥赛图-B(02315)发布截至2022年12月31日止年度业绩,该集团取得收益
法治日报全媒记者潘从武截至目前,新疆维吾尔自治区已建成交通安全管理服务站839个、交通安全劝导站6462个,劝导站乡镇覆盖率达到100%。这是
1、画出刷子的基本结构,从上到下分为三层,手柄,连接体,绒毛区。2、画出笔刷的细节,顶部的小圆孔和下面绒毛的多层次结构。
据平安许昌:来自受害者的自述~不认识的人让转账,你转吗?某日,我收到一个快递,里面装有一个杯子,因为杯子上印有“优质客户回馈品,扫码免
硅谷国家银行和美国第一公民银行都在竞购硅谷银行,美国,联邦,硅谷银行,保险公司,硅谷国家银行,个人银行业务
今日之中国,江山壮丽、人民豪迈,处处焕发着蓬勃的生机,升腾着发展的希望。从浩瀚苍穹,神舟十五号航天员乘组漫步太空,中国空间站全面建成