Bootstrap

基于STM32-RTX-FATFS-U盘操作实现.ini文件读写操作

INI文件以良好的可阅读与修改特性,在程序运行初期,能够提供一些初始变量。使用C++进行程序编写过程中,微软官方提供了良好的文件操作API,便于直接使用。但是在嵌入式操作系统中,缺乏一套良好的方法。现参考https://github.com/wernsey/rengine/blob/master/src/ini.c提供的方式,结合STM32进行实现。另附工程源码。

本文基于RTX嵌入式操作系统和FatFs文件操作系统,实现对INI文件的读写操作。

1. 采用STM32CubeMX生成相关的底层库,需要配置U盘、SD卡以及FatFs。

2. 打开配置好的工程文件,并添加RTX操作系统。

3. 添加Thread.c、ini.c以及utils.c文件

4. 修改ini.c以及utils.c文件,以适应FatFs操作系统

首先需要添加#include "fatfs.h"头文件,另外由于ini.c中运用了大量的断言语句,所以需要结合Keil自身的断言编写__aeabi_assert这个函数。

void __aeabi_assert(const char *s1, const char *s2, int len)

{

    printf(">> error: (%s). function: %s, line %d\r\n", s1, s2, len);

}

其次,将源文件中关于文件API接口统一修改为FatFs操作系统的API接口,主要有以下几类函数

a. 变量 FILE *f -> FIL *f = &SDFile

b. 文件打开操作 f = fopen(fname, "w"); -> f_open(f, fname, FA_WRITE)

c. 文件关闭操作 fclose(f) -> f_close(f)

d. 字符串写入操作 fputs("]\n", f) -> f_puts("]\n", f)

e. 读文件函数


char *my_readfile(const char *fname)
{
	FILE *f;
	long len, r;
	char *str;

	if (!(f = fopen(fname, "rb")))
		return NULL;

	fseek(f, 0, SEEK_END);
	len = ftell(f);
	rewind(f);

	if (!(str = malloc(len + 2)))
		return NULL;
	r = fread(str, 1, len, f);

	if (r != len)
	{
		free(str);
		return NULL;
	}

	fclose(f);
	str[len] = '\0';
	return str;
}

修改为


char *my_readfile(const char *fname)
{
	FIL *f = &SDFile;
	unsigned int len, r;
	char *str;

	if (f_open(f, fname, FA_READ) != FR_OK)
	{
		return NULL;
	}

	len = f_size(f);

	str = malloc(len + 2);
	if (str == NULL)
	{
		return NULL;
	}
	f_read(f, str, len, &r);
	if(r!=len)
	{
		free(str);
		return NULL;
	}
	f_close(f);
	str[len] = '\0';
	return str;
}

这样就完成了对ini.c以及utils.c文件的移植

5. 修改usbd_storage_if.c文件,以适配电脑读取操作

6. 在Thread.c文件中,编写相关读写函数入下

#include "cmsis_os2.h"
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "usart.h"

#include "ini.h"
#include "fatfs.h"

/*----------------------------------------------------------------------------
 *      Thread 1 'Thread_Name': Sample thread
 *---------------------------------------------------------------------------*/

int fputc(int ch, FILE *file)
{
	HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 1000);
	return ch;
}

typedef enum
{
    TSK_RECORD,
    TSK_MAX,
} TSK_ID_e;

#define T(x) \
    case x:  \
        return #x
char *Fil_RetInfo(FRESULT ret)
{
    switch (ret)
    {
        T(FR_OK);
        T(FR_DISK_ERR);
        T(FR_INT_ERR);
        T(FR_NOT_READY);
        T(FR_NO_FILE);
        T(FR_NO_PATH);
        T(FR_INVALID_NAME);
        T(FR_DENIED);
        T(FR_EXIST);
        T(FR_INVALID_OBJECT);
        T(FR_WRITE_PROTECTED);
        T(FR_INVALID_DRIVE);
        T(FR_NOT_ENABLED);
        T(FR_NO_FILESYSTEM);
        T(FR_MKFS_ABORTED);
        T(FR_TIMEOUT);
        T(FR_LOCKED);
        T(FR_NOT_ENOUGH_CORE);
        T(FR_TOO_MANY_OPEN_FILES);
        T(FR_INVALID_PARAMETER);
    default:
        return "Erro Status";
    }
}
#undef T

#define FF_INFO(x) printf(">> Fatfs Status(%s).\r\n", Fil_RetInfo(sta));

#define IF_RET_BOOL(x, sta)                                                                                      \
    retSD = x;                                                                                                   \
    if ((retSD != sta))                                                                                          \
    {                                                                                                            \
        printf(">> Fatfs Error(%s). Fun %s, Line %d.\r\n", Fil_RetInfo((FRESULT)retSD), __FUNCTION__, __LINE__); \
        return false;                                                                                            \
    }

