基于小熊派SD卡+Fatfs+移植开源iniparse解析库并使用

网友投稿 849 2022-05-25

在实际产品开发过程中,我们常常会对一些产品的内置参数进行存储,比如:

1、当前属于哪种语言

2、机器开机密码

3、机器出厂序列号

等等其它关键的参数,最常用的存储方法是基于xxx.ini的文件形式来进行存储,ini文件是什么在以往的文章中也有相应的介绍。

1、了解什么是INI文件?

ini 文件是Initialization File的缩写,即初始化文件,这是用来配置应用软件以实现不同用户的要求。

2、INI文件的格式

INI文件由节、键、值组成。一个简单的的INI文件例子如下:

[Setting]

INIT_FLAG=0;

VOLUME=1;

LANGUAGE=1;

如上例子,[Setting]就是节,=号左边的值是键,=号右边的是值。

3、关于ini_parse开源C库

在github上,关于ini文件的解析已经有相应的开源软件了,网址如下:

https://github.com/ndevilla/iniparser

上面会非常详细介绍这个开源程序是如何来编译以及使用的,并且也开源了相应的源代码,具体原理本节不会多讲,因为开源的文档已经讲解得非常详细了,本节,我将基于小熊派,配置一个SD卡+Fatfs的工程,在确保文件系统在SD卡构建的情况下,来移植ini_parse库,以便于我们日常开发的使用。

4、stm32cubeMX SD卡+Fatfs文件系统工程配置

4.1 时钟配置

这里我选择是外部时钟。

4.2 串行调试接口配置

4.3 SD卡接口参数配置

以下是SD卡接口在小熊派上的电路原理图。

对应主控MCU管脚的连接

由于这里只有一条输出D0输出线,所以在CubeMX上选择SD 1bit模式,其余参数默认。

4.4 配置调试串口

4.5 配置SD卡支持Fatfs

4.6 配置一路调试灯+2个按键

我们通过两个按键来实现更改参数和读取参数,并且用LED来提示。

最后生成代码即可 。

5、下载ini_parse库到生成的代码中并进行移植以支持fatfs

将对应的库文件包含到工程源码中:

由于ini_parse基于标准库所写,所以文件操作都是基于标准文件操作进行编写的,在这里我们需要把这些接口全部转变成fatfs的接口。

在iniparse.h中包含fatfs的头文件

#include "fatfs.h"

原来标准文件操作的接口

//void iniparser_dump_ini(dictionary * d, FILE * f);

替换为现在支持Fatfs文件的操作接口

void iniparser_dump_ini(dictionary * d, /*FILE *f*/FIL * f);

原来标准文件操作的接口

//void iniparser_dumpsection_ini(dictionary * d, char * s, FILE * f);

替换为现在支持Fatfs文件的操作接口

void iniparser_dumpsection_ini(dictionary * d, char * s, /*FILE * f*/ FIL *f);

原来标准文件操作的接口

//void iniparser_dump(dictionary * d,*FILE * f);

替换为现在支持Fatfs文件的操作接口

void iniparser_dump(dictionary * d, /*FILE * f*/ FIL *f);

调整支持fatfs的接口

iniparser_dump函数修改

void iniparser_dump(dictionary * d, /*FILE * f*/FIL *f)

{

int     i ;

if (d == NULL || f == NULL) return ;

for (i = 0 ; i < d->size ; i++)

{

if (d->key[i] == NULL)

continue ;

if (d->val[i] != NULL)

{

f_printf(f, "[%s]=[%s]\n", d->key[i], d->val[i]);

//注释这里为标准库操作接口

//fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]);

}

else

{

f_printf(f, "[%s]=UNDEF\n", d->key[i]);

//注释这里为标准库操作接口

//fprintf(f, "[%s]=UNDEF\n", d->key[i]);

}

}

return ;

}

iniparser_dumpsection_ini修改

void iniparser_dumpsection_ini(dictionary * d, char * s, /*FILE * f*/ FIL *f)

{

int     j ;

char    *keym;

int     secsize ;

if (d == NULL || f == NULL) return ;

if (! iniparser_find_entry(d, s)) return ;

//注释这里为标准库操作接口

//fprintf(f, "\n[%s]\n", s);

f_printf(f, "\n[%s]\n", s);

secsize = (int)strlen(s) + 2;

keym = (char *)malloc(secsize);

snprintf(keym, secsize, "%s:", s);

for (j = 0 ; j < d->size ; j++)

{

if (d->key[j] == NULL)

continue ;

if (!strncmp(d->key[j], keym, secsize - 1))

{

//注释这里为标准库操作接口

/*

fprintf(f,

"%-30s = %s\n",

d->key[j]+secsize-1,

d->val[j] ? d->val[j] : "");

*/

f_printf(f,

"%-30s = %s\n",

d->key[j] + secsize - 1,

d->val[j] ? d->val[j] : "");

}

}

//注释这里为标准库操作接口

//fprintf(f, "\n");

f_printf(f, "\n");

free(keym);

return ;

}

