Virtual File System
In early embedded systems, the data that needed to be stored was relatively small and the data type was relatively simple. The method of directly writing data to the specified address in the storage device was often used to store data. However, with the development of embedded device functions, the data that needs to be stored is increasing and becoming more and more complex. At this time, it becomes very cumbersome and difficult to use the old method to store and manage data. Therefore, we need a new data management method to simplify the organization of stored data. This method is the file system that we will introduce next.
The file system is an abstract data type that implements data storage, hierarchical organization, access, and retrieval. It is a mechanism for providing users with low-level data access. The basic unit of storage in a file system is usually a file, that is, data is organized in a file-by-file manner. When there are many files, there will be a large number of files that are difficult to classify and have duplicate names. A folder exists as a container for multiple files.
This chapter explains the RT-Thread file system and takes you to understand the architecture, functional features and usage of the RT-Thread virtual file system.
DFS is a virtual file system component provided by RT-Thread. Its full name is Device File System, which is a device virtual file system. The name of the file system uses a style similar to UNIX files and folders. The directory structure is shown in the following figure:
In RT-Thread DFS, the file system has a unified root directory, which /
is represented by . The f1.bin file in the root directory is /f1.bin
represented by , and the files in the 2018 directory f1.bin
are /data/2018/f1.bin
represented by . That is, the directory separator is /
, which is exactly the same as UNIX/Linux, but different from Windows (Windows operating system uses \
as the directory separator).
The main features of the RT-Thread DFS component are:
Provides a unified POSIX file and directory operation interface for applications: read, write, poll/select, etc.
It supports multiple types of file systems, such as FatFS, RomFS, DevFS, etc., and provides management of common files, device files, and network file descriptors.
Supports various types of storage devices, such as SD Card, SPI Flash, Nand Flash, etc.
The hierarchical architecture of DFS is shown in the figure below, which is mainly divided into POSIX interface layer, virtual file system layer and device abstraction layer.
POSIX stands for Portable Operating System Interface of UNIX (POSIX for short). The POSIX standard defines the interface standards that the operating system should provide to applications. It is the general name for a series of API standards defined by IEEE for software to run on various UNIX operating systems.
The POSIX standard is intended to achieve software portability at the source code level. In other words, a program written for a POSIX-compatible operating system should be compilable and executable on any other POSIX operating system (even if it comes from another manufacturer). RT-Thread supports the POSIX standard interface, so it is easy to port Linux/Unix programs to the RT-Thread operating system.
In Unix-like systems, ordinary files, device files, and network file descriptors are the same file descriptors. In the RT-Thread operating system, DFS is used to achieve this unification. With this unification of file descriptors, we can use the poll/select interface to uniformly poll these descriptors, which brings convenience to the implementation of program functions.
The poll/select interface can be used to block and simultaneously detect whether a group of I/O devices that support non-blocking have events (such as readable, writable, high-priority error output, error, etc.) until a device triggers an event or exceeds the specified waiting time. This mechanism can help the caller find the currently ready device and reduce the complexity of programming.
Users can register specific file systems to DFS, such as FatFS, RomFS, DevFS, etc. The following are some common file system types:
FatFS is a file system compatible with Microsoft FAT format developed specifically for small embedded devices. It is written in ANSI C and has good hardware independence and portability. It is the most commonly used file system type in RT-Thread.
The traditional RomFS file system is a simple, compact, read-only file system that does not support dynamic erasure and storage and stores data in sequence. It supports applications running in XIP (execute in place) mode, saving RAM space when the system is running.
Jffs2 file system is a log flash file system. It is mainly used for NOR flash memory, based on MTD driver layer, and has the following features: readable and writable, supports data compression, is a log file system based on hash table, provides crash/power-off safety protection, provides write balancing support, etc.
DevFS is the device file system. After enabling this function in the RT-Thread operating system, the devices in the system can be virtualized into files in the /dev folder, so that the devices can be operated using read, write and other interfaces in the same way as files.
NFS Network File System (NFS) is a technology for sharing files between different machines and different operating systems over the network. During the development and debugging phase of the operating system, this technology can be used to create an NFS-based root file system on the host and mount it on an embedded device, so that the content of the root file system can be easily modified.
UFFS is the abbreviation of Ultra-low-cost Flash File System. It is an open source file system developed by Chinese people and is specially designed for using Nand Flash in small memory environments such as embedded devices. Compared with the Yaffs file system commonly used in embedded systems, it has the advantages of less resource usage, fast startup speed, and free of charge.
The device abstraction layer abstracts physical devices such as SD Card, SPI Flash, and Nand Flash into devices that can be accessed by the file system. For example, the FAT file system requires that storage devices must be block device types.
Different file system types are implemented independently of storage device drivers. Therefore, the file system function can only be used correctly after the driver interface of the underlying storage device is connected with the file system.
The initialization process of the file system is generally divided into the following steps:
Initializes the DFS components.
Initialize a file system of a specific type.
Create a block device on the storage.
Format a block device.
Mount the block device to the DFS directory.
When a file system is no longer in use, it can be unmounted.
The initialization of the DFS component is completed by the dfs_init() function. The dfs_init() function will initialize the relevant resources required by DFS and create some key data structures. With these data structures, DFS can find a specific file system in the system and obtain the operation method for files in a specific storage device. If automatic initialization is enabled (enabled by default), this function will be called automatically.
After the DFS component is initialized, the specific type of file system to be used needs to be initialized, that is, the specific type of file system needs to be registered in DFS. The interface for registering a file system is as follows:
parameter | describe |
ops | A collection of file system operation functions |
return | —— |
0 | File registration successful |
-1 | File registration failed |
This function does not need to be called by the user, it will be called by the initialization function of different file systems, such as the initialization function of the elm-FAT file system elm_init()
. After the corresponding file system is enabled, if automatic initialization is enabled (enabled by default), the file system initialization function will also be automatically called.
elm_init()
The function will initialize the elm-FAT file system. This function will call dfs_register(
the function to register the elm-FAT file system to DFS. The file system registration process is shown in the figure below:
Because only block devices can be mounted on the file system, it is necessary to create the required block devices on the storage device. If the storage device is SPI Flash, you can use the "Serial Flash Universal Driver Library SFUD" component, which provides various SPI Flash drivers and abstracts SPI Flash into block devices for mounting. The process of registering a block device is shown in the following figure:
After registering the block device, you need to create a file system of the specified type on the block device, that is, format the file system. You can use dfs_mkfs()
the function to format the specified storage device and create a file system. The interface for formatting the file system is as follows:
parameter | describe |
fs_name | File system type |
device_name | Block device name |
return | —— |
0 | The file system was formatted successfully |
-1 | File system formatting failed |
The possible values of the file system type (fs_name) and the corresponding file systems are shown in the following table:
Value | File system type |
elm | elm-FAT file system |
jffs2 | jffs2 Journaling Flash File System |
nfs | NFS Network File System |
ram | RamFS File System |
rom | RomFS read-only file system |
uffs | uffs file system |
lfs | littlefs file system |
Taking the elm-FAT file system to format a block device as an example, the formatting process is shown in the following figure:
You can also use mkfs
the command to format the file system. The result of formatting the block device sd0 is as follows:
In RT-Thread, mounting means attaching a storage device to an existing path. To access files in a storage device, we must mount the partition where the file is located to an existing path, and then access the storage device through this path. The interface for mounting a file system is as follows:
parameter | describe |
device_name | The name of the formatted block device |
path | Mount path, that is, mount point |
filesystemtype | The type of file system to be mounted. For possible values, see the dfs_mkfs() function description. |
rwflag | Read and write flag |
data | Private data for a specific file system |
return | —— |
0 | The file system is mounted successfully |
-1 | File system mount failed |
If there is only one storage device, you can mount it directly to the root directory /
.
When a file system is no longer needed, it can be uninstalled. The interface for uninstalling a file system is as follows:
parameter | describe |
specialfile | Mount path |
return | —— |
0 | Unmounting the file system successfully |
-1 | Failed to unmount the file system |
This section introduces the functions related to file operations. File operations are generally based on the file descriptor fd, as shown in the following figure:
To open or create a file, you can call the following open() function:
parameter | describe |
file | The name of the file to open or create |
flags | Specify the method to open the file. The values can be found in the following table. |
return | —— |
File Descriptors | File opened successfully |
-1 | File open failed |
A file can be opened in multiple ways, and multiple opening modes can be specified at the same time. For example, if a file is opened in O_RDONLY and O_CREAT modes, then if the specified file does not exist, the file will be created first and then opened in read-only mode. The file opening modes are shown in the following table:
parameter | describe |
O_RDONLY | Open the file in read-only mode |
O_WRONLY | Open the file for writing only |
O_RDWR | Open the file for reading and writing |
O_CREAT | If the file to be opened does not exist, it is created. |
O_APPEND | When reading and writing files, they start moving from the end of the file, that is, the written data is added to the end of the file in an appended manner. |
O_TRUNC | If the file already exists, clear the contents of the file |
When you have finished using a file and no longer need to use it, you can use close()
the function to close the file, which close()
will write the data back to the disk and release the resources occupied by the file.
parameter | describe |
fd | File Descriptors |
return | —— |
0 | File closed successfully |
-1 | File close failed |
To read the file contents, use read()
the function:
parameter | describe |
fd | File Descriptors |
buf | Buffer pointer |
len | The number of bytes read from the file |
return | —— |
int | The actual number of bytes read |
0 | The read data has reached the end of the file or there is no more data to read |
-1 | Read error, error code see errno of the current thread |
This function will read len bytes of the file pointed to by the parameter fd into the memory pointed to by the buf pointer. In addition, the file's read and write position pointer will move with the bytes read.
To write data to a file, use write()
the function:
parameter | describe |
F | File Descriptors |
buf | Buffer pointer |
len | The number of bytes written to the file |
return | —— |
int | The actual number of bytes written |
-1 | Writing error, error code see errno of the current thread |
This function will write len bytes in the memory pointed to by the buf pointer to the file pointed to by the parameter fd. In addition, the file's read and write position pointers will move with the bytes written.
To rename a file, use rename()
the function:
parameter | describe |
old | Old file name |
new | New File Name |
return | —— |
0 | Name changed successfully |
-1 | Change name failed |
This function will change the file name specified by the parameter old to the file name specified by the parameter new. If the file specified by new already exists, it will be overwritten.
To get the file status, use the following stat()
function:
parameter | describe |
file | file name |
buf | Structure pointer, pointing to a structure that stores file status information |
return | —— |
0 | Get status successfully |
-1 | Failed to get status |
To delete files in a specified directory, you can use unlink()
the function:
parameter | describe |
pathname | Specify the absolute path of the file to be deleted |
return | —— |
0 | Delete file successfully |
-1 | Failed to delete file |
To synchronize all modified file data in memory to the storage device, you can use fsync()
the function:
parameter | describe |
Fildes | File Descriptors |
return | —— |
0 | Synchronize files successfully |
-1 | Failed to sync files |
To query the file system related information, you can use statfs()
the function:
parameter | describe |
path | The mount path of the file system |
buf | Pointer to a structure used to store file system information |
return | —— |
0 | Query file system information successfully |
-1 | Failed to query file system information |
To monitor whether an I/O device has an event, you can use select()
the function:
parameter | describe |
nfds | The range of all file descriptors in the set, that is, the maximum value of all file descriptors plus 1 |
readfds | The set of file descriptors that need to be monitored for read changes |
writefds | The set of file descriptors that need to be monitored for write changes |
exceptfds | Need to monitor the set of file descriptors that have abnormalities |
timeout | select timeout |
return | —— |
Positive | The monitored file collection has a read/write event or an error |
0 | Waiting timed out, no files to read or write or error |
Negative Values | Error |
The interface can be used to select()
block and simultaneously detect whether a group of I/O devices that support non-blocking have events (such as readable, writable, high-priority error output, error, etc.) until a device triggers an event or exceeds the specified waiting time.
This section introduces the functions commonly used in directory management. The operations on directories are generally based on the directory address, as shown in the following figure:
To create a directory, use mkdir()
the function:
parameter | describe |
path | The absolute address of the directory |
mode | Create a schema |
return | —— |
0 | Directory created successfully |
-1 | Failed to create directory |
This function is used to create a directory or folder. The parameter path is the absolute path of the directory. The parameter mode is not enabled in the current version, so fill in the default parameter 0x777.
To delete a directory, use rmdir()
the function:
parameter | describe |
pathname | The absolute path of the directory to be deleted |
return | —— |
0 | Directory deleted successfully |
-1 | Directory deletion error |
To open a directory, use opendir()
the function:
parameter | describe |
name | The absolute address of the directory |
return | —— |
DIR | Open the directory successfully and return a pointer to the directory stream |
NULL | Open Failed |
To close a directory, you can use closedir()
the function:
parameter | describe |
d | Directory stream pointer |
return | —— |
0 | Directory closed successfully |
-1 | Directory Close Error |
This function is used to close a directory and must opendir()
be used in conjunction with the function.
To read the directory, use readdir()
the function:
parameter | describe |
d | Directory stream pointer |
return | —— |
dirent | A successful read returns a structure pointer to the directory entry |
NULL | End of directory read |
This function is used to read a directory. The parameter d is the directory stream pointer. In addition, each time a directory is read, the pointer position of the directory stream will automatically move back by 1 position.
To get the read position of the directory stream, use telldir()
the function:
parameter | describe |
d | Directory stream pointer |
return | —— |
long | Read the offset of the position |
The return value of this function records the current position of a directory stream. This return value represents the offset from the beginning of the directory file . You can use this value in subsequent seekdir()
function calls to reset the directory to the current position. In other words, telldir()
the function can seekdir()
be used in conjunction with the function to reset the reading position of the directory stream to the specified offset.
To set the location of the directory to be read next time, you can use seekdir()
the function:
parameter | describe |
d | Directory stream pointer |
offset | Offset value, the displacement from this directory |
This is used to set the reading position of the directory stream parameter d. When calling readdir(), reading starts from this new position.
To reset the directory stream's reading position to the beginning, use rewinddir()
the function:
parameter | describe |
d | Directory stream pointer |
This function can be used to set the current reading position of the directory stream d to the initial position of the directory stream.
The specific configuration path of the file system in menuconfig is as follows:
The configuration menu description and corresponding macro definition are shown in the following table:
Configuration options | Corresponding macro definition | describe |
[*] Using device virtual file system | RT_USING_DFS | Enable DFS virtual file system |
[*] Using working directory | DFS_USING_WORKDIR | Enable relative paths |
(2) The maximum number of mounted file system | DFS_FILESYSTEMS_MAX | Maximum number of mounted file systems |
(2) The maximum number of file system type | DFS_FILESYSTEM_TYPES_MAX | Maximum number of supported file systems |
(4) The maximum number of opened files | DFS_FD_MAX | Maximum number of open files |
[ ] Using mount table for file system | RT_USING_DFS_MNTTABLE | Enable automatic mount table |
[*] Enable elm-chan fatfs | RT_USING_DFS_ELMFAT | Enable elm-FatFs file system |
[*] Using devfs for device objects | RT_USING_DFS_DEVFS | Enable DevFS device file system |
[ ] Enable ReadOnly file system on flash | RT_USING_DFS_ROMFS | Enable RomFS file system |
[ ] Enable RAM file system | RT_USING_DFS_RAMFS | Enable RamFS file system |
[ ] Enable UFFS file system: Ultra-low-cost Flash File System | RT_USING_DFS_UFFS | Enable UFFS file system |
[ ] Enable JFFS2 file system | RT_USING_DFS_JFFS2 | Enable JFFS2 file system |
[ ] Using NFS v3 client file system | RT_USING_DFS_NFS | Enable NFS file system |
By default, the RT-Thread operating system does not enable the relative path function in order to obtain a smaller memory footprint. When the relative path support option is not enabled, you should use absolute directories when using file and directory interfaces for operations (because the current working directory does not exist in the system at this time). If you need to use the current working directory and relative directories, you can enable the relative path function in the file system configuration item.
After the option [*] Using mount table for file system
is selected, the corresponding macro will be enabled RT_USING_DFS_MNTTABLE
to turn on the automatic mount table function. The automatic mount table mount_table[]
is provided by the user in the application code. The user needs to specify the device name, mount path, file system type, read-write flags and private data in the table. After that, the system will traverse the mount table to perform the mount. It should be noted that the mount table must end with {0} to determine the end of the table.
An example of the automatic mount table mount_table[]
is shown below, where mount_table[0]
the five members are dfs_mount()
the five parameters of the function, which means mounting the elm file system to the / path of the flash0 device, with rwflag being 0 and data being 0; mount_table[1]
and ending with {0} to determine the end of the table.
After enabling the elm-FatFs file system in menuconfig, you can further configure elm-FatFs. The configuration menu description and corresponding macro definitions are shown in the following table:
Configuration options | Corresponding macro definition | describe |
(437) OEM code page | RT_DFS_ELM_CODE_PAGE | Encoding |
[*] Using RT_DFS_ELM_WORD_ACCESS | RT_DFS_ELM_WORD_ACCESS | |
Support long file name (0: LFN disable) ---> | RT_DFS_ELM_USE_LFN | Open the long file name submenu |
(255) Maximal size of file name length | RT_DFS_ELM_MAX_LFN | Maximum file name length |
(2) Number of volumes (logical drives) to be used. | RT_DFS_ELM_DRIVES | Number of devices mounted with FatFs |
(4096) Maximum sector size to be handled. | RT_DFS_ELM_MAX_SECTOR_SIZE | File system sector size |
[ ] Enable sector erase feature | RT_DFS_ELM_USE_ERASE | |
[*] Enable the reentrancy (thread safe) of the FatFs module | RT_DFS_ELM_REENTRANT | Enable reentrancy |
By default, FatFs file naming has the following disadvantages:
The file name (excluding the suffix) cannot be longer than 8 characters, and the suffix cannot be longer than 3 characters. The file name and suffix will be truncated if they exceed the limit.
File names are not case sensitive (displayed as uppercase letters)
If you need to support long file names, you need to turn on the long file name support option. The long file name submenu description is as follows:
Configuration options | Corresponding macro definition | describe |
( ) 0: LFN disable | RT_DFS_ELM_USE_LFN_0 | Turn off long file names |
( ) 1: LFN with static LFN working buffer | RT_DFS_ELM_USE_LFN_1 | Use static buffer to support long file names, which will cause reentrancy problems when multi-threaded file name operations |
( ) 2: LFN with dynamic LFN working buffer on the stack | RT_DFS_ELM_USE_LFN_2 | Uses a temporary buffer in the stack to support long file names. Large stack space requirements |
(X) 3: LFN with dynamic LFN working buffer on the heap | RT_DFS_ELM_USE_LFN_3 | Use heap (malloc) buffer to store long file names, which is the safest (default mode) |
When long file name support is enabled, you can set the file name encoding. RT-Thread/FatFs uses 437 encoding (American English) by default. If you need to store Chinese file names, you can use 936 encoding (GBK encoding). 936 encoding requires a font library of about 180KB. If you only use English characters as files, it is recommended to use 437 encoding (American English), which can save 180KB of Flash space.
The file encodings supported by FatFs are as follows:
Specify the internal sector size of FatFs, which needs to be greater than or equal to the sector size of the actual hardware driver. For example, if the sector size of a spi flash chip is 4096 bytes, the above macro needs to be modified to 4096, otherwise the array will be out of bounds when FatFs reads data from the driver, causing the system to crash (the new version will give a warning message when the system is executed).
Generally, the sector size of Flash devices can be set to 4096, and the sector size of common TF cards and SD cards is set to 512.
FatFs takes into full consideration the safety of multi-threaded reading and writing. When reading and writing FatFs in multiple threads, in order to avoid the problem of reentry, the above macro needs to be turned on. If the system has only one thread operating the file system and there will be no reentry problem, this function can be turned off to save resources.
FatFs itself supports a lot of configuration options and is very flexible. The following file is the configuration file of FatFs, which can be modified to customize FatFs.
After the file system is mounted successfully, you can operate files and directories. The commonly used FinSH commands for file system operations are shown in the following table:
FinSH Commands | describe |
ls | Display information about files and directories |
cd | Enter the specified directory |
cp | Copying Files |
rm | Deleting a file or directory |
mv | Move or rename the file |
echo | Write the specified content to the specified file. If the file exists, write it to the file. If the file does not exist, create a new file and write it to it. |
cat | Display the contents of a file |
pwd | Print out the current directory address |
mkdir | Create a folder |
mkfs | Format the file system |
Use ls
the command to view the current directory information. The running results are as follows:
Use mkdir
the command to create a folder. The running result is as follows:
Use echo
the command to output the input string to the specified output location. The running result is as follows:
Use cat
the command to view the file contents. The running results are as follows:
Use rm
the command to delete folders or files. The running results are as follows:
After the file system works properly, you can run the application example. In this sample code, you will first use open()
the function to create a file text.txt, use the write() function to write a string to the file “RT-Thread Programmer!\n”
, and then close the file. Use the function again open()
to open the text.txt file, read out the content and print it out, and finally close the file.
The sample code is as follows:
The sample code in this section shows how to modify the file name. The program will create a function to operate the file rename_sample()
and export it to the msh command list. This function will call rename()
the function to rename the file named text.txt to text1.txt. The sample code is as follows:
Run this example in the FinSH console, and the results are as follows:
In the example display process, we first use the echo command to create a file named text.txt, and then run the sample code to change the file name of the text.txt file to text1.txt.
The sample code in this section shows how to get the file status. The program will create a function to operate the file stat_sample()
and export it to the msh command list. This function will call stat()
the function to get the file size information of the text.txt file. The sample code is as follows:
Run this example in the FinSH console, and the results are as follows:
When the example is running, echo
the command is used to create the file text.txt first, and then the example code is run to print out the file size information of the file text.txt.
The sample code in this section shows how to create a directory. The program will create a function to operate files mkdir_sample()
and export it to the msh command list. This function will call mkdir()
the function to create a folder named dir_test. The sample code is as follows:
Run this example in the FinSH console, and the results are as follows:
This example demonstrates creating a folder named dir_test in the root directory.
The sample code in this section shows how to read a directory. The program will create a function to operate files readdir_sample()
and export it to the msh command list. The function will call readdir()
the function to obtain the content information of the dir_test folder and print it out. The sample code is as follows:
Run this example in the FinSH console, and the results are as follows:
In this example, we first enter the dir_test folder to create the hello.txt file, and then exit the dir_test folder. Now run the sample program to print out the contents of the dir_test folder.
The sample code in this section shows how to set the location of the next directory read. The program will create a function to operate the file telldir_sample()
and export it to the msh command list. The function will first open the root directory, then read all the directory information under the root directory and print out the directory information. At the same time, telldir()
the function is used to record the location information of the third directory item. Before reading the directory information under the root directory for the second time, seekdir()
the function is used to set the reading position to the address of the third directory item previously recorded. At this time, the information under the root directory is read again and the directory information is printed out. The sample code is as follows:
In this demonstration, you need to manually mkdir
create five folders from hello_1 to hello_5 in the root directory using the command to ensure that the root directory contains the folders required to run the example.
Run this example in the FinSH console, and the results are as follows:
After running the sample program, you can see that the first time the root directory information is read, it starts from the first folder and prints out all the directory information under the root directory. When the directory information is printed for the second time, the seekdir()
function is used to set the starting position of the read to the position of the third folder. Therefore, when the root directory is read for the second time, it starts from the third folder to the last folder and only prints out the directory information from hello_3 to hello_5.
A: Check whether long file name support is enabled in the DFS function configuration section.
A: Check whether the types and number of file systems allowed to be mounted in the file system configuration items are sufficient.
A: Check whether the storage device exists. If it exists, check whether the device driver can pass the functional test. If not, check the driver error. Check whether the libc function is enabled.
A:
Check whether the specified mount path exists. The file system can be mounted directly to the root directory ("/"), but if you want to mount it to another path, such as ("/sdcard"), you need to ensure that the ("/sdcard") path exists, otherwise you need to create
sdcard
a folder in the root directory before mounting successfully.Check whether a file system has been created on the storage device. If no file system has been created on the storage device, you need to use
mkfs
the command to create a file system on the storage device.
A:
Check whether the hardware pin settings are correct.
Whether the SPI device has been registered.
Whether the SPI device has been mounted on the bus.
Check whether the and configuration items
RT-Thread Components → Device Drivers -> Using SPI Bus/Device device drivers -> Using Serial Flash Universal Driver
under the menu are selected. If not, you need to enable these two options.Using auto probe flash JEDEC SFDP parameterUsing defined supported flash chip information table
If the storage device is still not recognized after enabling the above options, you can file an issue in the SFUD project.
A:
You can compare the benchmark test data
system tick
when is 1000 and the time required for this test. If the time difference is too large, it can be considered that the test work is not running normally.Check the system tick settings, because some delay operations are determined by the tick time, so you need to set a suitable
system tick
value according to the system situation. If the systemsystem tick
value is not less than 1000, you need to use a logic analyzer to check the waveform to confirm that the communication rate is normal.
A: You can use the partition tool package provided by RT-Thread to create multiple block devices for the entire storage device, and assign different functions to the created multiple block devices.
A: Try to use a debugger or print some necessary debugging information to determine where the program is stuck before asking the question.
A:
You can use a bottom-up approach to gradually troubleshoot the problem.
First, check whether the storage device is successfully registered and functions normally.
Check whether a file system has been created on the storage device.
Checks whether the specified file system type is registered with the DFS framework, and always checks whether the allowed file system types and number are sufficient.
Check whether DFS is initialized successfully. This step of initialization is purely software-based, so the possibility of error is low. It should be noted that if automatic component initialization is enabled, there is no need to manually initialize it again.
Last updated