บทความนี้กล่าวถึงฟังก์ชันการใช้งานของบัส SPI ของเฟรมเวิร์ก Arduino เพื่อใช้กับ STM32F030F4P6, STM32F103C8, STM32F401, esp8266 และ esp32 ซึ่งการทำงานของบัสนี้ต้องการสายสัญญาณสำหรับสื่อสารระหว่างกันอย่างน้อย 3 เส้น คือ SCLK, MISO และ MOSI สำหรับทำหน้าที่ส่งสัญญาณนาฬิการะหว่างกันของผู้ส่งและผู้รับ ทำหน้าที่รับข้อมูลจากผู้ส่ง และใช้สำหรับส่งข้อมูลไปให้ผู้รับ
จากการใช้สายสัญญาณ 3 เส้นจะพบว่า สามารถส่งและรับข้อมูลพร้อมกันได้ ซึ่งแตกต่างกับการสื่อสารแบบบัส I2C ที่ใช้สาย SDA เพียงเส้นเดียวในการสื่อสาร ดังนั้น อาจจะกล่าวได้ว่า ด้วยความเร็วในการสื่อสารที่เท่ากัน บัสแบบ SPI จะรับและส่งข้อมูลได้โดยไม่ต้องรอสายสัญญาณว่าง ขณะที่ I2C จะต้องรอให้ว่างก่อน ด้วยหลักคิดนี้จึงทำให้ SPI รับ/ส่งข้อมูลได้รวดเร็วกว่า
นอกจากนี้ SPI ใช้วิธีการเลือกปลายทางที่ต้องการสื่อสารด้วยการสั่งให้ปลายทางรู้ด้วยการส่งสัญญาณไปที่ขา SS ของอุปกรณ์ปลายทาง ดังนั้น เมื่อเชื่อมต่อกับหลายอุปกรณ์จึงส่งผลให้ SPI ต้องการจำนวนขาในการทำงานที่มากกว่า ขณะที่ I2C ใช้การระบุคำแหย่งของอุปกรณ์ในการสื่อสารระหว่างกัน โดยยังคงใช้สาย SDA เพียงเส้นเดียวทำให้ประหยัดขาได้มากกว่า

ขาสำหรับบัส SPI
รายการขาของบัส SPI ที่รองรับด้วยฮาร์ดแวร์ของไมโครคอนโทรลเลอร์ที่ทางทีมงานเราคลุกคลีใช้งาน เป็นดังนี้
STM32F030F4P6
จากภาพที่ 1 จะพบว่าชิพ Cortex-M0 ตัวนี้มี SPI ให้ใช้งาน 1 ชุดผ่านทางขา PA5, PA6 และ PA7 สำหรับ SCK, MISO และ MOSI ตามลำดับ ส่วนขา SS นั้นให้เลือก GPIO_Output ที่ต้องการจากขาที่เหลือ

STM32F103C8
สำหรับ STM32F103C8 มีบัส SPI ให้ใช้งานจำนวน 2ชุด คือ SPI1 และ SPI2 ที่ใช้ขา PA5 และ PB13 สำหรับเป็น SCK, PA6 และ PB14 สำหรับ MISO และ PA7 กับ PB15 สำหรับ MOSI ดังภาพที่ 2 โดยความเร็วสูงสุดที่รองรับคือ 18Mbits/second

STM32F401CC
สำหรับ Cortex-M4 อย่าง STM32F401CC นั้นมี SPI ให้ใช้งานดังภาพที่ 3 ซึ่งการเปิดใช้ SPI3 มีผลทำให้ไม่สามารถใช้ I2C1, I2C2 และ I2C3 ได้ และจากภาพที่ 3 จะพบว่า ขา PA5, PB10 และ PB3 ทำหน้าที่ SCK ส่วนขา PA6, PB14 และ PB4 ทำหน้าที่ MISO และขา PA7, PB15 และ PB5 ทำหน้าที่ MOSI ของ SPI1, SPI2 และ SPI3 ตามลำดับ