iniparser_load函数由于太长,限于篇幅,这里我们只具体截出更改的函数:

注释原始的文件描述符

//FILE * in = NULL ;

//修改fopen为f_open

//if ((in=fopen(ininame, "r"))==NULL)

//{

//    fprintf(stderr, "iniparser: cannot open %s\n", ininame);

//    goto out;

//}

retSD = f_open(&SDFile, ininame, FA_OPEN_EXISTING | FA_READ);

if(FR_OK != retSD)

{

fprintf(stderr, "iniparser: cannot open %s\n", ininame);

goto out ;

}

//修改fgets为f_gets

//while (fgets(line, ASCIILINESZ, in)!=NULL) {

while(f_gets(line, ASCIILINESZ, &SDFile) != NULL)

//修改feof为f_eof

// if (line[len]!='\n' && !feof(in)) {

if (line[len] != '\n' && !f_eof(&SDFile))

修改fclose为f_close

// if (in) {

//     fclose(in);

f_close(&SDFile);

到这里移植就结束了,非常简单,只需要把对应的接口做替换就可以了。

6、编写测试代码

对应头文件包含

/* USER CODE BEGIN Includes */

#include "iniparser.h"

#include "multi_button.h"

/* USER CODE END Includes */

定义串口重定向

int fputc(int ch, FILE *stream)

{

/* 堵塞判断串口是否发送完成 */

while((USART1->ISR & 0X40) == 0);

/* 串口发送完成,将该字符发送 */

USART1->TDR = (uint8_t) ch;

return ch;

}

定义例程对应的全局参数

multi_button相关,用于按键操作

/* USER CODE BEGIN PV */

Button button1, button2;

/* USER CODE END PV */

/*文件名*/

#define SETTING_PARA "0:Para.ini"

/*默认系统配置信息*/

char *System_Config_Info =

"[Setting]\n"

"led_flag=1;\n" /*LED标志*/

"plot_flag=0;\n" /*曲线开关*/

"WIFI_NAME=BearPi_ESP8266;\n" /*WIFI热点*/

"WIFI_PASSWORD=12345678;\n" /*WIFI密码*/

;

typedef struct

{

int led_flag ;

int plot_flag ;

char wifi_name[32] ;

char wifi_password[32];

} info ;

info ini_info ;

dictionary  *Config_ini = NULL;

/*系统配置文件全局变量*/

uint8_t retUSER_SYS_CONFIG ;

FATFS USERFatFS_SYS_CONFIG ;

FIL USER_SYS_CONFIG_File ;

/*挂载SD卡*/

int Mount_SD(void)

{

/*挂载SD卡*/

retSD = f_mount(&SDFatFS, SDPath, 1);

if(FR_OK != retSD)

return -1 ;

return 0 ;

}

/*创建一个默认的配置文件*/

void Create_Default_InI_File(void)

{

retUSER_SYS_CONFIG = f_open(&USER_SYS_CONFIG_File, SETTING_PARA, FA_OPEN_ALWAYS | FA_WRITE);

if(FR_OK != retUSER_SYS_CONFIG)

{

fprintf(stderr, "iniparser: cannot open %s\n", SETTING_PARA);

return ;

}

f_printf(&USER_SYS_CONFIG_File, System_Config_Info);

f_close(&USER_SYS_CONFIG_File);

}

/*加载INI文件*/

int Load_Confg_INI_Process(void)

{

/*加载INI文件*/

Config_ini = iniparser_load(SETTING_PARA);

if(NULL == Config_ini)

{

printf("加载出错\n");

Create_Default_InI_File();

Config_ini = iniparser_load(SETTING_PARA);

if(NULL == Config_ini)

{

printf("创建默认INI文件后继续加载出错\n");

return  -1;

}

}

printf("加载INI文件成功\n");

return 0 ;

}

/*保存参数*/

int INI_Para_Save_Process(void)

{

/*write config.ini parse*/

retUSER_SYS_CONFIG = f_open(&USER_SYS_CONFIG_File, SETTING_PARA, FA_OPEN_EXISTING | FA_WRITE);

if(FR_OK != retUSER_SYS_CONFIG)

{

printf("iniparser: cannot open %s\n", SETTING_PARA);

return -1;

}

printf("参数设置保存成功\n");

iniparser_dump_ini(Config_ini, &USER_SYS_CONFIG_File);

f_close(&USER_SYS_CONFIG_File);

iniparser_freedict(Config_ini);

return 0 ;

}

//读取按键1

uint8_t read_button1()

{

return HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) ;

}

//读取按键2

uint8_t read_button2()

{

return HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) ;

}

//按键1回调

void button1_callback(void *ptr)

