quarta-feira, maio 15, 2019

ESP32/Arduino: Partições da Flash e emulação de EEProm

Nos Arduinos baseados nos microcontroladores ATmega, a Flash é interna ao microcontrolador e tem uma organização bastantes simples: o bootloader e a aplicação, com tamanhos e posições definidos pelos fuses.

No ESP32 a Flash é externa (mas está dentro do módulo) e possui um esquema mais flexível que permite criar várias partições.

A Flash é o CI na parte de baixo - fonte: https://randomnerdtutorials.com/esp32-pinout-reference-gpios/


A Tabela de Partição

O nome, tipo, posição e tamanho de cada partição da Flash é definido em uma tabela (normalmente no endereço 0x8000 e com tamanho 0xC00 bytes). Cada definição de partição ocupa 32 bytes (), portanto podem existir até 96 partições. A documentação da tabela está aqui.

O programa abaixo lista a tabela de partição, usando a Partition Table API.
// Fonte: https://github.com/espressif/arduino-esp32/issues/703#issuecomment-348255862

#include <esp_partition.h>

// Iniciação
void setup() {
  Serial.begin (115200);
  dump();
}

// Nada a fazer
void loop() {
  delay(500);
}

// Lista a tabela de partições
void dump() {
  size_t ul;
  esp_partition_iterator_t _mypartiterator;
  const esp_partition_t *_mypart;
  
  ul = spi_flash_get_chip_size(); 
  Serial.print("Flash chip size: "); 
  Serial.println(ul);
  
  Serial.println("Partiton table:");
  _mypartiterator = esp_partition_find(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_ANY, NULL);
  if (_mypartiterator) {
    do {
      _mypart = esp_partition_get(_mypartiterator);
      printf("%x - %x - %x - %x - %s - %i\r\n", _mypart->type, _mypart->subtype, _mypart->address, _mypart->size, _mypart->label, _mypart->encrypted);
    } while (_mypartiterator = esp_partition_next(_mypartiterator));
  }
  esp_partition_iterator_release(_mypartiterator);
  _mypartiterator = esp_partition_find(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, NULL);
  if (_mypartiterator) {
    do {
      _mypart = esp_partition_get(_mypartiterator);
      printf("%x - %x - %x - %x - %s - %i\r\n", _mypart->type, _mypart->subtype, _mypart->address, _mypart->size, _mypart->label, _mypart->encrypted);
    } while (_mypartiterator = esp_partition_next(_mypartiterator));
    esp_partition_iterator_release(_mypartiterator);
  }
}
A saída abaixo corresponde a uma ESP32 DevKit com configuração padrão do Arduino.
Flash chip size: 4194304
Partiton table:
0 - 10 - 10000 - 140000 - app0 - 0
0 - 11 - 150000 - 140000 - app1 - 0
1 - 2 - 9000 - 5000 - nvs - 0
1 - 0 - e000 - 2000 - otadata - 0
1 - 99 - 290000 - 1000 - eeprom - 0
1 - 82 - 291000 - 16f000 - spiffs - 0
De Onde Vem a Tabela de Partição

O kit de desenvolvimento da ExpressIf (ESP-IDF) contém uma ferramenta (um script python) que gera uma imagem binária da tabela a partir de um arquivo csv. Esta ferramente é rodada sempre que você manda gravar um sketch através da IDE do Arduino e a imagem gerada é gravada na flash.

O arquivo csv usado pela IDE é o xxx\packages\esp32\hardware\esp32\1.0.2\tools\partitions\default.csv, onde xxx é a pasta onde estão os pacotes do Arduino e 1.0.2 é a versão do suporte ao ESP32. Para descobrir a pasta, entre em Preferência e veja onde estão armazenadas as preferências da IDE.
# Name,   Type, SubType, Offset,  Size, Flags
nvs,      data, nvs,     0x9000,  0x5000,
otadata,  data, ota,     0xe000,  0x2000,
app0,     app,  ota_0,   0x10000, 0x140000,
app1,     app,  ota_1,   0x150000,0x140000,
eeprom,   data, 0x99,    0x290000,0x1000,
spiffs,   data, spiffs,  0x291000,0x16F000,
Emulação de EEProm (v1.0.2)

Importante: a próxima versão do suporte ao ESP32 vai mudar o que está escrito abaixo e provavelmente quebrar as aplicações que usam EEProm...

Nos microcontroladores ATmega temos a EEProm, que é uma memória não volátil que permite a alteração de bytes isolados. Isto é muito útil para armazenar configurações. O ESP32 não tem este tipo de memória. A solução (até a versão 1.02) é emular a EEProm usando uma partição da Flash.

O código da biblioteca EEProm para o release da versão 1.0.2 está aqui. Funciona parecido com a biblioteca do ATmega, exceto que:

  • É preciso chamar EEProm.begin() no início
  • Para tornar as gravações permanentes é necessário chamar EEprom.commit()
Na versão 1.0.3 (que está em beta) o módulo foi re-escrito para usar a NVS (non-volatile store) e marcado como deprecated com a recomendação de usar a biblioteca Preferences (que vai ser objeto do próximo post).







Nenhum comentário: