Principes de base du didacticiel d'introduction ESP32-C3 (8, NVS - l'utilisation de la bibliothèque de stockage non volatile)

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

avant-propos

Les tests fonctionnels ESP32-C3 suivants sont basés sur la carte de développement conçue par nous-mêmes :

Dessinez vous-même une carte de développement d'ESP32-C3 (la première fois que vous utilisez Lichuang EDA) (PCB est disponible)

L'environnement de développement est l'ESP-IDF officiel d'Espressif, construit sur la base du plugin VScode :

L'environnement de développement ESP32-C3 VScode est construit (basé sur l'ESP-IDF officiel d'Espressif - double environnement Windows et Ubuntu)

Parce que c'est le fonctionnement du Flash interne de l'ESP32-C3, nous n'utilisons pas d'autres périphériques ici.

1, introduction de base NVS

En termes simples, NVS est un morceau d'espace mémoire alloué sur le flash, qui est fourni à l'utilisateur pour enregistrer les données qui ne sont pas perdues lorsque l'alimentation est coupée.

L'objectif principal de cet article est d'apprendre à utiliser NVS basé sur le SDK officiel, et de mentionner brièvement les connaissances connexes, telles que la table de partition, etc.

1.1 Introduction de base

Pour l'introduction d'ESP32-C3 NVS, le lien de description du site officiel d'Espressif est le suivant :

Description officielle de la pièce Espressif ESP32-C3 NVS

Simplement à travers l'introduction officielle pour comprendre, ou utilisez quelques images officielles pour montrer :
insérez la description de l'image iciinsérez la description de l'image ici

1.2 Tableau des partitions

On dit que NVS est une zone en mémoire, alors quelle est l'adresse ? Quelle est la taille?

Celui-ci doit mentionner le contenu lié à la table de partition. L'adresse de description officielle est la suivante :

Table de partition flash ESP32-C3

Selon l'introduction officielle, nous énumérons ce qui doit être utilisé ici :

Chaque entrée de la table de partition comprend les parties suivantes : nom (étiquette), type (application, données, etc.), sous-type et décalage dans la mémoire flash (adresse de chargement de la partition).

  • Le champ Type peut être spécifié en tant qu'application (0x00) ou données (0x01), ou utiliser directement les nombres 0-254 (ou hexadécimal 0x00-0xFE). Notez que 0x00-0x3F ne doit pas être utilisé (réservé aux fonctionnalités de base d'esp-idf).
  • La longueur du champ SubType est de 8 bits et le contenu est lié au type de partition spécifique. À l'heure actuelle, esp-idf ne spécifie que la signification du sous-type des deux types de partition "app" et "data".
    insérez la description de l'image ici
    insérez la description de l'image ici

Par conséquent, selon l'introduction de la table de partition ci-dessus, pour l'ESP32-C3 que nous utilisons, la puce imprimera automatiquement les informations système au démarrage.La description NVS correspondante est la suivante :
insérez la description de l'image ici

Pour savoir comment générer une partition NVS, nous n'en discuterons pas ici, vous pouvez vous référer à la section officielle : Description officielle du programme de génération de partition NVS

1.3 Étapes d'utilisation de NVS

Le test NVS de cet article est basé sur la table de partition par défaut, nous n'avons donc pas besoin d'utiliser la table de partition pendant le processus d'utilisation.

L'API que NVS doit utiliser, dans le nvs_flash.hfichier, le chemin est :esp-idf/components/nvs_flash/include/nvs_flash.h

  1. Pour initialiser NVS, utilisez la fonction nvs_flash_init:
    Dans l'exemple :
    insérez la description de l'image ici

  2. Ouvrez NVS et utilisez la nvs_openfonction :
    insérez la description de l'image ici
    Dans l'exemple, le deuxième paramètre doit indiquer que la zone ouverte peut être lue ou écrite, et la lecture seule estNVS_READONLY :
    insérez la description de l'image ici

  3. Opérations de lecture et d'écriture, utilisez nvs_get_*(* pour indiquer différents types de données, tels que nvs_get_i32, nvs_get_u16) Opérations de lecture, utilisez nvs_set_*pour les opérations d'écriture :
    insérez la description de l'image iciinsérez la description de l'image ici
    Dans l'exemple, les applications de lecture et d'écriture sont les suivantes :
    insérez la description de l'image ici

  4. Après avoir écrit la valeur, une nvs_commitfonction est requise pour s'assurer que la valeur est écrite avec succès.
    insérez la description de l'image ici

  5. Fermez NVS et lorsque vous avez fini d'écrire, utilisez nvs_closeclose.
    insérez la description de l'image ici

2. Exemple d'essai

2.1 Exemple de test de base

Dans la routine officielle, nous nous référons à deux exemples de programmes, comme indiqué ci-dessous :
insérez la description de l'image ici
Exemple nvs_rw_value :

L'exemple dans les étapes d'utilisation de NVS que nous avons présentées ci-dessus est le nvs_rw_valueprojet utilisé. Il n'y a rien pour modifier le projet de base. Les résultats des tests sont les suivants, et restart_counterla augmentera de 1, comme illustré dans la figure suivante :
insérez la description de l'image ici
Exemple nvs_rw_blob :

