ESP32-C3入門チュートリアルの基本(8、NVS-不揮発性ストレージライブラリの使用)

前面的7节课把开发板上基本的外设都测试过一边,接下来马上就要进入wifi和蓝牙应用的测试了
在此之前,还需要把掉电数据保存的功能给实现,在STM32中,可以使用内部的flash或者有些自带的EEPROM
在 ESP32-C3 上,使用非易失性存储 (NVS)  库的方式,进行简单数据的掉电保存

序文

次のESP32-C3機能テストは、自社で設計した開発ボードに基づいています。

ESP32-C3の開発ボードを自分で作成します(Lichuang EDAを初めて使用する場合)(PCBが利用可能です)

開発環境は、VScodeプラグインに基づいて構築されたEspressifの公式ESP-IDFです。

ESP32-C3 VScode開発環境が構築されています(Espressifの公式ESP-IDF-WindowsとUbuntuのデュアル環境に基づいています)

ESP32-C3の内蔵フラッシュの動作であるため、ここでは他の周辺機器は使用していません。

1、NVSの基本的な紹介

素人の言葉で言えば、NVSはフラッシュに割り当てられたメモリスペースの一部であり、電源をオフにしても失われないデータを保存するためにユーザーに提供されます。

この記事の主な目的は、公式SDKに基づいたNVSの使用方法を学び、パーティションテーブルなどの関連知識について簡単に説明することです。

1.1基本的な紹介

ESP32-C3 NVSの紹介については、Espressifの公式Webサイトの説明リンクは次のとおりです。

Espressifの公式ESP32-C3NVSパーツの説明

公式の紹介を通して理解するか、いくつかの公式の写真を使用して表示します。
ここに画像の説明を挿入ここに画像の説明を挿入

1.2パーティションテーブル

NVSはメモリ内の領域であると言いますが、アドレスは何ですか?サイズは何ですか?

パーティションテーブルに関連する内容について言及する必要があります。正式な説明アドレスは次のとおりです。

ESP32-C3フラッシュパーティションテーブル

公式の紹介によると、ここで使用する必要があるものをリストします。

パーティションテーブルの各エントリは、名前(ラベル)、タイプ(アプリ、データなど)、サブタイプ、およびフラッシュ内のオフセット(パーティションのロードアドレス)の部分で構成されます。

  • タイプフィールドは、アプリ(0x00)またはデータ(0x01)として指定するか、0〜254(または16進数の0x00〜0xFE)の数字を直接使用できます。0x00-0x3Fは使用できないことに注意してください(esp-idfのコア機能用に予約されています)。
  • SubTypeフィールドの長さは8ビットで、内容は特定のパーティションタイプに関連しています。現在、esp-idfは、2つのパーティションタイプ「app」と「data」のサブタイプの意味のみを指定しています。
    ここに画像の説明を挿入
    ここに画像の説明を挿入

したがって、上記のパーティションテーブルの紹介によると、使用するESP32-C3の場合、チップは起動時にシステム情報を自動的に出力します。対応するNVSの説明は次のとおりです。
ここに画像の説明を挿入

NVSパーティションを生成する方法については、ここでは説明しません。公式の「公式NVSパーティション生成プログラムの説明」セクションを参照してください。

1.3NVSの使用手順

この記事のNVSテストはデフォルトのパーティションテーブルに基づいているため、使用プロセス中にパーティションテーブルを操作する必要はありません。

