01076051Lecture 09

Serial Communication: I2C

บทที่ 9: การสื่อสารอนุกรมแบบ I2C (Inter-Integrated Circuit)

บทนี้เรียนโปรโตคอลสื่อสารอนุกรมตัวที่ชื่อ I2C (อ่านว่า ไอ-สแควร์-ซี) ย่อมาจาก Inter-Integrated Circuit. จุดเด่นคือใช้สายสัญญาณเพียง 2 เส้นก็ต่ออุปกรณ์ได้หลายตัวพร้อมกันบนสายเดียว เพราะเป็นการสื่อสารแบบบัส (bus). เราจะไล่ตั้งแต่สายสัญญาณ, การแบ่งบทบาท Master/Slave, ระบบ address, จังหวะ start/stop, จนถึงตัวอย่างจริงคือการอ่านเซนเซอร์วัดอุณหภูมิและความชื้น AM2320 ผ่าน I2C ของบอร์ด NUCLEO-F767ZI.

ผลการเรียนรู้ (Learning Outcomes)

  • อธิบายที่มาและสายสัญญาณของ I2C (SDA = ข้อมูล, SCL = clock) พร้อมความเร็วมาตรฐาน
  • อธิบายบทบาท Master กับ Slave และระบบ address 7-bit บนบัสเดียว
  • อธิบายว่าทำไมสาย I2C ต้องเป็น open-drain และต้องต่อ pull-up resistor
  • อ่าน frame ของ I2C ได้: start condition → address+R/W → ACK → data → stop condition
  • แมปขา I2C ของ STM32F767ZI ให้ตรงกับตำแหน่ง Arduino header บนบอร์ด Nucleo
  • เขียนลำดับขั้นการอ่านเซนเซอร์ AM2320 ด้วย HAL (Master transmit → delay → Master receive → ตรวจ CRC)

§9.1 I2C คืออะไร และสายสัญญาณ

I2C ย่อมาจาก Inter-Integrated Circuit. คำว่า Inter แปลว่า “ระหว่าง” สื่อว่ามันทำให้ IC (ชิป) หลายตัวคุยกันได้. ที่มาคือบริษัท Philips สร้างโปรโตคอลนี้ขึ้นเพื่อให้ IC ภายในเครื่องเล่นวิดีโอเทปสื่อสารกันเอง.

I2C ใช้สายสัญญาณเพียง 2 เส้น. เส้นแรกคือ SDA (Serial Data) ใช้ส่งข้อมูลทีละบิต. เส้นที่สองคือ SCL (Serial Clock) ใช้ส่งสัญญาณ clock เพื่อกำหนดจังหวะการอ่านแต่ละบิต.

ความเร็วมาตรฐาน (Standard Mode) อยู่ที่ 100 kbit/s. ถ้าเป็น Fast Mode จะขยับขึ้นเป็น 400 kbit/s. บางยี่ห้อยังทำได้เร็วกว่านี้ถึงระดับ MHz แต่ค่ามาตรฐานทั่วไปคือ 100 kbit/s.

เพราะ I2C มี clock แยกต่างหาก (SCL) จึงจัดเป็นการสื่อสารแบบ synchronous คือมีสัญญาณนาฬิกากำกับจังหวะ ต่างจาก UART ที่เป็น asynchronous (ไม่มีสาย clock ร่วม).

หัวข้อรายละเอียด
จำนวนสาย2 เส้น (SDA + SCL)
SDASerial Data — ส่งข้อมูลทีละบิต
SCLSerial Clock — จังหวะ clock
Standard Mode100 kbit/s
Fast Mode400 kbit/s
ชนิดsynchronous (มี clock), แบบบัส (bus)
สายสัญญาณและความเร็วของ I2C
VIZ-47บัส I2C สองเส้น (SDA + SCL)
SCLSDAMaster(MCU)SensorICSensorStandard 100 kbit/s · Fast 400 kbit/s

วางเมาส์ (หรือโฟกัส) บนอุปกรณ์เพื่อดูจุดที่มันแตะบัส

§9.2 Master / Slave และระบบ address

