简单 time I/O 写入日志文件 get_time

本博客展示了如何使用 C 语言中的 localtime 函数获取当前时间,并将其转换为易读格式,然后输出到文件中。通过编程实现了一个简单的定时器功能,用于连续记录和输出时间戳。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#if 0
#ifndef _TM_DEFINED

struct tm {
int tm_sec; /* 秒 – 取值区间为[0,59] */
int tm_min; /* 分 - 取值区间为[0,59] */
int tm_hour; /* 时 - 取值区间为[0,23] */
int tm_mday; /* 一个月中的日期 - 取值区间为[1,31] */
int tm_mon; /* 月份(从一月开始,0代表一月) - 取值区间为[0,11] */
int tm_year; /* 年份,其值等于实际年份减去1900 */
int tm_wday; /* 星期 – 取值区间为[0,6],其中0代表星期天,1代表星期一,以此类推 */
int tm_yday; /* 从每年的1月1日开始的天数 – 取值区间为[0,365],其中0代表1月1日,1代表1月2日,以此类推 */
int tm_isdst; /* 夏令时标识符,实行夏令时的时候,tm_isdst为正。不实行夏令时的进候,tm_isdst为0;不了解情况时,tm_isdst()为负。*/
};
#define _TM_DEFINED

#endif

struct tm *localtime(const time_t *timep);
localtime()将参数timep 所指的time_t 结构中的信息转换成
真实世界所使用的时间日期表示方法,然后将结果由结构tm 返回。
结构tm 的定义请参考gmtime()。
此函数返回的时间日期已经转换成当地时区。

#endif

#include <stdio.h>
#include <time.h>

