Lập trình STM32 sử dụng giao thức SPI sẽ giúp chúng ta hiểu được.
- Nguyên lý truyền nhận của chuẩn SPI
- Cách thiết lập giao tiếp SPI trên STM32 Cube MX
- Lập trình với giao thức SPI
Bài số 12 trong serie Học lập trình STM32 từ A tới Z
Giao thức SPI là gì
Cơ bản về giao thức SPI
SPI Serial Peripheral Interface là một chuẩn truyền thông nối tiếp đồng bộ dùng để chuyền dữ liệu ở chế độ song công toàn phần (full duplex).
Giao thức SPI thường được sử dụng On Board hoặc các đường tín hiệu ngắn. Tốc độ của SPI khá cao thường phụ thuộc vào tốc độ xung truyền vào bộ SPI
Giao thức SPI là dao thức dạng Master –Slave trong đó Master giữ quyền điều khiển xung Clock và chọn Slave nào giao tiếp với mình.
Bus SPI bao gồm 4 đường tín hiệu:
- MOSI (Master Out Slave In): Đường truyền tín hiệu từ Master đến Slave
- MISO (Master In Slave Out): Đường truyền tín hiệu từ Slave đến Master
- CLK (Serial Clock): Đường phát xung đồng hồ được điều khiển bởi Master
- CS (Chip Select): Đường chọn chip giao tiếp với Master, được kéo xuống 0 khi được chọn
Nguyên lý hoạt động của giao thức SPI
Nguyên lý hoạt động như sau:
- Khi muốn truyền nhận dữ liệu tới các Slave, đầu tiên Master kéo đường CS kết nối từ Master tới Slave đó xuống 0.
- Gửi xung Clock, tương ứng với mỗi Clock sẽ gửi DATA trên chân MOSI tại thời điểm Clock ở mức cao (hoặc thấp tùy người lập trình)
- Slave cũng có thể gửi ngược lại DATA tại chân MISO tới Master
- Sự truyền nhận dữ liệu là liên tục nên SPI thường có tốc độ rất cao
Chế độ hoạt động của giao thức SPI
Có 4 chế độ hoạt động của SPI dựa trên hai Bit CPOL và CPHA
CPOL: Clock Polarity xác định mức tín hiệu tại Clock lúc nhàn rồi (Idle), Nếu CPOL = 0, lúc không gửi dữ liệu (nhàn rỗi) Clock sẽ ở mức thấp CPOL = 1 sẽ ngược lại
CPHA: Phase Clock xác định quá trình truyền dữ liệu tại cạnh lên (CPHA = 0) hay cạnh xuống CPHA = 1
Mode 0:
- Mode 0: xảy ra khi Clock Polarity và Clock Phase là 0 (CPOL = 0 và CPHA = 0). Trong Mode 0, truyền dữ liệu xảy ra trong khi cạnh lên của xung đồng hồ.
- Mode 1: xảy ra khi Clock Polarity là 0 và Clock Phase là 1 (CPOL = 0 và CPHA = 1). Trong mode 1, truyền dữ liệu xảy ra trong khi cạnh xuống của xung đồng hồ.
- Mode 2: xảy ra khi Clock Polarity là 1 và Clock Phase là 0 (CPOL = 1 và CPHA = 0). Trong mode 2, truyền dữ liệu xảy ra trong khi cạnh lên của xung đồng hồ.
- Mode 3: xảy ra khi Clock Polarity là 1 và Clock Phase là 1 (CPOL = 1 và CPHA = 1). Trong mode 3, truyền dữ liệu xảy ra trong khi cạnh lên của xung đồng hồ.
Các kiểu cấu hình giao thức SPI
Có 2 loại cấu hình SPI
- Cấu hình Master và các Slave độc lập (Independent Slave Configuration)
- Cấu hình Daisy Chain (Daisy Chain Configuration).
Trong cấu hình Master và các Slave độc lập, Master đã dành riêng các đường Slave Select cho tất cả các Slave và mỗi Slave có thể được chọn riêng lẻ. Tất cả tín hiệu đồng hồ của các Slave được kết nối với chung với SCK của Master.
Trong cấu hình Daisy Chain, chỉ có một đường Slave Select được kết nối với tất cả các Slave. MOSI của Master được kết nối với MOSI của Slave 1. MISO của Slave 1 được kết nối với MOSI của Slave 2 và v.v.. MISO của Slave cuối cùng được kết nối với MISO của Master.
Tuy nhiên, cầu hình Daisy Chain không phải lúc nào cũng áp dụng được cho tất cả các thiết bị Slave. Do đó, ta cần phải tham khảo datasheet trước khi tiến hành kết nối.
Cấu hình STM32 SPI trong Cube MX
Trong bài này chúng ta sẽ sử dụng Bộ SPI1 ở chế độ Master và SPI2 ở chế độ Slave. Truyền nhận ở chế độ ngắt và DMA
Trong CubeMX cấu hình như sau. SYS debug: Serial Wire
SPI1Chế độ Full Duplex, Hard ware NSS: Disable, chân chọn chip ta sẽ sử dụng chân GPIO khác để dễ dàng điều khiển nếu sử dụng nhiều Slave
Parameter cấu hình như trong hình
SPI2 chế độ Full-Duplex Slave, Hard ware NSS: Enable Bật chân chọn chip cho SPI2
Parameter cấu hình giống SPI1
Chọn thêm chân PA4 làm chân Chip Select cho SPI1
Bật ngắt cho SPI1 và SPI2
Chọn toolchain đặt tên và Gen code
Lập trình giao thức SPI trong Keil C
Trong Keil C thêm các biến u8_SPI1_TxBuff để làm bộ đệm truyền SPI1, u8_SPI2_RxBuff làm bộ đệm nhận từ SPI2, biến Count để xem sự thay đổi của mảng truyền đi
Tìm hàm HAL_SPI_RxCpltCallback copy và dán vào trên hàm main(), bên trong chúng ta kiểm tra có phải ngắt SPI2 không, sau đó bật ngắt nhận RX để nhận thêm chuỗi
Trước while(1) chúng ta truyền dữ liệu đầu tiên lên SPI1 bằng hàm:
HAL_SPI_Transmit(&hspi1,u8_SPI1_TxBuff,sizeofBuff,100);
, sau đó bận nhận SPI2 bằng ngắt bằng hàm:
HAL_SPI_Receive_IT(&hspi2, u8_SPI2_RxBuff,sizeofBuff);
Trong While (1) copy dữ liệu vào SPI1_TxBuff
Cho chân CS xuống 0 để chọn chip, gửi dữ liệu, cho chân CS về 1 để kết thúc quá trình gửi.
Tăng biến Count và delay 1s
Nhấn Build và nạp chương trình. Kết nối phần cứng như sau:
Các chân MOSI MISO CLK nối với nhau, chân CS của SPI1 nối với chân NSS của SPI2
Vào chế độ debug, chuột phải vào 2 Buffer truyền và nhận, chọn Add to watch 1
Nhấn Run và xem kết quả
Giá trị truyền và nhận đã giống nhau
Nhận dữ liệu bằng DMA
Trong SPI2 ta chuyển sang tab DMA, nhấn Add
DMA Request là SPI2_RX, Mode là Circular (ta chỉ cần gọi hàm nhận DMA 1 lần , không cần phải gọi lại trong ngắt )
Data Width là Byte
Trong Keil C comment hoặc xóa phần xử lý ngắt (không dùng tới hoặc dùng để làm tác vụ nào đó khi truyền xong)
Phần trước While (1) thay hàm Recive_IT bằng Recive_DMA
Nhấn Build và nạp chương trình, chạy debug để xem kết quá
Kết luận
Giao thức SPI là một trong những giao thức cơ bản và phổ biến nhất trong lập trình vi điều khiển. Với SPI bạn có thể sử dụng để giao tiếp với nhiều loại device khác nhau như: TFT LCD, RFID, Camera ….
Giao thức SPI có thể được bắt gặp tại bất cứ một sản phẩm nhúng nào, hãy rèn luyện và làm chủ giao thức này nhé.
anh có thể làm về giao tiếp stm32 với thẻ sd card qua spi đc ko ạ.
Sẽ có bài đấy sau tết nhé
ad làm bài giao tiếp stm32 với thẻ sd card giúp em với ad ơi
Em làm theo không biết sao chỉ có các ký tự từ 0 đến 12 là bên truyền và nhận giống nhau, còn từ 13 đến 19 thì khác nhau?
Nếu độ dài chuỗi mỗi lần truyền khác nhau thì nên xóa buffer trc khi nhận dữ liệu mới nhé
anh ơi em làm theo nhưng trong phần u8_SPI2_Rxbuff chỉ có kí tự 0 là chữ ‘g’ còn lại từ 1-19 nó k hiện gì ạ
Em thử down phần example anh viết rồi chạy thử xem