PSRAM trên ESP32 là gì: bo nào có, nhanh chậm ra sao, khi nào cần và cách cấp phát bộ nhớ

PSRAM là RAM ngoài chip của ESP32: module nào có, chậm hơn SRAM bao nhiêu, dữ liệu nào nên đặt vào PSRAM, ps_malloc và heap_caps_malloc, chương trình kiểm tra bộ nhớ trên mọi bo.

ESP32 có 520 KB SRAM trong chip, nhưng sau khi trừ phần của hệ thống và Wi-Fi, chương trình thường chỉ còn khoảng 200–300 KB, và khối liên tục lớn nhất chỉ khoảng 100 KB. Với camera, màn hình lớn, âm thanh hay nhận dạng bằng AI, chừng đó không đủ. Một số module ESP32 có thêm PSRAM: 4 hoặc 8 MB RAM nằm ngoài chip. Bài này giải thích PSRAM là gì, bo nào có, nhanh chậm ra sao, dữ liệu nào nên đặt vào PSRAM, cách cấp phát, và một chương trình kiểm tra bộ nhớ chạy được trên cả bo có và không có PSRAM.

1. PSRAM là gì

Sơ đồ chip ESP32 gồm SRAM 520 KB và hai nhân 240 MHz có bộ nhớ đệm, nối qua bus SPI QSPI 80 MHz tới flash 4 MB giữ dữ liệu khi mất điện và PSRAM 4 hoặc 8 MB mất dữ liệu khi mất điện
SRAM trong chip, flash và PSRAM bên ngoài (giá trị theo tài liệu ESP32, hình minh họa).

PSRAM (pseudo-static RAM) là chip RAM rời, nối với ESP32 qua bus SPI bốn đường giống bus của flash. Nhờ bộ nhớ đệm (cache), chương trình đọc ghi PSRAM như RAM thường, không cần lệnh đặc biệt. Nhưng mỗi lần dữ liệu không có sẵn trong bộ nhớ đệm, CPU phải chờ đọc qua bus SPI, nên PSRAM chậm hơn SRAM trong chip nhiều lần. Như mọi RAM, dữ liệu trong PSRAM mất khi mất điện.

Module, boPSRAM
ESP32-WROOM-32 (ESP32 DevKit V1 30 chân, 38 chân)Không có
ESP32-WROVER, ESP32-CAM4 MB (một số loại 8 MB, ESP32 đời đầu dùng được 4 MB một lúc)
ESP32-S3 (ví dụ module N16R8)2–8 MB, loại 8 MB thường là PSRAM tám đường (OPI) nhanh hơn
ESP32-C3Không có

Trên module WROVER và ESP32-CAM, PSRAM chiếm GPIO16 và GPIO17: hai chân này không dùng cho việc khác được, kể cả làm UART2.

2. Nhanh chậm ra sao

Biểu đồ thanh: RAM trong ghi 812,4 MB/s, chép 386,2 MB/s; PSRAM ghi 38,9 MB/s, chép 17,6 MB/s
Tốc độ ghi, chép một vùng 64 KB (số liệu ví dụ).

Với vùng dữ liệu lớn hơn bộ nhớ đệm, PSRAM chậm hơn SRAM trong chip khoảng 10–20 lần. Khi chương trình đọc đi đọc lại một vùng nhỏ, bộ nhớ đệm giữ sẵn dữ liệu nên khoảng cách nhỏ hơn nhiều. Vì vậy PSRAM hợp với dữ liệu lớn, được xử lý tuần tự (ảnh, âm thanh, bộ đệm màn hình), không hợp với vòng lặp tính toán nặng trên dữ liệu nhỏ.

3. Dữ liệu nào để ở đâu

Hai khung: nên để trong PSRAM gồm khung ảnh camera, bộ đệm màn hình, âm thanh, chuỗi JSON lớn, mô hình AI; nên để trong SRAM gồm biến dùng trong ngắt, stack của task, bộ đệm DMA của một số ngoại vi, dữ liệu nhỏ dùng liên tục
PSRAM cho dữ liệu lớn; SRAM trong chip cho dữ liệu cần nhanh hoặc dùng trong ngắt (hình minh họa).

Hàm ngắt không nên đọc ghi PSRAM: lúc đang ghi flash, bộ nhớ đệm bị tắt và PSRAM tạm thời không truy cập được. Với ESP32 đời đầu, nhiều ngoại vi dùng DMA (I2S, SPI tốc độ cao) cũng cần bộ đệm nằm trong SRAM; thư viện camera của ESP32-CAM đã tự lo việc này.

