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

      網友投稿 1003 2025-04-04

      在實際產品開發過程中,我們常常會對一些產品的內置參數進行存儲,比如:

      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"

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

      #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小時內刪除侵權內容。

      上一篇:為什么我的wps沒有登錄(為什么wps無法登錄)
      下一篇:比較全的Excel Cell content 單元格內容操作筆錄(比較全的游戲下載平臺)
      相關文章
      亚洲精品无码久久久久去q| 亚洲人成无码网站久久99热国产| 亚洲香蕉成人AV网站在线观看| 亚洲精品国产电影| 国产成人综合亚洲| 亚洲hairy多毛pics大全| 亚洲精品国产综合久久久久紧 | 亚洲人成77777在线播放网站不卡| 久久精品a亚洲国产v高清不卡| 亚洲精选在线观看| 亚洲午夜精品一区二区| 精品日韩亚洲AV无码一区二区三区| 亚洲va在线va天堂va888www| 久久久亚洲欧洲日产国码二区 | 久久久亚洲AV波多野结衣| 亚洲午夜视频在线观看| 亚洲第一二三四区| 亚洲AV色吊丝无码| 亚洲欧美日韩综合俺去了| 亚洲国产精品日韩av不卡在线| 亚洲国产精品18久久久久久| MM1313亚洲精品无码久久| avtt亚洲天堂| 在线观看亚洲天天一三视| 亚洲欧洲日产国码av系列天堂| 亚洲国产AV无码专区亚洲AV| 亚洲视频在线视频| 91亚洲性爱在线视频| 亚洲а∨天堂久久精品9966 | 亚洲欧美成人一区二区三区| 亚洲av无码日韩av无码网站冲| 一级毛片直播亚洲| 亚洲线精品一区二区三区影音先锋 | 亚洲精品无码国产| 亚洲国产精品自在线一区二区| 亚洲无线一二三四区| 亚洲午夜精品久久久久久app| 精品亚洲av无码一区二区柚蜜| 亚洲国产午夜福利在线播放| 亚洲人成网站在线观看播放| 亚洲网站在线观看|