บน I2C จะมีบทบาทสองแบบ. Master คือฝ่ายที่เริ่มการสื่อสาร (initiate) และควบคุมสัญญาณ clock เป็นคนเปิดบทสนทนา. Slave คือฝ่ายที่คอยตอบเมื่อถูกเรียก. โดยทั่วไป MCU ทำหน้าที่เป็น Master และเซนเซอร์เป็น Slave.

I2C รองรับ multi-master คือมี Master ได้หลายตัวบนบัสเดียว. STM32 ตั้งค่าโมดูล I2C ให้ทำงานได้ทั้ง Master mode และ Slave mode.

เพราะเป็นบัส อุปกรณ์ทุกตัวใช้สายสัญญาณชุดเดียวกัน จึงต้องมีวิธีแยกว่า Master กำลังคุยกับใคร. วิธีคือให้อุปกรณ์แต่ละตัวมี address เหมือนบ้านเลขที่. address มาตรฐานมีขนาด 7-bit บางตัวที่ซับซ้อนกว่าใช้ 10-bit แต่ส่วนใหญ่ที่ซื้อกันใช้ 7-bit.

เมื่อ Master ส่ง address ออกไป อุปกรณ์ทุกตัวบนบัสจะคอยเทียบว่าตรงกับ address ของตัวเองหรือไม่. ถ้าตรง ตัวนั้นจะตอบกลับด้วยสัญญาณ ACK (acknowledge) และเตรียมรับ/ส่งข้อมูลต่อไป ถ้าไม่ตรงก็เพิกเฉย.

ปัญหาที่พบบ่อยคือซื้อเซนเซอร์เบอร์เดียวกันมาสองตัว address จะชนกัน. ผู้ผลิตมักออกแบบให้ปรับ bit ท้ายของ address ได้ (ส่วนใหญ่ 3 bit ท้าย) เพื่อหลบการชน. บน STM32F767ZI มีโมดูล I2C ให้ 4 ตัว ชื่อ I2C1 ถึง I2C4 ความสามารถเท่ากันทุกตัว.

หัวข้อMasterSlave
ใครเริ่มเริ่มการสื่อสาร (initiate)คอยตอบเมื่อถูกเรียก
คุม clock (SCL)คุมตามจังหวะ clock
มักเป็นใครMCU (STM32)เซนเซอร์/IC
จำนวนบนบัสได้หลายตัว (multi-master)ได้หลายตัว
ใช้ addressส่ง address ไปเรียกมี address ของตัวเอง
บทบาทบน I2C
VIZ-48การระบุที่อยู่ (addressing) บนบัสร่วม
Master เรียกที่อยู่:
SDAMaster0x440x5CSlave0x44SlaveACK0x5CSlave

Master เรียก 0x44 → มีเพียงตัวที่ตรงตอบ ACK ตัวอื่นเพิกเฉย

§9.3 Open-drain และ pull-up resistor

สาย SDA และ SCL ออกแบบให้เป็นแบบ open-drain. หมายความว่าปลายทรานซิสเตอร์ของขานี้เปิดลอยอยู่ ไม่มีวงจรจ่ายไฟให้สายเป็น 1 ด้วยตัวเอง. อุปกรณ์แต่ละตัวทำได้แค่ “ดึงสายลงเป็น 0” เท่านั้น ดึงขึ้นเป็น 1 เองไม่ได้.

ด้วยเหตุนี้จึงต้องต่อ pull-up resistor จากไฟเลี้ยง (VDD) เข้าที่สาย SDA และ SCL. ตัวต้านทานนี้ทำหน้าที่ดึงสายให้เป็น 1 (ระดับ VDD) เมื่อไม่มีใครดึงลง. ค่าที่ใช้บ่อยคือ 4.7 kΩ แต่ปรับลงได้เล็กน้อยถ้าต้องการ.

ต้องเลือกว่าจะ pull-up ขึ้นไฟ 5V หรือ 3.3V ให้ตรงกับระดับ logic ของอุปกรณ์. บอร์ด Nucleo ใช้ไฟ 3.3V ดังนั้นเซนเซอร์หรือโมดูลที่นำมาต่อควรมีสวิตช์เลือกได้ว่าใช้ 3.3V หรือ 5V. ถ้าโมดูลต่อ 5V ไว้ตายตัว logic จะกลายเป็น 5V และใช้กับบอร์ด 3.3V ไม่ได้.

