基于小熊派SD卡+Fatfs+移植開源iniparse解析庫并使用
在實際產品開發過程中,我們常常會對一些產品的內置參數進行存儲,比如:
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);
}
/* 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小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。