L'effet de test du deuxième projet est le suivant :
insérez la description de l'image ici
regardez d'abord l'effet de test, expliquons brièvement le code source, la première fonction save_restart_counterfonction est fondamentalement la même que l'exemple nvs_rw_blob, pas grand chose à dire.

Regardons la deuxième fonction save_run_timeDans cette fonction, nous utilisons une fonction de sum nvs_get_blobet nvs_set_blobremarquons qu'ils ont tous les deux un paramètre de void*type , ce qui signifie que les opérations de ces deux nvs peuvent être appliquées à n'importe quel type de données.
insérez la description de l'image ici
Pour la save_run_timefonction ci-dessus, nous avons directement ajouté le code source commenté :

/* 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;
}

Après avoir compris la fonction ci-dessus, l' nvs_rw_blobexemple ne pose pratiquement aucun problème. Dans la print_what_saved(void)fonction suivante, différentes fonctions get sont utilisées pour extraire différentes données de différentes valeurs de clé dans le même espace de noms (nous en discuterons dans la section suivante). ), les autres n'ont rien de spécial :
insérez la description de l'image ici

2.2 Suppression des données

Comme mentionné précédemment, NVS ouvre en fait une zone dans l'espace Flash, donc cette zone doit avoir une adresse, mais l'ESP32-C3 utilise la méthode NVS, et l'adresse mémoire est opaque pour l'utilisateur, uniquement via l'espace de noms et la clé- les paires de valeurs sont automatiquement affectées (expliqué dans la section suivante). Puisqu'il est enregistré, vous devez également savoir le supprimer, car s'il n'est pas supprimé, les données peuvent toujours exister dans cet espace d'adressage.

Les exemples et les instructions officielles expliquent uniquement comment utiliser NVS pour enregistrer des données sans perte de puissance, mais n'expliquent pas spécifiquement comment effacer les données.

Nous avons nvs.htrouvé deux fonctions par :

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

esp_err_t nvs_erase_all(nvs_handle_t handle);

À l'aide de ces deux fonctions, testons-la. La fonction de test est basée sur Exemplenvs_rw_blob : , puis ajoutez le pilote de bouton :

Test d'apprentissage ESP32-C3 (deux, interruption GPIO, test de lecteur de bouton)

Nous supprimons les données enregistrées dans l'exemple par une opération de clé. Dans le pilote de clé, nous modifions le code :

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);
}

L' image est la suivante : L'
insérez la description de l'image ici
effet de test est le suivant :
insérez la description de l'image ici
Ensuite, changez une clé et essayez-la :
insérez la description de l'image ici
Enfin, testez-la nvs_erase_all, comme indiqué dans l'image ci-dessous :
insérez la description de l'image ici

2.3 Espaces de noms, paires clé-valeur

Les espaces de noms sont utilisés dans les routines ci-dessus, qui peuvent distinguer différentes zones de stockage, mais il existe également un paramètre clé, qui doit indiquer les paires clé-valeur. Il ressemble aux espaces de noms et est également utilisé pour distinguer différentes données, comme indiqué dans la figure:
insérez la description de l'image ici

Dans les multiples exemples de tests précédents, nous avons également une certaine compréhension des espaces de noms (généralement représentés par des paramètres dans functions const char* name) et des paires clé-valeur (généralement représentées par des paramètres dans functions const char* key).Tout d'abord, dans l'exemple, le même espace de noms est utilisé , obtenez différentes données via différentes paires clé-valeur :
insérez la description de l'image ici

Effectuez quelques tests simples pour les espaces de noms et les paires clé-valeur. Appuyez sur le bouton pour enregistrer la valeur du nouvel espace de noms, et enregistrez les nouvelles données dans l'opération clé :
insérez la description de l'image ici
ces données sont également lues à la mise sous tension : les
insérez la description de l'image ici
résultats du test sont les suivants :
insérez la description de l'image ici
Pour résumer, sur la base de l'exemple de test précédent, une image est dessinée, comme suit :
insérez la description de l'image ici

2.4 Stockage du type de données chaîne

Les données utilisées dans notre exemple ci-dessus sont essentiellement des entiers. Bien que nous les utilisions dans notre exemple nvs_set_blob, il s'agit toujours des données entières entrantes. Testons et enregistrons les données de type chaîne et numérique.

Comme dans la routine ci-dessus, enregistrez une chaîne en appuyant sur une touche :

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);
}

Dans la fonction principale, créez une nouvelle fonction de lecture :

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;
}

Bien sûr, il doit toujours app_mainêtre appelé dans my_test_str, et l'effet de test est le suivant :
insérez la description de l'image ici
dans l'apprentissage et l'utilisation ultérieurs du wifi ESP32-C3, le SSID et le mot de passe enregistrés seront fréquemment utilisés par les opérations de chaîne NVS.

Je suppose que tu aimes

Origine blog.csdn.net/weixin_42328389/article/details/122703875
conseillé
Classement