ประโยชน์ของ open-drain คือเมื่อ Master ดึงสายลง (จาก 1 เป็น 0) จะเกิดขอบขาลง อุปกรณ์ทุกตัวบนบัสเห็นพร้อมกัน ใช้เป็นจังหวะ start condition ได้. อุปกรณ์มักมีวงจรตรวจจับขอบขาลงนี้เพื่อปลุกตัวประมวลผลภายในให้เตรียมพร้อม.

I2C ต่างจากอีเทอร์เน็ตแบบ CSMA/CD ตรงที่ไม่มีการตรวจชน (collision detection) ที่ซับซ้อนขนาดนั้น มีเพียงเงื่อนไข start/stop และกติกาเรื่อง ACK ที่เรียบง่ายกว่า.

VIZ-49Open-drain + pull-up (wired-AND)
VDDRp1 (VDD)SDAOFFOFFOFFGND

ไม่มีใครดึง → pull-up ดึงเป็น 1. นี่คือเหตุผลที่ NACK = สายคงอยู่ที่ 1

§9.4 Frame Format ของ I2C

การสื่อสาร I2C เริ่มด้วย start condition. นิยามคือ ขณะที่ SCL เป็น 1 แล้วสาย SDA เปลี่ยนจาก 1 เป็น 0 (ขอบขาลง) ถือเป็น start condition. และปิดท้ายด้วย stop condition คือ ขณะที่ SCL เป็น 1 แล้ว SDA เปลี่ยนจาก 0 เป็น 1.

หลัง start condition ไบต์แรกที่ Master ส่งคือ address ของ Slave (7-bit) บวกกับ 1 bit บอกทิศทางคือ R/W bit (Read = 1, Write = 0) รวมเป็น 8-bit พอดี 1 ไบต์. I2C ส่ง MSB (บิตสูงสุด) ก่อน.

หลังจากนั้น Slave ที่ address ตรงจะดึงสายลงเป็น 0 เพื่อตอบ ACK ในจังหวะ clock ที่ 9. ถ้าไม่มีใครตอบ สายจะคงเป็น 1 (เพราะ pull-up) นับเป็น NACK (not-acknowledge) แปลว่าไม่มีใครรับ. ใน HAL จะได้ค่า HAL error กลับมา.

I2C ไม่ได้กำหนดตายตัวว่าต้องส่งกี่ไบต์ กำหนดแค่ว่าไบต์แรกคือ address ส่วนที่เหลือแล้วแต่ Device กำหนด. ดังนั้นผู้เขียนโปรแกรมต้องรู้ว่าอุปกรณ์แต่ละตัวต้องส่ง/รับกี่ไบต์.

ตัวอย่าง frame ของเซนเซอร์ AM2320: ไบต์ที่ 1 = address + R/W bit, ไบต์ที่ 2 = function code, ไบต์ที่ 3 = payload (ความยาวขึ้นกับ function code) และปิดท้ายด้วย CRC 16-bit เพื่อตรวจความผิดพลาดของข้อมูล.

ลำดับสิ่งที่ส่งหมายเหตุ
1Start conditionSCL=1, SDA ตกจาก 1→0
2Address (7-bit) + R/W bitรวม 8-bit, ส่ง MSB ก่อน
3ACK / NACKSlave ดึงลง 0 = ACK; คงเป็น 1 = NACK
4Data byte(s)กี่ไบต์ขึ้นกับ Device
5Stop conditionSCL=1, SDA ขึ้นจาก 0→1
ลำดับ frame พื้นฐานของ I2C
VIZ-50ไทม์ไลน์เฟรม I2C
STARTAddressR/WACKDataSTOPSCLSDASTART

START: SDA ตกขณะ SCL สูง

VIZ-51ACK / NACK ที่ clock ลูกที่ 9
SCLSDAสุ่มค่าตรงนี้ACK (0)

slave ดึง SDA = 0 ที่ clock 9 → master รู้ว่ามีคนตอบ (ACK)

§9.5 ตัวอย่างจริง: อ่านเซนเซอร์ AM2320 ด้วย I2C