{

static uint8_t index = 0 ;

char *wifi_name_para[] = {"Yangyuanxin", "Bruce_Yang", "BearPi", "mculover666"};

if(index == 4)

index = 0 ;

printf("\r\n按下KEY1,改变并保存WIFI_NAME参数!\n");

Load_Confg_INI_Process();

iniparser_set(Config_ini, "Setting:WIFI_NAME", wifi_name_para[index]);

INI_Para_Save_Process();

printf("设置wifi_name:%s成功\n",wifi_name_para[index]);

HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);

++index ;

}

//按键2回调

void button2_callback(void *ptr)

{

char *wifi_name = NULL ;

printf("\r\n按下KEY2,读取WIFI_NAME参数!\n");

Load_Confg_INI_Process();

wifi_name = iniparser_getstring(Config_ini,  "Setting:WIFI_NAME",   "not found");

INI_Para_Save_Process();

memset(ini_info.wifi_name, 0, strlen(ini_info.wifi_name));

memcpy(ini_info.wifi_name, wifi_name, strlen(wifi_name));

printf("wifi_name:%s\n", ini_info.wifi_name);

HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);

}

主函数实现:

/**

* @brief  The application entry point.

* @retval int

*/

int main(void)

{

/* USER CODE BEGIN 1 */

int ret = -1 ;

char *wifi_name = NULL ;

char *wifi_password = NULL ;

/* USER CODE END 1 */

/* MCU Configuration--------------------------------------------------------*/

/* Reset of all peripherals, Initializes the Flash interface and the Systick. */

HAL_Init();

/* USER CODE BEGIN Init */

/* USER CODE END Init */

/* Configure the system clock */

SystemClock_Config();

/* USER CODE BEGIN SysInit */

/* USER CODE END SysInit */

/* Initialize all configured peripherals */

MX_GPIO_Init();

MX_SDMMC1_SD_Init();

MX_USART1_UART_Init();

MX_FATFS_Init();

/* USER CODE BEGIN 2 */

//初始化并注册按键

button_init(&button1, read_button1, 0);

button_init(&button2, read_button2, 0);

button_attach(&button1, SINGLE_CLICK, button1_callback);

button_attach(&button2, SINGLE_CLICK, button2_callback);

button_start(&button1);

button_start(&button2);

//挂载SD卡

ret = Mount_SD();

if(ret != 0)

{

printf("SD Card mount ERROR\r\n");

HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);

return -1 ;

}

printf("SD卡挂载成功!\n");

//加载INI文件

ret = Load_Confg_INI_Process();

if(ret != 0)

{

printf("读取INI文件失败!\r\n");

HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);

return -2 ;

}

/*加载系统参数*/

ini_info.led_flag = iniparser_getint(Config_ini,  "Setting:led_flag",  -1);

ini_info.plot_flag = iniparser_getint(Config_ini, "Setting:plot_flag", -1);

wifi_name = iniparser_getstring(Config_ini,  "Setting:WIFI_NAME",   "not found");

wifi_password = iniparser_getstring(Config_ini,   "Setting:WIFI_PASSWORD", "not found");

memcpy(ini_info.wifi_name, wifi_name, strlen(wifi_name));

memcpy(ini_info.wifi_password, wifi_password, strlen(wifi_password));

/*改变参数led_flag*/

ret = iniparser_set(Config_ini, "Setting:led_flag", "0");

if(ret != 0)

{

printf("改变参数失败!\n");

return -3 ;

}

printf("改变参数成功\n");

/*参数保存,并释放内存*/

ret = INI_Para_Save_Process();

if(ret != 0)

{

printf("写入保存INI文件失败!\r\n");

HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);

return -4 ;

}

printf("写入保存INI文件成功!\r\n");

/* USER CODE END 2 */

/* Infinite loop */

/* USER CODE BEGIN WHILE */

while (1)

{

/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */

//以5ms周期调用按键tick

button_ticks();

HAL_Delay(5);

}

基于小熊派SD卡+Fatfs+移植开源iniparse解析库并使用

/* USER CODE END 3 */

}

注意,在Keil中,把堆栈尽量设大一些,以支持fatfs和iniparse的使用,可以通过CubeMX工程设置:

也可以直接在Keil中设置,但是下次用CubeMX生成的时候参数又会复原噢,建议采用CubeMX工程设置:

7、运行效果

还记得在上上节WIFI配网的粗暴方式,就可以以这种粗暴的方式直接改SSID和PASSWORD,然后产品开机直接就加载SD卡ini文件中的SSID和PASSWORD。

基于小熊派WIFI-ESP8266实践(上)

例程下载

链接:https://pan.baidu.com/s/13AJ6Pds4UzNSdkk1PcCGDQ

提取码:7y54

复制这段内容后打开百度网盘手机App,操作更方便哦

软件开发 小熊派 IoT

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:从高盛的技术“开源”看金融业软件发展未来
下一篇:哪个更安全?白名单还是黑名单?Agent端对监控指标黑白名单的支持
相关文章