4. Cấp phát bộ nhớ trong PSRAM

  • Bật PSRAM: với bo "ESP32 Dev Module" chọn Tools → PSRAM → Enabled; bo "AI Thinker ESP32-CAM" đã bật sẵn; ESP32-S3 chọn đúng loại QSPI hoặc OPI theo module. Gói esp32 3.x tự dò PSRAM lúc khởi động, psramFound() cho biết kết quả.
  • ps_malloc(n) (Arduino) hoặc heap_caps_malloc(n, MALLOC_CAP_SPIRAM) (ESP-IDF): cấp phát chắc chắn trong PSRAM.
  • heap_caps_malloc(n, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT): cấp phát chắc chắn trong SRAM trong chip.
  • malloc() thường: với cấu hình của gói esp32, khối lớn hơn 4 KB tự được đặt vào PSRAM khi có PSRAM; khối nhỏ ở lại SRAM.
  • Biến toàn cục lớn (mảng khai báo sẵn) vẫn nằm trong SRAM; muốn dùng PSRAM thì cấp phát lúc chạy.

ESP.getFreeHeap() chỉ cho biết tổng còn trống; khi cần một khối lớn, xem thêm heap_caps_get_largest_free_block(): bộ nhớ bị chia nhỏ thì tổng còn nhiều mà vẫn không cấp được một khối lớn.

5. Chương trình kiểm tra bộ nhớ

Chương trình chạy được trên mọi bo. Trên ESP32 DevKit V1 (không PSRAM), nó in bộ nhớ trong còn bao nhiêu và thử cấp phát 200 KB liên tục. Trên bo có PSRAM, nó đo thêm tốc độ ghi, chép của hai loại bộ nhớ, cấp phát và kiểm tra một khối 2 MB, và cho biết malloc(100 KB) rơi vào đâu.

// PYWORLD.VN - PSRAM trên ESP32: kiểm tra có PSRAM không, xem bộ nhớ trong và PSRAM còn bao nhiêu, cấp phát khối lớn, đo tốc độ ghi đọc
// Chạy được trên mọi bo: DevKit V1 (module WROOM-32, không có PSRAM) chỉ in phần bộ nhớ trong; ESP32-CAM, WROVER, S3 có PSRAM thì đo thêm
// Không cần thư viện ngoài

#include <esp_heap_caps.h>

const size_t KHOI_DO = 64 * 1024;        // 64 KB để đo tốc độ
const int SO_LAN = 50;

// Đo tốc độ ghi (memset) và chép (memcpy) trên một vùng nhớ, trả về MB/s.
void doTocDo(const char *ten, uint8_t *vung, uint8_t *dich) {
  uint32_t t = micros();
  for (int i = 0; i < SO_LAN; i++) memset(vung, i, KHOI_DO);
  float ghi = (float)KHOI_DO * SO_LAN / (micros() - t);       // byte/µs = MB/s
  t = micros();
  for (int i = 0; i < SO_LAN; i++) memcpy(dich, vung, KHOI_DO);
  float chep = (float)KHOI_DO * SO_LAN / (micros() - t);
  Serial.printf("  %-12s ghi %6.1f MB/s, chep %6.1f MB/s\n", ten, ghi, chep);
}

void inBoNho() {
  Serial.printf("Bo nho trong: con %u byte, khoi lien tuc lon nhat %u byte\n",
                (unsigned)heap_caps_get_free_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT),
                (unsigned)heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT));
  if (psramFound()) {
    Serial.printf("PSRAM: tong %lu byte, con %lu byte, khoi lon nhat %lu byte\n", (unsigned long)ESP.getPsramSize(),
                  (unsigned long)ESP.getFreePsram(), (unsigned long)ESP.getMaxAllocPsram());
  }
}

