0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

用U盘记录系统LOG信息的简单步骤和方法

Geehy极海半导体 来源:Geehy极海半导体 2024-07-31 10:22 次阅读

# 01 前言

MCU 组成的系统在实际应用中,经常需要记录系统 LOG 信息,可以是系统不同任务执行情况的 LOG 信息,也可以是内核寄存器等便于维护调试的信息,或者是传感器的信息等。

上述的这些 LOG 信息,如果在能联网的系统中,那么直接传输回服务器即可,但如果是离线的系统,那么就需要一个存储设备来记录这些 LOG 信息。一般有以下几种方式:

- 记录到 Nor Flash 等板载存储器

- 记录到可移动的存储器,比如 U 盘等存储设备

本文提供后一种方式,即数据记录到 U 盘,这种方式方便进行现场维护,但实际应用中,可以结合 Nor Flash 加 U 盘的方式,避免读写 U 盘频率过快导致损坏。以下内容利用 APM32F407xx 的 OTG Host 和 Fatfs 文件系统,加上 RTC 功能来实现数据记录,包括以下功能。

- 按日期自动创建文件夹存放 `LOG.xls` 文件

- 按 1 秒的记录频率往 `LOG.xls` 文件写入带时间戳的数据

- 在 U 盘存储空间使用超过 90%时,删除日期最早的文件夹及 LOG 文件

# 02 外设和组件配置

下面简单介绍一下所用到的外设和组件的配置。

## OTG Host

OTG Host 的配置比较简单,配置为普通 FS 速度和 MSC Class 类即可。

void USB_HostInitalize(void)

{

/* USB host and class init */

USBH_Init(&gUsbHostFS, USBH_SPEED_FS, &USBH_MSC_CLASS, USB_HostUserHandler);

}

U 盘在枚举完成后,需要获取 `LUN` 的信息,以便后续对特定 `LUN` 进行命令和数据读写等操作。

101ee3c0-4e57-11ef-b8af-92fbcf53809c.jpg

有关 MSC Class 的处理在 `usbh_msc.c/h` 文件中的 `USBH_MSC_CLASS` 结构体句柄,其包含以下处理函数:

- `USBH_MSC_ClassInitHandler`:Class 初始化函数。

- `USBH_MSC_ClassDeInitHandler`:Class 解初始化函数。

- `USBH_MSC_ClassReqHandler`:Class 特定请求处理函数,用于处理 `MASS_STORAGE_RESET`、`GET_MAX_LUN` 等特定请求。

- `USBH_MSC_CoreHandler`:Class 内核处理函数,包括 BOT 命令、SCSI 命令的处理。

- `USBH_MSC_SOFHandler`:SOF 事件处理函数。

/* MSC class handler */

USBH_CLASS_T USBH_MSC_CLASS =

{

"Class MSC",

USBH_CLASS_MSC,

NULL,

USBH_MSC_ClassInitHandler,

USBH_MSC_ClassDeInitHandler,

USBH_MSC_ClassReqHandler,

USBH_MSC_CoreHandler,

USBH_MSC_SOFHandler,

};

下面的 `USBH_MSC_Handler` 状态机由 `USBH_MSC_CoreHandler` 函数调用,包含常见的 BOT 命令、SCSI 命令的处理。

/* USB host MSC state handler function */

USBH_MscStateHandler_T USBH_MSC_Handler[] =

{

USBH_MSC_InitHandler,

USBH_MSC_IdleHandler,

USBH_MSC_InquiryHandler,

USBH_MSC_TestUnitReadyHandler,

USBH_MSC_RequestSenseHandler,

USBH_MSC_ReadCapacityHandler,

USBH_MSC_ErrorUnrecoveredHandler,

USBH_MSC_ReadHandler,

USBH_MSC_WriteHandler,

USBH_MSC_RWRequestSenseHandler,

};

BOT SCSI 命令处理。

10462660-4e57-11ef-b8af-92fbcf53809c.jpg

OTG Host 部分还需要了解以下 API 函数,因为后续需要应用到 FatFs 文件系统中。

- `USBH_MSC_ReadDevInfo()`:用于获取所枚举成功的 `MSC` 设备信息,比如 `LUN` 逻辑单元是否准备完毕、多媒体设备是否存在、扇区数、block 数等。

- `USBH_MSC_DevStatus()`:获取 `LUN` 是否准备就绪的状态。

- `USBH_MSC_ReadDevWP()`:获取 `LUN` 写保护状态。