int main(void)
{
	char *wday[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
	struct tm *p_time;
	time_t Get_All_Time;
	time(&Get_All_Time);
	p_time = localtime(&Get_All_Time);
	printf("%d-%d-%d\n",  p_time -> tm_year +1900, p_time -> tm_mon + 1, p_time -> tm_mday);
	printf("%s-%d-%d-%d\n",  wday[p_time -> tm_wday], p_time -> tm_hour, p_time -> tm_min, p_time -> tm_sec);

	return 0;
}


/*
akaedu@akaedu-G41MT-D3:~/lin/806_io$ ./4
2014-8-23
Sat-18-53-18
akaedu@akaedu-G41MT-D3:~/lin/806_io$ ./4
2014-8-23
Sat-18-53-21
akaedu@akaedu-G41MT-D3:~/lin/806_io$ 
*/



*************************日志文件**************

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>

void* get_time(void)
{
	char *wday[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
	struct tm *p_time;
	time_t Get_All_Time;
	time(&Get_All_Time);
	p_time = localtime(&Get_All_Time);
//	printf("%d-%d-%d\n",  p_time -> tm_year +1900, p_time -> tm_mon + 1, p_time -> tm_mday);
//	printf("%s-%d-%d-%d\n",  wday[p_time -> tm_wday], p_time -> tm_hour, p_time -> tm_min, p_time -> tm_sec);

	return p_time;
}

void input_time_file(FILE *fpw)
{
	int ret = 0;
	int count_i = 1;
	int count_j = 1;
	char l_s_str[20];
	char ch;
	struct tm *p_time;
#if 0
	while( (ch = fgetc(fpw)) != EOF){
		if( ch  == '\n'){
			count_i++;
			}
	
	}

#endif

#if 1
	while( (fscanf(fpw,"%d %s %s", &count_i, l_s_str, l_s_str)) != EOF ){
	}
	count_j = count_i;
//	printf("%d\n", count_i); 每次运行程序时,得到最大行数

#endif
	while(1){
		p_time = get_time();
		fprintf(stdout, "%d :%d-%d-%d %d:%d:%d\n", count_i++, p_time -> tm_year + 1900, p_time -> tm_mon + 1, p_time -> tm_mday,
											p_time -> tm_hour, p_time -> tm_min, p_time -> tm_sec );
		fprintf(fpw, "%d :%d-%d-%d %d:%d:%d\n", count_j++, p_time -> tm_year + 1900, p_time -> tm_mon + 1, p_time -> tm_mday,
											p_time -> tm_hour, p_time -> tm_min, p_time -> tm_sec );
		fflush(fpw);  //记得冲刷缓冲区
		sleep(1);
	}
	fclose(fpw);
}


int main(int argc, char *argv[])
{
	FILE *fp;
	if(argc != 2){
		printf("Use:./a.out filename\n");
		return 0;
	}
	if( (fp = fopen(argv[1], "a+")) == NULL ){
		perror("open fail");
		exit(1);
	
	}
	input_time_file(fp);

	return 0;
}


/*
akaedu@akaedu-G41MT-D3:~/lin/806_io$ ./4.1 data
6 :2014-8-23 18:57:35
7 :2014-8-23 18:57:36
8 :2014-8-23 18:57:37
9 :2014-8-23 18:57:38
10 :2014-8-23 18:57:39
^C
akaedu@akaedu-G41MT-D3:~/lin/806_io$ ./4.1 data
10 :2014-8-23 18:57:40
11 :2014-8-23 18:57:41
^C
akaedu@akaedu-G41MT-D3:~/lin/806_io$ 
*/





void shangdian(void) { // 1. 从Flash读取当前ID spi_flash_buffer_read((uint8_t*)&sys_config.log_file_id, 0x7E000, sizeof(sys_config.log_file_id)); // 2. 处理首次使用情况(全FF表示未初始化) if(sys_config.log_file_id == 0xFFFFFFFF) { sys_config.log_file_id = 0; } // 3. ID递增 sys_config.log_file_id++; // 4. 擦除扇区并写入新ID spi_flash_sector_erase(0x7E000); delay_ms(10); // 等待擦除完成 spi_flash_buffer_write((uint8_t*)&sys_config.log_file_id, 0x7E000, sizeof(sys_config.log_file_id)); delay_ms(10); // 等待写入完成 // 5. 打印新ID printf("New Log File ID: %lu\n", sys_config.log_file_id); } // 全局变量 uint32_t sample_cycle = CYCLE_5S; // 当前采样周期,单位秒 uint8_t hide_mode = 0; // 隐藏模式标志 /************************ 函数定义 ************************/ ErrStatus memory_compare(uint8_t* src,uint8_t* dst,uint16_t length); void nvic_config(void); void write_file(void); void create_required_folders(void); // 创建文件夹 void store_log_entry(const char *action); /************************ 存储日志文件 ************************/ void store_log_entry(const char *action) { static uint8_t log_initialized = 0; char filename[50]; // 当前文件名缓存 /* 上电时创建新日志文件 */ if (!log_initialized) { sprintf(filename, "log/log%lu.txt", sys_config.log_file_id); f_mkdir("log"); if (f_open(&log_file, filename, FA_CREATE_ALWAYS | FA_WRITE) == FR_OK) { log_file_open = 1; log_initialized = 1; } /* 写入初始信息 */ if (log_file_open) { rtc_time_t current_time; rtc_current_time_get(&rtc_initpara); char header[128]; sprintf(header, "System started at 20%02d-%02d-%02d %02d:%02d:%02d\r\n", current_time.year, current_time.month, current_time.day, current_time.hour, current_time.minute, current_time.second); UINT bw; f_write(&log_file, header, strlen(header), &bw); f_sync(&log_file); } } /* 写入日志条目 */ if (log_file_open) { rtc_time_t current_time; rtc_current_time_get(&rtc_initpara); char log_entry[128]; sprintf(log_entry, "[20%02d-%02d-%02d %02d:%02d:%02d] %s\r\n", current_time.year, current_time.month, current_time.day, current_time.hour, current_time.minute, current_time.second, action); UINT bw; f_write(&log_file, log_entry, strlen(log_entry), &bw); f_sync(&log_file); } } /************************ 保存config文件到flash************************/ void save_config_to_flash(void){ uint32_t sector_addr = target_addr & ~(SECTOR_SIZE - 1); uint16_t size = sizeof(sys_config); // 擦除目标扇区 spi_flash_write_enable(); // 使能写操作 spi_flash_sector_erase(sector_addr); spi_flash_wait_for_write_end(); // 等待擦除完成 // 写入数据 spi_flash_write_enable(); // 再次使能写操作 spi_flash_buffer_write((uint8_t*)&sys_config, target_addr, size); spi_flash_wait_for_write_end(); // 等待写入完成 } /************************************************************ * Function : System_Init * Comment : 用于初始化MCU * Parameter: null * Return : null * Author : Lingyu Meng * Date : 2025-02-30 V0.1 original ************************************************************/ void System_Init(void) { systick_config(); // 时钟配置 nvic_config(); // 配置中断控制器 KEY_Init(); // KEY初始化 LED_Init(); USART0_Config(); // 串口初始化 nvic_irq_enable(USART0_IRQn, 0, 0); // 使能USART0中断 usart_interrupt_enable(USART0, USART_INT_RBNE); // 接收中断打开 ADC_port_init(); // ADC初始化 OLED_Init(); TF_Init(); spi_flash_init(); // 初始化SPI Flash RTC_Init(); /* 初始化日志 */ shangdian(); // 每次上电递增 save_config_to_flash(); // 保存新的文件ID store_log_entry("System started"); // 从Flash读取保存的采样周期 uint32_t saved_cycle; spi_flash_buffer_read((uint8_t*)&saved_cycle, SAMPLE_CYCLE_ADDR, sizeof(uint32_t)); // 验证读取的值是否有效 if (saved_cycle == CYCLE_5S || saved_cycle == CYCLE_10S || saved_cycle == CYCLE_15S) { sample_cycle = saved_cycle; // printf("Loaded saved sample cycle: %d s\r\n", sample_cycle); } else { // 无效值,使用默认5秒 sample_cycle = CYCLE_5S; // printf("Using default sample cycle: %d s\r\n", sample_cycle); // 保存默认值到Flash spi_flash_sector_erase(SAMPLE_CYCLE_ADDR); spi_flash_buffer_write((uint8_t*)&sample_cycle, SAMPLE_CYCLE_ADDR, sizeof(uint32_t)); } // 读取队伍编号 Team_num(); delay_1ms(10); }上电sys_config.log_file_id自增现在有问题,检测ft卡中log文件夹无文件时sys_config.log_file_id=0,然后上电自增
最新发布
06-18
<think>我们面临的问题是:上电时,如果TF卡中的log文件夹没有文件,我们希望`sys_config.log_file_id`从0开始,然后上电自增。但是当前的`shangdian`函数在首次使用(全FF)时将`sys_config.log_file_id`设置为0,然后立即自增为1,并写入Flash。这样,第一次上电后,Flash中存储的ID为1,而log文件夹中却没有ID为0的文件(因为首次自增后创建的是ID为1的文件)。我们需要调整逻辑:在首次运行时(全FF),ID应该从0开始,并且创建文件0,但当前代码在首次运行时将ID设为0后立即自增为1,然后创建文件1。期望的行为:1.首次上电(全FF):ID从0开始,创建log0.txt,然后ID自增为1并保存,这样下次上电就会创建log1.txt。2.非首次上电:读取Flash中的ID,自增后创建新文件。然而,在`shangdian`函数中,我们递增ID并保存,然后在`store_log_entry`函数中根据递增后的ID创建文件。所以,当前首次上电的行为:-读取Flash:0xFFFFFFFF->设置为0->递增为1->保存1到Flash->创建log1.txt。这样,log0.txt没有被创建。我们想要的是:首次上电创建log0.txt,然后保存的ID为1(为下次创建log1.txt做准备)。因此,我们需要在首次运行时(全FF)不立即递增,而是先使用0,然后递增并保存,但递增后的值用于下次使用。修改思路:-在`shangdian`函数中,如果是首次(全FF),则设置ID为0,然后递增为1并保存,但是这次启动应该使用0作为本次的文件ID。-但是`shangdian`函数递增后,我们保存了1,然后`store_log_entry`函数中使用的`sys_config.log_file_id`已经是1了。所以我们需要将本次要使用的文件ID和下次要使用的分开。我们可以这样:-在`shangdian`函数中,我们只管理Flash中存储的“下一个可用的文件ID”。-在首次运行时,Flash中为全FF,我们设置“下一个可用的文件ID”为0(表示还没有任何文件),然后当我们创建文件时,使用这个“下一个可用的文件ID”作为本次的文件ID,然后将其递增并保存。但是,当前`shangdian`函数在系统初始化时就被调用,它递增ID并保存,然后`store_log_entry`使用递增后的ID。调整步骤:1.修改`shangdian`函数:让它返回本次要使用的文件ID(即递增前的值),并且将递增后的值保存到Flash。2.在`System_Init`中调用`shangdian`时,将返回的文件ID赋值给`sys_config.log_file_id`(本次使用的文件ID)。但是注意,`shangdian`函数现在没有返回值,而且直接修改了`sys_config.log_file_id`。另一种方法(不改变现有结构):-在`shangdian`函数中,如果是首次运行(全FF),我们设置`sys_config.log_file_id`为0,然后递增为1并保存。但是本次启动我们想要使用0作为文件ID,所以我们需要在递增前保存0到一个临时变量,用于本次创建文件。-但是`shangdian`函数之后,`sys_config.log_file_id`已经是1了,所以我们需要在`store_log_entry`中不使用`sys_config.log_file_id`,而是使用一个临时保存的0。这样会破坏现有逻辑。我建议重构:将`shangdian`函数改为返回本次要使用的文件ID,并且将递增后的值保存到Flash。同时,不再直接修改`sys_config.log_file_id`,而是让调用者将返回的文件ID用于本次。修改`shangdian`函数如下:```cuint32_tshangdian(void){uint32_tcurrent_id;//1.从Flash读取当前IDspi_flash_buffer_read((uint8_t*)&current_id,0x7E000,sizeof(current_id));//2.处理首次使用情况(全FF表示未初始化)if(current_id==0xFFFFFFFF){current_id=0;//首次,本次使用0}else{//非首次,本次使用current_id(即上次保存的值),然后递增current_id++;//这里注意:我们原本的设计是每次上电递增,所以非首次时,我们使用上一次保存的下一个ID,然后递增保存为再下一次准备。//但是这样会导致非首次启动时,current_id已经递增了,然后下面还要递增?所以不对。//我们需要重新设计逻辑。}//3.保存递增后的值(为下一次启动准备)uint32_tnext_id=current_id+1;//4.擦除扇区并写入新IDspi_flash_sector_erase(0x7E000);delay_ms(10);//等待擦除完成spi_flash_buffer_write((uint8_t*)&next_id,0x7E000,sizeof(next_id));delay_ms(10);//等待写入完成//5.打印新ID(实际是本次使用的ID)printf("CurrentLogFileID:%lu\n",current_id);returncurrent_id;}```但是这样修改后,非首次启动时,我们读取到的current_id是上一次保存的next_id(即本次应该使用的ID),然后我们将其递增得到next_id(为下次准备)。这样逻辑是连续的。首次启动:current_id=0(因为读取到0xFFFFFFFF,我们设为0)next_id=1保存1到Flash返回0(本次使用0)第二次启动:读取到1(current_id=1)next_id=2保存2返回1(本次使用1)第三次启动:返回2,以此类推。这样,我们在`System_Init`中:uint32_tcurrent_log_id=shangdian();然后,将这个current_log_id赋值给一个全局变量(比如g_current_log_id),在`store_log_entry`中使用这个g_current_log_id来创建文件。但是,注意`store_log_entry`函数中目前使用的是`sys_config.log_file_id`,我们需要改变这个变量的含义,或者使用一个新的变量。由于`sys_config`结构体可能被保存到Flash(在`save_config_to_flash`中),所以我们不能轻易改变它的结构。我们可以将本次的文件ID保存到`sys_config`的一个新字段,但这样会改变结构体。或者,我们单独设置一个全局变量`current_log_file_id`。考虑到`sys_config.log_file_id`原本就是用来存储当前文件ID的,我们可以改变其含义:它不再存储在Flash中(除了在0x7E000位置存储的下一个ID),而是作为本次运行的文件ID。修改步骤:1.修改`shangdian`函数,使其返回本次使用的文件ID,并保存下一次的ID到Flash。2.在`System_Init`中,调用`shangdian`并将返回值赋给`sys_config.log_file_id`(这样`store_log_entry`中就可以直接使用它了)。3.注意,`shangdian`函数中不再直接修改`sys_config.log_file_id`,而是返回一个值。修改后的`shangdian`函数:```cuint32_tshangdian(void){uint32_tcurrent_id;//从Flash读取存储的ID(这个ID是下一次启动时要使用的文件ID)spi_flash_buffer_read((uint8_t*)&current_id,0x7E000,sizeof(current_id));//处理首次使用情况(全FF表示未初始化)if(current_id==0xFFFFFFFF){//首次运行,本次使用0,下一次启动应该使用1current_id=0;//本次使用0}else{//非首次运行:current_id中存储的是本次启动应该使用的文件ID//直接使用current_id,然后准备下一次的ID为current_id+1//注意:这里不需要递增current_id,因为current_id已经是本次要用的了//但是,我们读取到的current_id其实是上一次保存的“下一次ID”,所以本次使用它,然后下一次使用current_id+1//所以这里不需要修改current_id,它已经是正确的了}//计算下一次启动应该使用的ID(即本次使用的ID+1)uint32_tnext_id=current_id+1;//擦除扇区并写入新ID(next_id)spi_flash_sector_erase(0x7E000);delay_ms(10);//等待擦除完成spi_flash_buffer_write((uint8_t*)&next_id,0x7E000,sizeof(next_id));delay_ms(10);//等待写入完成printf("CurrentLogFileID:%lu\n",current_id);returncurrent_id;}```在`System_Init`中:```cvoidSystem_Init(void){//...其他初始化//调用shangdian获取本次的文件ID,并赋值给sys_config.log_file_idsys_config.log_file_id=shangdian();//注意:这里覆盖了sys_config.log_file_id//注意:这里不再需要save_config_to_flash来保存文件ID,因为shangdian已经将下一次的ID保存到Flash的0x7E000位置了//所以我们可以注释掉下面这行//save_config_to_flash();//保存新的文件ID->不再需要,因为shangdian已经保存了store_log_entry("Systemstarted");//...其他代码}```这样,首次启动:shangdian读取到0xFFFFFFFF->设置current_id=0,next_id=1,保存1到Flash,返回0。所以sys_config.log_file_id=0,然后创建log0.txt。第二次启动:shangdian读取到1->current_id=1(本次使用1),next_id=2,保存2到Flash,返回1。创建log1.txt。这样就符合要求了。但是,注意`save_config_to_flash`函数会保存整个`sys_config`结构体,其中包含了`log_file_id`(即本次的文件ID)。但是,这个文件ID我们并不需要永久保存(因为每次启动都会重新生成),而且我们已经在0x7E000位置保存了下一个ID。所以,我们可以考虑从`sys_config`结构体中移除`log_file_id`,或者不再将它保存到Flash(在`save_config_to_flash`中保存的地址可能是另一个地方,与0x7E000无关)。但是,如果`save_config_to_flash`保存了`log_file_id`,那么下次启动时,我们通过`shangdian`得到的是1,但是`sys_config.log_file_id`被保存为0,然后我们通过`Team_num`或其他函数读取`sys_config`时,可能会使用这个0,而不是1。所以,我们需要确保:-`sys_config.log_file_id`仅在本次运行时有效,不需要保存到Flash(除了0x7E000位置专门用于存储下一次文件ID的Flash区域)。因此,我们可以在`save_config_to_flash`函数中,不再保存`log_file_id`字段?或者,我们改变`sys_config`结构体,去掉`log_file_id`,用一个全局变量代替。为了最小改动,我们可以:-在`save_config_to_flash`函数中,保存`sys_config`结构体时,不包含`log_file_id`字段?但是结构体是连续的,不太可能单独去掉一个字段。另一种方法:将`log_file_id`从`sys_config`结构体中移除,改为全局变量。步骤:1.在全局变量区域定义:`uint32_tg_current_log_file_id;`2.修改`shangdian`函数返回的文件ID赋值给`g_current_log_file_id`。3.在`store_log_entry`函数中,使用`g_current_log_file_id`来创建文件名。4.修改`store_log_entry`函数,不再使用`sys_config.log_file_id`。这样,`sys_config`结构体中就没有`log_file_id`了,所以`save_config_to_flash`不会保存它。我们按照这个思路修改:首先,移除`sys_config`结构体中的`log_file_id`(如果原来有的话)。如果没有,则跳过。然后,定义全局变量:`uint32_tg_current_log_file_id;`修改`shangdian`函数(同上),返回本次文件ID。在`System_Init`中:`g_current_log_file_id=shangdian();`修改`store_log_entry`函数:将`sprintf(filename,"log/log%lu.txt",sys_config.log_file_id);`改为`sprintf(filename,"log/log%lu.txt",g_current_log_file_id);`同时,在`store_log_entry`中,不再使用`sys_config.log_file_id`,而使用`g_current_log_file_id`。这样,问题就解决了。由于你提供的代码片段中,`store_log_entry`函数已经使用了`sys_config.log_file_id`,所以我们需要修改它。另外,在`shangdian`函数中,我们读取Flash地址0x7E000,这个地址和`save_config_to_flash`中使用的地址(`target_addr`)应该是不同的,所以不会互相影响。现在,我们按照上述思路修改代码。由于无法直接运行,请根据实际情况调整。注意:在`shangdian`函数中,我们使用了0x7E000地址,这个地址应该是一个单独的扇区,专门用于存储下一个日志文件ID。最后,记得在`System_Init`中注释掉`save_config_to_flash`的调用,因为我们已经不需要它来保存文件ID了,而且它可能会覆盖其他配置。但是,`save_config_to_flash`可能还保存了其他配置,所以我们不能完全去掉。我们只需要确保它不保存文件ID即可。由于我们已经将文件ID移出`sys_config`结构体,所以`save_config_to_flash`不会保存文件ID。修改后的代码示例:全局变量定义:```cuint32_tg_current_log_file_id;//本次启动使用的日志文件ID````shangdian`函数修改为返回文件ID,并保存下一次的ID到Flash。`System_Init`中:```c//...其他初始化spi_flash_init();//初始化SPIFlashRTC_Init();g_current_log_file_id=shangdian();//获取本次的文件ID//初始化日志store_log_entry("Systemstarted");//从Flash读取保存的采样周期//...其他代码```修改`store_log_entry`函数:```cvoidstore_log_entry(constchar*action){staticuint8_tlog_initialized=0;charfilename[50];//当前文件名缓存/*上电时创建新日志文件*/if(!log_initialized){sprintf(filename,"log/log%lu.txt",g_current_log_file_id);//使用全局变量f_mkdir("log");if(f_open(&log_file,filename,FA_CREATE_ALWAYS|FA_WRITE)==FR_OK){log_file_open=1;log_initialized=1;}/*写入初始信息*/if(log_file_open){rtc_time_tcurrent_time;rtc_current_time_get(&rtc_initpara);charheader[128];sprintf(header,"Systemstartedat20%02d-%02d-%02d%02d:%02d:%02d\r\n",current_time.year,current_time.month,current_time.day,current_time.hour,current_time.minute,current_time.second);UINTbw;f_write(&log_file,header,strlen(header),&bw);f_sync(&log_file);}}/*写入日志条目*/if(log_file_open){rtc_time_tcurrent_time;rtc_current_time_get(&rtc_initpara);charlog_entry[128];sprintf(log_entry,"[20%02d-%02d-%02d%02d:%02d:%02d]%s\r\n",current_time.year,current_time.month,current_time.day,current_time.hour,current_time.minute,current_time.second,action);UINTbw;f_write(&log_file,log_entry,strlen(log_entry),&bw);f_sync(&log_file);}}```这样,首次上电时,`g_current_log_file_id`为0,创建log0.txt;第二次为1,创建log1.txt,以此类推。注意:由于我们修改了`shangdian`函数,非首次运行时,它返回的是从Flash读取的值(即上一次保存的下一次ID),而这个值就是本次应该使用的ID。然后保存本次ID+1作为下一次的ID。请测试这个逻辑。另外,注意Flash的读写操作需要确保正确性,比如擦除和写入的等待时间,以及读取后的校验(比如读取后检查是否成功)等,这里没有做,实际中可能需要增加校验。最后,由于修改了结构,请确保所有用到`sys_config.log_file_id`的地方都改为使用`g_current_log_file_id`,并移除了`sys_config`中的`log_file_id`字段。如果还有其他地方使用`sys_config.log_file_id`,请一并修改。由于代码片段不完整,以上修改仅供参考。如果按照上述修改后,应该可以解决首次上电创建log0.txt,之后依次递增的问题。注意:在`shangdian`函数中,我们保存了下一个ID,即使本次运行在创建文件时失败(例如TF卡不存在),我们仍然递增了ID。这可能会导致ID不连续(有跳号)。如果要求严格连续,可以在成功创建文件后再保存下一个ID。但这样就需要改变调用顺序:先调用`shangdian`获取ID,然后尝试创建文件,如果创建成功再保存下一个ID,否则不保存(这样下次启动还会用同一个ID)。但这样会增加复杂性。根据需求选择。目前的设计是:上电就递增ID并保存,不管文件是否创建成功。这样简单,但可能会有跳号。如果TF卡被移除,那么每次上电都会递增ID,但不会创建文件。当TF卡插入时,会创建对应ID的文件,但中间缺失的ID不会被使用。如果需要避免跳号,可以将保存下一个ID的操作放到文件成功创建之后。这需要调整`store_log_entry`函数,使其返回是否成功创建文件,然后在成功后再调用一个函数来保存下一个ID。但这样会使得逻辑复杂,且需要确保即使系统崩溃也不会重复使用同一个ID。考虑到日志文件ID主要用于区分不同的运行,跳号问题不大,所以当前设计可以接受。现在,我们按照上述修改代码。由于代码较长,这里只给出关键部分的修改。如果还有问题,请反馈。注意:由于原代码片段中`shangdian`函数已经存在,我们修改了它的行为,所以请确保替换。另外,原`shangdian`函数中,在非首次运行时,我们不需要对读取到的ID做任何修改,直接使用,然后保存该ID+1。所以代码中非首次运行的分支是空的。最后,请测试首次运行和非首次运行的情况。相关问题:§§相关问题§§:1.在嵌入式系统中,如何确保Flash写入的原子性(避免在写入过程中掉电导致数据损坏)?2.如果Flash写入失败,如何检测并进行错误处理?3.除了使用单独的Flash扇区存储日志文件ID,还有哪些方法可以管理递增的ID?4.在文件系统创建文件失败时(例如TF卡被拔出),如何重试或记录错误?</think>根据您提供的代码,问题在于`shangdian()`函数在首次启动时的逻辑处理。当前逻辑在检测到全FF(未初始化)时将ID设为0,然后立即自增到1,导致首次启动时实际文件ID为1,而log0.txt文件从未被创建。以下是解决方案: ### 解决方案 修改`shangdian()`函数,使其在首次启动时不立即自增ID,而是先使用0创建文件,然后为下次启动准备自增后的ID: ```c uint32_t shangdian(void) { uint32_t next_id; // 存储下一次启动时应使用的ID // 1. 从Flash读取存储的ID spi_flash_buffer_read((uint8_t*)&next_id, 0x7E000, sizeof(next_id)); // 2. 处理首次使用情况(全FF表示未初始化) if(next_id == 0xFFFFFFFF) { // 首次启动:本次使用ID=0,设置下次启动使用ID=1 next_id = 1; // 为下一次启动准备 sys_config.log_file_id = 0; // 本次使用0 } else { // 非首次启动:使用存储的ID作为本次文件ID sys_config.log_file_id = next_id; // 准备下一次启动的ID (当前ID+1) next_id++; } // 3. 擦除扇区并写入下一次的ID spi_flash_sector_erase(0x7E000); delay_ms(10); // 等待擦除完成 spi_flash_buffer_write((uint8_t*)&next_id, 0x7E000, sizeof(next_id)); delay_ms(10); // 等待写入完成 // 4. 打印本次使用的ID printf("Current Log File ID: %lu\n", sys_config.log_file_id); return sys_config.log_file_id; } ``` ### 修改说明: 1. **变量重命名**: - `next_id` 表示**下一次启动**时应该使用的文件ID - `sys_config.log_file_id` 表示**本次启动**实际使用的文件ID 2. **首次启动逻辑**: ```c if(next_id == 0xFFFFFFFF) { next_id = 1; // 下次启动用1 sys_config.log_file_id = 0; // 本次用0 } ``` - 检测到未初始化(全FF)时,设置本次ID=0 - 存储下一次ID=1到Flash 3. **非首次启动逻辑**: ```c else { sys_config.log_file_id = next_id; // 本次使用存储的ID next_id++; // 为下次启动准备 } ``` 4. **保存机制**: - 始终将`next_id`(下一次的ID)写入Flash - 不再修改`sys_config.log_file_id`的Flash存储位置 ### 需要同步修改的地方: 1. **在`System_Init()`中移除冗余保存**: ```c void System_Init(void) { // ...其他初始化代码... shangdian(); // 获取本次文件ID(已修改函数) // 移除 save_config_to_flash(); // 不再需要此调用 store_log_entry("System started"); // ...其他代码... } ``` 2. **确保`store_log_entry()`使用正确的ID**: ```c void store_log_entry(const char *action) { // ... sprintf(filename, "log/log%lu.txt", sys_config.log_file_id); // ... } ``` ### 工作流程示例: | 启动次数 | Flash存储值 | 本次文件ID | 创建文件 | 下次Flash存储值 | |---------|------------|-----------|---------|---------------| | 首次 | 0xFFFFFFFF | 0 | log0.txt| 1 | | 第二次 | 1 | 1 | log1.txt| 2 | | 第三次 | 2 | 2 | log2.txt| 3 | ### 关键改进点: 1. **物理分离**: - Flash的0x7E000位置**始终存储下一次的ID** - `sys_config.log_file_id` **仅用于本次运行时**的文件操作 2. **首次启动处理**: - 当检测到未初始化时,直接使用ID=0 - 避免首次启动时的ID跳跃(0→1) 3. **原子性操作**: - 每次启动只修改Flash一次(存储下一次ID) - 运行时ID保存在RAM中,不依赖Flash ### 注意事项: 1. **Flash寿命**:确保0x7E000位于独立的Flash扇区,避免频繁擦写其他数据 2. **错误处理**:添加Flash操作返回值检查 3. **时间敏感**:`delay_ms(10)`可能需要根据实际芯片调整
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值