AM2320 เป็น Digital Temperature and Humidity Sensor วัดอุณหภูมิและความชื้นสัมพัทธ์ แล้วส่งออกมาเป็นข้อมูล digital ผ่าน I2C ได้เลย (มาตรฐาน) โดยไม่ต้องใช้ ADC. ใช้ไฟ 3.3V ได้ ความละเอียด 0.1 (ทศนิยม 1 ตำแหน่ง) ทั้งอุณหภูมิและความชื้น.

ค่าที่วัดได้: ความชื้นช่วง 0–99.9% และอุณหภูมิช่วง -40 ถึง 80 °C. ความแม่นยำที่ 25 °C ราว ±3% สำหรับความชื้น และ ±0.5 °C สำหรับอุณหภูมิ. ควรเลือกใช้เซนเซอร์ให้ช่วงการวัดเหมาะกับงานจริง เพราะที่ช่วงสุดขอบ (ความชื้นสูงมากหรือต่ำมาก, อุณหภูมิต่ำหรือสูงมาก) ค่าความคลาดเคลื่อนจะเพิ่มขึ้น.

ข้อมูลภายในเก็บใน register ของเซนเซอร์. อุณหภูมิและความชื้นแต่ละค่ามีขนาด 16-bit แต่ register แต่ละตัวเก็บได้ 8-bit จึงใช้ 2 register ต่อค่า. register address 0 = high byte ความชื้น, address 1 = low byte ความชื้น, address 2 = high byte อุณหภูมิ, address 3 = low byte อุณหภูมิ.

ขั้นตอนอ่าน: ใช้ function code 0x03 (อ่าน multiple register) โดยระบุ address เริ่มต้น = 0 และจำนวน register ที่อ่าน = 4 ตัว (register 0,1,2,3). Host (MCU) ส่ง start → address+Write → function code 0x03 → start register 0x00 → จำนวน 0x04.

ค่า 16-bit ที่ส่งกลับมาเป็นจำนวนเต็มที่คูณ 10 ไว้แล้ว เพื่อเก็บทศนิยม 1 ตำแหน่งโดยไม่ต้องใช้รูปแบบ floating point. ตอนใช้งานต้องหารด้วย 10 เอง. เช่นความชื้นส่งมา 0x01F4 = 500 หารด้วย 10 ได้ 50.0%; อุณหภูมิส่งมา 0x00FA = 250 หารด้วย 10 ได้ 25.0 °C.

สำคัญ: เซนเซอร์จะเข้าสู่ sleep ถ้าไม่ถูกใช้งานเกินราว 5 วินาที. ก่อนอ่านต้อง wake up ก่อน โดยส่ง start + address แล้วรออย่างน้อย 800 ไมโครวินาที จบด้วย stop แล้วค่อยส่ง command อ่านจริง. ถ้าช่วงเวลาอ่านถี่กว่า 5 วินาที ก็ไม่ต้อง wake up ซ้ำ.

เมื่อรับ 8 ไบต์กลับมา (index 0 = function code, 1 = จำนวนไบต์, 2–3 = ความชื้น, 4–5 = อุณหภูมิ, 6–7 = CRC) ต้องนำ 6 ไบต์แรกไปคำนวณ CRC 16-bit แล้วเทียบกับ 2 ไบต์สุดท้าย ถ้าตรงกันแปลว่าข้อมูลถูกต้อง. สุดท้ายรวม high byte กับ low byte กลับเป็น 16-bit แล้วหาร 10 เพื่อได้ค่าจริง.

Register addressความหมาย
0x00High byte ของความชื้น
0x01Low byte ของความชื้น
0x02High byte ของอุณหภูมิ
0x03Low byte ของอุณหภูมิ
register ของ AM2320 ที่ใช้งาน
Function codeการทำงาน
0x03อ่าน multiple register (อุณหภูมิ/ความชื้น)
0x10เขียนหลาย register
function code หลักของ AM2320
ลำดับขั้นอ่าน AM2320 ด้วย HAL (I2C1)c
// address เซนเซอร์ 7-bit = 0x5C, HAL ต้องการ address ชิดซ้าย + bit R/W
// 0x5C << 1 = 0xB8 (Write)
uint8_t cmd[3] = {0x03, 0x00, 0x04};  // func 0x03, start reg 0x00, อ่าน 4 register
uint8_t data[8];                      // buffer รับ 8 ไบต์