- `USBH_MSC_DevRead()`:读取 `LUN` 数据。

- `USBH_MSC_DevWrite()`:往 `LUN` 写入数据。

USBH_STA_T USBH_MSC_ReadDevInfo(USBH_INFO_T* usbInfo, uint8_t lun, USBH_MSC_STORAGE_INFO_T* device);

uint8_t USBH_MSC_DevStatus(USBH_INFO_T* usbInfo, uint8_t lun);

uint8_t USBH_MSC_ReadDevWP(USBH_INFO_T* usbInfo, uint8_t lun);

USBH_STA_T USBH_MSC_DevRead(USBH_INFO_T* usbInfo, uint8_t lun, uint32_t address,

uint8_t* buffer, uint16_t cnt);

USBH_STA_T USBH_MSC_DevWrite(USBH_INFO_T* usbInfo, uint8_t lun, uint32_t address,

uint8_t* buffer, uint16_t cnt);

## RTC

APM32F4xx 的 RTC 是一个独立的 BCD 定时计数器,提供完整的日历时钟功能。不像 F1 系列的 RTC 只是个计数器,需要自行设定元年,然后换算日期和时间。RTC 的配置比较简单,配置时钟源为 LSE,24 小时制即可,然后利用 RTC 的备份寄存器来判断是否需要配置日历和时间。

void RTC_CalendarConfig(void)

{

RTC_DateTypeDef Date_Structure;

RTC_TimeTypeDef Time_Structure;

/* Configure the Date */

Date_Structure.Year = 0x23;

Date_Structure.Month = RTC_MONTH_NOVEMBER;

Date_Structure.Date = 0x22;

Date_Structure.WeekDay = RTC_WEEKDAY_MONDAY;

if(DAL_RTC_SetDate(&hrtc,&Date_Structure,RTC_FORMAT_BCD) != DAL_OK)

{

DAL_ErrorHandler();

}

/* Configure the Time */

Time_Structure.Hours = 0x00;

Time_Structure.Minutes = 0x00;

Time_Structure.Seconds = 0x00;

Time_Structure.TimeFormat = RTC_HOURFORMAT12_AM;

Time_Structure.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;

Time_Structure.StoreOperation = RTC_STOREOPERATION_RESET;

if(DAL_RTC_SetTime(&hrtc,&Time_Structure,RTC_FORMAT_BCD) != DAL_OK)

{

DAL_ErrorHandler();

}

DAL_RTCEx_BKUPWrite(&hrtc,RTC_BKP_DR0, RTC_BKP_VALUE);

}

## TMR

APM32F4xx 系列的 RTC 没有秒中断,这里我们实现一秒记录一次数据到 U 盘,则开启一个定时为 1 秒的定时器中断来实现。

void DAL_TMR3_Config(void)

{

htmr3.Instance = TMR3;

htmr3.Init.Period = 10000 - 1;

htmr3.Init.Prescaler = 8400 - 1;

htmr3.Init.ClockDivision = 0;

htmr3.Init.CounterMode = TMR_COUNTERMODE_UP;

htmr3.Init.AutoReloadPreload = TMR_AUTORELOAD_PRELOAD_DISABLE;

if(DAL_TMR_Base_Init(&htmr3) != DAL_OK)

{

DAL_ErrorHandler();

}

}

## FatFs

使用 FatFs 文件系统前,要重新实现 `diskio.c` 及配置 `ffconf.h` 文件。

其中在 `diskio.c` 文件中,要利用 OTG Host 章节最后提到的 `MSC Class` 的 API ,重新实现以下函数。

`DSTATUS disk_status()`

`DSTATUS disk_initialize()`

`DRESULT disk_read()`

