STM32H750XB_RT-THREAD/26-27-LTDC—液晶显示/刷外部FLASH程序(如何恢复字库)/User/res_mgr/res_writer.c
2025-07-21 14:34:29 +08:00

541 lines
14 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//#include "Include.h"
//#include "x_file.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "res_mgr.h"
#include "ff.h"
#include "./led/bsp_led.h"
#include "./flash/bsp_qspi_flash.h"
/*============================================================================*/
/*flash及sd卡的文件系统句柄*/
FATFS flash_fs;
FATFS sd_fs; /* Work area (file system object) for logical drives */
//SD卡源数据路径
char src_dir[512]= RESOURCE_DIR;
static FIL file_temp; /* file objects */
static char full_file_name[512];
static char line_temp[512];
static uint8_t state = QSPI_ERROR;
uint32_t loop = 0;
/**
* @brief 检查是否要忽略文件夹或文件
* @param check_name[in] 要检查的文件名,全路径
* @param ignore_file[in] ignore文件的路径全路径
* @retval 1表示忽略0表示不忽略
*/
uint8_t Is_Ignore(char *check_name, char *ignore_file)
{
FRESULT result;
uint32_t i;
char *ignore_file_dir;
char *dir_ptr ;
char *full_path;
result = f_open(&file_temp, ignore_file, FA_OPEN_EXISTING | FA_READ);
if(result == FR_NO_FILE)
{
BURN_DEBUG("不存在忽略文件,直接返回");
return 0;
}
ignore_file_dir = malloc(512);
full_path = malloc(512);
if(ignore_file_dir == NULL || full_path == NULL)
{
BURN_ERROR("错误malloc时内存不足");
return 0;
}
/* 得到ignore文件所在的目录 */
strcpy(ignore_file_dir, ignore_file);
dir_ptr = strrchr(ignore_file_dir, '/');
*(dir_ptr) = '\0';
/* 遍历忽略文件 */
for(i=0;1;i++)
{
/* 读一行忽略的文件名 */
f_gets(line_temp, sizeof(line_temp), &file_temp);
/* 注释行,跳过*/
if(line_temp[0] == '#')
continue;
/* 替换掉回车 */
line_temp[strlen(line_temp)-1] = '\0';
/* 拼接出完整的忽略文件路径 */
sprintf(full_path, "%s/%s",ignore_file_dir,line_temp);
// BURN_DEBUG("full_path=%s,\r\ncheck_name=%s\r\n",
// full_path,
// check_name);
/* 比较是否一致 */
if(strcasecmp(check_name,full_path) == 0)
{
f_close(&file_temp);
free(ignore_file_dir);
free(full_path);
return 1;
}
/* 已到文件尾,遍历完毕,跳出循环 */
if(f_eof(&file_temp) != 0 )
{
f_close(&file_temp);
break;
}
}
free(ignore_file_dir);
free(full_path);
return 0;
}
/**
* @brief Make_Catalog 创建资源目录文件,这是个递归函数
* @param path[in]:资源路径,必须输入一个空间足够的数组,
函数执行时会对它进行路径赋值操作
* @param clear: 0 表示重新生成目录文件1表示在原文件下追加内容
* @note 在外部调用本函数时clear必须设置为0
* 函数内部递归调用才设置为1.
* @retval result:文件系统的返回值
*/
FRESULT Make_Catalog (char* path,uint8_t clear)
{
FRESULT res; //部分在递归过程被修改的变量,不用全局变量
FILINFO fno;
DIR dir;
int i;
char *fn; // 文件名
/* 记录地址偏移信息 */
static uint32_t resource_addr = CATALOG_SIZE ;
uint32_t addr_temp = 0;
#if _USE_LFN
/* 长文件名支持 */
/* 简体中文需要2个字节保存一个“字”*/
static char lfn[_MAX_LFN*2 + 1];
fno.lfname = lfn;
fno.lfsize = sizeof(lfn);
#endif
if(clear == 0)
{
BURN_INFO("正在生成烧录信息文件catalog.txt...\r\n");
/* 第一次执行Make_Catalog函数时删除旧的烧录信息文件 */
f_unlink(BURN_INFO_NAME_FULL);
}
//打开目录
res = f_opendir(&dir, path);
if (res == FR_OK)
{
i = strlen(path);
for (;;)
{
//读取目录下的内容,再读会自动读下一个文件
res = f_readdir(&dir, &fno);
//为空时表示所有项目读取完毕,跳出
if (res != FR_OK || fno.fname[0] == 0) break;
#if _USE_LFN
fn = *fno.lfname ? fno.lfname : fno.fname;
#else
fn = fno.fname;
#endif
//点表示当前目录,跳过
if (*fn == '.') continue;
//目录,递归读取
if (fno.fattrib & AM_DIR)
{
//合成完整目录名
sprintf(&path[i], "/%s", fn);
BURN_INFO("-------------------------------------");
BURN_INFO("文件夹:%s",path);
/* 若名字跟ignore文件中记录的相同忽略*/
if(Is_Ignore(path, IGNORE_NAME_FULL) == 1)
{
BURN_INFO("忽略文件夹:%s",path);
continue;
}
//递归遍历
res = Make_Catalog(path,1);
path[i] = 0;
//打开失败,跳出循环
if (res != FR_OK)
break;
}
else
{
uint32_t file_size;
/* 忽略本身的目录文件 和 忽略文件 */
if(strcasecmp(fn,BURN_INFO_NAME) == 0 ||
strcasecmp(fn,IGNORE_NAME) == 0)
continue;
/* 得到全文件名 */
sprintf(full_file_name, "%s/%s",path, fn);
/* 若名字跟ignore文件中记录的相同忽略*/
if(Is_Ignore(full_file_name, IGNORE_NAME_FULL) == 1)
{
BURN_INFO("-------------------------------------");
BURN_INFO("忽略文件:%s",full_file_name);
continue;
}
f_open(&file_temp, full_file_name, FA_OPEN_EXISTING | FA_READ);
file_size = f_size(&file_temp);
f_close(&file_temp);
BURN_INFO("-------------------------------------");
BURN_INFO("资源文件路径:%s", full_file_name); //完整文件名
BURN_INFO("资源文件名:%s", fn); //文件名
BURN_INFO("资源大小:%d", file_size); //文件大小
BURN_INFO("要烧录到的偏移地址:%d绝对地址:%d",
resource_addr,
resource_addr + RESOURCE_BASE_ADDR ); //文件名要存储到的资源目录
f_open(&file_temp, BURN_INFO_NAME_FULL, FA_OPEN_ALWAYS |FA_WRITE | FA_READ);
f_lseek(&file_temp, f_size(&file_temp));
/* 每个文件记录占5行 */
f_printf(&file_temp, "%s\n", full_file_name); //完整文件名
f_printf(&file_temp, "%s\n", fn); //文件名
f_printf(&file_temp, "%d\n", file_size); //文件大小
f_printf(&file_temp, "%d\n\n", resource_addr); //文件名要存储到的资源目录(未加上基地址)
f_close(&file_temp);
/* 进行地址对齐运算,偏移文件+4096对齐的大小 */
// addr_temp = (uint32_t)(file_size+4096)/4096;
// addr_temp = (uint32_t)addr_temp*4096;
//
// resource_addr += addr_temp; /* 偏移文件的大小 */
resource_addr += file_size; /* 偏移文件的大小 */
/* 可以在这里提取特定格式的文件路径 */
}//else
} //for
}
/* 输出烧录文件信息 */
if(clear == 0)
{
BURN_INFO("-------------------------------------\r\n");
/* 执行至最后时resource_addr是最后一个资源的尾地址 */
BURN_INFO("所有烧录的总数据大小:%d (含目录)", resource_addr );
BURN_INFO("烧录后占用的地址空间:%d - %d",
RESOURCE_BASE_ADDR,
resource_addr + RESOURCE_BASE_ADDR );
}
return res;
}
/**
* @brief 读取目录文件的信息到
* @param catalog_name[in] 存储了目录信息的文件名
* @param file_index 第file_index条记录
* @param dir[out] 要烧录到FLASH的目录信息
* @param full_file_name[out] 第file_index条记录中的文件的全路径
* @retval 1表示到达了文件尾0为正常
*/
uint8_t Read_CatalogInfo(uint32_t file_index,
CatalogTypeDef *dir,
char *full_name)
{
uint32_t i;
f_open(&file_temp, BURN_INFO_NAME_FULL, FA_OPEN_EXISTING | FA_READ);
/* 跳过前N行,每个文件记录5行 */
for(i=0;i<file_index*5;i++)
{
f_gets(line_temp, sizeof(line_temp), &file_temp);
}
/* 到达文件尾*/
if(f_eof(&file_temp) != 0 )
return 1;
/* 文件全路径 */
f_gets(line_temp, sizeof(line_temp), &file_temp);
memcpy(full_name, line_temp, strlen(line_temp)+1);
/* 替换掉回车 */
full_name[strlen(full_name)-1] = '\0';
/* 文件名 */
f_gets(line_temp, sizeof(line_temp), &file_temp);
memcpy(dir->name, line_temp, strlen(line_temp)> (sizeof(CatalogTypeDef)-8) ? (sizeof(CatalogTypeDef)-8):strlen(line_temp)+1 );
dir->name[strlen(dir->name)-1] = '\0';
/* 文件大小 */
f_gets(line_temp, sizeof(line_temp), &file_temp);
dir->size = atoi(line_temp);
/* 文件要烧录的位置 */
f_gets(line_temp, sizeof(line_temp), &file_temp);
dir->offset = atoi(line_temp);
f_close(&file_temp);
BURN_DEBUG("ful name=%s,\r\nname=%s,\r\nsize=%d,\r\noffset=%d\r\n",
full_name,
dir->name,
dir->size,
dir->offset
);
return 0;
}
/**
* @brief 烧录目录到FLASH中
* @param catalog_name 记录了烧录信息的目录文件名
* @note 使用本函数前需要确认flash已为擦除状态
* @retval 1表示到达了文件尾0为正常
*/
void Burn_Catalog(void)
{
CatalogTypeDef dir;
uint8_t i;
uint8_t is_end;
/* 遍历目录文件 */
for(i=0;1;i++)
{
is_end = Read_CatalogInfo(i,
&dir,
full_file_name);
/* 已遍历完毕,跳出循环 */
if(is_end !=0)
break;
loop = 0;
if(sizeof(dir) == 0)break;//为0时不进行读写跳出
/* 把dir信息烧录到FLASH中 */
state = BSP_QSPI_Write((uint8_t*)&dir,RESOURCE_BASE_ADDR + sizeof(dir)*i,sizeof(dir));
if(state != QSPI_OK)
{
BURN_ERROR("Burn_Catalog QSPI_Write ERROR");
BURN_ERROR("loop=%d",loop);
}
}
}
/**
* @brief 根据目录文件信息烧录内容
* @param catalog_name 记录了烧录信息的目录文件名
* @note 使用本函数前需要确认flash已为擦除状态
* @retval 文件系统操作返回值
*/
FRESULT Burn_Content(void)
{
CatalogTypeDef dir;
uint8_t i;
uint8_t is_end;
FRESULT result;
UINT bw; /* File R/W count */
uint32_t write_addr=0;
uint8_t tempbuf[256];
/* 遍历目录文件 */
for(i=0;1;i++)
{
is_end = Read_CatalogInfo(i,
&dir,
full_file_name);
/* 已遍历完毕,跳出循环 */
if(is_end !=0)
break;
BURN_INFO("-------------------------------------");
BURN_INFO("准备烧录内容:%s",full_file_name);
LED_BLUE;
result = f_open(&file_temp,full_file_name,FA_OPEN_EXISTING | FA_READ);
if(result != FR_OK)
{
BURN_ERROR("打开文件失败!");
LED_RED;
return result;
}
BURN_INFO("正在向FLASH写入内容...");
write_addr = dir.offset + RESOURCE_BASE_ADDR;
loop = 0;
while(result == FR_OK)
{
loop++;
result = f_read( &file_temp, tempbuf, 256, &bw);//读取数据
if(result!=FR_OK) //执行错误
{
BURN_ERROR("读取文件失败result = %d",result);
LED_RED;
return result;
}
if(bw == 0)break;//为0时不进行读写跳出
state = BSP_QSPI_Write(tempbuf,write_addr,bw); //拷贝数据到外部flash上
if(state != QSPI_OK)
{
BURN_ERROR("Burn_Content QSPI_Write ERROR");
BURN_ERROR("loop=%d bw =%d",loop,bw);
}
write_addr+=bw;
if(bw !=256)break;
}
BURN_INFO("内容写入完毕");
f_close(&file_temp);
}
BURN_INFO("************************************");
BURN_INFO("所有文件均已烧录完毕!(非文件系统部分)");
return FR_OK;
}
/**
* @brief 校验写入的内容
* @param catalog_name 记录了烧录信息的目录文件名
* @retval 文件系统操作返回值
*/
FRESULT Check_Resource(void)
{
CatalogTypeDef dir;
uint8_t i;
uint8_t is_end;
int offset;
FRESULT result;
UINT bw; /* File R/W count */
uint32_t read_addr=0,j=0;
uint8_t tempbuf[256],flash_buf[256];
/* 遍历目录文件 */
for(i=0;1;i++)
{
is_end = Read_CatalogInfo(i,
&dir,
full_file_name);
/* 已遍历完毕,跳出循环 */
if(is_end !=0)
break;
/* 在FLASH的目录里查找对应的offset */
offset = GetResOffset(dir.name);
if(offset == -1)
{
LED_RED;
BURN_INFO("校验失败无法在FLASH中找到文件%s 的目录",dir.name);
return FR_NO_FILE;
}
else
dir.offset = offset + RESOURCE_BASE_ADDR;
BURN_INFO("-------------------------------------");
BURN_INFO("准备校验内容:%s",full_file_name);
BURN_INFO("内容名:%s ",dir.name);
BURN_INFO("内容绝对地址:%d ",dir.offset);
BURN_INFO("内容大小:%d ",dir.size);
result = f_open(&file_temp,full_file_name,FA_OPEN_EXISTING | FA_READ);
if(result != FR_OK)
{
BURN_ERROR("打开文件失败!");
LED_RED;
return result;
}
//校验数据
read_addr = dir.offset;
f_lseek(&file_temp,0);
loop = 0;
while(result == FR_OK)
{
loop++;
result = f_read( &file_temp, tempbuf, 256, &bw);//读取数据
if(result!=FR_OK) //执行错误
{
BURN_ERROR("读取文件失败!");
LED_RED;
return result;
}
if(bw == 0)break;//为0时不进行读写跳出
state = BSP_QSPI_FastRead(flash_buf,read_addr,bw); //从FLASH中读取数据
if(state != QSPI_OK)
{
BURN_ERROR("Check_Resource BSP_QSPI_Read ERROR");
BURN_ERROR("loop=%d bw =%d",loop,bw);
}
read_addr+=bw;
for(j=0;j<bw;j++)
{
if(tempbuf[j] != flash_buf[j])
{
BURN_ERROR("数据校验失败!");
BURN_ERROR("j=%d ",j);
BURN_ERROR("tempbuf");
BURN_DEBUG_ARRAY(tempbuf,bw);
BURN_ERROR("flash_buf");
BURN_DEBUG_ARRAY(flash_buf,bw);
LED_RED;
return FR_INT_ERR;
}
}
if(bw !=256)break;//到了文件尾
}
BURN_INFO("数据校验正常!");
LED_BLUE;
f_close(&file_temp);
}
LED_GREEN;
BURN_INFO("************************************");
BURN_INFO("所有文件校验正常!(非文件系统部分)");
return FR_OK;
}
/*********************************************END OF FILE**********************/