Chuẩn giao tiếp SPI – Hướng dẫn sử dụng giao tiếp SPI trên STM32F4

Khi bạn kết nối giữa một vi điều khiển với một hay nhiều ngoại vi khác, chẳng hạn như một cảm biến gia tốc, một LCD, hay bất cứ module nào khác, liệu bạn có thắc mắc về cách thức các Module kết nối và truyền nhận dữ liệu với nhau như thế nào hay bằng cách nào không?

Sẽ có hai cách để MCU trao đổi dữ liệu với các thiết bị bên ngoài, đó là truyền dữ liệu nối tiếp và song song. Bạn có thể tìm hiểu rõ hơn trong bài viết tại đây.

Đối với kênh truyền nối tiếp, một số giao thức chúng ta thường sử dụng: SPI (Serial Peripheral Interface), I2C (Inter-Integrated Circuit) và UART (Universal Asynchronous Receiver/ Transmitter).

Bài viết này sẽ giúp các bạn hiểu rõ về chuẩn giao tiếp SPI và hướng dẫn thực hành trên vi điều khiển STM32.

A. TỔNG QUAN VỀ GIAO TIẾP SPI.

  • Chuẩn giao tiếp SPI được hãng Motorola đề xuất, ra đời vào giữa những năm 1980 để giải quyết về nhu cầu về việc giao tiếp giữa các thiết bị với thông lượng nhanh hơn giao thức I2C hiện có.

  • SPI (Serial Peripheral Interface) – Hay còn được gọi là Four wire – Chuẩn giao tiếp 4 dây. Kênh truyền nối tiếp song công (full duplex – có 2 đường truyền dữ liệu riêng biệt, một đường truyền và một đường nhận, cho phép dữ liệu truyền nhận theo 2 hướng cùng lúc).
  • SPI là một giao thức kiểu Master – Slave. Master là thiết bị điều khiển (thường là vi điều khiển), còn Slave (thường là các cảm biến, màn hình LCD hoặc chip nhớ) sẽ nhận lệnh từ Master.
  • Một hệ thống SPI chỉ gồm một thiết bị Master, nhưng lại có thể có một hoặc nhiều thiết bị Slave.
  • Khối SPI của thiết bị Master sẽ kết nối với khối SPI của thiết bị Slave qua 4 chân tín hiệu:

MOSI (Master Output/Slave Input) – Master gửi dữ liệu đến Slave.

MISO (Master Input/Slave Output) – Slave gửi dữ liệu cho Master.

SCLK (Serial Clock) – xung giữ nhịp Clock.

SS/CS (Slave Select/Chip Select) – Master lựa chọn Slave để giao tiếp. Cụ thể trong trường hợp hệ thống gồm một Master giao tiếp với nhiều Slave, Master điều khiển tín hiệu mức logic trên chân SS/CS của Slave nào xuống mức LOW (mức 0), nghĩa là Master đang giao tiếp với Slave đó.

B. CƠ CHẾ HOẠT ĐỘNG.

Nguồn: electrosome.com

 

 

 

 

 

 

Mỗi thiết bị Master và Slave đều có một thanh ghi dịch 8 bits (Shift Register), một bộ tạo xung nhịp (Clock Generator).

Khi Master truyền dữ liệu, Master truyền đi 8 bits dữ liệu vào thanh ghi dịch của nó, sau đó 8 bits dữ liệu được truyền theo đường tín hiệu MOSI sang thiết bị Slave. Tương tự khi Slave truyền dữ liệu, các bits trên thanh ghi dịch của Slave truyền đến Master thông qua đường tín hiệu MISO.

Bằng cách này, dữ liệu của hai thanh ghi sẽ được trao đổi với nhau. Việc đọc và ghi dữ liệu vào Slave diễn ra cùng một lúc nên tốc độ trao đổi dữ liệu diễn ra rất nhanh. Do đó, giao thức SPI là một giao thức rất có hiệu quả.

C. ƯU NHƯỢC ĐIỂM CỦA CHUẨN GIAO TIẾP SPI.

Ưu điểm:

  • Dữ liệu được truyền liên tục và không gián đoạn, vì không có bit Start và bit Stop.
  • Hệ thống định địa chỉ đơn giản.
  • Dây MOSI và MISO riêng biệt nên có thể đồng thời nhận và truyền dữ liệu.
  • Tốc độ truyền dữ liệu nhanh và tiết kiệm năng lượng.

Nhược điểm:

  • Sử dụng nhiều dây để kết nối với ngoại vi.
  • Không có dấu hiệu nhận biết dữ liệu đã được truyền thành công.
  • Không có hình thức kiểm tra lỗi (như bit chẵn lẻ của UART).
  • Khoảng cách các thiết bị truyền nhận bằng SPI rất ngắn.

D. SPI TRONG STM32F4.

1. Sơ đồ khối SPI trong STM32F4

STM32F4 cung cấp 5 khối SPI (SPI1, SPI2, SPI3, SPI4, SPI5) để người dùng kết nối với các ngoại vi giao tiếp bằng SPI.

Theo như lý thuyết, khối SPI trên STM32F4 gồm:

  • 4 chân để kết nối với các thiết bị bên ngoài MOSI, MISO, SCK và NSS.
  • Thanh ghi dịch – Shift Register để thực hiện phép dịch trong quá trình truyền và nhận dữ liệu giữa Master và Slave.
  • Các thanh ghi SPI_CR1, SPI_CR2 và SPI_SR cho phép cấu hình và trạng thái.
  • Có thể làm việc với SPI qua 3 chế độ: Polling, Interrupt và DMA. Các bạn có thể tham khảo về 3 chế độ này qua bài viết Các kỹ thuật thiết kế luồng chương trình.

2. Một số thanh ghi quan trọng của khối SPI trong STM32F4.