`DRESULT disk_write()

`DRESULT disk_ioctl()`

另外,简单介绍下后面应用代码中用到的几个 API,详细的使用方法,大家可以参考 FatFs 官方文档。

### f_opendir

该 API 用于打开一个已存在的目录文件夹。

FRESULT f_opendir (

DIR* dp, /* [OUT] Pointer to the directory object structure */

const TCHAR* path /* [IN] Directory name */

);

### f_readdir

该 API 用于读取目录项,后续用于筛选所有的有效目录。

FRESULT f_readdir (

DIR* dp, /* [IN] Directory object */

FILINFO* fno /* [OUT] File information structure */

);

### f_mkdir

该 API 用于创建一个新的目录文件夹。

FRESULT f_mkdir (

const TCHAR* path /* [IN] Directory name */

);

### f_open

该 API 用于打开一个文件。要注意的是参数 `mode flags` 需要使用 `FA_OPEN_ALWAYS`,避免文件内容被覆盖。

FRESULT f_open (

FIL* fp, /* [OUT] Pointer to the file object structure */

const TCHAR* path, /* [IN] File name */

BYTE mode /* [IN] Mode flags */

);

### f_unlink

该 API 用于删除文件或子目录。

FRESULT f_unlink (

const TCHAR* path /* [IN] Object name */

);

### f_close

该 API 用于关闭一个文件。

FRESULT f_close (

FIL* fp /* [IN] Pointer to the file object */

);

### f_lseek

该 API 用于移动被打开的文件对象的文件读或写指针,后面应用时,需要用来将写指针移动到文件末,以添加新的 LOG 信息。

FRESULT f_lseek (

FIL* fp, /* [IN] File object */

FSIZE_t ofs /* [IN] Offset of file read/write pointer to be set */

);

### f_printf

该 API 用于将格式化的字符串写入文件。使用该 API,我们可以方便的将各种数据类型,转换为字符串,再写入文件中。

int f_printf (

FIL* fp, /* [IN] File object */

const TCHAR* fmt, /* [IN] Format stirng */

...

);

### f_getfree

该 API 用于获取 volume 中的剩余空间。使用该 API,我们可以知道 U 盘剩余空间是多少,从而对老旧的 LOG 数据进行处理。

FRESULT f_getfree (

const TCHAR* path, /* [IN] Logical drive number */

DWORD* nclst, /* [OUT] Number of free clusters */

FATFS** fatfs /* [OUT] Corresponding filesystem object */

);

# 03 实现数据记录

## 定义参数结构体

定义 `rtcInfo` 和 `diskInfo` 来记录应用过程中的信息。

/* 记录 RTC 时间信息 */

typedef struct

{

uint16_t year;

uint8_t month;

uint8_t day;

uint8_t hour;

uint8_t minute;

uint8_t second;

} RTC_TIME_INFO_T;

/* 记录 U 盘和文件系统信息 */

typedef struct

{

char dirPath[512];

uint16_t dirNum;

uint32_t totSect;

uint32_t freSect;

double usedPercent;

} DISK_INFO_T;

RTC_TIME_INFO_T rtcInfo;

DISK_INFO_T diskInfo;

## 获取时间参数

在 TMR3 回调函数中获取 RTC 当前时间,并使能数据更新标志。

void RTC_Application(void)

{

char strBuf[50];

RTC_GetCalendar(&rtcInfo.year, &rtcInfo.month, &rtcInfo.day, &rtcInfo.hour, &rtcInfo.minute, &rtcInfo.second);

/* Display time */

sprintf(strBuf, "20%02d-%02d-%02d %02d:%02d:%02d", rtcInfo.year, rtcInfo.month, rtcInfo.day, rtcInfo.hour, rtcInfo.minute, rtcInfo.second);

DAL_LOGI(tag, "%s", strBuf);

}

void DAL_TMR_PeriodElapsedCallback(TMR_HandleTypeDef *htmr)

{

if(htmr->Instance == TMR3)

{

RTC_Application();

dataUpdate = 1;

}

}

## 记录数据

### 提供日期和时间给 FatFs

FatFs 系统的文件和目录的日期、时间信息由 `get_fattime` 函数提供。该函数为 `__weak` 定义,我们在应用文件中重新实现即可,这里要注意 APM32F4xx 的 RTC 日历的起始年份和 FatFs 的不一致,做好偏移即可。

DWORD get_fattime(void)

{

return (DWORD)(rtcInfo.year + 20) << 25 |

(DWORD)rtcInfo.month << 21 |

(DWORD)rtcInfo.day << 16 |

(DWORD)rtcInfo.hour << 11 |

(DWORD)rtcInfo.minute << 5 |

(DWORD)rtcInfo.second >> 1;

}

### 创建和写数据到 Excel 文件

下面这个函数中,首先创建一个目录,然后创建或打开一个 `.xls` 文件,接着打开文件并用 `f_lseek` 函数移动写指针到文件末,最后用 ` f_printf ` 函数写入格式化后的字符串数据。

void FATFS_WriteXlsFile(FIL* file, const TCHAR *path, char *dir, char *fileName, char* logInfo, char* timeStamp)

{

FRESULT status;

char filePath[32];

char fileDir[20];

/* Write file */

sprintf(fileDir, "%s", dir);

status = f_mkdir(fileDir);

if (status == FR_OK)

{

DAL_LOGI(tag, ">>> Create direction success");

}

/* Open or create file */

sprintf(filePath, "%s%s%c%s.xls", path, fileDir, '/', fileName);

status = f_open(file, filePath, FA_OPEN_ALWAYS | FA_WRITE);

if (status == FR_OK)

{

DAL_LOGI(tag, ">>> Open or create %s %s file success", fileDir, fileName);

/* Move the file pointer to the end */

f_lseek(file, f_size(file));

f_printf(file, "%s + %s + %s ", fileName, logInfo, timeStamp);

}

else

{

DAL_LOGE(tag, ">>> Open or create file fail, status is %d", status);

}

/* Close file */

f_close(file);

}

### 获取 U 盘剩余空间信息

Volume 的剩余空间,从之前 FatFs 章节中知道,可以用 `f_getfree` 函数来获取 U 盘剩余空间信息,并转换为使用率。

void MSC_Application(void)

{

...

res = f_getfree(USBHPath, &freClust, &fs);

diskInfo.totSect = (fs->n_fatent - 2) * fs->csize;

diskInfo.totSect /= 2;

diskInfo.freSect = freClust * fs->csize;

diskInfo.freSect /= 2;

diskInfo.usedPercent = (1.0 - (double)diskInfo.freSect / diskInfo.totSect) * 100.0;

...

}

### 删除旧文件夹

以下代码,使用 `FATFS_ViewRootDir()` 函数获取目录文件夹信息,并按日期进行排序输出。最后由 `sscanf()` 来找到第一个目录,然后删除目录和文件。

void MSC_Application(void)

{

...

/* Get dir information */

DAL_LOGI(tag, "------> Get dir information");

FATFS_ViewRootDir(&USBHFatFS, USBHPath, diskInfo.dirPath, &diskInfo.dirNum);

DAL_LOGI(tag, ">>> dir path :%s", diskInfo.dirPath);

DAL_LOGI(tag, ">>> dir number :%d", diskInfo.dirNum);

/* Used percent > 90% and dir number > 1 */

if((diskInfo.usedPercent - DISK_SPACE_MAX_PERCENT) > EPS && (diskInfo.dirNum > 1))

{

DAL_LOGW(tag, "<<< Disk space is not enough");

/* Delete first dir */

sscanf(diskInfo.dirPath, "/%8[^/]", delectDirName);

FATFS_DelectDir(USBHPath, delectDirName);

}

...

}

# 04 下载验证

经过之前几节的组件的配置,实现数据记录的 API 函数后,最后下载到 APM32F4xx 开发板看下应用的过程。

从下面 LOG 信息可以看到,插入 U 盘后,从 UART1 输出了 `MSC device is ready` 的 LOG 信息,这表明开发板已完成 U 盘的枚举识别。

同时可以看到 LOG 中带有时间戳信息,证明 RTC 已经开发工作,并按每秒打印一次的频率输出时间戳 LOG 信息。

...

[main] 2023-11-22 0037

[main] 2023-11-22 0038

[main] 2023-11-22 0039

[main] 2023-11-22 0040

USB Device Reset Completed

USB device speed is FS

PID: 0x1234

VID: 0x048D

Endpoint 0 max packet size if 64

USB device address: 1

Manufacturer: USB

Product: Disk 2.0k

SerialNumber: 9207156342331724518

USB device enumration ok

This is a Mass Storage device

Use 2 endpoint:

Endpoint 0x01: max packet size is 64 bytes

Endpoint 0x82: max packet size is 64 bytes

USB device has only one configuration

Set to default configuration

Inquiry Revision :2.00

Inquiry Product :ProductCode

Inquiry Vendor :VendorCo

MSC device is ready

MSC device capacity : 1006390784 bytes

MSC device block number : 1965607

MSC device block size : 512

Class is ready

紧接着,就会进行 LOG 信息的记录,从下面 LOG 可以看到,FatFs 的操作过程。首先在 U 盘加载文件系统,然后创建或打开以 `20231122` 日期命名的文件夹并写入数据。接着获取 U 盘剩余空间,获取目录信息,最后卸载文件系统。

[main] 2023-11-22 0012

[main] ------> Update information

[main] ------> Mount U disk file system

[main] ------> Write xls file

[fatfs] >>> Open or create 20231122 LOG file success

[main] ------> Get volume information

[main] >>> (964657 / 966404) KiB Used 0.18 %

[main] ------> Get dir information

[fatfs] >>> View Directory

[fatfs] >>> 20231122, (0x10)

[fatfs] >>> 20231031, (0x10)

[fatfs] >>> 20231030, (0x10)

[fatfs] >>> 20231101, (0x10)

[fatfs] >>> Sort Directory

[main] >>> dir path :/20231030/20231031/20231101/20231122

[main] >>> dir number :4

[main] ------> Unmount U disk file system

[main] >>> U disk file system unmounted ok

我们打开 U 盘可以看到已经在设定的文件夹中创建了一个命名为 `LOG.xls` 的 Excel 文件。

106c9d5e-4e57-11ef-b8af-92fbcf53809c.jpg

以 LOG + RTC 时间戳为内容写入到 Excel 文件中。实际应用时记录的信息可以是系统不同任务执行情况,也可以是内核寄存器等便于维护调试的信息,或者是传感器的信息等。

108a7856-4e57-11ef-b8af-92fbcf53809c.jpg

以上,就是用 U 盘记录系统 LOG 信息的一些简单步骤和方法,实际应用中还要考虑更多情况,附件是源码,供大家参考。

审核编辑:彭菁

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 存储器
    +关注

    关注

    38

    文章

    7360

    浏览量

    163003
  • 数据
    +关注

    关注

    8

    文章

    6699

    浏览量

    88258
  • U盘
    +关注

    关注

    7

    文章

    487

    浏览量

    62992
  • Log
    Log
    +关注

    关注

    0

    文章

    14

    浏览量

    11290

原文标题:APM32芯得 EP.36 | APM32F4实现用U盘记录LOG信息

文章出处:【微信号:geehysemi,微信公众号:Geehy极海半导体】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    U系统小工具

    启动时按“del”或“F8”键进入BIOS设置具体设置请参阅 电脑店U系统设置U启动教程 第五步:
    发表于 04-25 16:36

    简单方便U系统小工具

    “del”或“F8”键进入BIOS设置具体设置请参阅 电脑店U系统设置U启动教程 第五步:
    发表于 07-15 18:26

    轻松U快速装系统工具

    U启动教程第五步:U启动快速安装系统(第一种方法
    发表于 10-03 20:21

    U快速装系统工具

    U启动教程第五步:U启动快速安装系统(第一种方法
    发表于 10-07 17:38

    U快速装系统工具

    启动教程第五步:U启动快速安装系统(第一种方法)进PE
    发表于 10-19 19:23

    U快速装系统工具

    启动教程第五步:U启动快速安装系统(第一种方法)进PE
    发表于 10-21 17:07

    U系统工具

    启动教程第五步:U启动快速安装系统(第一种方法)进PE
    发表于 10-29 18:21

    U做DOS启动的制作方法及详细步骤

    U做DOS启动的制作方法及详细步骤 启动型U
    发表于 04-13 19:14 3104次阅读

    quartus ii仿真实验简单步骤

    关于fpga实验的简单步骤,更利于初学者学习
    发表于 09-13 17:00 0次下载

    电脑卡怎么办简单步骤

    本视频主要详细介绍了电脑卡怎么办简单步骤,分别是卸载同类型的软件、清理回收站、借助软件清理电脑垃圾、定期清理磁盘碎片、系统垃圾文件、重装系统
    的头像 发表于 03-08 16:22 8618次阅读

    怎样U系统

    大家随便在搜索引擎上搜索“U系统”,就能轻松找到数种重装系统方法
    的头像 发表于 08-29 17:35 2455次阅读

    led显示屏u怎么改字_ledu导入字幕步骤

    本文详细阐述了led显示屏u改字的教程,另外还阐述了ledu导入字幕的详细
    发表于 03-27 09:11 9.9w次阅读

    电脑蓝屏的时候如何用u重装系统

    本文主要介绍了电脑蓝屏u重装系统的操作步骤
    发表于 04-17 08:55 1w次阅读

    linux挂载u方法

    linux挂载U具体步骤如下:
    发表于 05-19 09:14 2577次阅读
    linux挂载<b class='flag-5'>u</b><b class='flag-5'>盘</b>的<b class='flag-5'>方法</b>

    使用LTpowerCAD在五个简单步骤中设计电源

    使用LTpowerCAD在五个简单步骤中设计电源
    发表于 04-17 16:57 10次下载
    使用LTpowerCAD在五个<b class='flag-5'>简单步骤</b>中设计电源