คลาส I2C -- โปรโตคอลอนุกรมแบบสองสาย

I2C เป็นโปรโตคอลแบบสองสายสำหรับการสื่อสารระหว่างอุปกรณ์ ในระดับฟิสิกส์ประกอบด้วยสาย 2 เส้น ได้แก่ SCL และ SDA ซึ่งเป็นสายสัญญาณนาฬิกาและสายข้อมูลตามลำดับ

ออบเจ็กต์ I2C ถูกสร้างขึ้นโดยผูกกับบัสที่ระบุ สามารถกำหนดค่าได้ในขณะสร้าง หรือกำหนดค่าในภายหลังก็ได้

การพิมพ์ออบเจ็กต์ I2C จะแสดงข้อมูลการกำหนดค่าของมัน

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

Note

บัส I2C ต้องการวงจร pull-up ทั้งบน SDA และ SCL เพื่อการทำงาน โดยทั่วไปจะเป็นตัวต้านทานในช่วง 1 - 10 kOhm ที่ต่อจากแต่ละ SDA/SCL ไปยัง Vcc หากไม่มีสิ่งเหล่านี้ พฤติกรรมจะไม่ถูกกำหนดและอาจตั้งแต่การบล็อก การรีเซ็ต watchdog ที่ไม่คาดคิด ไปจนถึงค่าที่ผิดพลาด บ่อยครั้งวงจร pull-up นี้จะถูกสร้างไว้ในบอร์ด MCU หรือบอร์ด sensor breakout แล้ว แต่ไม่มีกฎตายตัว ดังนั้นโปรดตรวจสอบหากมีปัญหา ดูเพิ่มเติมที่ คู่มือการเรียนรู้ที่ยอดเยี่ยมนี้ โดย Adafruit เกี่ยวกับการเดินสาย I2C

ตัวอย่างการใช้งาน:

from machine import I2C

i2c = I2C(freq=400000)          # create I2C peripheral at frequency of 400kHz
                                # depending on the port, extra parameters may be required
                                # to select the peripheral and/or pins to use

i2c.scan()                      # scan for peripherals, returning a list of 7-bit addresses

i2c.writeto(42, b'123')         # write 3 bytes to peripheral with 7-bit address 42
i2c.readfrom(42, 4)             # read 4 bytes from peripheral with 7-bit address 42

i2c.readfrom_mem(42, 8, 3)      # read 3 bytes from memory of peripheral 42,
                                #   starting at memory-address 8 in the peripheral
i2c.writeto_mem(42, 2, b'\x10') # write 1 byte to memory of peripheral 42
                                #   starting at address 2 in the peripheral

ตัวสร้าง

class machine.I2C(id: int, *, scl: Pin | None = None, sda: Pin | None = None, freq: int = 400000, timeout: int = 50000)

สร้างและคืนค่าออบเจ็กต์ I2C ใหม่โดยใช้พารามิเตอร์ต่อไปนี้:

  • id ระบุอุปกรณ์ต่อพ่วง I2C ที่ต้องการ ค่าที่อนุญาตขึ้นอยู่กับพอร์ต/บอร์ดที่ใช้งาน

  • scl ควรเป็นออบเจ็กต์พินที่ระบุพินที่จะใช้สำหรับ SCL

  • sda ควรเป็นออบเจ็กต์พินที่ระบุพินที่จะใช้สำหรับ SDA

  • freq ควรเป็นจำนวนเต็มที่กำหนดความถี่สูงสุดสำหรับ SCL

  • timeout คือเวลาสูงสุดในหน่วยไมโครวินาทีที่อนุญาตสำหรับการทำธุรกรรม I2C พารามิเตอร์นี้ไม่ได้รับอนุญาตในบางพอร์ต

โปรดทราบว่าบางพอร์ต/บอร์ดจะมีค่าเริ่มต้นของ scl และ sda ที่สามารถเปลี่ยนแปลงได้ในตัวสร้างนี้ บางอันจะมีค่าคงที่ของ scl และ sda ที่ไม่สามารถเปลี่ยนแปลงได้

เมธอดทั่วไป

init(scl: Pin, sda: Pin, *, freq: int = 400000) None

