ESP32 SPI – Cách cấu hình, sử dụng giao thức truyền thông SPI
Đây là bài hướng dẫn chi tiết về chủ đề ESP32 SPI, trong đó chúng ta sẽ cùng tìm hiểu về các chân SPI trên ESP32, cách kết nối các thiết bị SPI, xác định các chân SPI tùy chỉnh hoặc cách dùng cùng lúc nhiều ngoại vi SPI khác nhau,… Cùng theo dõi nhé!
Giới thiệu ESP32 SPI
SPI là viết tắt của cụm từ Serial Peripheral Interface, đây là giao thức dữ liệu nối tiếp đồng bộ, giúp các vi điều khiển như ESP32 có thể giao tiếp với những thiết bị module, cảm biến ngoại vi bên ngoài.
Trong giao tiếp ESP32 SPI, luôn có một bộ điều khiển (master) điều khiển các thiết bị ngoại vi (slave). Việc gửi và nhận dữ liệu giữa master và slave có thể triển khai đồng thời, nghĩa là master gửi dữ liệu đến slave và cùng lúc đó slave vẫn có thể gửi dữ liệu đến master.
Trong mô hình này, chúng ta chỉ cần 1 Master (cũng chính là vi điều khiển ESP32), nhưng chúng ta có thể có nhiều Slave khác nhau.
Các Slave có thể là màn hình hiển thị thông tin, cảm biến, thẻ nhớ, thiết bị điều khiển như đèn, quạt,… hoặc thậm chí là một vi điều khiển ESP32 khác.
Điều này đồng nghĩa là bạn có thể kết nối một ESP32 với nhiều cảm biến, nhưng không thể kết nối một cảm biến với nhiều ESP32 cùng một lúc.
Giới thiệu giao diện SPI
Để liên lạc qua giao thức SPI, bạn cần 4 dòng sau:
- MISO: Master In Slave Out
- MOSI: Master In Slave In
- SCK: Serial Clock (Đồng hồ nối tiếp)
- CS / SS: Chip Select (dùng để chọn thiết bị khi kết nối nhiều ngoại vi trên cùng một bus SPI)
Trên thiết bị chỉ dùng làm Slave như cảm biến, màn hình,… bạn có thể thấy những khái niệm khác:
- MISO có thể được ghi chú là SDO (Serial Data Out – Đầu ra dữ liệu nối tiếp)
- MOSI có thể được ghi chú là SDI (Serial Data In – Đầu vào dữ liệu nối tiếp)
Các chân ngoại vi SPI trên ESP32
ESP32 thường tích hợp sẵn 4 cổng ngoại vi SPI, bao gồm SPI 0, SPI 1, SPI 2 (thường gọi là HSPI) và SPI 3 (VSPI).
Trong đó, cổng SPI 0 và SPI 1 thường dùng để liên lạc nội bộ với bộ nhớ Flash tích hợp, chúng ta không nên dùng các thiết bị này cho các tác vụ khác.
Khi dùng để liên lạc với các thiết bị ngoại vi, bạn có thể dùng cả HSPI và VSPI. Mỗi một cổng đều có các tín hiệu bus riêng biệt, và mỗi cổng có thể điều khiển tối đa 3 thiết bị Slave.
>> Xem thêm: ESP32 WiFi – Giao tiếp giữa 2 ESP32 với nhau
Chân SPI mặc định trên ESP32
Trên ESP32 thường gồm các chân SPI đã được gắn sẵn, thông thường đều được ánh xạ tới GPIO như sau:
SPI | MOSI | MISO | SCLK | CS |
VSPI | GPIO 23 | GPIO 19 | GPIO 18 | GPIO 5 |
HSPI | GPIO 13 | GPIO 12 | GPIO 14 | GPIO 15 |
Lưu ý quan trọng: Tùy thuộc vào mạch ESP32 đang dùng, các chân ESP32 SPI sẽ khác nhau. Do đó, bạn hãy kiểm tra sơ đồ mạch kỹ trước khi sử dụng nhé!
Cách tìm các chân ESP32 SPI mặc định trên mạch của bạn
Nếu không chắc chắn về các chân ESP32 SPI mặc định hoặc không biết cách tìm, bạn có thể dùng code sau để tìm nhé:
void setup() { // đặt code setup của bạn tại đây, để chạy 1 lần Serial.begin(115200); Serial.print("MOSI: "); Serial.println(MOSI); Serial.print("MISO: "); Serial.println(MISO); Serial.print("SCK: "); Serial.println(SCK); Serial.print("SS: "); Serial.println(SS); } void loop() { // Đặt code chính của bạn tại đây, để chạy lặp đi lặp lại liên tục }
Sau khi upload code, bạn hãy mở Serial Monitor, nhấn RST trên mạch ESP32.
Khi đó, trên màn hình Serial Monitor sẽ hiển thị các chân ESP32 SPI như hình:
Cách dùng chân ESP32 SPI tùy chỉnh
Khi làm việc với các chân ESP32 SPI, bạn có thể dùng các chân SPI tùy chỉnh dễ dàng, vì bạn có thể chọn chúng làm thông tin đối số trong các hàm tạo thư viện.
Ví dụ, dưới đây là đoạn code mẫu về cảm biến BME280, sử dụng với thư viện Adafbean_BME280:
#include <Wire.h> #include <Adafruit_Sensor.h> #include <Adafruit_BME280.h> #include <SPI.h> #define BME_SCK 25 #define BME_MISO 32 #define BME_MOSI 26 #define BME_CS 33 #define SEALEVELPRESSURE_HPA (1013.25) //Adafruit_BME280 bme; // I2C //Adafruit_BME280 bme(BME_CS); // hardware SPI Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); // software SPI unsigned long delayTime; void setup() { Serial.begin(9600); Serial.println(F("BME280 test")); bool status; // Cài đặt mặc định // (you can also pass in a Wire library object like &Wire2) status = bme.begin(); if (!status) { Serial.println("Could not find a valid BME280 sensor, check wiring!"); while (1); } Serial.println("-- Default Test --"); delayTime = 1000; Serial.println(); } void loop() { printValues(); delay(delayTime); } void printValues() { Serial.print("Temperature = "); Serial.print(bme.readTemperature()); Serial.println(" *C"); // Chuyển nhiệt độ về độ F /*Serial.print("Temperature = "); Serial.print(1.8 * bme.readTemperature() + 32); Serial.println(" *F");*/ Serial.print("Pressure = "); Serial.print(bme.readPressure() / 100.0F); Serial.println(" hPa"); Serial.print("Approx. Altitude = "); Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA)); Serial.println(" m"); Serial.print("Humidity = "); Serial.print(bme.readHumidity()); Serial.println(" %"); Serial.println(); }
Bạn có thể dễ dàng đổi các chân ESP32 SPI tùy chỉnh của mình, bằng hàm tạo thư viện bên dưới:
Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK);
Trong code trên, mình đã dùng các chân SPI sau và chương trình vẫn chạy tốt:
#define BME_SCK 25
#define BME_MISO 32
#define BME_MOSI 26
#define BME_CS 33
Nếu bạn không dùng thư viện, hoặc thư viện bạn dùng không chấp nhận các chân ESP32 SPI đã tạo trong hàm trên, bạn cần phải tự tạo bus SPI. Khi đó, bạn cần phải gọi hàm SPI.begin() trong setup() và gán chân SPI làm thông tin sối số:
SPI.begin(SCK, MISO, MOSI, SS);
Cách dùng ESP32 với nhiều ngoại vi SPI
Như đã giới thiệu, bạn có thể dùng 2 bus SPI khác nhau trên ESP32, mỗi bus có thể kết nối tối đa 3 ngoại vi khác nhau. Nếu muốn dùng nhiều hơn, bạn cần dùng đến bộ ghép kênh SPI.
Cụ thể, để kết nối nhiều ngoại vi SPI cùng bus, nhưng có chân CS khác nhau, bạn có thể thoải mái dùng một bus SPI:
Để chọn ngoại vi cần liên lạc, bạn cần cấu hình CS của ngoại vi đó thành LOW. Ví dụ, code dưới đây giúp bạn điều khiển ngoại vi 1:
digitalWrite(CS_1, LOW);
Sau đó, nếu muốn kết nối ngoại vi 2, bạn cần tắt liên lạc với ngoại vi 1 bằng cách cấu hình CS của ngoại vi 1 thành HIGH, rồi cấu hình CS của ngoại vi 2 thành LOW:
digitalWrite(CS_1, HIGH); digitalWrite(CS_2, LOW);