NVSがnvs_flash.hファイルで使用する必要のあるAPIのパスは、次のとおりです。esp-idf/components/nvs_flash/include/nvs_flash.h

  1. NVSを初期化するには、次の関数を使用します。nvs_flash_init

    ここに画像の説明を挿入

  2. NVSを開き、nvs_open関数を使用します。
    ここに画像の説明を挿入
    この例では、2番目のパラメーターは、オープン領域の読み取りまたは書き込みが可能であることを示している必要があり、読み取り専用はNVS_READONLY次のとおりです。
    ここに画像の説明を挿入

  3. 読み取りおよび書き込み操作、使用nvs_get_*(*は、などのさまざまなデータ型を示しますnvs_get_i32nvs_get_u16読み取り操作、nvs_set_*書き込み操作に使用:
    ここに画像の説明を挿入ここに画像の説明を挿入
    この例では、読み取りおよび書き込みアプリケーションは次のとおりです。
    ここに画像の説明を挿入

  4. 値を書き込んだ後nvs_commit、値が正常に書き込まれるようにするための関数が必要です。
    ここに画像の説明を挿入

  5. NVSを閉じ、書き込みが完了したら、nvs_closecloseを使用します。
    ここに画像の説明を挿入

2.テスト例

2.1基本的なテスト例

公式ルーチンでは、以下に示すように、2つのサンプルプログラムを参照します。
ここに画像の説明を挿入
例nvs_rw_value:

上記で紹介したNVSの使用手順の例は、nvs_rw_value使用したプロジェクトです。基本プロジェクトを変更する必要はありません。テスト結果は次のとおりで、次の図に示すように、再起動するたびrestart_counterに増加します。
ここに画像の説明を挿入
例nvs_rw_blob:

2番目のプロジェクトのテスト効果は次のとおりです。
ここに画像の説明を挿入
最初にテスト効果を見て、ソースコードを簡単に説明しましょう。最初の関数save_restart_counter関数は基本的に例と同じですが、nvs_rw_blob言うまでもありません。

2番目の関数を見てみましょうsave_run_time。この関数では、sumの関数を使用し、両方にタイプnvs_get_blob。これは、これら2つのnvの操作を任意のタイプのデータに適用できることを意味します。上記の関数では、コメント付きのソースコードを直接追加しました。nvs_set_blobvoid*
ここに画像の説明を挿入
save_run_time

/* Save new run time value in NVS
   by first reading a table of previously saved values
   and then adding the new value at the end of the table.
   Return an error if anything goes wrong
   during this process.
 */
esp_err_t save_run_time(void)
{
    
    
    nvs_handle_t my_handle;
    esp_err_t err;

    // Open  正常的操作步骤,打开nvs,第一个命名空间,读写,句柄名称
    err = nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, &my_handle);
    if (err != ESP_OK) return err;

    /* Read the size of memory space required for blob
     unsigned int required_size
     读nvs,读取键值对为 "run_time" 处的内容放入 变量required_size
    */
    size_t required_size = 0;  // value will default to 0, if not set yet in NVS
    /*先使用一次nvs_get_blob函数,但是第三个参数输出地址使用的是NULL,
    表示读出的数据不保存,因为这里使用只是为了看一下 "run_time" 处是否
    有数据,只是先读一下数据,看一下读完以后 required_size 还是不是0
    */
    err = nvs_get_blob(my_handle, "run_time", NULL, &required_size);
    if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err;

    /* Read previously saved blob if available
    这里申请一块地址,定义为 run_time ,地址上存放的示uint32_t数据,大小为
    required_size大小 + sizeof(uint32_t) 
    */
    uint32_t* run_time = malloc(required_size + sizeof(uint32_t));
    /*
    如果上面读到的 required_size 大于0 ,说明"run_time" 处以前是保存过数据的
    那么,就先读出来,保存在刚才申请的 地址 run_time 处(第3个参数)。
    */
    if (required_size > 0) {
    
    
        err = nvs_get_blob(my_handle, "run_time", run_time, &required_size);
        if (err != ESP_OK) {
    
    
            free(run_time);
            return err;
        }
    }
    /* 
    Write value including previously saved blob if available
    不管 required_size 有过还是没有过,进行这步操作,都会使得 required_size 增加
    增加 sizeof(uint32_t) 大小,因为示例本意示重启一次,计数必须加一次
    */
    required_size += sizeof(uint32_t);
    /*
    每一次保存的是使用数组形式保存数据:类似于 uint32_t run_time[] 数组
    给数组赋值
    */
    run_time[required_size / sizeof(uint32_t) - 1] = xTaskGetTickCount() * portTICK_PERIOD_MS;
    /*
    最后把需要保存的数组处理完成以后
    调用 nvs_set_blob 函数进行保存
    */ 
    err = nvs_set_blob(my_handle, "run_time", run_time, required_size);
    free(run_time);

    if (err != ESP_OK) return err;

    // Commit
    err = nvs_commit(my_handle);
    if (err != ESP_OK) return err;

    // Close
    nvs_close(my_handle);
    return ESP_OK;
}

上記の関数を理解した後、nvs_rw_blob例は基本的に問題ありません。次のprint_what_saved(void)関数では、異なるget関数を使用して、同じ名前空間内の異なるキー値から異なるデータを抽出します(これについては次のセクションで説明します)。 )、その他は特別なものではありません:
ここに画像の説明を挿入

2.2データの削除

前述のように、NVSは実際にフラッシュスペースの領域を開くため、この領域にはアドレスが必要ですが、ESP32-C3はNVS方式を使用し、メモリアドレスは名前空間とキーを介してのみユーザーに不透明です-値のペアは自動的に割り当てられます(次のセクションで説明します)。保存されているので、削除することも知っておく必要があります。削除しないと、データは常にそのアドレス空間に存在する可能性があるためです。

例と公式の説明は、NVSを使用して電力損失なしでデータを保存する方法を説明するだけであり、データをクリアする方法を具体的に説明していません。

nvs.h次の2つの関数が見つかりました

esp_err_t nvs_erase_key(nvs_handle_t handle, const char* key);

