Utilisation de la puce mémoire 24CSM01 avec le framework ESP-IDF – Partie 1.

Posted on dim. 12 janvier 2025 in Electronics

Ça faisait quelques temps que j’avais envie de tester la programmation d’un ESP32 avec le framework natif ESP-IDF. Il me fallait pour ça un sujet de test. Et maintenant que l’interfaçage 24CSM01 / Arduino fonctionne, pourquoi ne pas tenter d’interagir avec cette puce en utilisant ce fameux framework.

Une première tentative en suivant la doc du framework s’est soldée assez vite par un échec me disant que le fichier driver/i2c/i2c_master.h n’existait pas. Vérification faite, je suivais la documentation de la version stable (au jour de la rédaction, c’est la 5.4), alors que j’avais installé (il y a longtemps) la 5.0.
Or, Espressif a sorti une nouvelle version de la bibliothèque d’interfaçage I2C dans l’intervalle. Après une mise à jour du framework, ça fonctionne beaucoup mieux.

Le fonctionnement de ce framework me rappelle celui de la HAL STM32, avec la configuration du bus en mode maître, la définition d’un handler… Le code correspondant, commenté, est présenté ci-dessous.

#include <stdio.h>
#include "stdint.h"
#include "driver/i2c_master.h"
// Ces deux en-têtes freertos permettent d’utiliser la fonction vTaskDelay,
//permettant d’introduire un délai dans l’exécution du programme
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#define LENGTH 48

// On créée une configuration mode maître
i2c_master_bus_config_t config = {
    .clk_source = I2C_CLK_SRC_DEFAULT,
    // On sélectionne le port I2C numéro 0. On aurait aussi pu metter `-1`
    //pour que ce port soit alloué automatiquement.
    .i2c_port = I2C_NUM_0,
    // Sur mon ESP32-WROOM-32, SCL est mappé sur le pin 22 et SDA sur le pin 21.
    // Ça pourrait être différent sur un autre contrôleur (par exemple
    // ESP32-C3-MINI-1).
    .scl_io_num = 22,
    .sda_io_num = 21,
    .glitch_ignore_cnt = 7,
    // Le bus I2C est active-low open-drain par définition. Il faut donc que les
    /// lignes SDA et SCL soient liées à la tension haute par une résistance de
    // tirage haut. On peut soit intégrer nos résistors au circuit, soit utiliser
    // des pullups internes à l’ESP32, ce que nous faisons ici.
    .flags.enable_internal_pullup = true,
};

// Définition du handler pour le bus I2C dans son ensemble
i2c_master_bus_handle_t hi2c0;

// On a une configuration spécifique par périphérique I2C.
// Si je voulais pouvoir aussi interagir avec le registre de configuration,
// il faudrait que je définisse une seconde configuration
i2c_device_config_t eepromconfig = {
    .dev_addr_length = I2C_ADDR_BIT_LEN_7,
    .device_address = 0b1010000,
    .scl_speed_hz = 100000,
};
// Définition d’un handler pour un périphérique spécifique.
i2c_master_dev_handle_t eeprom_handle;

// Là je me fie aux exemples fournis par Espressif sur leur Github.
static void disp_buf(uint8_t *buf, int len)
{
    int i;
    for (i = 0; i < len; i++) {
        printf("%02x ", buf[i]);
        if ((i + 1) % 16 == 0) {
            printf("\n");
        }
    }
    printf("\n");
}

void app_main(void)
{
    uint8_t tx_buffer[LENGTH] = {0x00, 0x01};
    uint8_t rx_buffer[LENGTH];
    // On crée un nouveau bus I2C en mode maître, géré par le handler pré-défini
    // et avec la configuration édictée précédemment
    i2c_new_master_bus(&config, &hi2c0);
    // On crée un nouveau périphérique que l’on ajoute au bus I2C, avec sa
    // configuration et son handler spécifiques
    i2c_master_bus_add_device(hi2c0, &eepromconfig, &eeprom_handle);

    // C’est ici que la communication se passe. On envoie 2 bytes depuis tx_buffer
    // (l’adresse du périphérique est donnée par le contenu du handler), et on
    // récupère 3 bytes dans rx_buffer. Le -1 permet de définir un timeout infini.
    i2c_master_transmit_receive(eeprom_handle, tx_buffer, 2, rx_buffer, 3, -1);
    disp_buf(rx_buffer, 3);
    vTaskDelay(1000/portTICK_PERIOD_MS);
}

L’exécution de ce code me donne les résultats suivants:

I (291) main_task: Calling app_main()
05 0a 0f >> C’est le contenu de mon rx_buffer !
I (1291) main_task: Returned from app_main()

Le buffer de réception contient 0x05 (5), 0x0a (10), 0x0f (15), ce qui correspond aux dernières données que j’avais écrites sur la puce que j’ai utilisé, à cette adresse, avec le framework Arduino. Ce petit code fonctionne donc parfaitement !

Allez, c’est bien assez pour ce soir. Rendez-vous dans un prochain post pour voir comment écrire des données, et peut-être ajouter un second périphérique (le registre de configuration) au bus.

Bibliographie