จากบทความเรื่อง machine.Pin ได้กล่าวถึงการใช้งานขาของไมโครคอนโทรลเลอร์เพื่อนำเข้าข้อมูลและส่งออกข้อมูลไปแล้ว และในบทความการสื่อสารผ่านบัส I2C ซึ่งใช้คลาส machine.I2C เพื่อสื่อสารระหว่างอุปกรณ์ ในคราวนี้มารู้จักคลาส machine.SPI ซึ่งเป็นการสื่อสารอีกรูปแบบหนึ่งที่ได้รับการยอมรับถึงความเร็วในการทำงาน และนิยมใช้กับจอแสดงผลกราฟิก (บทความของ esp8266/esp32) หรือช่องอ่าน SD-Card (บทความของ esp8266, esp32)

SPI

SPI หรือ Serial Peripheral Interface bus protocol เป็นรูปแบบการสื่อสารของบัสที่อาศัยการสื่อสารแบบอนุกรม โดยแบ่งการทำงานเป็นฝั่งผู้ควบคุมการสื่อสารและอุปกรณ์ที่ถูกสื่อสารผ่านทางสายสัญญาณอย่างน้อย 3 เส้น ได้แก่

  • SCK สายสัญญาณนาฬิกาที่ใช้เป็นตัวควบคุมการรับส่งข้อมูลระหว่างกัน
  • MOSI สายส่งออกข้อมูลจากผู้ควบคุมการสื่อสารไปให้อุปกรณ์ที่สื่อสาร
  • MISO สายรับข้อมูลจากอุปกรณ์ที่สื่อสารด้วย

เมื่อเปรียบเทียบกับการสื่อสารแบบ I2C จะพบว่าในระบบบัส I2C เน้นการใช้สายสัญญาณ SCL และ SDA โดย SDA ทำหน้าที่ทั้งรับและส่งข้อมูล พร้อมทั้งทุกอุปกรณ์ที่อยู่บนบัสจะมีหมายเลขประจำตัวสำหรับอ้างอิง เมื่อมีข้อมูลในสายแต่ไม่ใช่หมายเลขของตนเองจึงไม่ต้องรับหรือส่งข้อมูลออกไป แต่สำหรับ SPI ได้แยกสายสื่อสารเพื่อรับและส่งออกจากกันและการกำหนดให้ทำงานหรือไม่ทำงานมักอาศัยขาเปิด/ปิดการทำงานของอุปกรณ์ที่สื่อสารด้วยเป็นตัวกำหนด ดังนั้น อุปกรณ์แบบ SPI จึงมีจำนวนขาสำหรับการเชื่อมต่อมากกว่า 3 เส้น แต่ I2C ต้องการเพียง 2 เส้นเท่านั้น แต่ด้วยการออกแบบสายให้แยกการรับและส่งทำให้ความเร็วในการสื่อสารของ SPI จึงสูงกว่าแบบ I2C แต่ยิ่งเร็วยิ่งส่งผลเรื่องระยะของสายสัญญาณด้วยเช่นกัน

ใน Micropython รองรับการทำงานของ SPI ทั้งในแบบฮาร์ดแวร์และซอฟต์แวร์โดยแยกคลาสออกมาเป็น 2 คลาส คือ machine.SPI และ machine.SoftSPI ซึ่งโครงสร้างของคลาสทั้ง 2 เป็นดังภาพที่ 1 และ 2

ภาพที่ 1 คลาส machine.SPI
ภาพที่ 2 คลาส machine.SoftSPI

จากภาพที่ 1 และ 2 จะพบว่าทั้งสองคลาสนี้มีโครงสร้างฟังก์ชันและค่าคงที่เหมือนกัน ความแตกต่างคือ machine.SPI รองรับการทำงานด้วยฮาร์ดแวร์ และ machine.SoftSPI รองรับด้วยการทำงานของซอฟต์แวร์ ซึ่งความเร็วในการทำงานยังไม่สามารถยืนยันได้ว่าแบบใดเร็วกว่ากันในทุกกรณี แต่การที่มีฮาร์ดแวร์ใช้งานทำให้การเขียนโปรแกรมสะดวกขึ้น และเมื่อ Micropython มีให้ใช้ทั้ง 2 กรณี ทางทีมงานเลยมักเลือกว่าถ้ามีฮาร์ดแวร์ใช้ส่วนของฮาร์ดแวร์ก่อน ถ้าไม่พอใช้ก็ใช้ขาที่เหลือมาทำงานแบบซอฟต์แวร์

การเรียกใช้คลาส SPI และ SoftSPI ทำโดยการนำเข้าจาก machine ดังนี้

from machine import SPI

หรือ

from machine import SoftSPI