esp_err_t nvs_erase_all(nvs_handle_t handle);

これらの2つの関数を使用して、テストしてみましょう。テスト関数はnvs_rw_blobに基づいており、ボタンドライバーを追加します。

ESP32-C3学習テスト(2つ、GPIO割り込み、ボタンドライブテスト)

キー操作により、例に保存されているデータを削除します。キードライバーでは、次のコードを変更します。

static void button_single_click_cb(void *arg){
    
    
   
    uint8_t *num = (uint8_t *)arg;
    uint8_t gpio_num = *num;
    ESP_LOGI(TAG, "BTN%d: BUTTON_SINGLE_CLICK\n", gpio_num);
    printf("nvs_erase_key test!\r\n");
    // nvs_erase_key(my_handle,"restart_conter");
    nvs_handle_t my_handle;

    // Open
    nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, &my_handle);

    nvs_erase_key(my_handle,"restart_conter");

    // err = nvs_commit(my_handle);

    // nvs_close(my_handle);
}

写真は次のとおりです。
ここに画像の説明を挿入
テスト効果は次のとおりです。
ここに画像の説明を挿入
次に、キーを変更して試してください。
ここに画像の説明を挿入
最後に、nvs_erase_all下の写真に示すように、テストします。
ここに画像の説明を挿入

2.3名前空間、キーと値のペア

名前空間は上記のルーチンで使用され、さまざまなストレージ領域を区別できますが、キーと値のペアを示す必要があるキーパラメータもあります。名前空間と同じように感じられ、さまざまなデータを区別するためにも使用されます。図:
ここに画像の説明を挿入

const char* name前の複数のテスト例では、名前空間(通常は関数のパラメーターで表される)とキーと値のペア(通常は関数のパラメーターで表される)についてもある程度理解していますconst char* key。まず、この例では、同じ名前空間が使用されています、さまざまなキーと値のペアを介してさまざまなデータを取得します。
ここに画像の説明を挿入

名前空間とキーと値のペアに対していくつかの簡単なテストを実行します。ボタンを押して新しい名前空間の値を保存し、キー操作で新しいデータを保存します。
ここに画像の説明を挿入
このデータは、電源を入れたときにも読み取られます。
ここに画像の説明を挿入
テスト結果要約すると、前のテスト例に基づいて、
ここに画像の説明を挿入
のように画像が描画されます。
ここに画像の説明を挿入

2.4文字列データ型の保存

上記の例で使用されているデータは基本的に整数です。この例では使用していますnvs_set_blobが、それでも入力整数データです。文字列型と数値型のデータをテストして保存しましょう。

上記のルーチンと同様に、キーを押して文字列を保存します。

static void button_long_press_start_cb(void *arg){
    
    
    uint8_t *num = (uint8_t *)arg;
    uint8_t gpio_num = *num;
    ESP_LOGI(TAG, "BTN%d: BUTTON_LONG_PRESS_START\n", gpio_num);

    char test_str[]="this is my test str,boom!";
    printf("nvs_new_name test!\r\n");
    nvs_handle_t my_handle;
    nvs_open(TEST_NAMESPACE, NVS_READWRITE, &my_handle);
    nvs_set_str(my_handle,"str_test",test_str);
    nvs_commit(my_handle);
    nvs_close(my_handle);
}

main関数で、新しい読み取り関数を作成します。

esp_err_t my_test_str(void)
{
    
    
    nvs_handle_t my_handle;
    esp_err_t err;
    char get_char[30] = {
    
    0};
    // Open
    err = nvs_open(TEST_NAMESPACE, NVS_READWRITE, &my_handle);
    if (err != ESP_OK) return err;

    // Read
    size_t required_size = 0;
    err = nvs_get_str(my_handle, "str_test",NULL,&required_size);
    // err = nvs_get_i32(my_handle, "str_test", &restart_counter);
    // if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err;
    // err = nvs_get_blob(my_handle, "run_time", NULL, &required_size);
    if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err;
   
    if (required_size > 0) {
    
    
        err = nvs_get_str(my_handle, "str_test", get_char, &required_size);
        printf("test str is: %s \nsize is %d \n",get_char,required_size);
        if (err != ESP_OK) {
    
    
            return err;
        }
    }
    else{
    
    
        printf("no str data now!!!\n");
    }
    // Close
    nvs_close(my_handle);
    return ESP_OK;
}

もちろん、それでもapp_main呼び出す必要がmy_test_strあり、テスト効果は次のとおりです。
ここに画像の説明を挿入
後のESP32-C3 Wi-Fiの学習と使用では、保存されたSSIDとパスワードがNVS文字列操作で頻繁に使用されます。

おすすめ

転載: blog.csdn.net/weixin_42328389/article/details/122703875