กำหนดค่าบัส I2C ด้วยอาร์กิวเมนต์ที่ระบุ:

  • scl คือออบเจ็กต์พินสำหรับสาย SCL

  • sda คือออบเจ็กต์พินสำหรับสาย SDA

  • freq คืออัตราสัญญาณนาฬิกา SCL

ในกรณีของ I2C ฮาร์ดแวร์ ความถี่สัญญาณนาฬิกาจริงอาจต่ำกว่าความถี่ที่ร้องขอ ขึ้นอยู่กับฮาร์ดแวร์ของแพลตฟอร์ม อัตราจริงสามารถดูได้โดยการพิมพ์ออบเจ็กต์ I2C

scan() List[int]

สแกนที่อยู่ I2C ทั้งหมดระหว่าง 0x08 ถึง 0x77 รวมขอบเขต และคืนค่ารายการที่อยู่ที่ตอบสนอง อุปกรณ์ตอบสนองหากดึงสาย SDA ลงต่ำหลังจากที่อยู่ของมัน (รวมบิตการเขียน) ถูกส่งบนบัส

การดำเนินการ I2C พื้นฐาน

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

เมธอดเหล่านี้มีให้ใช้เฉพาะบนคลาส SoftI2C เท่านั้น

start() None

สร้างเงื่อนไข START บนบัส (SDA เปลี่ยนเป็นต่ำในขณะที่ SCL สูง)

stop() None

สร้างเงื่อนไข STOP บนบัส (SDA เปลี่ยนเป็นสูงในขณะที่ SCL สูง)

readinto(buf: bytearray, nack: bool = True, /) None

อ่านไบต์จากบัสและเก็บลงใน buf จำนวนไบต์ที่อ่านเท่ากับความยาวของ buf จะส่ง ACK บนบัสหลังจากรับทุกไบต์ยกเว้นไบต์สุดท้าย หลังจากรับไบต์สุดท้าย ถ้า nack เป็น true จะส่ง NACK ไม่เช่นนั้นจะส่ง ACK (และในกรณีนี้อุปกรณ์ปลายทางจะถือว่าจะมีการอ่านไบต์เพิ่มเติมในการเรียกครั้งถัดไป)

write(buf: bytes) int

เขียนไบต์จาก buf ไปยังบัส ตรวจสอบว่าได้รับ ACK หลังจากแต่ละไบต์ และหยุดส่งไบต์ที่เหลือหากได้รับ NACK ฟังก์ชันคืนค่าจำนวน ACK ที่ได้รับ

การดำเนินการบัสมาตรฐาน

เมธอดต่อไปนี้ใช้งานการอ่านและเขียนคอนโทรลเลอร์ I2C มาตรฐานที่กำหนดเป้าหมายอุปกรณ์ปลายทางที่ระบุ

readfrom(addr: int, nbytes: int, stop: bool = True, /) bytes

อ่าน nbytes จากอุปกรณ์ปลายทางที่ระบุโดย addr ถ้า stop เป็น true จะสร้างเงื่อนไข STOP ที่ท้ายการถ่ายโอน คืนค่าออบเจ็กต์ bytes พร้อมข้อมูลที่อ่าน

readfrom_into(addr: int, buf: bytearray, stop: bool = True, /) None

อ่านลงใน buf จากอุปกรณ์ปลายทางที่ระบุโดย addr จำนวนไบต์ที่อ่านจะเท่ากับความยาวของ buf ถ้า stop เป็น true จะสร้างเงื่อนไข STOP ที่ท้ายการถ่ายโอน

เมธอดคืนค่า None

writeto(addr: int, buf: bytes, stop: bool = True, /) int

เขียนไบต์จาก buf ไปยังอุปกรณ์ปลายทางที่ระบุโดย addr ถ้าได้รับ NACK หลังจากเขียนไบต์จาก buf แล้ว ไบต์ที่เหลือจะไม่ถูกส่ง ถ้า stop เป็น true จะสร้างเงื่อนไข STOP ที่ท้ายการถ่ายโอน แม้ว่าจะได้รับ NACK ฟังก์ชันคืนค่าจำนวน ACK ที่ได้รับ

writevto(addr: int, vector: tuple | list, stop: bool = True, /) int

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

