ESP32 core 3.x - pinMode() bisa merusak bus SPI
Kartu SD saya butuh resistor pull-up di MISO. Banyak modul SD tidak punya, padahal kartu SD membutuhkannya. ESP32 punya pull-up internal, jadi baris yang paling jelas setelah bus dinyalakan adalah ini.
sdSpi.begin(PIN_SD_SCLK, PIN_SD_MISO, PIN_SD_MOSI, PIN_SD_CS);
pinMode(PIN_SD_MISO, INPUT_PULLUP); // don't do this in core 3.x
Di Arduino core untuk ESP32 versi 3.x, baris itu melepas MISO dari bus SPI. ESP32 masih bisa mengirim ke kartu, tapi tidak lagi menerima jawabannya, jadi SD.begin() gagal. Tidak ada yang terlihat salah di kode.
Kenapa ini terjadi
Core 3.x menambahkan “peripheral manager”. Ia mencatat periferal mana yang memiliki setiap pin (SPI, I2C, UART, PWM dan lainnya). Saat kamu memanggil pinMode(), core mengambil pin itu kembali sebagai GPIO biasa dan melepaskannya dari pemilik sebelumnya. Untuk pin MISO milik SPI, pelepasan itu menjalankan kode SPI yang melepas MISO dari bus. Ini terlihat di source code core, di esp32-hal-gpio.c (pinMode() memanggil perimanClearPinBus()) dan esp32-hal-spi.c (spiDetachBus_MISO()).
Secara umum ini ide yang bagus. Dua driver tidak bisa lagi berebut satu pin tanpa sengaja. Tapi pinMode() jadi berarti “ambil pin ini dari periferalnya”, bukan sekadar “pasang pull-up”.
Solusinya
Pasang pull-up langsung dengan fungsi ESP-IDF, yang hanya mengubah pull-up dan membiarkan pin tetap terhubung ke SPI. Fungsi ini tersedia di semua sketch Arduino untuk ESP32.
#include <driver/gpio.h>
sdSpi.begin(PIN_SD_SCLK, PIN_SD_MISO, PIN_SD_MOSI, PIN_SD_CS);
// Cards need MISO pulled up and many modules lack the resistor. Not pinMode():
// in core 3.x that would detach the pin from the SPI bus.
gpio_pullup_en((gpio_num_t)PIN_SD_MISO);
Panggil setelah begin(), supaya tidak ada yang mengatur ulang pin itu sesudahnya. Itulah yang berjalan di penampil foto ESP32 saya.
Pull-up internal itu lemah (sekitar 45 kΩ). Cukup untuk kartu SD di 20 MHz dengan kabel pendek. Dengan kabel panjang atau error di kecepatan tinggi, resistor luar 10 kΩ dari MISO ke 3,3 V lebih andal.
Jebakan yang sama di tempat lain
Aturannya sama untuk semua pin yang dimiliki periferal, bukan hanya SPI.
- Jangan memanggil
pinMode()pada pin I2C, UART, SPI atau PWM setelah periferalnya dinyalakan. - Untuk mengubah pull-up atau pull-down saja pada pin seperti itu, pakai
gpio_pullup_en(),gpio_pulldown_en()atau versi_dis()-nya. - Kalau sebuah periferal tiba-tiba tidak berfungsi setelah update core, cari
pinMode()pada pin-pinnya.