2.1. SPI Control Register 1 (SPI_CR1)

Thanh ghi có các chức năng chính:

– Thiết lập, cấu hình hay vô hiệu các chế độ nhận dữ liệu.

– Cấu hình kích thước khung dữ liệu.

2.2. SPI status register (SPI_SR)

Thanh ghi có chức năng chính:

– Thông báo trạng thái SPI có đang giao tiếp hay không.

– Cờ báo các lỗi xảy ra trong quá trình truyền nhận dữ liệu.

2.3. SPI data register (SPI_DR)

Thanh ghi dữ liệu 16 bits chia làm hai bộ đệm: bộ đệm chứa dữ liệu đã nhận được và bộ đệm chứa dữ liệu sẽ được truyền đi (Rx_Buffer).

E. THỰC HÀNH SPI TRÊN STM32F411 DISCOVERY

Bài toán: Sử dụng một Master truyền dữ liệu đến Slave. STM32F411 DISCOVERY có 5 khối SPI, chúng ta sẽ sử dụng bộ SPI1 làm chức năng Master và bộ SPI2 làm chức năng Slave.

Bước 1: Khởi động phần mềm STM32CubeIDE. Kích chọn dòng STM32F411VETx và chọn “Next”.

Bước 2: Tại mục System Core, chọn SYS và chọn Serial Wire để cấu hình chân nạp code.

Bước 3: Cấu hình SPI1 (Master) ở mục Connectivity.

Bước 4: Cấu hình SPI2 (Slave) ở mục Connectivity.

Bước 5: Cấu hình nhận ngắt cho chân nhận dữ liệu tại mục NVIC. Bài toán yêu cầu Slave (SPI2) nhận dữ liệu được truyền đi từ Master (SPI1), nên chúng ta sẽ cấu hình nhận ngắt cho SPI2.

Bước 6: Tại mục System Core, chọn GPIO và cấu hình chân PA4 là chân CS (chân Output) để Master điều khiển chọn thiết bị Slave trong trường hợp có nhiều Slave.

Bước 7: Sinh Code.

Bước 8: Kết nối dây giữa Master và Slave trên STM32F411 DISCOVERY

Bước 9: Lập trình bằng phần mềm STM32Cube IDE.

– Thêm thư viện “string.h”.

– Khai báo mảng Tx_Master chứa dữ liệu mà Master muốn truyền đi “Master2Slave\r\n”.

– Khai báo mảng Rx_Slave có giá trị khởi tạo bằng 0, để lưu trữ 1 ký tự dữ liệu khi ngắt nhận xảy ra tại Slave.

– Khai báo mảng Rx_buffer_Slave để hiển thị dữ liệu mà Slave nhận được từ Master.

– Tại hàm main(), trước hàm while(1), khai báo việc ngắt nhận tín hiệu bằng hàm:

  • HAL_SPI_Receive_IT(&hspi2, Rx_Slave, 1);

Với ý nghĩa các thông số:

         + &hspi2: SPI2 đang sử dụng để nhận dữ liệu.

         + Rx_Slave: Mảng nhận dữ liệu.

         + 1 là số ký tự nhận sau mỗi lần xảy ra ngắt.

  • HAL_SPI_Transmit(&hspi1, Tx_Master, strlen(Tx_Master), 1000);

Với ý nghĩa các thông số:

         + &hspi1: SPI1 đang sử dụng để truyền dữ liệu.

         + Tx_Master: Mảng chứa dữ liệu sẽ được truyền đi.

         + strlen(Tx_Master): Số ký tự của mảng chứa dữ liệu.

         + 1000 là thời hạn kết thúc việc truyền dữ liệu.

Các thông số trên các bạn sẽ thay đổi tùy theo mục đích của người viết và yêu cầu của bài toán đặt ra.

– Gọi hàm ngắt nhận dữ liệu SPI:

– Trong STM32F411 DISCOVERY có đến 5 bộ SPI, nên trước tiên, ta kiểm tra thử tín hiệu ngắt xảy ra có phải trên SPI2 không (Master truyền tín hiệu đến Slave dẫn đến việc Slave xảy ra ngắt).

– Gọi lại hàm ngắt nhận HAL_SPI_Receive_IT(&hspi2, Rx_Slave, 1); để tiếp tục nhận dữ liệu. Khi ngắt nhận xảy ra, chỉ phần tử đầu tiên của mảng Rx_Slave sẽ nhận được các ký tự liên tục. Mảng Rx_buffer_Slave có chức năng lưu trữ toàn bộ dữ liệu nhận được.

– Khi nhận được ký tự \n kết thúc một chuỗi truyền, mảng Rx_buffer_Slave sẽ nhận các chuỗi dữ liệu truyền từ phần tử đầu tiên.

Bước 10: Chọn Run > Debug As > STM32 MCU C/C++ Application để nạp chương trình và hiển thị giao diện Debug.

– Kích chọn bảng Live Expressions.

– Sau đó kích vào Add new expression, gõ vào tên biến, mảng để theo dõi sự thay đổi giá trị trong quá trình Debug. Chúng ta đang cần theo dõi mảng nhận dữ liệu của Slave, nên sẽ điền vào tên mảng Rx_buffer_Slave.

– Nhấn Resume (F8) để chương trình thực thi. Bạn có thể dùng Step Into, Step Over, and Step Return để nhảy từng lệnh, nhảy vào hay nhảy ra từng hàm.

– Quan sát các phần tử trong mảng.

KẾT QUẢ THU ĐƯỢC.

Tìm hiểu thêm:
Fanpage Cộng đồng Kỹ thuật TAPIT: TAPIT – Learning, Research and Sharing Community

Chúc bạn thực hiện thành công và học được nhiều điều mới nhé!

– Nhien Nhien –