ถ้าได้รับ NACK หลังจากเขียนไบต์จากออบเจ็กต์ใน vector ไบต์ที่เหลือและออบเจ็กต์ที่เหลือจะไม่ถูกส่ง ถ้า stop เป็น true จะสร้างเงื่อนไข STOP ที่ท้ายการถ่ายโอน แม้ว่าจะได้รับ NACK ฟังก์ชันคืนค่าจำนวน ACK ที่ได้รับ

การดำเนินการหน่วยความจำ

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

readfrom_mem(addr: int, memaddr: int, nbytes: int, *, addrsize: int = 8) bytes

อ่าน nbytes จากอุปกรณ์ปลายทางที่ระบุโดย addr โดยเริ่มจากที่อยู่หน่วยความจำที่ระบุโดย memaddr อาร์กิวเมนต์ addrsize ระบุขนาดที่อยู่เป็นบิต คืนค่าออบเจ็กต์ bytes พร้อมข้อมูลที่อ่าน

readfrom_mem_into(addr: int, memaddr: int, buf: bytearray, *, addrsize: int = 8) None

อ่านลงใน buf จากอุปกรณ์ปลายทางที่ระบุโดย addr โดยเริ่มจากที่อยู่หน่วยความจำที่ระบุโดย memaddr จำนวนไบต์ที่อ่านเท่ากับความยาวของ buf อาร์กิวเมนต์ addrsize ระบุขนาดที่อยู่เป็นบิต

เมธอดคืนค่า None

writeto_mem(addr: int, memaddr: int, buf: bytes, *, addrsize: int = 8) None

เขียน buf ไปยังอุปกรณ์ปลายทางที่ระบุโดย addr โดยเริ่มจากที่อยู่หน่วยความจำที่ระบุโดย memaddr อาร์กิวเมนต์ addrsize ระบุขนาดที่อยู่เป็นบิต

เมธอดคืนค่า None

คลาส SoftI2C -- บัส I2C จำลองด้วยซอฟต์แวร์

คลาส SoftI2C ใช้งาน I2C โดยการ bit-banging พิน GPIO ใดก็ได้ มันเปิดเผยเมธอดเหมือนกับ I2C บวกกับการดำเนินการบัสพื้นฐานระดับต่ำ (start(), stop(), readinto(), write()) สำหรับผู้เรียกที่ต้องการประกอบธุรกรรมที่ไม่ใช่มาตรฐาน ใช้เมื่อพินที่คุณต้องการไม่ได้ต่อกับบล็อก I2C ฮาร์ดแวร์ เมื่อคุณต้องการบัสมากกว่าที่ฮาร์ดแวร์รองรับ หรือเพื่อสื่อสารกับอุปกรณ์ที่ต้องการลำดับที่ผิดปกติ (สัญญาณนาฬิกาพิเศษ การเริ่มซ้ำหลังจากการเขียน เป็นต้น)

ตัวสร้าง

class machine.SoftI2C(scl: Pin, sda: Pin, *, freq: int = 400000, timeout: int = 50000)

สร้างบัส I2C ซอฟต์แวร์ที่ขับเคลื่อนโดย scl / sda

freq คืออัตราสัญญาณนาฬิกา SCL เป้าหมายในหน่วย Hz (อัตราจริงมักจะต่ำกว่าเนื่องจากค่าใช้จ่ายของลูป bit-bang)

timeout คือเวลาสูงสุดในหน่วยไมโครวินาทีที่รอสำหรับการยืด clock (SCL ถูกดึงต่ำโดยอุปกรณ์อื่นบนบัส) เมื่อหมดเวลาจะเกิด OSError(ETIMEDOUT)

เมธอดทั่วไป

init(scl: Pin, sda: Pin, *, freq: int = 400000) None

กำหนดค่าบัส I2C ซอฟต์แวร์ใหม่ด้วยพินและความถี่ที่ระบุ เทียบเท่ากับการสร้าง SoftI2C ใหม่บนออบเจ็กต์เดิม

scan() List[int]

สแกนที่อยู่ I2C ทั้งหมดระหว่าง 0x08 ถึง 0x77 รวมขอบเขต และคืนค่ารายการที่อยู่ที่ตอบสนอง