ESP8266
ไมโครคอนโทรลเลอร์ esp8266 มีบัส SPI แบบฮาร์ดแวร์ให้ใช้งาน 1 ชุด โดยทำงานด้วยความเร็วสูงสุด 80MHz ซึ่งใช้ขาตามหน้าต่อไปนี้
- SCLK ใช้ขา GPIO14
- MISO ใช้ขา GPIO12
- MOSI ใช้งานขา GPIO13
ESP32
สำหรับชิพ esp32 มีพอร์ต SPI ให้ใช้งาน 3 ชุด แต่ใช้งานได้โดยผู้ใช้จำนวน 2 ชุดดังนี้
- SPI id=1 หรือ HSPI
- SCLK ใช้ขา GPIO14
- MISO ใช้ขา GPIO12
- MOSI ใช้ชา GPIO13
- SPI id=2 หรือ VSPI
- SCLK ใช้ขา GPIO18
- MISO ใช้ขา GPIO23
- MOSI ใช้ขา GPIO19
คำสั่ง
การเรียกใช้งานคลาส SPI ต้องนำเข้าไฟล์ส่วนหัวดังนี้
#include <SPI.h>
การสั่งเริ่มต้นและสิ้นสุดการทำงานของคลาส SPI ใช้คำสั่ง begin และ end ดังรูปแบบต่อไปนี้
SPI.begin()
SPI.end()
กรณีที่ต้องการตั้งค่าการทำงานของการสื่อสารผ่านบัส SPI ให้ใช้การตั้งค่าจากคำสั่ง SPISettings( ) เพื่อสร้างวัตถุเก็บค่าการตั้งค่า ตามรูปแบบดังนี้ โดยความเร็วในการส่งขึ้นอยู่กับความเร็วสูงสุดที่ไมโครคอนโทรลเลอร์แต่ละชนิดกำหนด และมีปัจจัยเรื่องคุณภาพของสายสัญญาณ/ความยาวของสายสัญญาณที่ส่งผลต่อความเร็วด้วยเช่นกัน การเรียงลำดับข้อมูลเป็นการกำหนดวิธีการส่งข้อมูลบิตว่าเป็นแบบ MSBFIRST หรือ LSBFIRST สุดท้ายโหมดข้อมูลได้แก่ SPI_MODE0, SPI_MODE1, SPI_MODE2 หรือ SPI_MODE3
SPISettings( ความเร็วสูงสุดในการส่ง, การเรียงลำดับข้อมูล, โหมดข้อมูล )
คุณสมบัติของโหมดข้อมูลเป็นตามตารางต่อไปนี้ ซึ่งค่ามีผลต่อเรื่องของ Timing Diagram
SPI mode | Clock polarity (CPOL/CKP) | Clock phase (CPHA) | Clock edge (CKE/NCPJA) | Output Edge | Data Capture |
---|---|---|---|---|---|
0 | 0 | 0 | 1 | falling ช่วงเปลี่ยนจาก 1 มาเป็น 0 | Rising ช่วงเปลี่ยนจาก 0 มาเป็น 1 |
1 | 0 | 1 | 0 | Rising ช่วงเปลี่ยนจาก 0 มาเป็น 1 | falling ช่วงเปลี่ยนจาก 1 มาเป็น 0 |
2 | 1 | 0 | 1 | Rising ช่วงเปลี่ยนจาก 0 มาเป็น 1 | falling ช่วงเปลี่ยนจาก 1 มาเป็น 0 |
3 | 1 | 1 | 0 | falling ช่วงเปลี่ยนจาก 1 มาเป็น 0 | Rising ช่วงเปลี่ยนจาก 0 มาเป็น 1 |
การนำค่า SPISettings ไปใช้นั้นมักใช้ในการสั่งเริ่มต้นสื่อสารด้วยคำสั่ง beginTransaction() เพื่อนำค่าการตั้งค่านั้นมากำหนดให้บัส SPI เริ่มทำงานตามค่านั้นดังรูปแบบการใช้งานต่อไปนี้
SPI.beginTransaction( SPISettings( … ) )
การสั่งสิ้นสุดรายการสื่อสารใช้คำสั่ง endTransaction( ) ดังนี้ ซึ่งมักเรียกหลังจากสิ้นสุดการเลือกให้อุปกรณ์ปลายทางนั้นทำงาน
SPI.endTransaction()
เมื่อกำหนดค่าเริ่มต้นการสื่อสาร ขั้นตอนต่อไปคือการสั่งเพื่ออ่านค่าหรือส่งค่ากับอุปกรณ์ปลายทาง ด้วยคำสั่ง transfer( ) ด้วยหลักการส่งและรอรับข้อมูล ตามรูปแบบต่าง ๆ ต่อไปนี้ ซึ่งสามารถส่งข้อมูลขนาด 1 ไบต์หรือ 2 ไบต์ได้
ค่าที่อ่านได้ = SPI.transfer( ค่าที่ส่ง )
ค่าที่อ่านได้16 =SPI.transfer16( ค่าที่ส่ง16 )
SPI.transferr( บัฟเฟอร์, ขนาดของบัฟเฟอร์ )
นอกจากตั้งค่าของการสื่อสารผ่านทาง SPISettings() แล้ว ผู้เขียนโปรแกรมสามารถตั้งค่าจากฟังก์ชัน setBitOrder, setClockDivider และ setDataMode ได้ตามรูปแบบต่อไปนี้
SPI.setBitOrder( การเรียงลำดับข้อมูล )
SPI.setClockDivider( ค่าตัวหารสัญญาณนาฬิกา )
SPI.setDataMode( โหมดข้อมูล )
ค่าของตัวหารได้แก่
- SPI_CLOCK_DIV2
- SPI_CLOCK_DIV4
- SPI_CLOCK_DIV8
- SPI_CLOCK_DIV16
- SPI_CLOCK_DIV32
- SPI_CLOCK_DIV64
- SPI_CLOCK_DIV128
สรุป
จากบทความนี้จะพบว่าการใช้บัส SPI นั้นมีรายละเอียดของการสื่อสารมากกว่า I2C และใช้สายสัญญาณที่มากกว่า แต่สามารถรับหรือส่งข้อมูลทั้งในรูปแบบ 8 และ 16 บิต หรืออ่านข้อมูลแบบก้อนบัฟเฟอร์ได้ นอกจากนี้ สิ่งที่ต้องพึงระวังคือ การตั้งค่าสัญญาณนาฬิกาของ STM32 จะต้องกำหนดตัวหารเพื่อให้ส่วนของ ABI ทำงานด้วยความถี่ที่รองรับได้ ถ้าไม่สามารถตั้งค่าได้ในขั้นตอนของการคอนฟิกเหมือนใน STM32CubeMX/IDE ให้ผู้เขียนโปรแกรมใช้คำสั่ง setClpckDivider( ) ก่อนทำการสื่อสาร สุดท้ายขอให้สนุกกับการเขียนโปรแกรมครับ
ท่านใดต้องการพูดคุยสามารถคอมเมนท์ไว้ได้เลยครับ
แหล่งอ้างอิง
(C) 2021, โดย อ.ดนัย เจษฎาฐิติกุล/อ.จารุต บุศราทิจ
ปรับปรุงเมื่อ 2021-07-29, 2021-07-30, 2021-11-03