void setup() {
  Serial.begin(115200);
  delay(500);
  inBoNho();
  if (!psramFound()) {
    Serial.println("Khong co PSRAM (module WROOM). Thu cap phat 200 KB lien tuc trong bo nho trong:");
    void *p = malloc(200 * 1024);
    Serial.println(p ? "  Duoc" : "  That bai: bo nho trong khong co khoi lien tuc lon nhu vay");
    free(p);
    return;
  }

  // Cấp phát rõ ràng: heap_caps_malloc chọn loại bộ nhớ; ps_malloc là cách viết gọn của Arduino cho PSRAM
  uint8_t *trong = (uint8_t *)heap_caps_malloc(KHOI_DO, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
  uint8_t *trong2 = (uint8_t *)heap_caps_malloc(KHOI_DO, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
  uint8_t *ngoai = (uint8_t *)ps_malloc(KHOI_DO);
  uint8_t *ngoai2 = (uint8_t *)ps_malloc(KHOI_DO);
  if (!trong || !trong2 || !ngoai || !ngoai2) {
    Serial.println("Khong cap phat duoc vung do toc do");
    return;
  }
  Serial.println("Toc do (64 KB x 50 lan):");
  doTocDo("RAM trong", trong, trong2);
  doTocDo("PSRAM", ngoai, ngoai2);
  free(trong);
  free(trong2);
  free(ngoai);
  free(ngoai2);

  // Một khối 2 MB: bộ nhớ trong không bao giờ có, PSRAM thì có
  size_t lon = 2 * 1024 * 1024;
  uint8_t *khoiLon = (uint8_t *)ps_malloc(lon);
  if (khoiLon) {
    for (size_t i = 0; i < lon; i++) khoiLon[i] = i & 0xFF;
    uint32_t tong = 0;
    for (size_t i = 0; i < lon; i++) tong += khoiLon[i];
    Serial.printf("Khoi 2 MB trong PSRAM: ghi va doc lai, tong = %lu (dung: %lu)\n", (unsigned long)tong,
                  (unsigned long)(lon / 256 * (255 * 256 / 2)));
    free(khoiLon);
  }
  // malloc thường: khối lớn hơn 4 KB tự được đặt vào PSRAM khi có (cấu hình của gói esp32)
  void *m = malloc(100 * 1024);
  Serial.printf("malloc(100 KB) nam o %s\n", esp_ptr_external_ram(m) ? "PSRAM" : "bo nho trong");
  free(m);
  inBoNho();
}

void loop() {}

6. Kết quả mong đợi

Hai khung Serial Monitor: ESP32 DevKit V1 bộ nhớ trong còn khoảng 293 KB, khối lớn nhất khoảng 110 KB, không có PSRAM, cấp phát 200 KB thất bại; ESP32-CAM có PSRAM tổng khoảng 4 MB, đo tốc độ, khối 2 MB ghi và đọc lại đúng, malloc 100 KB nằm ở PSRAM
Cùng chương trình trên ESP32 DevKit V1 (không PSRAM) và ESP32-CAM (PSRAM 4 MB); số liệu ví dụ.

Trên DevKit V1, cấp phát 200 KB thất bại dù tổng bộ nhớ trống gần 300 KB: SRAM trong chip được chia thành nhiều vùng, không vùng nào đủ lớn. Đây là lý do các dự án camera, màn hình lớn cần module có PSRAM. Với các dự án cảm biến, điều khiển, gửi dữ liệu thông thường, ESP32 DevKit V1 không có PSRAM là đủ dùng.

7. Xử lý lỗi thường gặp

Hiện tượngNguyên nhân thường gặpCách xử lý
psramFound() trả về false trên bo có PSRAMChọn sai bo; chưa bật PSRAM; chọn sai QSPI/OPI trên ESP32-S3Chọn đúng bo, bật PSRAM đúng loại
ESP32-CAM báo "Camera init failed", thiếu bộ nhớKhông có PSRAM hoặc chưa bật, khung ảnh quá lớnChọn bo AI Thinker ESP32-CAM; giảm độ phân giải
Cấp phát khối lớn thất bại dù heap còn nhiềuBộ nhớ bị chia nhỏCấp phát khối lớn ngay đầu chương trình; dùng PSRAM
Chương trình chậm hẳn khi chuyển dữ liệu sang PSRAMVòng lặp tính toán trên dữ liệu ở PSRAMChép phần đang xử lý vào SRAM rồi tính
UART2 hoặc GPIO16, 17 không hoạt động trên WROVERHai chân này nối với PSRAMDùng chân khác cho UART2
Khởi động lại khi ngắt đọc dữ liệuDữ liệu của ngắt nằm trong PSRAMĐể dữ liệu dùng trong ngắt ở SRAM

8. Xem thêm

  • ESP32 DevKit V1 30 chân và 38 chân: module WROOM-32, không PSRAM, đủ cho phần lớn dự án cảm biến, điều khiển.
  • Bài ESP32-CAM trong loạt module phổ biến: bo có PSRAM phổ biến nhất.
  • Bài bảng phân vùng flash và bài so sánh ESP32, S3, C3, C6 trong cùng loạt ESP32 nâng cao.

Tin tức liên quan

Mạch điều khiển bay của flycam: IMU, vòng PID, bộ lọc rung, chế độ bay, Betaflight, ArduPilot
Mạch điều khiển bay của flycam: IMU, vòng PID, bộ lọc rung, chế độ bay, Betaflight, ArduPilot

Mạch điều khiển bay của flycam: MCU, IMU, cảm biến, vòng điều khiển nhiều tầng, bộ trộn động cơ, PID, bộ lọc rung, các chế độ bay, Betaflight, INAV, ArduPilot, PX4 và khả năng dùng ESP32.

Hiệu chuẩn áp kế lò xo và áp kế hiện số: trình tự A, B, C, sai số, độ trễ và độ không đảm bảo
Hiệu chuẩn áp kế lò xo và áp kế hiện số: trình tự A, B, C, sai số, độ trễ và độ không đảm bảo

Hiệu chuẩn áp kế lò xo và áp kế hiện số theo ĐLVN 76:2001, EURAMET cg-17: ba trình tự đo, cách tính sai số, độ trễ, độ lặp lại, độ không đảm bảo và ví dụ áp kế 0–10 bar cấp 0,6.

Arduino là gì: bo mạch, Arduino IDE, code Arduino, setup() và loop(), Uno và Nano khác nhau
Arduino là gì: bo mạch, Arduino IDE, code Arduino, setup() và loop(), Uno và Nano khác nhau

Arduino là gì: bo mạch, Arduino IDE, lõi và thư viện; code Arduino là C++; setup() và loop() được gọi từ đâu; chuyện gì xảy ra khi bấm Upload; so sánh Uno, Nano, Mega và giới hạn.

Các nguồn sai số khi đo áp suất: cột chất lỏng, dao động, nhiệt độ, trôi và ngân sách sai số
Các nguồn sai số khi đo áp suất: cột chất lỏng, dao động, nhiệt độ, trôi và ngân sách sai số

Vì sao đo áp suất tại hiện trường sai hơn lúc hiệu chuẩn: điểm lấy áp, cột chất lỏng trong ống dẫn, dao động và giảm chấn, nhiệt độ, trôi, lắp đặt; ví dụ ngân sách sai số và danh sách kiểm tra.

Sơ đồ chân USB Type-C (pinout): cổng 24 chân, 16 chân, 6 chân
Sơ đồ chân USB Type-C (pinout): cổng 24 chân, 16 chân, 6 chân

Sơ đồ chân (pinout) cổng USB Type-C loại 24 chân, 16 chân và 6 chân: bảng tra VBUS, GND, CC1, CC2, D+, D−, SBU, TX RX, lý do cắm được hai chiều và cách các chân gom thành jack 2, 4, 6 dây.

Điện trở: đọc mã vạch màu, mã SMD, sai số, công suất và cách chọn
Điện trở: đọc mã vạch màu, mã SMD, sai số, công suất và cách chọn

Điện trở dùng để làm gì, các loại điện trở, cách đọc mã vạch màu 4 vòng và 5 vòng, đọc mã điện trở dán SMD, dãy giá trị chuẩn E12, sai số, công suất và các bước chọn điện trở cho mạch.

Xúc xắc điện tử với ESP32 và OLED: thí nghiệm xác suất cho học sinh
Xúc xắc điện tử với ESP32 và OLED: thí nghiệm xác suất cho học sinh

Làm xúc xắc điện tử bằng ESP32 và màn hình OLED kèm 4 nút: tung một lần, tung nhanh 60 lần, vẽ biểu đồ; xác suất lý thuyết, thực nghiệm và luật số lớn cho học sinh.

esptool trên Windows: sao lưu firmware ESP32 ra file .bin, xóa flash và nạp lại khi bị lỗi
esptool trên Windows: sao lưu firmware ESP32 ra file .bin, xóa flash và nạp lại khi bị lỗi

Dùng esptool trên Windows cho ESP32: kiểm tra chip, sao lưu toàn bộ firmware ra file .bin, xóa flash, nạp lại file .bin ở đúng địa chỉ và bảng tra lỗi thường gặp.


Bình luận
Đã thêm vào giỏ hàng