การดำเนินการ I2C พื้นฐาน

เมธอดต่อไปนี้ใช้งานการดำเนินการบัสคอนโทรลเลอร์ I2C พื้นฐานและสามารถนำมารวมกันเพื่อสร้างธุรกรรม I2C ใดก็ได้ เมธอดเหล่านี้มีเฉพาะใน SoftI2C -- คลาส I2C ฮาร์ดแวร์ไม่เปิดเผย

start() None

สร้างเงื่อนไข START บนบัส (SDA เปลี่ยนเป็นต่ำในขณะที่ SCL สูง)

stop() None

สร้างเงื่อนไข STOP บนบัส (SDA เปลี่ยนเป็นสูงในขณะที่ SCL สูง)

readinto(buf: bytearray, nack: bool = True, /) None

อ่านไบต์จากบัสลงใน buf โดยอ่าน len(buf) ไบต์ และส่ง ACK หลังจากทุกไบต์ยกเว้นไบต์สุดท้าย หลังจากไบต์สุดท้าย nack=True (ค่าเริ่มต้น) จะส่ง NACK เพื่อสิ้นสุดการถ่ายโอน nack=False จะส่ง ACK เพื่อให้อุปกรณ์ยังคงเลือกสำหรับ readinto() ถัดไป

write(buf: bytes) int

เขียน buf ไปยังบัส โดยตรวจสอบ ACK หลังจากทุกไบต์ การส่งจะหยุดเมื่อ NACK แรก คืนค่าจำนวน ACK ที่ได้รับ

การดำเนินการบัสมาตรฐาน

เมธอดต่อไปนี้ใช้งานการอ่านและเขียนคอนโทรลเลอร์ I2C มาตรฐานที่กำหนดเป้าหมายอุปกรณ์ปลายทางที่ระบุ

readfrom(addr: int, nbytes: int, stop: bool = True, /) bytes

อ่าน nbytes จากอุปกรณ์ที่ที่อยู่ 7 บิต addr ถ้า stop เป็น true จะสร้างเงื่อนไข STOP ที่ท้ายการถ่ายโอน

readfrom_into(addr: int, buf: bytearray, stop: bool = True, /) None

อ่าน len(buf) ไบต์จากอุปกรณ์ที่ addr ลงใน buf ถ้า stop เป็น true จะสร้างเงื่อนไข STOP ที่ท้ายการถ่ายโอน

writeto(addr: int, buf: bytes, stop: bool = True, /) int

เขียน buf ไปยังอุปกรณ์ที่ addr การส่งจะหยุดเมื่อ NACK แรก ถ้า stop เป็น true จะสร้างเงื่อนไข STOP ที่ท้ายการถ่ายโอนเสมอ (แม้ว่าจะเกิด NACK ก่อนกำหนด) คืนค่าจำนวน ACK ที่ได้รับ

writevto(addr: int, vector: tuple | list, stop: bool = True, /) int

เขียนการต่อกันของบัฟเฟอร์ใน vector ไปยังอุปกรณ์ที่ addr เป็นธุรกรรมเดียว บัฟเฟอร์ว่างเปล่าจะถูกละเว้น มีพฤติกรรมเหมือน writeto() สำหรับ semantics ของ stop และค่าที่คืน

การดำเนินการหน่วยความจำ

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

readfrom_mem(addr: int, memaddr: int, nbytes: int, *, addrsize: int = 8) bytes

อ่าน nbytes จากอุปกรณ์ที่ addr โดยเริ่มจากรีจิสเตอร์ memaddr โดย addrsize คือความกว้างของที่อยู่รีจิสเตอร์เป็นบิต (โดยทั่วไปคือ 8 หรือ 16)

readfrom_mem_into(addr: int, memaddr: int, buf: bytearray, *, addrsize: int = 8) None

อ่านลงใน buf จากอุปกรณ์ที่ addr โดยเริ่มจากรีจิสเตอร์ memaddr

writeto_mem(addr: int, memaddr: int, buf: bytes, *, addrsize: int = 8) None

เขียน buf ไปยังอุปกรณ์ที่ addr โดยเริ่มจากรีจิสเตอร์ memaddr