// 1) wake up เซนเซอร์: ส่ง address เปล่า แล้วหน่วง > 800us
HAL_I2C_Master_Transmit(&hi2c1, 0xB8, cmd, 1, 10);
HAL_Delay(1);                         // >800us

// 2) ส่ง command อ่านจริง
HAL_I2C_Master_Transmit(&hi2c1, 0xB8, cmd, 3, 10);
HAL_Delay(1);                         // รอ sensor ประมวลผล

// 3) รอรับผลลัพธ์ (address Read = 0xB9)
HAL_I2C_Master_Receive(&hi2c1, 0xB9, data, 8, 10);

// 4) ตรวจ CRC ของ data[0..5] เทียบ data[6],data[7]
// 5) รวม byte: humidity = (data[2]<<8 | data[3]) / 10.0f
//              temperature = (data[4]<<8 | data[5]) / 10.0f
VIZ-52AM2320: register map + อ่านค่า
Register map
คู่ที่เลือก = Humidity (reg 0+1) → รวมเป็นค่า 16 บิต
Response bytes
0x03func0x04len0x01H hi0xF4H lo0x01T hi0x1AT lo0xCCCRC lo0xABCRC hi
6 ไบต์แรก = เข้า CRC2 ไบต์ท้าย = ค่า CRC
รวม 2 ไบต์ → ค่า
(hi << 8) | lo = 0x01 0xF4 → 0x01F4 = 500
500 / 10 = 50.0 %RH

เซ็นเซอร์ส่งค่าเป็นจำนวนเต็ม × 10 แยกเป็น 2 ไบต์ (hi, lo). รวมเป็น 16 บิตแล้วหาร 10. ไบต์ตอบ 6 ตัวแรกเข้า CRC, 2 ตัวท้ายคือค่า CRC.

แบบฝึกหัด

12 ข้อ — แต่ละข้อมีชนิดและระดับความยากกำกับ กดปุ่ม "ดูเฉลย" เพื่อดูคำตอบและคำอธิบาย

  1. 1.ปรนัยง่าย

    I2C ใช้สายสัญญาณกี่เส้น และคือเส้นใด

  2. 2.ปรนัยง่าย

    หน้าที่ของสาย SCL ใน I2C คืออะไร

  3. 3.ปรนัยง่าย

    ความเร็ว Standard Mode ของ I2C คือเท่าใด

  4. 4.ถูก/ผิดง่าย

    I2C รองรับการมี Master ได้หลายตัวบนบัสเดียว (multi-master)

  5. 5.ปรนัยกลาง

    เหตุใดสาย SDA และ SCL ของ I2C จึงต้องต่อ pull-up resistor

  6. 6.ปรนัยกลาง

    start condition ของ I2C เกิดขึ้นเมื่อใด

  7. 7.ปรนัยกลาง

    ไบต์แรกที่ Master ส่งหลัง start condition ประกอบด้วยอะไร

  8. 8.ถูก/ผิดกลาง

    ถ้าไม่มี Slave ตัวใดดึงสายลงตอบ สายจะคงเป็น 1 ซึ่งถือเป็น NACK

  9. 9.เติมคำกลาง

    AM2320 เก็บค่าอุณหภูมิและความชื้นเป็น 16-bit แต่ register ละ 8-bit จึงใช้ ___ register ต่อหนึ่งค่า

  10. 10.เติมคำยาก

    AM2320 ส่งค่าความชื้นกลับมาเป็น 0x01F4 (= 500). ค่าความชื้นจริงเป็นกี่เปอร์เซ็นต์

  11. 11.วิเคราะห์ยาก

    ทำไมก่อนอ่านค่า AM2320 บางครั้งต้องส่ง address เปล่าแล้วหน่วงราว 800 ไมโครวินาทีก่อน แล้วค่อยส่ง command อ่านจริง

  12. 12.วิเคราะห์ยาก

    ในโค้ด HAL ทำไมจึงใส่ address เป็น 0xB8 ทั้งที่ datasheet ระบุ address ของ AM2320 คือ 0x5C