osThreadId_t tskId[TSK_MAX];

unsigned char USB_IsConected(void);
DIR dataDir;
const char dirData[] = "Data";
const char dirTask[] = "Task";
const char dirInit[] = "Init";
const char filData[] = "Data.txt";
const char filTask[] = "Task.auv";
const char filInit[] = "Init.ini";
const char *dirContent[] = {dirData, dirTask, dirInit};
const char *filName[] = {filData, filTask, filInit};
char *filContent[3];
bool isSDLoadOK = false;
bool isDirExist[3] = {false};

/* 加载 SD 卡 
 *
 */
bool SD_Load(void)
{
    IF_RET_BOOL(f_mount(&SDFatFS, SDPath, FM_FAT32), FR_OK);
    return true;
}

/* 创建子目录
 *
 */
bool Dir_CreateOpen(const char *name)
{
    if (!isSDLoadOK)
    {
        return false;
    }
    retSD = f_stat(name, NULL);
    if (retSD == FR_OK)
    {
        IF_RET_BOOL(f_opendir(&dataDir, name), FR_OK);
        IF_RET_BOOL(f_closedir(&dataDir), FR_OK);
    }
    else
    {
        IF_RET_BOOL(f_mkdir(name), FR_OK);
        IF_RET_BOOL(f_opendir(&dataDir, name), FR_OK);
        IF_RET_BOOL(f_closedir(&dataDir), FR_OK);
    }
    return true;
}

/* 创建文件目录名称
 *
 */
bool Fil_CreateName(const char *s1, const char *s2, char **ret)
{
    if (ret[0] != NULL)
        return false;
    const char spliter[] = "/";
    ret[0] = calloc(strlen(s1) + strlen(s2) + 2 + 1, sizeof(char));
    if (ret[0] == NULL)
        return false;

    strcpy(ret[0], spliter);
    strcat(ret[0], s1);
    strcat(ret[0], spliter);
    strcat(ret[0], s2);
    return true;
}

bool Fil_Create(bool isDirOK, char *path)
{
    if (isDirOK)
    {
        if (f_stat(path, NULL) != FR_OK)
        {
            IF_RET_BOOL(f_open(&SDFile, path, FA_CREATE_NEW), FR_OK);
            IF_RET_BOOL(f_close(&SDFile), FR_OK);
        }
        return true;
    }
    else
    {
        return false;
    }
}

void Thread_Record(void *arg)
{
    uint32_t ulTim = 0;
    while (1)
    {
        ulTim = osKernelGetTickCount();
        osDelayUntil(ulTim + 100);
    }
}

bool test(void);
int Thread_Init(void)
{
    printf("=== %s %s === Init Test ===\r\n", __TIME__, __DATE__);
    isSDLoadOK = SD_Load();
    for (unsigned char i = 0; i < 3; i++)
    {
        isDirExist[i] = Dir_CreateOpen(dirContent[i]);
        Fil_CreateName(dirContent[i], filName[i], &filContent[i]);
        Fil_Create(isDirExist[i], filContent[i]);
    }
    test();
    tskId[TSK_RECORD] = osThreadNew(Thread_Record, NULL, NULL);
    if (tskId[TSK_RECORD] == 0)
        return -1;
    return 0;
}
static struct ini_file *iniTst = NULL;
double p1;
char numToStrBuf[15];
bool test()
{
    int err, line;
    p1 = 1234.56789111;
    sprintf(numToStrBuf,"%lf", p1);
    iniTst = ini_read(filContent[2], &err, &line);
    ini_put(iniTst, "Tst1", "a", "0");
    ini_put(iniTst, "Tst2", "b", "0.0");
    ini_put(iniTst, "Tst2", "c", "0.0");
    ini_put(iniTst, "Tst2", "d", "0.0");

    ini_put(iniTst, "Tst3", "b", "0.0");
    ini_put(iniTst, "Tst3", "c", "0.0");
    ini_put(iniTst, "Tst3", "d", numToStrBuf);

    int result = ini_write(iniTst, filContent[2]);
    if (result != SUCCESS)
    {
        printf(">> function %s, line %d\r\n", __FUNCTION__, __LINE__);
    }
    else
    {
        printf(">> ok\r\n");
    }
    ini_free(iniTst);

    iniTst = ini_read(filContent[2], &err, &line);
    p1 = atof(ini_get(iniTst,"Tst3", "d", NULL));
    printf(">> p1 = %lf, numToStrBuf = %s\r\n", p1, numToStrBuf);
    ini_free(iniTst);

    iniTst = NULL;
    return true;
}

7. 程序运行结果如下

a. 串口打印图示

b. U盘插入电脑中图示

至此,基于C语言加载ini文件方式介绍完毕。

 

 

 

 

 

;