เมื่อนำเข้าคลาสเป็นที่เรียบร้อย ขั้นตอนถัดไปคือ การสร้างวัตถุสำหรับเป็นตัวสื่อกลางในการอ้างอิงถึงบัสสื่อสารด้วยรูปแบบการสร้างดังนี้ ซึ่งความเร็วในการสื่อสารขึ้นอยู่กับระยะของสายสัญญาณและการรองรับของอุปกรณ์ และสำหรับ SPI แบบฮาร์ดแวร์ไม่ต้องกำหนดหมายเลขขา เพียงกำหนดหมายเลขช่องสื่อสารซึ่งได้กล่าวไว้ในบทความ machine.Pin เช่น เมื่อใช้กับ esp32 จะสามารถเลือกได้เป็น 1 หรือ 2 เพื่อเลือกชุดขาของ HSPI หรือ VSPI

วัตถุ = SoftSPI( baudrate=ความเร็วในการสื่อสาร, polarity=1, phase=0, sck=machine.Pin(ขาSCK), mosi=machine.Pin(ขาMOSI), miso=machine.Pin(ขาMISO))

วัตถุ = SPI(หมายเลข, baudrate=ความเร็วในการสื่อสาร, polarity=0, phase=0)

กรณีที่ต้องการเปลี่ยนอัตราความเร็วในการสื่อสารหรือ baudrate สามารถสั่งงานด้วยคำสั่ง init() ตามรูปแบบต่อไปนี้ ซึ่งความเร็วในการสื่อสารสูงสุดที่รองรับได้ คือ 80MHz

วัตถุ.init( baudrate=ความเร็วในการสื่อสาร )

เมื่อใช้การสื่อสารเสร็จ จำเป็นต้องยกเลิกด้วยคำสั่งดังรูปแบบต่อไปนี้

วัตถุ.deinit()

คำสั่งส่งข้อมูลจำนวน n ไบต์ไปยังอุปกรณ์ปลายทางมีรูปแบบดังนี้

วัตถุ.write( ข้อมูลไบต์จำนวนnตัว )

คำสั่งอ่านข้อมูลมีรูปแบบการใช้งานดังนี้ โดยคำสั่งแรกเป็นการอ่านข้อมูลจากบัสจำนวน n ไบต์ตามที่ระบุ แต่ในรูปแบบที่ 2 แตกต่างที่กำหนดให้อ่านสูงสุด n ไบต์ และถ้าพบข้อมูลตรงกับข้อมูลปิดท้าย หรือตามที่กำหนดใน x จะหยุดการอ่านข้อมูล (แต่อ่านไม่เกิน n ไบต์)

ข้อมูลที่อ่านได้ = วัตถุ.read( n )
ข้อมูลที่อ่านได้ = วัตถุ.read( n, x )

ถ้าต้องการอ่านไปเก็บในหน่วยความจำแบบไบต์ที่จองเอาไว้ ซึ่งการจองไว้แล้วใช้ซ้ำมีความเร็วในการทำงานสูงกว่าต้องสร้างข้อมูลและจองหน่วยความจำเพื่อไปเก้บในบัฟเฟอร์หรือตัวแปรใหม่ทุกครั้งเหมือนกับในคำสั่ง read( ) โดยคำสั่งดังกล่าวคือ

วัตถุ.readinto( buf )
วัตถุ.readinto( buf, x )

จากรูปแบบคำสั่งการอ่านข้างต้นต้องดำเนินการจองหน่วยความจำไบต์ขึ้นมาก่อน โดยใช้รูปแบบการจองดังนี้

buf = bytearray( จำนวนไบต์ที่ขอจอง )

คำสั่งกลุ่มสุดท้ายคือ คำสั่งส่งข้อมูลลงไปในบัสและนำผลลัพธ์เก็บในบัฟเฟอร์ ดังรูปแบบการใช้งานต่อไปนี้

วัตถุ.write_readinto( ข้อมูลที่ส่งออก, บัฟเฟอร์เก็บผลลัพธ์ )
วัตถุ.write_readinfo( บัฟเฟอร์ข้อมูลส่งออก, บัฟเฟอร์เก็บผลลัพธ์ )

สรุป

จากบทความนี้จะพบว่าการใช้งาน SPI ทั้งโหมดของฮาร์ดแวร์และซอฟต์แวร์นั้นไม่มีความซับซ้อนเนื่องจากมีเพียงการสั่งข้อมูลและอ่านข้อมูล ส่วนวิธีการสั่งงานโมดูลภายนอกนั้นจะต้องศึกษารายละเอียดของโมดูลนั้นว่ามีขั้นตอนของการใช้งานอย่างไร ทางทีมเราคาดหวังว่าบทความนี้คงเป็นแหล่งศึกษาสำหรับผู้นำไปใช้ต่อไป สุดท้ายขอให้สนุกกับการเขียนโปรแกรมครับ

ท่านใดอยากพูดคุยสามารถคอมเมนท์ไว้ได้เลยครับ

แหล่งอ้างอิง

  1. Micropython, Quick reference for esp8266
  2. Micropython, Quick reference for esp32
  3. Micropython, Class SPI

(C) 2020-2021, โดย อ.ดนัย เจษฎาฐิติกุล/อ.จารุต บุศราทิจ
ปรับปรุงเมื่อ 2021-07-16, 2021-10-20