อินเทอร์เน็ต. คอมพิวเตอร์. ช่วย. คำแนะนำ. ซ่อมแซม

บัส I2C และการใช้ใน MCU STM32 STM32, อินเทอร์เฟซแบบอนุกรม I2C Stm32 i2c ส่งสัญญาณ ack และ nack

เซลล์ทรัม วันที่ 20 กุมภาพันธ์ 2560 เวลา 01:17 น

ขั้นตอนแรกกับคอมไพเลอร์ STM32 และ mikroC สำหรับสถาปัตยกรรม ARM - ส่วนที่ 4 - การเชื่อมต่อ LCD ที่ใช้ I2C, pcf8574 และ HD4478

  • การเขียนโปรแกรมไมโครคอนโทรลเลอร์

ฉันอยากจะอุทิศบทความถัดไปเพื่อทำงานกับอินเทอร์เฟซ i2c ทั่วไปซึ่งมักใช้ในวงจรไมโครต่าง ๆ ที่เชื่อมต่อกับไมโครคอนโทรลเลอร์

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


สำหรับ i2c อุปกรณ์ทั้งหมด (ทั้งอุปกรณ์หลักและอุปกรณ์รอง) จะใช้เอาต์พุต open-drain พูดง่ายๆ ก็คือ พวกมันสามารถดึงดูดยางไปที่พื้นเท่านั้น รับประกันระดับบัสที่สูงด้วยตัวต้านทานแบบดึงขึ้น โดยปกติค่าของตัวต้านทานเหล่านี้จะถูกเลือกในช่วงตั้งแต่ 4.7 ถึง 10 kOhm i2c ค่อนข้างไวต่อสายทางกายภาพที่เชื่อมต่ออุปกรณ์ ดังนั้นหากใช้การเชื่อมต่อกับความจุขนาดใหญ่ (เช่น สายเคเบิลยาวบางหรือหุ้มฉนวน) อิทธิพลของความจุนี้สามารถ "เบลอ" ขอบของสัญญาณและรบกวน การทำงานปกติของรถบัส ยิ่งตัวต้านทานแบบดึงขึ้นมีขนาดเล็กลง ความจุนี้ก็จะยิ่งมีอิทธิพลต่อลักษณะของขอบสัญญาณน้อยลงเท่านั้น แต่โหลดบนทรานซิสเตอร์เอาท์พุตบนอินเทอร์เฟซ i2c ก็จะยิ่งมากขึ้นเท่านั้น ค่าของตัวต้านทานเหล่านี้ถูกเลือกสำหรับการใช้งานแต่ละอย่าง แต่ไม่ควรน้อยกว่า 2.2 kOhms มิฉะนั้นคุณสามารถเผาทรานซิสเตอร์เอาต์พุตในอุปกรณ์ที่ทำงานร่วมกับบัสได้

บัสประกอบด้วยสองบรรทัด: SDA (สายข้อมูล) และ SCL (สัญญาณนาฬิกา) นาฬิกาอุปกรณ์บัสมาสเตอร์โดยปกติแล้ว MK ของเรา เมื่อ SCL สูง ข้อมูลจะถูกอ่านจากบัสข้อมูล สถานะ SDA สามารถเปลี่ยนแปลงได้เมื่อสัญญาณนาฬิกาต่ำเท่านั้น- เมื่อ SCL สูง สัญญาณบน SDA จะเปลี่ยนเมื่อมีการสร้างสัญญาณ เริ่ม (เมื่อ SCL สูง สัญญาณบน SDA จะเปลี่ยนจากสูงไปต่ำ) และ หยุด - เมื่อระดับ SCL สูง สัญญาณบน SDA จะเปลี่ยนจากต่ำไปสูง)

ควรแยกกันว่าใน i2c ที่อยู่ถูกระบุเป็นตัวเลข 7 บิต 8 - บิตที่มีนัยสำคัญน้อยที่สุดระบุทิศทางของการถ่ายโอนข้อมูล 0 - หมายความว่าทาสจะส่งข้อมูล 1 - รับ- โดยสรุปอัลกอริทึมสำหรับการทำงานกับ i2c มีดังนี้:

  • ระดับสูงใน SDA และ SCL- รถบัสฟรี สามารถเริ่มทำงานได้
  • ลิฟต์ต้นแบบ เอสซีแอลเป็น 1 และเปลี่ยนสถานะ เอส.ดี.เอ.จาก 1 ถึง 0 - ดึงดูดมันลงสู่พื้น - สัญญาณเกิดขึ้น เริ่ม
  • ต้นแบบส่งที่อยู่ทาส 7 บิตพร้อมบิตทิศทาง (data on เอส.ดี.เอ.จัดแสดงเมื่อ เอสซีแอลดึงลงไปที่พื้นแล้วอ่านโดยทาสเมื่อมันถูกปล่อย) หากทาสไม่มีเวลา "คว้า" บิตก่อนหน้า มันจะดึงดูด เอสซีแอลบนพื้น ทำให้อาจารย์เข้าใจชัดเจนว่าไม่จำเป็นต้องเปลี่ยนสถานะของบัสข้อมูล: “ฉันยังคงอ่านอันก่อนหน้าอยู่” หลังจากที่นายปล่อยยางแล้วเขาก็ตรวจสอบ ทาสปล่อยเธอไปแล้วเหรอ?.
  • หลังจากส่งที่อยู่ 8 บิตแล้ว ต้นแบบจะสร้างรอบสัญญาณนาฬิกาที่ 9 และปล่อยบัสข้อมูล ถ้าทาสได้ยินคำปราศรัยของเขาและยอมรับก็แสดงว่าเขา จะกด เอส.ดี.เอ.ลงไปที่พื้น- นี่คือวิธีการสร้างสัญญาณ ถาม- ยอมรับ ทุกอย่างโอเค ถ้าทาสไม่เข้าใจหรือเขาไม่อยู่ที่นั่นก็จะไม่มีใครกดยาง อาจารย์จะรอระยะหมดเวลาและเข้าใจว่าเขาไม่เข้าใจ
  • หลังจากส่งที่อยู่แล้วหากเรากำหนดทิศทางแล้ว จากนายสู่ทาส(ที่อยู่ 8 บิตเท่ากับ 1) จากนั้นมาสเตอร์จะส่งข้อมูลไปยังสเลฟโดยไม่ลืมที่จะตรวจสอบการมีอยู่ของ ถามจากสเลฟ โดยรอให้อุปกรณ์สเลฟประมวลผลข้อมูลที่เข้ามา
  • เมื่อต้นแบบได้รับข้อมูลจากทาส ต้นแบบจะสร้างสัญญาณขึ้นมาเอง ถามหลังจากได้รับแต่ละไบต์แล้ว และสเลฟจะควบคุมการมีอยู่ของมัน นายอาจไม่ได้ส่งโดยเฉพาะ ถามก่อนที่จะส่งคำสั่ง หยุดมักจะทำให้ทาสชัดเจนว่าไม่จำเป็นต้องให้ข้อมูลอีกต่อไป
  • หลังจากส่งข้อมูลโดยมาสเตอร์ (โหมดเขียน) แล้ว จำเป็นต้องอ่านข้อมูลจากสเลฟ จากนั้นต้นแบบจะสร้างสัญญาณอีกครั้ง เริ่ม โดยส่งที่อยู่ทาสพร้อมแฟล็กการอ่าน (หากอยู่ก่อนคำสั่ง เริ่มไม่ได้ถูกโอน หยุดจากนั้นจึงจัดตั้งทีมขึ้น เริ่มต้นใหม่- ใช้เพื่อเปลี่ยนทิศทางของการสื่อสารแบบมาสเตอร์-สเลฟ ตัวอย่างเช่น เราส่งที่อยู่การลงทะเบียนไปยังสเลฟ แล้วอ่านข้อมูลจากสเลฟ)
  • เมื่อเสร็จสิ้นการทำงานกับทาส ต้นแบบจะสร้างสัญญาณ หยุด- ที่สัญญาณนาฬิการะดับสูง จะสร้างการเปลี่ยนบัสข้อมูลจาก 0 เป็น 1
STM 32 มีตัวรับส่งสัญญาณบัส i2c ที่ใช้ฮาร์ดแวร์ MK สามารถมีได้ 2 หรือ 3 โมดูล ในการกำหนดค่าจะใช้รีจิสเตอร์พิเศษตามที่อธิบายไว้ในข้อมูลอ้างอิงสำหรับ MK ที่ใช้

ใน MicroC ก่อนที่จะใช้ i2c (รวมถึงอุปกรณ์ต่อพ่วงใดๆ) จะต้องเริ่มต้นอย่างถูกต้อง เมื่อต้องการทำเช่นนี้ เราใช้ฟังก์ชันต่อไปนี้ (การเริ่มต้นเป็นแบบหลัก):

I2Cn_Init_Advanced (ไม่ได้ลงนามแบบยาว: I2C_ClockSpeed, const Module_Struct * โมดูล);

  • n- จำนวนโมดูลที่ใช้ เป็นต้น I2C1หรือ I2C2.
  • I2C_ความเร็วนาฬิกา- ความเร็วบัส 100,000 (100 kbs, โหมดมาตรฐาน) หรือ 400000 (400 kbs, โหมดเร็ว) อันที่สองเร็วขึ้น 4 เท่า แต่ไม่ใช่ทุกอุปกรณ์ที่รองรับ
  • *โมดูล- ตัวชี้ไปยังโมดูลต่อพ่วง เป็นต้น &_GPIO_MODULE_I2C1_PB67อย่าลืมที่นี่ด้วย ผู้ช่วยรหัส (Ctrl-เว้นวรรค ) ช่วยได้มาก
ก่อนอื่น เรามาตรวจสอบว่ารถบัสว่างหรือไม่ I2Cn_Is_ไม่ได้ใช้งาน();ส่งคืน 1 หากรถบัสว่างและ 0 หากมีการแลกเปลี่ยน

I2Cn_Start();
ที่ไหน n- จำนวนโมดูล i2c ที่ใช้ของไมโครคอนโทรลเลอร์ของเรา ฟังก์ชันจะส่งคืนค่า 0 ถ้ามีข้อผิดพลาดบนบัส และ 1 หากทุกอย่างเรียบร้อยดี

ในการถ่ายโอนข้อมูลไปยังทาสเราใช้ฟังก์ชัน:

I2Cn_Write (ถ่านทาสที่ไม่ได้ลงนาม, ถ่าน *buf ที่ไม่ได้ลงนาม, การนับแบบยาวที่ไม่ได้ลงนาม, โหมด END_mode แบบยาวที่ไม่ได้ลงนาม);

  • n- จำนวนโมดูลที่ใช้
  • ทาส_ที่อยู่- ที่อยู่ทาส 7 บิต
  • *บุฟ- ตัวชี้ไปยังข้อมูลของเรา - ไบต์หรืออาร์เรย์ไบต์
  • นับ- จำนวนไบต์ข้อมูลที่ถูกถ่ายโอน
  • END_โหมด- จะทำอย่างไรหลังจากถ่ายโอนข้อมูลไปยังทาส END_MODE_STOP - ส่งสัญญาณ หยุด, หรือ END_MODE_RESTART ส่งอีกครั้ง เริ่ม, ทำให้เกิดสัญญาณ เริ่มต้นใหม่และแจ้งให้แผนกทราบอย่างชัดเจนว่าเซสชันกับเขายังไม่สิ้นสุด และข้อมูลจะถูกอ่านจากเขาแล้ว
หากต้องการอ่านข้อมูลจากทาสให้ใช้ฟังก์ชัน:

I2Cn_Read(char Slave_address, char *ptrdata, การนับแบบยาวที่ไม่ได้ลงนาม, โหมด END_mode แบบยาวที่ไม่ได้ลงนาม);

  • n- จำนวนโมดูลที่ใช้
  • ทาส_ที่อยู่- ที่อยู่ทาส 7 บิต
  • *บุฟ- ตัวชี้ไปยังตัวแปรหรืออาร์เรย์ที่เราได้รับข้อมูล พิมพ์ char หรือ short int
  • นับ- จำนวนไบต์ข้อมูลที่ได้รับ
  • END_โหมด- จะทำอย่างไรหลังจากได้รับข้อมูลจากสเลฟ - END_MODE_STOP - ส่งสัญญาณ หยุด, หรือ END_MODE_RESTART ส่งสัญญาณ เริ่มต้นใหม่.
ลองเชื่อมต่อบางอย่างกับ MK ของเรา เริ่มต้นด้วย: วงจรไมโคร PCF8574(A) ที่แพร่หลาย ซึ่งเป็นส่วนขยายของพอร์ตอินพุต/เอาท์พุตที่ควบคุมผ่านบัส i2c ชิปนี้มีรีจิสเตอร์ภายในเพียงตัวเดียวเท่านั้น ซึ่งก็คือพอร์ต I/O ทางกายภาพ นั่นคือถ้าคุณส่งไบต์ให้เธอ ข้อสรุปของเธอจะถูกเปิดเผยทันที หากคุณนับหนึ่งไบต์จากนั้น (Transmit เริ่มที่อยู่พร้อมแฟล็กอ่านสัญญาณ ย้อนกลับอ่านข้อมูลและสร้างสัญญาณในที่สุด หยุด) จากนั้นจะสะท้อนสถานะลอจิคัลบนเอาต์พุต มาเชื่อมต่อไมโครวงจรของเราตามแผ่นข้อมูล:


ที่อยู่ของไมโครเซอร์กิตนั้นเกิดขึ้นจากสถานะของพิน A0, A1, A2- สำหรับไมโครเซอร์กิต PCF8574ที่อยู่จะเป็น: 0100A0A1A2- (เช่น เรามี A0, A1, A2 อยู่ในระดับสูง ดังนั้น ที่อยู่ของไมโครวงจรของเราจะเป็น 0b0100 111 = 0x27) สำหรับ PCF8574A - 0111A0A1A2ซึ่งด้วยแผนภาพการเชื่อมต่อของเราจะให้ที่อยู่ 0b0111 111 = 0x3F- ถ้าสมมติว่า A2 เชื่อมต่อกับกราวด์แล้วที่อยู่ของ PCF8574Aจะ 0x3B- โดยรวมแล้วสามารถติดตั้งไมโครวงจรได้ 16 ตัวพร้อมกันบนบัส i2c หนึ่งบัส, 8 PCF8574A และ PCF8574 แต่ละอัน

ลองถ่ายโอนบางสิ่งบางอย่าง เริ่มต้นบัส i2c และถ่ายโอนบางอย่างไปยัง PCF8574 ของเรา

#define PCF8574A_ADDR 0x3F // ที่อยู่ของ PCF8574 ของเราเป็นโมฆะ I2C_PCF8574_WriteReg (ถ่าน wData ที่ไม่ได้ลงนาม) ( I2C1_Start (); // สร้างสัญญาณ START I2C1_Write (PCF8574A_ADDR, & wData, 1, END_MODE_STOP); // ถ่ายโอนข้อมูล 1 ไบต์และสร้าง STOP สัญญาณ ) ถ่าน PCF8574A_reg ; // ตัวแปรที่เราเขียนใน PCF8574 void main () ( I2C1_Init_Advanced(400000, &_GPIO_MODULE_I2C1_PB67); // Start I2C Delay_ms(25); // รออีกหน่อย PCF8574A_reg.b0 = 0; // เปิดไฟ LED อันแรก PCF8574A_reg.b1 = 1; // ปิด LED ตัวที่สองในขณะที่ (1) (delay_ms(500); PCF8574A_reg.b0 = ~PCF8574A_reg.b0; PCF8574A_reg.b1 = ~PCF8574A_reg.b1; // กลับสถานะของ LED I2C_PCF8574_WriteReg (PCF8574A_reg) ; // ถ่ายโอนข้อมูลไปยัง PCF8574 ของเรา ) )
เรารวบรวมและรันโปรแกรมของเราและพบว่าไฟ LED ของเรากะพริบสลับกัน
ฉันเชื่อมต่อแคโทด LED กับ PCF8574 ของเราด้วยเหตุผล ประเด็นก็คือเมื่อมีการจ่ายลอจิคัล 0 ให้กับเอาต์พุตไมโครเซอร์กิตจะดึงเอาต์พุตลงกราวด์โดยสุจริต แต่เมื่อใช้ลอจิคัล 1 มันจะเชื่อมต่อกับ + พลังงานผ่านแหล่งกระแส 100 μA นั่นคือคุณไม่สามารถรับตรรกะ 1 ที่ "ซื่อสัตย์" ที่เอาต์พุตได้ และคุณไม่สามารถส่องสว่าง LED ด้วย 100 µA ได้ สิ่งนี้ทำเพื่อกำหนดค่าเอาต์พุต PCF8574 ให้กับอินพุตโดยไม่ต้องลงทะเบียนเพิ่มเติม เราเพียงแค่เขียนไปที่เอาท์พุตรีจิสเตอร์ 1 (โดยพื้นฐานแล้วตั้งค่าสถานะพินเป็น Vdd) และสามารถย่อให้สั้นลงกราวด์ได้ แหล่งที่มาปัจจุบันจะไม่อนุญาตให้ระยะเอาท์พุตของตัวขยาย I/O ของเรา "เบิร์นเอาท์" หากดึงขาลงพื้น ค่าศักย์ไฟฟ้าของพื้นก็จะอยู่ที่ขานั้น และค่าตรรกะ 0 จะถูกอ่าน ถ้าดึงขาไปที่ + แสดงว่าค่าตรรกะ 1 จะถูกอ่านง่าย แต่ในทางกลับกัน คุณควรจำไว้เสมอเมื่อทำงานกับไมโครวงจรเหล่านี้


ลองอ่านสถานะของพินของชิปขยายของเรา

#define PCF8574A_ADDR 0x3F // ที่อยู่ของ PCF8574 ของเราเป็นโมฆะ I2C_PCF8574_WriteReg (ถ่าน wData ที่ไม่ได้ลงนาม) ( I2C1_Start (); // สร้างสัญญาณ START I2C1_Write (PCF8574A_ADDR, & wData, 1, END_MODE_STOP); // ถ่ายโอนข้อมูล 1 ไบต์และสร้าง STOP signal ) โมฆะ I2C_PCF8574_ReadReg (ถ่าน rData ที่ไม่ได้ลงชื่อ) ( I2C1_Start(); // สร้างสัญญาณ START I2C1_Read(PCF8574A_ADDR, &rData, 1, END_MODE_STOP); // อ่านข้อมูล 1 ไบต์และสร้างสัญญาณ STOP) ถ่าน PCF8574A_reg; //ตัวแปรที่เราเขียนไปที่ PCF8574 char PCF8574A_out; // ตัวแปรที่เราอ่านเข้าไปและ PCF8574 char lad_state; // LED ของเราเปิดหรือปิด void main () ( I2C1_Init_Advanced(400000, &_GPIO_MODULE_I2C1_PB67); // Start I2C Delay_ms(25); // รอสักครู่ PCF8574A_reg.b0 = 0; // เปิดไฟ LED ตัวแรก PCF8574A_reg.b1 = 1; / / ปิด LED ตัวที่สอง PCF8574A_reg.b6 = 1; // เชื่อมต่อพิน 6 และ 7 เข้ากับพลังงาน PCF8574A_reg.b7 = 1; ในขณะที่ (1) (delay_ms(100); I2C_PCF8574_WriteReg (PCF8574A_reg); // เขียนข้อมูลไปที่ PCF8574 I2C_PCF8574_ReadReg (PCF8) 574A_out ); // อ่านจาก PCF8574 ถ้า (~PCF8574A_out.b6) PCF8574A_reg.b0 = ~PCF8574A_reg.b0; // หากกดปุ่ม 1 ปุ่ม (บิต 6 ของไบต์ที่อ่านจาก PCF8574 คือ 0 ให้หมุน เปิด/ปิด LED ของเรา) ถ้า (~PCF8574A_out .b7) PCF8574A_reg.b1 = ~PCF8574A_reg.b1; // คล้ายกันสำหรับ 2 ปุ่มและ LED 2 ดวง ) )
ตอนนี้โดยการกดปุ่มเราจะเปิดหรือปิด LED ของเรา ไมโครเซอร์กิตมีเอาต์พุตอื่น อินเตอร์เนชั่นแนล- พัลส์จะถูกสร้างขึ้นทุกครั้งที่สถานะของพินของส่วนขยาย I/O ของเราเปลี่ยนแปลง ด้วยการเชื่อมต่อกับอินพุตขัดจังหวะภายนอกของ MK ของเรา (ฉันจะบอกวิธีกำหนดค่าการขัดจังหวะภายนอกและวิธีการทำงานกับสิ่งเหล่านั้นในบทความใดบทความหนึ่งต่อไปนี้)

ลองใช้ตัวขยายพอร์ตของเราเพื่อเชื่อมต่อการแสดงอักขระผ่านมัน มีสิ่งเหล่านี้อยู่มากมาย แต่เกือบทั้งหมดถูกสร้างขึ้นบนพื้นฐานของชิปควบคุม HD44780และร่างโคลนของเขา เช่น ผมใช้จอ LCD2004


เอกสารข้อมูลและตัวควบคุม HD44780 สามารถพบได้ง่ายบนอินเทอร์เน็ต มาเชื่อมต่อจอแสดงผลของเรากับ PCF8574 และของเธอตามลำดับกับ STM32 ของเรา

HD44780ใช้อินเทอร์เฟซที่มีรั้วรอบขอบชิดแบบขนาน ข้อมูลถูกส่งโดยพัลส์เกท 8 (ต่อรอบสัญญาณนาฬิกา) หรือ 4 (ต่อ 2 รอบสัญญาณนาฬิกา) ที่พิน อี- (อ่านโดยตัวควบคุมการแสดงผลบนขอบจากมากไปน้อย เปลี่ยนจาก 1 เป็น 0) บทสรุป อาร์.เอส.ระบุว่าเรากำลังส่งข้อมูลไปที่จอแสดงผลของเราหรือไม่ ( อาร์เอส = 1) (อักขระที่ควรแสดงคือรหัส ASCII จริงๆ) หรือคำสั่ง ( อาร์เอส = 0). รวระบุทิศทางการถ่ายโอนข้อมูล การเขียน หรือการอ่าน โดยปกติแล้วเราจะเขียนข้อมูลลงจอแสดงผล ดังนั้น ( RW=0- ตัวต้านทาน R6 ควบคุมคอนทราสต์ของจอแสดงผล คุณไม่สามารถเชื่อมต่ออินพุตการปรับคอนทราสต์เข้ากับกราวด์หรือกำลังไฟได้ ไม่เช่นนั้นคุณจะไม่เห็นอะไรเลย- VT1 ใช้เพื่อเปิดและปิดไฟแบ็คไลท์ของจอแสดงผลตามคำสั่ง MK MicroC มีไลบรารีสำหรับการทำงานกับจอแสดงผลดังกล่าวผ่านอินเทอร์เฟซแบบขนาน แต่โดยปกติแล้วจะมีราคาแพงหากต้องใช้ 8 ขาบนจอแสดงผล ดังนั้นฉันจึงใช้ PCF8574 เกือบทุกครั้งเพื่อทำงานกับหน้าจอดังกล่าว (หากใครสนใจฉันจะเขียนบทความเกี่ยวกับการทำงานกับจอแสดงผลที่ใช้ HD44780 ที่สร้างใน MicroC ผ่านอินเทอร์เฟซแบบขนาน) โปรโตคอลการแลกเปลี่ยนไม่ซับซ้อนเป็นพิเศษ (เราจะใช้สายข้อมูล 4 เส้นและถ่ายโอนข้อมูลใน 2 รอบนาฬิกา) แสดงให้เห็นอย่างชัดเจนด้วยแผนภาพเวลาต่อไปนี้:


ก่อนที่จะถ่ายโอนข้อมูลไปยังจอแสดงผลของเรา จะต้องเริ่มต้นข้อมูลนั้นด้วยการส่งคำสั่งบริการ (อธิบายไว้ในเอกสารข้อมูล ที่นี่เรานำเสนอเฉพาะรายการที่ใช้มากที่สุดเท่านั้น)

  • 0x28- สื่อสารกับตัวบ่งชี้ผ่าน 4 สาย
  • 0x0C- เปิดใช้งานเอาต์พุตรูปภาพ ปิดใช้งานการแสดงเคอร์เซอร์
  • 0x0E- เปิดใช้งานเอาต์พุตรูปภาพ เปิดใช้งานการแสดงเคอร์เซอร์
  • 0x01- ล้างตัวบ่งชี้
  • 0x08- ปิดการใช้งานเอาต์พุตภาพ
  • 0x06- หลังจากที่สัญลักษณ์แสดงขึ้น เคอร์เซอร์จะเลื่อนไป 1 ตำแหน่งที่คุ้นเคย
เนื่องจากเราจะต้องทำงานกับตัวบ่งชี้นี้ค่อนข้างบ่อย เราจะสร้างไลบรารีปลั๊กอิน "i2c_lcd.h" - เพื่อทำสิ่งนี้ใน ผู้จัดการโครงการ ไฟล์ส่วนหัว และเลือก เพิ่มไฟล์ใหม่ - มาสร้างไฟล์ส่วนหัวของเรากัน

#define PCF8574A_ADDR 0x3F // ที่อยู่ของ PCF8574 ของเรา #define DB4 b4 // ความสอดคล้องระหว่างหมุด PCF8574 และตัวบ่งชี้ #define DB5 b5 #define DB6 b6 #define DB7 b7 #define EN b3 #define RW b2 #define RS b1 #define BL b0 // การควบคุมแบ็คไลท์ #define displenth 20 // จำนวนอักขระในบรรทัดแสดงผลของเรา ถ่านที่ไม่ได้ลงนามแบบคงที่ BL_status; // ตัวแปรจัดเก็บสถานะของแบ็คไลท์ (เปิด/ปิด) โมฆะ lcd_I2C_Init(โมฆะ); // ฟังก์ชั่นการเริ่มต้นการแสดงผลและ PCF8574 เป็นโมฆะ lcd_I2C_txt (ถ่าน * pnt); // แสดงบรรทัดข้อความ พารามิเตอร์เป็นตัวชี้ไปยังบรรทัดนี้ void lcd_I2C_int(int pnt); // แสดงค่าของตัวแปรจำนวนเต็ม พารามิเตอร์คือค่าเอาต์พุตเป็นโมฆะ lcd_I2C_Goto (แถวสั้นที่ไม่ได้ลงนาม, คอลัมน์สั้นที่ไม่ได้ลงนาม); // เลื่อนเคอร์เซอร์ไปยังตำแหน่งที่ระบุ พารามิเตอร์ แถว - บรรทัด (จาก 1 ถึง 2 หรือ 4 ขึ้นอยู่กับการแสดงผล) และ col - (จาก 1 ถึง displenth)) เป็นโมฆะ lcd_I2C_cls(); // ล้างหน้าจอเป็นโมฆะ lcd_I2C_backlight (สถานะ int สั้นที่ไม่ได้ลงนาม); // เปิดใช้งาน (เมื่อส่งสัญญาณ 1 และปิดการใช้งาน - เมื่อส่งสัญญาณ 0 ไฟแบ็คไลท์ของจอแสดงผล)
ทีนี้มาอธิบายฟังก์ชันของเรากันอีกครั้ง เราไปกันที่ ผู้จัดการโครงการคลิกขวาที่โฟลเดอร์ แหล่งที่มา และเลือก เพิ่มไฟล์ใหม่ - สร้างไฟล์ "i2c_lcd.с" .

#include "i2c_lcd.h" //รวมไฟล์ส่วนหัวของเรา char lcd_reg; // การลงทะเบียนการจัดเก็บข้อมูลชั่วคราวของข้อมูลที่ส่งไปยัง PCF8574 VOID I2C_PCF8574_WRITEREG (Char WDATA ที่ไม่ได้ลงนาม) // ฟังก์ชั่นของข้อมูลบน I2C ในชิป PCF8574 (I2C1_START (); I2C1_WRITE (PCF8574A_ADDR, & WDATA, 1 , End_mode_stop)); ส่งคำสั่งไปยังจอแสดงผลของเรา ( lcd_reg = 0; // เขียน 0 ไปยังการลงทะเบียนชั่วคราว lcd_reg.BL = BL_status.b0; // ตั้งค่าพินแบ็คไลท์ตามค่าของตัวแปรที่จัดเก็บสถานะแบ็คไลท์ lcd_reg.DB4 = com .b4; // ตั้งค่า 4 บิตที่สำคัญที่สุดของคำสั่งของเราเป็น data bus ตัวบ่งชี้ lcd_reg.DB5 = com.b6; lcd_reg.DB7 = com.b7; // ตั้งค่าเอาต์พุตแฟลชเป็น 1 lcd_reg); การลงทะเบียน PCF8574 ส่งข้อมูลไปยังตัวบ่งชี้ล่าช้าจริง (300); // รอการหมดเวลา lcd_reg.EN = 0; // รีเซ็ตพัลส์แฟลชเป็น 0 ตัวบ่งชี้จะอ่านข้อมูล I2C_PCF8574_WriteReg (lcd_reg); lcd_reg.BL = BL_status.b0; lcd_reg.DB4 = com.b0; // เหมือนกันสำหรับ 4 บิตต่ำ lcd_reg.DB5 = com.b1; lcd_reg.DB6 = com.b2; lcd_reg.DB7 = com.b3; lcd_reg.EN = 1; I2C_PCF8574_WriteReg(lcd_reg); ล่าช้า_us(300); lcd_reg.EN = 0; I2C_PCF8574_WriteReg(lcd_reg); ล่าช้า_us(300); ) โมฆะ LCD_CHAR (ถ่าน com ที่ไม่ได้ลงนาม) //ส่งข้อมูล (รหัสอักขระ ASCII) ไปยังตัวบ่งชี้ ( lcd_reg = 0; lcd_reg.BL = BL_status.b0; lcd_reg.EN = 1; lcd_reg.RS = 1; // การส่งอักขระ แตกต่างจากการส่งคำสั่งโดยตั้งค่าบิต RS เป็น 1 lcd_reg.DB4 = com.b4; // ตั้งค่า 4 บิตที่สำคัญที่สุดที่อินพุต lcd_reg.DB5 = com.b5; lcd_reg.DB7 ; 300); lcd_reg.EN = 0; // รีเซ็ตแฟลชเป็น 0 ตัวบ่งชี้จะอ่านข้อมูล I2C_PCF8574_Reg (lcd_reg); 1; lcd_reg.DB4 = com.b0; // ตั้งค่า 4 บิตต่ำ .DB5 = com.b1; lcd_reg.DB7 = com.b3; lcd_reg); lcd_reg.EN = 0; I2C_PCF8574_WriteReg (lcd_reg); // เริ่มต้นโมดูล I 2c ของเรา ( 200);lcd_Command(0x28); // แสดงผลเป็น 4 บิตต่อโหมดนาฬิกาล่าช้า_ms (5); จอแอลซีดี_คำสั่ง(0x08); //ปิดการใช้งานข้อมูลที่ส่งออกไปยังจอแสดงผลล่าช้า_ms (5); จอแอลซีดี_คำสั่ง(0x01); //ล้างการแสดงผลล่าช้า_ms (5); จอแอลซีดี_คำสั่ง(0x06); //เปิดใช้งานการเลื่อนเคอร์เซอร์อัตโนมัติหลังจากแสดงสัญลักษณ์ล่าช้า_ms (5); จอแอลซีดี_คำสั่ง(0x0C); //เปิดการแสดงข้อมูลโดยไม่แสดงเคอร์เซอร์ล่าช้า_ms (25); ) โมฆะ lcd_I2C_txt(char *pnt) // ส่งออกสตริงของอักขระไปยังจอแสดงผล ( int i สั้นที่ไม่ได้ลงนาม; // ตัวแปรดัชนีอาร์เรย์อักขระชั่วคราว char tmp_str; // อาร์เรย์ชั่วคราวของอักขระ ความยาว 1 มากกว่าความยาวของจอแสดงผล บรรทัด เนื่องจากบรรทัดจะต้องถูกยกเลิก сiv ด้วยอักขระ NULL ASCII 0x00 strncpy(tmp_str, pnt, displenth); // คัดลอกไม่เกินอักขระที่แยกออกจากสตริงดั้งเดิมไปยังสตริงชั่วคราวของเราสำหรับ (i=0; i ตอนนี้เรามาเชื่อมต่อไลบรารีที่สร้างขึ้นใหม่กับไฟล์ด้วยฟังก์ชันหลักของเรา:

#include "i2c_lcd.h" //รวมไฟล์ส่วนหัวของเรา unsigned int i; // ตัวนับตัวแปรชั่วคราว void main() ( lcd_I2C_Init(); // เริ่มต้นการแสดงผล lcd_I2C_backlight (1); // เปิดแบ็คไลท์ lcd_I2C_txt ("Hello habrahabr"); // แสดงบรรทัดในขณะที่ (1) ( Delay_ms( 1,000); lcd_I2C_Goto (2,1); // ไปที่อักขระที่ 1 ของบรรทัดที่ 2 lcd_i2c_int (i); // แสดงค่า i++;

หากทุกอย่างประกอบอย่างถูกต้อง เราควรเห็นข้อความบนตัวบ่งชี้และตัวนับเพิ่มขึ้นทุกวินาที โดยทั่วไปไม่มีอะไรซับซ้อน :)

ในบทความถัดไป เราจะมาทำความเข้าใจโปรโตคอล i2c และอุปกรณ์ที่ใช้งานได้ต่อไป ลองพิจารณาการทำงานกับหน่วยความจำ EEPROM 24XX และมาตรความเร่ง/ไจโรสโคป MPU6050

ช่วงนี้ฉันพบคำวิจารณ์เชิงลบเกี่ยวกับยางมากขึ้นเรื่อยๆ ไอทูซีที่ เอสทีเอ็ม32พวกเขาบอกว่าการทำงานกับเธอหมายถึงการเต้นรำกับแทมบูรีน ฯลฯ
ในเดือนที่ผ่านมา ฉันสามารถเปิดตัวไมโครวงจรสองตัวที่ทำงานอยู่ได้ ไอทูซีและไม่มีการเต้นรำ เพียงแค่อ่านเอกสารข้อมูลอย่างไตร่ตรอง

โมดูล ไอทูซีที่ เอสทีเอ็ม32มีคุณสมบัติดังต่อไปนี้:

  • สามารถทำงานได้สองโหมด เอฟเอ็ม(โหมดเร็ว) และ เอสเอ็ม(โหมดมาตรฐาน) ตัวแรกทำงานที่ความถี่สูงถึง 400KHz ครั้งที่สองสูงถึง 100KHz
  • บัฟเฟอร์ 1 ไบต์พร้อมการสนับสนุน ดีเอ็มเอ
  • รองรับการคำนวณเช็คซัมฮาร์ดแวร์
  • โดยพื้นฐานแล้วสามารถนำไปใช้ได้ เอสเอ็มบัส(ระบบบัสการจัดการ) และ พีเอ็มบัส(บัสการจัดการพลังงาน)
  • เวกเตอร์ขัดจังหวะสองตัว สร้างขึ้นเมื่อการส่งข้อมูลสำเร็จและเมื่อเกิดข้อผิดพลาด
  • ตัวกรองสัญญาณรบกวน
  • สามารถทำงานในโหมด Master หรือ Slave ได้
ในโหมดมาสเตอร์:
  • จะสร้างสัญญาณบอกเวลา
  • สร้าง START และ STOP
ในโหมดทาส:
  • คุณสามารถตั้งโปรแกรมที่อยู่หลักและที่อยู่สำรองที่จะตอบกลับได้
  • กำหนดหยุด

โดยค่าเริ่มต้น โมดูลจะอยู่ในโหมด ทาสแต่จะเปลี่ยนเป็นโหมดโดยอัตโนมัติ ผู้เชี่ยวชาญหลังจากรุ่นของรัฐ เริ่ม.
ความแตกต่างพื้นฐานระหว่าง Master และ Slave ก็คือ ผู้เชี่ยวชาญ สร้างสัญญาณนาฬิกาและเริ่มและสิ้นสุดการถ่ายโอนข้อมูลเสมอ. ทาสเดียวกัน, ตอบสนองต่อที่อยู่และการออกอากาศและสามารถปิดการตอบสนองต่อที่อยู่การออกอากาศได้ อีกด้วย ทาส สร้างสถานะ อ๊ากกแต่ก็สามารถปิดการใช้งานได้เช่นกัน

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

  • เครื่องส่งสัญญาณทาส
  • ตัวรับทาส
  • เครื่องส่งสัญญาณหลัก
  • ผู้รับหลัก

โครงสร้างของโมดูล I2C แสดงไว้ด้านล่าง

ควบคุมการลงทะเบียน I2C_CR1:

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

เตือน(การแจ้งเตือน SMBus) - การตั้งค่าบิตนี้เป็นหนึ่งจะทำให้สามารถสร้างสัญญาณได้ เตือนอยู่ในโหมด เอสเอ็มบัส.

พีอีซี(การตรวจสอบข้อผิดพลาดของแพ็คเก็ต) - บิตนี้ควบคุมโดยซอฟต์แวร์ แต่ฮาร์ดแวร์สามารถรีเซ็ตได้เมื่อมีการส่ง PEC, START, STOP หรือ PE=0 หนึ่งในบิตนี้เปิดใช้งานการส่งข้อมูล ซีอาร์ซี.

จุดขาย(รับทราบ/ตำแหน่ง PEC (สำหรับการรับข้อมูล)) - สถานะของบิตนี้จะกำหนดตำแหน่ง อ๊ากก/พีอีซีในการกำหนดค่าแบบสองไบต์ในโหมด Master

อ๊ากก(ตอบรับการเปิดใช้งาน) - หน่วยในบิตนี้อนุญาตให้ส่งได้ อ๊ากก/แน็คหลังจากได้รับที่อยู่หรือไบต์ข้อมูลแล้ว

หยุด(หยุดการสร้าง) - การตั้งค่าบิตนี้เป็นหนึ่งจะสร้างสัญญาณ หยุดในโหมดมาสเตอร์

เริ่ม(เริ่มต้นการสร้าง) - การตั้งค่าบิตนี้เป็นหนึ่งจะสร้างสถานะ เริ่มในโหมดมาสเตอร์

รูจมูก(ปิดการใช้งานการยืดนาฬิกา (โหมดทาส)) - หากการประมวลผลข้อมูลต้องใช้เวลา ทาสสามารถหยุดการส่งสัญญาณหลักได้โดยการกดสาย เอสซีแอลอาจารย์จะรอและจะไม่ส่งสิ่งใดจนกว่าสายจะถูกปล่อย ศูนย์ในบิตนี้กด เอสซีแอลลงไปที่พื้น

กศน(เปิดใช้งานการโทรทั่วไป) - หากบิตนี้ถูกตั้งค่าเป็นหนึ่ง โมดูลจะตอบสนอง อ๊ากก om เพื่อออกอากาศที่อยู่ 0x00

อีเอ็นเปค(เปิดใช้งาน PEC) - การตั้งค่าบิตนี้เป็นหนึ่งเปิดใช้งานการนับฮาร์ดแวร์ ซีอาร์ซี.

เอนาร์ป(เปิดใช้งาน ARP) - ตั้งค่าบิตนี้เป็นหนึ่งเปิดใช้งาน เออาร์พี.

SMBTYPE(ประเภท SMBus) - หากบิตนี้ถูกตั้งค่าเป็นศูนย์ โมดูลจะทำงานในโหมดทาส หากอยู่ในโหมดมาสเตอร์

เอสเอ็มบัส(โหมด SMBus) - หากบิตนี้ถูกตั้งค่าเป็นศูนย์ โมดูลจะทำงาน ไอทูซีถ้าอย่างใดอย่างหนึ่ง เอสเอ็มบัส.

วิชาพลศึกษา.(เปิดใช้งานอุปกรณ์ต่อพ่วง) - หน่วยในบิตนี้เปิดใช้งานโมดูล

ควบคุมการลงทะเบียน I2C_CR2:

ล่าสุด(การถ่ายโอน DMA ครั้งล่าสุด) - หนึ่งในบิตนี้อนุญาต ดีเอ็มเอสร้างสัญญาณสิ้นสุดการส่งสัญญาณ อีโอที(สิ้นสุดการโอน)

ดีเมน(เปิดใช้งานคำขอ DMA) - หน่วยในบิตนี้อนุญาตให้ทำการร้องขอได้ ดีเอ็มเอเมื่อปักธง TxEหรือ RxNE.

อิทบูเฟิน(เปิดใช้งานการขัดจังหวะบัฟเฟอร์) - หากบิตนี้ชัดเจน การขัดจังหวะทั้งหมดจะถูกเปิดใช้งาน ยกเว้นการขัดจังหวะการรับและส่ง

ไอทีวีเทน(เปิดใช้งานการขัดจังหวะเหตุการณ์) - หนึ่งในบิตนี้เปิดใช้งานการขัดจังหวะเหตุการณ์

อิเทอร์เรน(เปิดใช้งานการขัดจังหวะข้อผิดพลาด) - หนึ่งในบิตนี้เปิดใช้งานการขัดจังหวะเมื่อเกิดข้อผิดพลาด

ความถี่(ความถี่สัญญาณนาฬิกาอุปกรณ์ต่อพ่วง) - ต้องเขียนความถี่สัญญาณนาฬิกาของโมดูลลงในฟิลด์บิตนี้ โดยสามารถรับค่าได้ตั้งแต่ 2 ถึง 50

ลงทะเบียน I2C_OAR1:

เพิ่มโหมด(โหมดการกำหนดแอดเดรส) - บิตนี้กำหนดขนาดของที่อยู่ Slave โดยศูนย์จะสอดคล้องกับขนาดที่อยู่ 7 บิต หนึ่ง - 10 บิต

เพิ่ม(ที่อยู่อินเทอร์เฟซ) - บิตที่สำคัญที่สุดของที่อยู่หากที่อยู่เป็น 10 บิต

เพิ่ม(ที่อยู่อินเทอร์เฟซ) - ที่อยู่อุปกรณ์

เพิ่ม0(ที่อยู่อินเทอร์เฟซ) - บิตที่มีนัยสำคัญน้อยที่สุดของที่อยู่ หากที่อยู่เป็น 10 บิต

ลงทะเบียน I2C_OAR2:

เพิ่ม2- ที่อยู่อื่นที่ Slave จะตอบกลับ

สิ้นสุด(เปิดใช้งานโหมดการกำหนดแอดเดรสคู่) - หนึ่งในบิตนี้อนุญาตให้ Slave ตอบสนองต่อที่อยู่อื่นในโหมด 7 บิต

I2C_DR- data register เพื่อส่งข้อมูลที่เราเขียนลง register ดร.สำหรับการต้อนรับเราอ่านมัน

การลงทะเบียนสถานะ I2C_SR1:

สมบาเลิร์ต(SMBus alert) - เกิดขึ้นในกรณีมีการแจ้งเตือนบนรถบัส เอสเอ็มบัส.

หมดเวลา(ข้อผิดพลาดการหมดเวลาหรือ Tlow) - เกิดขึ้นหากสาย เอสซีแอลถูกกดลงไปที่พื้น สำหรับ ผู้เชี่ยวชาญ 10ms สำหรับ ทาส 25มิลลิวินาที

เพเซอร์(PEC Error inception) - เกิดขึ้นเมื่อมีข้อผิดพลาด พีอีซีเมื่อเข้ารับการรักษา

โอวีอาร์(Overrun/Underrun) - เกิดขึ้นเมื่อข้อมูลล้น

เอเอฟ(รับทราบความล้มเหลว) - ตั้งค่าเมื่อได้รับสัญญาณ แน็ค- หากต้องการรีเซ็ต คุณต้องเขียน 0

อาร์โล(อนุญาโตตุลาการสูญหาย (โหมดหลัก)) - ตั้งค่าเมื่ออนุญาโตตุลาการสูญหาย หากต้องการรีเซ็ต คุณต้องเขียน 0

เบอร์(ข้อผิดพลาดของบัส) - ข้อผิดพลาดของบัส ตั้งค่าเมื่อมีสัญญาณเกิดขึ้น เริ่มหรือ หยุดในช่วงเวลาที่ผิด

TxE(การลงทะเบียนข้อมูลว่างเปล่า (ตัวส่งสัญญาณ)) - ตั้งค่าเมื่อการลงทะเบียน DR ว่างเปล่า หรือตั้งค่าเมื่อข้อมูลจากข้อมูลนั้นถูกย้ายไปยังกะการลงทะเบียน

RxNE(การลงทะเบียนข้อมูลไม่ว่างเปล่า (ตัวรับ)) - ตั้งค่าเมื่อรับไบต์ข้อมูลอื่นที่ไม่ใช่ที่อยู่

หยุด(หยุดการตรวจจับ (โหมดทาส)) - เมื่อใช้งาน ทาสตั้งค่าเมื่อตรวจพบสัญญาณ หยุดถ้ามีสัญญาณ ACK มาก่อน หากต้องการรีเซ็ตคุณต้องอ่าน เอสอาร์1และบันทึกไว้ใน CR1.

เพิ่ม10(ส่งส่วนหัว 10 บิต (โหมด Master)) - ตั้งค่าเมื่อส่งไบต์แรกของที่อยู่ 10 บิต

บีทีเอฟ(การถ่ายโอนไบต์เสร็จสิ้น) - ตั้งค่าสถานะเมื่อรับ/ส่งไบต์ ใช้งานได้เฉพาะเมื่อเท่านั้น รูจมูกเท่ากับศูนย์

อดีอาร์(ที่อยู่ที่ส่ง (โหมดหลัก)/ตรงกัน (โหมดสเลฟ)) - อยู่ในโหมด ผู้เชี่ยวชาญตั้งค่าหลังจากโอนที่อยู่ ในโหมด ทาสถูกตั้งค่าเมื่อที่อยู่ตรงกัน หากต้องการรีเซ็ต คุณต้องอ่านรีจิสเตอร์ SR1 จากนั้นจึงอ่าน SR2

เอส.บี.(บิตสตาร์ท (โหมดมาสเตอร์)) - ตั้งค่าเมื่อมีสัญญาณ START เกิดขึ้น หากต้องการรีเซ็ตการตั้งค่าสถานะคุณต้องอ่าน เอสอาร์1และเขียนข้อมูลลงทะเบียน ดร. .

การลงทะเบียนสถานะ I2C_SR2:

พีอีซี(การลงทะเบียนการตรวจสอบข้อผิดพลาดแพ็คเก็ต) - การตรวจสอบเฟรมถูกเขียนลงในฟิลด์บิตนี้

ดูอัลฟ์(แฟล็กคู่ (โหมดทาส)) - ศูนย์ในบิตนี้บ่งชี้ว่าที่อยู่ที่ทาสได้รับนั้นสอดคล้องกัน โออาร์1, มิฉะนั้น โอเออาร์2.

เอสเอ็มบีโฮสต์(ส่วนหัวโฮสต์ SMBus (โหมดทาส)) - ตั้งค่าเมื่อได้รับส่วนหัว โฮสต์ SMBus.

SMBค่าเริ่มต้น(ที่อยู่เริ่มต้นของอุปกรณ์ SMBus (โหมดทาส)) - ตั้งค่าว่าจะยอมรับที่อยู่เริ่มต้นหรือไม่
สำหรับ เอสเอ็มบัส-อุปกรณ์

เจนคอล(ที่อยู่การโทรทั่วไป (โหมดทาส)) - ตั้งค่าว่าจะรับที่อยู่การออกอากาศในโหมดทาสหรือไม่

ททท(เครื่องส่ง/เครื่องรับ) - หนึ่งในบิตนี้บ่งชี้ว่าโมดูลทำงานเป็นเครื่องส่ง หรือเครื่องรับ

ยุ่ง(รถเมล์ไม่ว่าง) - ธงไม่ว่าง

เอ็มเอสแอล(Master/slave) - หนึ่งในบิตนี้บ่งชี้ว่าโมดูลทำงานในโหมด Master หรือ Slave

การลงทะเบียนการควบคุมความถี่ I2C_CCR:

เอฟ/เอส(การเลือกโหมดต้นแบบ I2C) - เมื่อบิตนี้ถูกตั้งค่าเป็นหนึ่ง โมดูลจะทำงาน เร็ว, มิฉะนั้น มาตรฐาน.

หน้าที่(รอบการทำงานของโหมด Fm) - บิตนี้จะตั้งค่ารอบการทำงานของสัญญาณ เอสซีแอลอยู่ในโหมด เร็ว- หากตั้งค่าศูนย์ไว้ tlow/thigh = 2 มิฉะนั้น tlow/thigh = 16/9

ซีซีอาร์(บันทึกการควบคุมนาฬิกาในโหมด Fm/Sm (โหมด Master)) - เมื่อทำงานในโหมด Master ให้ตั้งค่าความถี่สัญญาณนาฬิกาของสาย SCL

โหมด Sm หรือ SMBus:
ต้นขา = CCR * TPCLK1
Tlow = CCR * TPCLK1

โหมดเอฟเอ็ม:
ถ้าหน้าที่ = 0:
ต้นขา = CCR * TPCLK1
ต่ำ = 2 * CCR * TPCLK1

ถ้า DUTY = 1: (เพื่อให้ถึง 400 kHz)
ต้นขา = 9 * CCR * TPCLK1
ต่ำ = 16 * CCR * TPCLK1

เราได้รับโหมด เอส.เอ็ม.กำลังติดตาม:
CCR * TPCLK1 + CCR * TPCLK1 = 10,000ns
CCR = 10,000/(2* TPCLK1)

I2C_TRISE ลงทะเบียน:

ทริส- กำหนดเวลาที่เพิ่มขึ้นของด้านหน้า คำนวณโดยใช้สูตร (Tr สูงสุด/TPCLK1)+1,
ที่ไหน สูงสุดสำหรับ เอส.เอ็ม.จำนวน 1,000nS, และสำหรับ เอฟเอ็ม 300nS,
ทีพีซีแอลเค1- ระยะเวลาคำนวณเป็น 1/ เอฟ(APB1)

การลงทะเบียนการควบคุมตัวกรอง I2C_FLTR:

อันนอฟ(ปิดตัวกรองสัญญาณรบกวนแบบอะนาล็อก) - ค่าศูนย์ในบิตนี้จะเปิดตัวกรองแบบอะนาล็อก

ดีเอ็นเอฟ(ตัวกรองสัญญาณรบกวนแบบดิจิทัล) - ฟิลด์บิตสำหรับการตั้งค่าตัวกรองสัญญาณดิจิทัล สำหรับรายละเอียด โปรดดูเอกสารประกอบ

การเริ่มต้นโมดูลจากโปรเจ็กต์การทำงาน
เป็นโมฆะ I2C2_Init (เป็นโมฆะ) ( /* SDL -> PB10 SDA -> PB11 RST -> PE15 */ // เปิดใช้งานการตอกบัตรของพอร์ตและโมดูล I2C RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN | RCC_AHB1ENR_GPIOEEN; RCC->APB1ENR |= RCC_APB1ENR_I2C2EN; / / ฟังก์ชันทางเลือก, เอาต์พุตเดรนแบบเปิด, 2 MHz GPIOB->AFR |= (0x04<<2*4); GPIOB->เอเอฟอาร์ |= (0x04<<3*4); GPIOB->MODER |= GPIO_MODER_MODER10_1; GPIOB->OTYPER |= GPIO_OTYPER_OT_10; GPIOB->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR10; GPIOB->MODER |= GPIO_MODER_MODER11_1; GPIOB->OTYPER |= GPIO_OTYPER_OT_11; GPIOB->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR11; //PE15 เอาต์พุตแบบพุชดึง 50MHz GPIOE->MODER |= GPIO_MODER_MODER15_0; GPIOE->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR15; AU_RST_HIGH // ตั้งค่าโมดูลเป็นโหมด I2C I2C2->CR1 &= ~2C_CR1_SMBUS; //ระบุความถี่สัญญาณนาฬิกาของโมดูล I2C2->CR2 &= ~I2C_CR2_FREQ; I2C2->CR2 |= 42; // Fclk1=168/4=42MHz // การกำหนดค่า I2C, โหมดมาตรฐาน, รอบการทำงาน 100 KHz 1/2 I2C2->CCR &= ~(I2C_CCR_FS | I2C_CCR_DUTY); // ตั้งค่าความถี่การทำงานของโมดูล SCL โดยใช้สูตร 10,000nS/(2* TPCLK1) I2C2->CCR |= 208; //10 000ns/48ns = 208 //Standart_Mode = 1000nS, Fast_Mode = 300nS, 1/42MHz = 24nS I2C2->TRISE = 42; //(1,000nS/24nS)+1 // เปิดโมดูล I2C2->CR1 |= I2C_CR1_PE; ) โมฆะ I2C_Write(uint8_t reg_addr, uint8_t data) ( //start I2C2->CR1 |= I2C_CR1_START; while(!(I2C2->SR1 & I2C_SR1_SB))(); (เป็นโมฆะ) I2C2->SR1; // ผ่านอุปกรณ์ ที่อยู่ I2C2->DR = I2C_ADDRESS(ADDR,I2C_MODE_WRITE); while(!(I2C2->SR1 & I2C_SR1_ADDR))(); (!(I2C2->SR1 & I2C_SR1_TXE))(); // เขียนข้อมูล I2C2->DR = data; while(!(I2C2->SR1 & I2C_SR1_BTF))(); |= I2C_CR1_STOP; ) ( ข้อมูล uint8_t; //start I2C2->CR1 |= I2C_CR1_START; while(!(I2C2->SR1 & I2C_SR1_SB))(); (เป็นโมฆะ) I2C2->SR1; / /ส่งที่อยู่อุปกรณ์ I2C2->DR = I2C_ADDRESS (ADR,I2C_MODE_WRITE); ในขณะที่(!(I2C2->SR1 & I2C_SR1_ADDR))(); (เป็นโมฆะ) I2C2->SR1; (เป็นโมฆะ) I2C2->SR2; I2C2->SR1 & I2C_SR1_TXE))(); I2C2->CR1 |= I2C_CR1_STOP; //รีสตาร์ท!!! // ส่งที่อยู่อุปกรณ์ แต่ตอนนี้สำหรับการอ่าน I2C2->DR = I2C_ADDRESS(ADR,I2C_MODE_READ); ในขณะที่(!(I2C2->SR1 & I2C_SR1_ADDR))(); (เป็นโมฆะ) I2C2->SR1; (เป็นโมฆะ) I2C2->SR2; //อ่าน I2C2->CR1 &= ~I2C_CR1_ACK; ในขณะที่(!(I2C2->SR1 & I2C_SR1_RXNE))(); ข้อมูล = I2C2->DR; I2C2->CR1 |= I2C_CR1_STOP; ส่งคืนข้อมูล -

บทความนี้จะอธิบายอินเทอร์เฟซแบบอนุกรม I2C ของไมโครคอนโทรลเลอร์ ARM 32 บิตของซีรีส์ STM32 จาก STMicroelectronics มีการพิจารณาสถาปัตยกรรม องค์ประกอบ และวัตถุประสงค์ของการลงทะเบียนการกำหนดค่าอินเทอร์เฟซ ตลอดจนตัวอย่างของโปรแกรมสำหรับการใช้งาน

การแนะนำ

อินเทอร์เฟซ I2C หรือ IIC ได้รับการย่อมาจากคำภาษาอังกฤษ Inter-Integrated Circuit และเป็นบัสอนุกรมที่ประกอบด้วยสายการสื่อสารสองทิศทางสองสายที่เรียกว่า SDA และ SCL ซึ่งเป็นตัวย่อของคำว่า Serial Data Address และ Serial Clock โดยให้การแลกเปลี่ยนข้อมูลระหว่างไมโครคอนโทรลเลอร์และอุปกรณ์ต่อพ่วงต่างๆ เช่น ADC, DAC, ชิปหน่วยความจำ, ไมโครคอนโทรลเลอร์และชิปอื่นๆ แผนภาพสำหรับการเชื่อมต่ออุปกรณ์ผ่านอินเทอร์เฟซ I2C แสดงในรูปที่ 1

ข้าว. 1. แผนผังการเชื่อมต่ออุปกรณ์ผ่านอินเทอร์เฟซ I 2 C

มาตรฐานอินเทอร์เฟซ I2C ได้รับการพัฒนาโดย Philips ในช่วงต้นทศวรรษ 1980 ตามมาตรฐานนี้ อินเทอร์เฟซมีที่อยู่ 7 บิต อนุญาตให้เข้าถึงอุปกรณ์ 127 เครื่องด้วยความเร็วสูงสุด 100 kbps ต่อมา อินเทอร์เฟซได้รับการพัฒนาและกลายเป็น 10 บิต ทำให้สามารถเข้าถึงอุปกรณ์ 1,023 เครื่องด้วยความเร็วสูงสุด 400 kbit/s ขีดสุด
จำนวนชิปที่อนุญาตที่เชื่อมต่อกับบัสหนึ่งถูกจำกัดโดยความจุบัสสูงสุดที่ 400 pF เวอร์ชัน 2.0 ของมาตรฐาน เปิดตัวในปี 1998
โหมดการทำงานความเร็วสูงด้วยความเร็วสูงถึง 3.4 Mbit/s พร้อมการใช้พลังงานที่ลดลง เวอร์ชัน 2.1 ตั้งแต่ปี 2544 มีการปรับปรุงเพียงเล็กน้อยเท่านั้น

คำอธิบายของอินเทอร์เฟซ I 2 C

ไมโครคอนโทรลเลอร์ STM32 มีอินเทอร์เฟซ I2C ซึ่งโดดเด่นด้วยการพัฒนา อนุญาตให้มีบัสมาสเตอร์ได้หลายตัวและรองรับโหมดความเร็วสูง นอกจากนี้ ไมโครคอนโทรลเลอร์ STM32 ยังสามารถใช้อินเทอร์เฟซ I2C สำหรับการใช้งานที่หลากหลาย รวมถึงการสร้างและการตรวจสอบผลรวมตรวจสอบ นอกจากนี้ยังสามารถดำเนินการผ่านโปรโตคอล SMBus (System Management Bus) และ PMBus (Power Management Bus) ได้อีกด้วย รุ่น STM32 ส่วนใหญ่มีอินเทอร์เฟซ I2C สองตัวชื่อ I2C1 และ I2C2 อินเทอร์เฟซสามารถทำงานได้ในหนึ่งในสี่โหมดต่อไปนี้:

  • เครื่องส่งสัญญาณทาส;
  • ตัวรับทาส;
  • เครื่องส่งสัญญาณหลัก (เครื่องส่งสัญญาณชั้นนำ);
  • ผู้รับหลัก

ตามค่าเริ่มต้น อินเทอร์เฟซจะทำงานในโหมด "สเลฟ" และสลับไปที่ "มาสเตอร์" โดยอัตโนมัติหลังจากสร้างเงื่อนไขการเริ่มต้น การเปลี่ยนจาก "Master" เป็น "Slave" เกิดขึ้นเมื่ออนุญาโตตุลาการสูญหายหรือหลังจากเกิดเงื่อนไขการหยุด ซึ่งช่วยให้ไมโครคอนโทรลเลอร์ "Master" หลายตัวสามารถทำงานในระบบเดียวสลับกันได้ ในโหมดมาสเตอร์ I2C จะเริ่มต้นการแลกเปลี่ยนข้อมูลและสร้างสัญญาณนาฬิกา การถ่ายโอนข้อมูลแบบอนุกรมจะมีเงื่อนไขการเริ่มต้นนำหน้าเสมอ และการแลกเปลี่ยนจะจบลงด้วยเงื่อนไขหยุดเสมอ เงื่อนไขทั้งสองนี้ถูกสร้างขึ้นในโหมด Master โดยทางโปรแกรม ในโหมดทาส I2C สามารถจดจำที่อยู่ของตัวเอง (7 หรือ 10 บิต) และที่อยู่การโทรทั่วไป การตรวจจับที่อยู่การโทรทั้งหมดสามารถเปิดหรือปิดใช้งานได้โดยทางโปรแกรม ที่อยู่และข้อมูลจะถูกส่งในรูปแบบแพ็คเกจ 8 บิต โดยบิตที่สำคัญที่สุดก่อน ไบต์แรกที่เป็นไปตามเงื่อนไขการเริ่มต้นจะมีที่อยู่ (หนึ่งไบต์ในโหมด 7 บิตและสองไบต์ในโหมด 10 บิต) ที่อยู่จะถูกส่งในโหมดหลักเสมอ

รอบนาฬิกา 8 รอบของการส่งไบต์ข้อมูลจะตามมาด้วยรอบนาฬิกาที่ 9 ในระหว่างนี้ผู้รับจะต้องส่งบิตการแจ้งเตือน ACK ซึ่งได้ชื่อมาจากคำว่า ACKnowledge รูปที่ 2 แสดงแผนภาพเวลาของหนึ่งข้อความของอินเทอร์เฟซ I2C การมีการแจ้งเตือนในการตอบกลับสามารถเปิดหรือปิดใช้งานโดยทางโปรแกรมได้ ขนาดที่อยู่อินเทอร์เฟซ I2C (7 บิตหรือ 10 บิตและที่อยู่
สามารถเลือก All Call) ได้โดยทางโปรแกรม


ข้าว. 2. แผนภาพเวลาของอินเทอร์เฟซหนึ่งแพ็คเก็ต I

สถาปัตยกรรมของบล็อกอินเทอร์เฟซ I 2 C

แผนภาพการทำงานของบล็อกอินเทอร์เฟซ I2C สำหรับไมโครคอนโทรลเลอร์ STM32 แสดงในรูปที่ 3


ข้าว. 3. แผนภาพการทำงานของบล็อกอินเทอร์เฟซ I 2 C

รีจิสเตอร์กะในแผนภาพนี้เป็นรีจิสเตอร์หลักที่ใช้ส่งและรับข้อมูล ข้อมูลที่ส่งจะถูกบันทึกไว้ล่วงหน้าในการลงทะเบียนข้อมูล หลังจากนั้นจะถูกส่งตามลำดับผ่าน shift register ไปยังสายสื่อสาร SDA ข้อมูลที่ได้รับผ่านสายการสื่อสารเดียวกันจะถูกสะสมใน shift register จากนั้นจึงย้ายไปยัง data register ดังนั้นอินเทอร์เฟซจึงสามารถส่งและรับข้อมูลได้ครั้งละหนึ่งรายการเท่านั้น นอกจากนี้ shift register ยังเชื่อมต่อฮาร์ดแวร์เข้ากับตัวเปรียบเทียบ ซึ่งช่วยให้คุณสามารถเปรียบเทียบที่อยู่ที่ได้รับได้
ด้วยการลงทะเบียนที่อยู่และกำหนดว่าบล็อกข้อมูลถัดไปมีไว้สำหรับใคร โหนดควบคุมความถี่ช่วยให้คุณสร้างสัญญาณการซิงโครไนซ์ SCL เป็นหลักและซิงโครไนซ์จากสัญญาณนี้เป็นอุปกรณ์รอง รีจิสเตอร์ CCR ให้การกำหนดค่าซอฟต์แวร์ของโหนดนี้ บล็อกอินเทอร์เฟซเชื่อมต่อกับเอาต์พุต PCLK1 ของบัส APB1 ผ่านทาง
พรีสเกลเลอร์สองตัว ไมโครคอนโทรลเลอร์รองรับโหมดการสื่อสารสองโหมด: ความเร็วมาตรฐาน – สูงสุด 100 kHz และความเร็วที่รวดเร็ว – สูงสุด 400 kHz ความถี่สัญญาณนาฬิกาของโมดูลต้องมีอย่างน้อย 2 MHz ในโหมดมาตรฐานและอย่างน้อย 4 MHz ในโหมดเร็ว ทั้งนี้ขึ้นอยู่กับโหมดการแลกเปลี่ยน บล็อกการคำนวณช่วยให้ฮาร์ดแวร์คำนวณผลรวมตรวจสอบของบล็อกข้อมูลและจัดเก็บไว้ในการลงทะเบียน PEC การควบคุมบล็อกอินเทอร์เฟซ I2C ตลอดจนการสร้างเหตุการณ์และแฟล็กขัดจังหวะจะดำเนินการโดยหน่วยตรรกะควบคุม นอกจากนี้ยังช่วยให้คุณสามารถให้บริการคำขอ DMA และสร้างสัญญาณ ACK ได้ การสื่อสารของบล็อกนี้กับไมโครคอนโทรลเลอร์นั้นดำเนินการโดยทางโปรแกรมโดยใช้รีจิสเตอร์ควบคุม CR1, CR2 และรีจิสเตอร์สถานะ SR1, SR2

ขัดจังหวะจาก I 2 C

อินเทอร์เฟซ I2C มีองค์กรฮาร์ดแวร์ที่สามารถสร้างคำขอขัดจังหวะได้ ขึ้นอยู่กับโหมดการทำงานและเหตุการณ์ปัจจุบัน ตารางที่ 1 แสดงคำขอขัดจังหวะจากอินเทอร์เฟซ I2C

ตารางที่ 1. คำร้องขอขัดจังหวะจากอินเตอร์เฟส I 2 C

คำอธิบายของการลงทะเบียน

ในการทำงานกับอินเทอร์เฟซ I2C ไมโครคอนโทรลเลอร์ STM32 มีรีจิสเตอร์พิเศษ แผนผังของรีจิสเตอร์เหล่านี้พร้อมชื่อของบิตที่รวมอยู่ในนั้นแสดงไว้ในตารางที่ 2 ลองพิจารณารีจิสเตอร์ที่จำเป็นสำหรับการทำงานของอินเทอร์เฟซ I2C ซึ่งรวมถึง:

  • ฉัน 2 C_CR1 - ควบคุมการลงทะเบียน 1;
  • ฉัน 2 C_CR2 - ควบคุมการลงทะเบียน 2;
  • ฉัน 2 C_OAR1 – ที่อยู่ของตัวเองลงทะเบียน 1;
  • ฉัน 2 C_OAR2 – ที่อยู่ของตัวเองลงทะเบียน 2;
  • ฉัน 2 C_DR – การลงทะเบียนข้อมูล;
  • ฉัน 2 C_SR1 – สถานะการลงทะเบียน 1;
  • ฉัน 2 C_SR2 – สถานะการลงทะเบียน 2;
  • I 2 C_CCR - การลงทะเบียนการควบคุมสัญญาณนาฬิกา;
  • ฉัน 2 C_TRISE – การลงทะเบียนพารามิเตอร์ TRISE

รีจิสเตอร์เหล่านี้บางส่วนใช้เพื่อทำงานในโหมด SMBus รีจิสเตอร์ I2C_CR1 เป็นรีจิสเตอร์ควบคุมแรกของอินเทอร์เฟซ I2C มีบิตควบคุมดังต่อไปนี้:

  • บิต 15 SWRST - ให้การรีเซ็ตซอฟต์แวร์ของบัส I 2 C
  • หลัก 14 – สงวนไว้;
  • บิต 13 SMBus – สร้างสัญญาณเตือนในโหมด SMBus
  • บิต 12 PEC - ทำหน้าที่สำหรับฟังก์ชันการตรวจสอบข้อผิดพลาดของแพ็คเก็ต
  • บิต 11 POS - ทำหน้าที่วิเคราะห์สัญญาณ ACK หรือ PEC เมื่อรับสัญญาณ
  • บิต 10 ACK – ส่งคืนบิตการแจ้งเตือน ACK หลังจากได้รับที่อยู่หรือไบต์ข้อมูลที่ถูกต้อง
  • บิต 9 STOP – ทำหน้าที่สร้างและวิเคราะห์เงื่อนไขการหยุด
  • บิต 8 START – ทำหน้าที่สร้างและวิเคราะห์เงื่อนไขการเริ่มต้น
  • บิต 7 NOSCETCH - ปิดใช้งานการยืดสัญญาณนาฬิกาในโหมดทาส
  • บิต 6 ENGC – อนุญาตการโทรทั่วไป
  • บิต 5 ENPEC – เปิดใช้งานสัญญาณ PEC
  • บิต 4 ENARP – เปิดใช้งานสัญญาณ ARP;
  • บิต 3 SMBTYPE – กำหนดประเภทอินเทอร์เฟซเป็นมาสเตอร์หรือทาสสำหรับโหมด SMBus
  • หลัก 2 – สงวนไว้;
  • บิต 1 SMBUS - สลับโหมด I 2 C และ SMBus
  • บิต 0 PE - อนุญาตให้อินเทอร์เฟซทำงาน

รีจิสเตอร์ I2C_CR2 เป็นรีจิสเตอร์ควบคุมที่สองของอินเทอร์เฟซ I2C และมีบิตควบคุมดังต่อไปนี้:

  • บิต 15…13 – สงวนไว้;
  • บิต 12 LAST - ใช้ในโหมดตัวรับหลักเพื่อให้สร้างสัญญาณ NACK ตามไบต์สุดท้ายที่ได้รับ
  • บิต 11 DMAEN – อนุญาตการร้องขอ DMA;
  • บิต 10 ITBUFEN - อนุญาตการขัดจังหวะจากบัฟเฟอร์
  • บิต 9 ITEVTEN - เปิดใช้งานการขัดจังหวะเหตุการณ์
  • บิต 8 ITERREN - เปิดใช้งานการขัดจังหวะข้อผิดพลาด
  • ตัวเลข 7 และ 6 – สงวนไว้;
  • บิต 5…0 FREQ – ตั้งค่าความถี่การทำงานของบัส

รีจิสเตอร์ I2C_OAR1 เป็นรีจิสเตอร์แรกของที่อยู่ของตัวเอง โดยมีบิตต่อไปนี้:

  • บิต 15 ADDMODE - ตั้งค่าโหมดการกำหนดแอดเดรส 7 หรือ 10 บิตเป็นทาส
  • บิต 14…10 – สงวนไว้;
  • บิต 9 และ 8 ADD – กำหนดบิตที่อยู่ 9 และ 8 สำหรับการกำหนดแอดเดรสอินเทอร์เฟซ 10 บิต
  • บิต 7…1 ADD – กำหนดบิตที่อยู่ 7…1;
  • บิต 0 ADD0 – กำหนดที่อยู่บิต 0 สำหรับการกำหนดแอดเดรสอินเทอร์เฟซ 10 บิต

รีจิสเตอร์ I2C_OAR2 เป็นรีจิสเตอร์ตัวที่สองของที่อยู่ของตัวเอง โดยมีบิตต่อไปนี้:

  • บิต 15...8 – สงวนไว้;
  • บิต 7…1 ADD – กำหนดบิตที่อยู่ 7…1 ในโหมดการกำหนดแอดเดรสคู่
  • บิต 0 ENDUAL - อนุญาตโหมดการกำหนดแอดเดรสแบบคู่

รีจิสเตอร์ข้อมูล I2C_DR มี DR บิต 8 บิตสำหรับรับและส่งข้อมูลบนบัส I2C ข้อมูลถูกเขียนลงในรีจิสเตอร์นี้เพื่อการส่งและอ่านเมื่อได้รับ บิต 15...9 ถูกสงวนไว้ รีจิสเตอร์ I2C_SR1 เป็นรีจิสเตอร์สถานะแรกและมีบิตต่อไปนี้:

  • บิต 15 SMBALERT – ส่งสัญญาณแจ้งเตือนบัส SMBus
  • หลัก 13 – สงวนไว้;
  • บิต 14 TIMEOUT - แจ้งข้อผิดพลาดการหมดเวลาสำหรับสัญญาณ SCL
  • บิต 12 PECERR - ระบุข้อผิดพลาด PEC ระหว่างการรับสัญญาณ
  • บิต 11 OVR – สร้างขึ้นเมื่อมีข้อผิดพลาดข้อมูลล้น
  • บิต 10 AF – เกิดขึ้นในกรณีที่การแจ้งเตือนผิดพลาด
  • บิต 9 ARLO - บ่งชี้ถึงการสูญเสียข้อผิดพลาดสิทธิ์บัส
  • บิต 8 BERR - ตั้งค่าเมื่อมีข้อผิดพลาดเกี่ยวกับบัส
  • บิต 7 TxE - แจ้งว่าการลงทะเบียนข้อมูลว่างเปล่า
  • หลัก 5 ​​- สงวนไว้;
  • บิต 6 RxNE - แจ้งว่าการลงทะเบียนข้อมูลไม่ว่างเปล่า
  • บิต 4 STOPF – ตรวจจับเงื่อนไขการหยุดในโหมดทาส
  • บิต 3 ADD10 - ตั้งค่าเมื่อต้นแบบส่งไบต์แรกของที่อยู่ด้วยการกำหนดแอดเดรส 10 บิต
  • บิต 2 BTF – แจ้งเกี่ยวกับความสำเร็จในการถ่ายโอนไบต์
  • บิต 1 ADDR - ตั้งค่าหากที่อยู่ถูกส่งในโหมดหลักหรือได้รับที่อยู่ในโหมดทาส
  • บิต 0 SB - ตั้งค่าเมื่อสร้างเงื่อนไขการเริ่มต้นในโหมดหลัก

รีจิสเตอร์ I2C_SR2 เป็นรีจิสเตอร์สถานะที่สอง ซึ่งมีบิตต่อไปนี้:

  • บิต 15…8 PEC – มีการตรวจสอบเฟรม
  • บิต 7 DUALF - เป็นการตั้งค่าสถานะที่อยู่คู่ในโหมดทาส
  • บิต 6 SMBHOST - ตั้งค่าเมื่อได้รับส่วนหัวของโฮสต์ SMBus ในโหมดทาส
  • บิต 5 SMBDEFAULT - เกิดขึ้นหากยอมรับที่อยู่เริ่มต้นสำหรับอุปกรณ์ SMBus ในโหมดทาส
  • บิต 4 GENCALL - ระบุว่าได้รับที่อยู่การโทรทั่วไปในโหมดสเลฟแล้ว
  • หลัก 3 – สงวนไว้;
  • บิต 2 TRA – แจ้งเตือนเกี่ยวกับโหมดการส่ง/การรับ
  • บิต 1 BUSY - แจ้งว่าบัสไม่ว่าง
  • บิต 0 MSL - ตรวจจับโหมด "Master"/"Slave"

รีจิสเตอร์ I2C_CCR คือรีจิสเตอร์ควบคุมสัญญาณนาฬิกา ซึ่งประกอบด้วยบิตต่อไปนี้:

  • บิต 15 F/S – ตั้งค่าความเร็วมาตรฐานหรือความเร็วที่รวดเร็วสำหรับโหมดมาสเตอร์
  • บิต 14 DUTY – กำหนดรอบหน้าที่ 2 หรือ 16/9 ในโหมดเร็ว
  • สงวนหมายเลข 13 และ 12;
  • บิต 11…0 CCR – ควบคุมสัญญาณนาฬิกาให้เร็วและเป็นความเร็วมาตรฐานในโหมดมาสเตอร์

รีจิสเตอร์ I2C_TRISE คือรีจิสเตอร์พารามิเตอร์ TRISE ซึ่งประกอบด้วย:

  • บิต 15...6 – สงวนไว้;
  • บิต 5…0 TRISE – กำหนดเวลาที่เพิ่มขึ้นสูงสุดสำหรับความเร็วที่รวดเร็วและเป็นมาตรฐานในโหมดมาสเตอร์ พารามิเตอร์นี้ระบุจุดในเวลาที่มีการสุ่มตัวอย่างสถานะของบรรทัด

คำอธิบายโดยละเอียดเพิ่มเติมเกี่ยวกับวัตถุประสงค์ของการลงทะเบียน I2C ทั้งหมดและบิตสามารถพบได้บนเว็บไซต์ www.st.com

การเขียนโปรแกรม อินเตอร์เฟซฉัน 2 กับ

ลองพิจารณาการใช้งานจริงของการใช้อินเทอร์เฟซ I2C ในการดำเนินการนี้ คุณสามารถใช้ไลบรารีอุปกรณ์ต่อพ่วงมาตรฐานของไมโครคอนโทรลเลอร์ STM32 ได้ สำหรับอินเทอร์เฟซ I2C การตั้งค่าสำหรับโหมด ความเร็ว และทุกอย่างจะอยู่ในไฟล์ส่วนหัวและประกาศเป็นโครงสร้าง:

I2C_InitTypeDef: typedef struct ( uint32_t I2C_ClockSpeed; uint16_t I2C_Mode; uint16_t I2C_DutyCycle; uint16_t I2C_OwnAddress1; uint16_t I2C_Ack; uint16_t I2C_ รับทราบที่อยู่; )I2C_InitTypeDef ;

ในโครงสร้างนี้ องค์ประกอบต่างๆ มีวัตถุประสงค์ดังต่อไปนี้:

  • uint32_t I 2 C_ClockSpeed ​​​​- ความถี่สัญญาณนาฬิกา, สูงสุด - 400 KHz;
  • uint16_t ฉัน 2 C_Mode – โหมดการทำงาน;
  • uint16_t I 2 C_DutyCycle – การตั้งค่าสำหรับการทำงานในโหมดรวดเร็ว
  • uint16_t I 2 C_OwnAddress – ที่อยู่ของอุปกรณ์เอง
  • uint16_t I 2 C_Ack – ไม่ว่าจะเปิดใช้งานหรือปิดใช้งานบิตตอบรับ ACK หรือไม่
  • uint16_t I 2 C_AcknowledgedAddress – เลือกรูปแบบที่อยู่: 7 บิตหรือ 10 บิต

มาดูขั้นตอนในการเริ่มต้นและการทำงานกับอินเทอร์เฟซ I2C ในการกำหนดค่าอินเทอร์เฟซ I2C เป็นอุปกรณ์หลักและส่งข้อมูลผ่านคุณต้องทำตามขั้นตอนต่อไปนี้:

  1. อนุญาตให้ทำการตอกบัตรพอร์ต
  2. เริ่มต้น I 2 C โดยระบุความเร็วที่อยู่และรูปแบบที่อยู่
  3. กำหนดพินไมโครคอนโทรลเลอร์
  4. เปิดใช้งานอินเทอร์เฟซ
  5. สร้างเงื่อนไขเริ่มต้น
  6. ส่งที่อยู่ของอุปกรณ์และข้อมูลที่ระบุ;
  7. สร้างเงื่อนไขการหยุด

เพื่ออำนวยความสะดวกในกระบวนการเขียนโปรแกรม ขอแนะนำให้สร้างชุดฟังก์ชันพื้นฐานสำหรับการทำงานกับ I2C รายการ 1 แสดงฟังก์ชันในการเริ่มต้นอินเทอร์เฟซ I2C ตามอัลกอริทึมที่อธิบายไว้ข้างต้น

รายการ 1 GPIO_InitTypeDef gpio; // สร้างโครงสร้างสำหรับพอร์ต I/O I2C_InitTypeDef i2c; // สร้างโครงสร้างสำหรับอินเทอร์เฟซ I2C void init_I2C1(void) ( // เปิดใช้งานการตอกบัตร RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); // เริ่มต้น I2C i2c.I2C_ClockSpeed ​​​​= 10,000 0; .I2C_โหมด = I2C_Mode_I2C; = I2C_DutyCycle_2; // ตั้งค่าที่อยู่=0x12 i2c.I2C_OwnAddress1 = 0x12; i2c_Ack_Disable;

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

รายการ 2 โมฆะ I2C_StartTransmission (I2C_TypeDef * I2Cx, uint8_t TransmissionDirection, uint8_t SlaveAddress) ( // รอให้รถบัสว่างในขณะที่ (I2C_GetFlagStatus (I2Cx, I2C_FLAG_BUSY)); // สร้างเงื่อนไขการเริ่มต้น I2C_GenerateSTART (I2Cx, ENABLE); // รอให้บิตถูกตั้งค่าในขณะที่ (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT)); // ส่งที่อยู่ไปยังทาส I2C_Send7bitAddress(I2Cx, SlaveAddress, TransmissionDirection); // หากข้อมูลถูกส่งถ้า (transmissionDirection== I2C_Direction_Transmitter) (ในขณะที่ (! I2C_CheckEvent(I2Cx, I2C_EVENT_MA) STER_TRANSMITTER_MODE_SELECTED));) // หากได้รับข้อมูล if(transmissionDirection== I2C_Direction_Receiver) (ในขณะที่(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));) )

ฟังก์ชั่นข้างต้นใช้ฟังก์ชั่นง่าย ๆ สำหรับการส่งและรับข้อมูลที่แสดงในรายการ 3

รายการ 3 // ฟังก์ชันการถ่ายโอนข้อมูลเป็นโมฆะ I2C_WriteData(I2C_TypeDef* I2Cx, uint8_t data) ( // เรียกใช้ฟังก์ชันการถ่ายโอนข้อมูลไลบรารี I2C_SendData(I2Cx, data); // รอการสิ้นสุดของการถ่ายโอนข้อมูลในขณะที่(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED )); ) // ฟังก์ชั่นการรับข้อมูล uint8_t I2C_ReadData(I2C_TypeDef* I2Cx) ( // รอให้ข้อมูลมาถึงในขณะที่ (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED)); data = I2C_ReceiveData(I2Cx); // อ่านข้อมูลจากการลงทะเบียนส่งคืน data; // ส่งคืนข้อมูลไปยังฟังก์ชันการโทร

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

บทสรุป

ข้อได้เปรียบที่ไม่อาจปฏิเสธได้ของอินเทอร์เฟซ I2C คือความง่ายในการเชื่อมต่ออุปกรณ์โดยใช้สายสื่อสารเพียงสองสายและสายสามัญ ต้องขอบคุณอินเทอร์เฟซนี้ที่ได้รับการยอมรับอย่างมั่นคงในด้านเทคโนโลยีและยังคงใช้กันอย่างแพร่หลายในอุปกรณ์สมัยใหม่

บัส I2C มีมานานแล้ว โดย Philips สร้างขึ้นในช่วงทศวรรษ 1980 สำหรับอุปกรณ์ความเร็วต่ำ ในขณะนี้มีการใช้กันอย่างแพร่หลายและเป็นไปได้มากว่าคุณมีอุปกรณ์อย่างน้อยหนึ่งเครื่องที่บ้านด้วยรถบัสนี้ ชื่อรถบัสย่อมาจาก Inter-Integrated Circuit ปัจจุบันไมโครคอนโทรลเลอร์ส่วนใหญ่มีโมดูลฮาร์ดแวร์ I2C บางตัวมีโมดูลหลายตัว เช่น STM32 (ซีรีส์ F4 มีโมดูล I2C สามโมดูล)

บัสประกอบด้วย 2 สาย สายหนึ่งคือข้อมูล (SDA) อีกสายหนึ่งคือสัญญาณนาฬิกา (SCL) ทั้งสองสายเชื่อมต่อกับพลังงานตั้งแต่แรก ควรสังเกตว่าไม่มีข้อบ่งชี้ที่ชัดเจนว่าแรงดันไฟฟ้าควรเป็นเท่าใด แต่มักใช้ +5V และ +3.3V บ่อยที่สุด อุปกรณ์บนสายไม่ใช่เพียร์ทูเพียร์ เช่นเดียวกับใน CAN ดังนั้นจึงต้องมีอุปกรณ์หลักเสมอ เป็นไปได้ที่จะมีอุปกรณ์ Master หลายเครื่อง แต่ก็ยังพบได้น้อยกว่าอุปกรณ์ Master หนึ่งเครื่องและอุปกรณ์ Slave หลายเครื่อง

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

มีการใช้งาน I2C เวอร์ชัน 2.0 ซึ่งช่วยให้คุณได้รับความเร็วที่สูงกว่ามาก 2-3 Mbit/s แต่ก็ยังหายากมาก สายนี้มีขีดจำกัดความจุอยู่ที่ 400 pF โดยปกติ เอกสารข้อมูลสำหรับเซ็นเซอร์และอุปกรณ์ I2C อื่นๆ จะระบุความจุ ดังนั้นคุณจึงสามารถคำนวณคร่าวๆ ได้ว่าเซ็นเซอร์ตัวอื่นจะพอดีหรือไม่

ไมโครคอนโทรลเลอร์มักจะมีการดึงขึ้นภายในพินซึ่งในสถานะอิสระจะให้ +3.3V (หรือ +5V) ที่จำเป็นในบรรทัด แต่การดึงขึ้นนี้ไม่เพียงพอสำหรับเส้นปกติอย่างแน่นอน ดังนั้นจึงคุ้มค่าที่จะดึงทั้ง SCL และ SDA ภายนอกเข้ากับแหล่งจ่ายไฟที่มีตัวต้านทาน 4.7 kOhm..2 kOhm

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

จากมุมมองของซอฟต์แวร์ การแลกเปลี่ยนบนบัส I2C มีลักษณะดังนี้: Master ส่งลำดับการเริ่มต้น START (ที่ระดับ SCL สูง SDA จะถูกดึงไปที่ศูนย์) จากนั้นส่งที่อยู่พร้อมแฟล็กบิตที่ระบุโหมดอ่านหรือเขียน ในรูปแบบต่อไปนี้:

หากบิตของโหมดเป็นศูนย์ หมายความว่า Master จะเขียนข้อมูลไปยังอุปกรณ์ Slave ซึ่งหมายถึงการอ่านจากอุปกรณ์ Slave หากคุณมองอีกนัยหนึ่ง อุปกรณ์ I2C แต่ละตัวจะมีอุปกรณ์ "เสมือน" สองตัว ซึ่งหมายความว่าหากไบต์ที่อยู่ทั้งหมด (เช่น 7 บิตดั้งเดิม + บิตโหมด) เป็นเลขคู่ แสดงว่าเป็นที่อยู่การเขียน หากเป็นเลขคี่ - การอ่าน ที่อยู่. ตามนี้ มีการจำกัดจำนวนอุปกรณ์บนบัส: 127

หลังจากได้รับที่อยู่ Slave แล้ว อุปกรณ์จะต้องแจ้งให้ต้นแบบทราบเกี่ยวกับการยอมรับที่อยู่ ซึ่งจะยืนยันความเป็นจริงของการมีอยู่ของอุปกรณ์ Slave ด้วยที่อยู่ดังกล่าวในบรรทัด การยืนยันเป็นบิตพิเศษที่ 9 ซึ่งเท่ากับศูนย์หากที่อยู่ตรงกันและพร้อมที่จะทำงาน และอีกอันหนึ่งหากไม่ตรงกัน นี่คือสัญญาณ ACK และ NACK ตามลำดับ นอกจากนี้ ACK ยังใช้สำหรับการรับและส่งข้อมูลในภายหลัง หากต้นแบบเขียนถึงทาส ดังนั้นทาสจะต้องรับทราบแต่ละไบต์ด้วยสัญญาณ ACK หากสเลฟส่งข้อมูลไปยังมาสเตอร์ มาสเตอร์จะต้องตอบสนองด้วย ACK ต่อไบต์ทั้งหมดยกเว้นไบต์สุดท้าย - นี่จะเป็นสัญญาณว่าไม่จำเป็นต้องส่งข้อมูลอีกต่อไป

เมื่อสิ้นสุดการส่งสัญญาณทั้งหมด Master จะต้องส่งลำดับ STOP สุดท้าย ซึ่งประกอบด้วยการยกระดับสาย SDA ไปที่ระดับสูงในขณะที่เพิ่มสาย SCL

ดังนั้น "แพ็คเกจ" มาตรฐานจึงมีลักษณะดังนี้:

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

ขั้นแรก คุณควรเปิดใช้งานการตอกบัตรของโมดูล I2C ซึ่งจำเป็นสำหรับอุปกรณ์ต่อพ่วงทั้งหมด นอกจากนี้ยังจำเป็นต้องเปิดใช้งานและกำหนดค่าพินในโหมดฟังก์ชันทางเลือกอีกด้วย หากต้องการดูว่ารถบัสคันใดอยู่ที่ใดคุณต้องไปที่แผ่นข้อมูลในส่วนภาพรวมอุปกรณ์ (สำหรับ F4 นี่คือหน้า 18) (รูปที่ 1) จากภาพจะเห็นว่า I2C อยู่บนบัส APB1 ขั้นตอนต่อไปคือการเปิดใช้งานและกำหนดค่า GPIO ทั้งหมดที่จำเป็นคือ: โหมดฟังก์ชันทางเลือก (ตามเอกสารข้อมูล I2C อ้างถึง AF4) ประเภท OpenDrain และการดึงขึ้นจะต้องอยู่ภายนอก สามารถเลือก "ความเร็ว" ของพินสำหรับ 100 kHz ต่ำ (2 MHz) และสำหรับ 400 kHz ST แนะนำให้เลือกปานกลางหรือเร็ว (จาก 10 MHz) ในที่สุดคุณสามารถกำหนดค่า I2C ได้ ไม่มีประโยชน์ในการแสดงรีจิสเตอร์ ทั้งหมดอยู่ในคู่มืออ้างอิง ทุกอย่างที่จำเป็นสำหรับเคสมาตรฐานจะอยู่ด้านล่าง ก่อนที่จะเปิดโมดูล I2C คุณควรเขียนค่าปัจจุบันของความถี่ของบัสที่โมดูล I2C อยู่ในรีจิสเตอร์ CR2 ในกรณีนี้คือความถี่ของบัส APB1 ภายในแผ่นข้อมูล ค่านี้เรียกว่า PCLK

ตัวควบคุมที่ต่างกันจะมีหมายเลขและชื่อรถโดยสารต่างกัน ดังนั้นในซีรีส์ F4xx จึงมีทั้ง APB1 และ APB2 และตัวแปร PCLK จะถูกกำหนดหมายเลขตามนั้น - PCLK1 และ PCLK2 หากต้องการดูหรือคำนวณความถี่สัญญาณนาฬิกาบัสเฉพาะ คุณสามารถใช้แอปพลิเคชัน CubeMX ซึ่งดาวน์โหลดจากเว็บไซต์อย่างเป็นทางการของ ST Microelectronics

ข้าว. 1 - ไดอะแกรมอุปกรณ์ต่อพ่วงของคอนโทรลเลอร์ STM32F407

การลงทะเบียน CR2 ยังเปิดใช้งานการขัดจังหวะจากโมดูลนี้ ความหมายก็คือโมดูลจะรายงานต่อ NVIC ว่ามีบางอย่างเกิดขึ้น หรือจะตั้งค่าสถานะที่จำเป็นในการลงทะเบียนสถานะ เป็นที่น่าสังเกตว่าแฟล็กเหตุการณ์จะถูกวางไว้ในการลงทะเบียนสถานะเสมอ ซึ่งเป็นตรรกะ ประการแรก การขัดจังหวะ ITEVTEN และ ITERREN เป็นเรื่องที่น่าสนใจ เหตุการณ์ และการขัดจังหวะข้อผิดพลาด ตามลำดับ คุณสามารถผ่านได้อย่างสมบูรณ์และเฉพาะกับกิจกรรมเท่านั้นซึ่งเป็นเรื่องทั่วไปที่สุด

I2C1->CR2 |= 48; // ความถี่อุปกรณ์ต่อพ่วง 24MHz I2C1->CR2 |= I2C_CR2_ITEVTEN; // เปิดใช้งานกิจกรรม

รีจิสเตอร์ CCR มีหน้าที่ในการตอกบัตรบัสออกไปด้านนอก ดังนั้น คุณต้องป้อนค่าที่นี่ซึ่งคำนวณโดยใช้สูตร PCLK/I2C_SPEED ตัวอย่างเช่นเราต้องการสตาร์ทบัสที่ 400 kHz บัสภายใน APB1 โอเวอร์คล็อกที่ 48 MHz ตามลำดับใน CCR เราจะเขียนค่าเท่ากับ 48 * 106/4 * 105 = 120 นอกจากนี้ในการลงทะเบียนนี้ด้วย จำเป็นต้องระบุโหมดการทำงาน Slow/Fast ซึ่งเป็นอันสุดท้าย บิตที่ 16

I2C1->ซีซีอาร์ &= ~I2C_CCR_CCR; I2C1->ซีซีอาร์ |= 120; I2C1->ซีซีอาร์ |= I2C_CCR_FS; // FastMode, 400 กิโลเฮิรตซ์

TRISE register รับผิดชอบขอบของสัญญาณบน SDA และ SCL โดยจะต้องป้อนค่าที่มีระยะขอบเล็กน้อยที่นี่ เป็นไปได้โดยไม่ต้องสำรองสิ่งสำคัญคือไม่น้อย - จะไม่ได้รับอะไรเลย ค่าที่ป้อนจะถูกคำนวณดังนี้: TRISE = RISE/tPCLK tPCLK = 1/PCLK ค่าคงที่ RISE คือเวลาที่เพิ่มขึ้นสูงสุดของสัญญาณ ตามข้อกำหนด คือ 1,000 ns สำหรับโหมดช้า และ 300 ns สำหรับโหมดเร็ว tPCLK เป็นเพียงคาบที่ได้รับตามมาตรฐานโดยใช้สูตร 1/F เนื่องจากเรามี Fast Mode ค่าใน TRISE จึงเป็นดังนี้: 3.000*10-7/2.083*10-8 = 14.4 เป็นต้น หากจำเป็นต้องมีมาร์จิ้น เราจะปัดเศษขึ้น เช่น 15.

ตัวบ่งชี้นี้มีความสำคัญ แต่ไม่สำคัญเท่ากับความถี่สัญญาณนาฬิกาที่ผิดพลาด ฉันคำนวณค่าคงที่ TRISE ในโหมดเร็วโดยไม่ตั้งใจโดยใช้สูตรสำหรับโหมดช้า และทุกอย่างได้ผล แต่ก็ยังดีกว่าถ้าทำอย่างถูกต้องตามข้อกำหนดของยาง คุณสามารถค้นหาได้โดยใช้วลีค้นหา “i2c specification” ใช่ ใช่ มันเป็นภาษาอังกฤษ

I2C1->ทริส = 24;

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

I2C1->CR1 |= I2C_CR1_PE; // เปิดใช้งานบล็อก I2C NVIC_EnableIRQ(I2C1_EV_IRQn); NVIC_SetPriority(I2C1_EV_IRQn, 1);

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

ดังนั้น สิ่งแรกที่คุณต้องทำคือเพิ่มตัวจัดการการขัดจังหวะเหตุการณ์จากโมดูล I2C ลงในโค้ด ฟังก์ชันจะต้องตั้งชื่อในลักษณะเฉพาะ และสามารถนำชื่อมาจากไฟล์เริ่มต้นได้ สำหรับโมดูล I2C1 ฟังก์ชันนี้เรียกว่า I2C1_EV_IRQHandler ดังนั้นเราจึงเพิ่มฟังก์ชันต่อไปนี้ลงในไฟล์ .c ที่ต้องการ:

เป็นโมฆะ I2C1_EV_IRQHandler (เป็นโมฆะ) ( )

แน่นอนว่าการตั้งชื่อวิธีนี้สามารถเปลี่ยนแปลงได้ ในไฟล์เริ่มต้น ป้ายกำกับจะถูกสร้างขึ้นในโค้ดแอสเซมบลีซึ่งไม่จำเป็นต้องมีฟังก์ชันนี้ คอมไพเลอร์ภาษา C เมื่อพบฟังก์ชันในไฟล์ต้นฉบับ เช่น I2C1_EV_IRQHandler เดียวกัน จะจัดสรรชื่อที่เหมือนกันทุกประการ เมื่อการประกอบขั้นสุดท้ายเกิดขึ้น ทั้งหมดนี้จะถูกนำมารวมกันและแทนที่ชื่อจะมีการเปลี่ยนแปลงไปยังตำแหน่งที่ต้องการในโค้ด ดังนั้นคุณสามารถเปลี่ยนชื่อป้ายกำกับได้ตามที่คุณต้องการ (ตามกฎสำหรับการตั้งชื่อฟังก์ชัน) แม้ว่าจะไม่แนะนำก็ตาม - นักพัฒนารายอื่นอาจไม่เข้าใจว่ามันเป็นตัวจัดการขัดจังหวะหรือไม่

หากคุณกำลังเขียนด้วยภาษา C++ อย่าลืม "ตัด" ตัวจัดการการขัดจังหวะในบล็อกภายนอก "C" ( ... ) เนื่องจากคอมไพเลอร์ C++ จะเปลี่ยนชื่อฟังก์ชันตามกฎของตัวเอง ณ เวลาคอมไพล์ (ข้อมูลเกี่ยวกับ มีการป้อนพารามิเตอร์และค่าส่งคืนที่นั่น เป็นต้น) ดังนั้นตัวรวบรวมจะไม่เชื่อมโยงตัวจัดการที่เขียนและเลเบลในไฟล์เริ่มต้น

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

ตัวอย่างเช่น เราต้องการส่งข้อมูลเพียง 1 ไบต์ไปยังอุปกรณ์และหยุดการถ่ายโอน ในการดำเนินการนี้ เราต้องการเพียงสถานะ EV5, EV6 และ EV8 เท่านั้น ที่ไหนสักแห่งในโค้ดโปรแกรมเรามีตัวแปรข้อมูลโกลบอลประเภท uint8_t ซึ่งเราเริ่มต้นด้วยค่าบางค่าและต้องการถ่ายโอนไปยังอุปกรณ์ทาส ดังที่เราทราบกันดีอยู่แล้วว่าการส่งข้อมูลเริ่มต้นตามลำดับ START:

I2C1->CR1 |= I2C_CR1_START;

สามารถวางบรรทัดนี้ได้ในระหว่างโปรแกรมที่ต้องเริ่มการถ่ายโอน ตัวอย่างเช่น หลังจากเตรียมใช้งานตัวแปรข้อมูลแล้ว ถัดไปจะมีโค้ดอยู่ภายในฟังก์ชันตัวจัดการการขัดจังหวะ ขั้นแรก คุณต้องบันทึกค่าสถานะเป็นตัวแปรแยกกัน:

ระเหย uint32_t sr1 = I2C1->SR1, sr2 = I2C1->SR2;

หลังจากส่งลำดับการเริ่มต้น การขัดจังหวะจะเกิดขึ้นกับเหตุการณ์ EV5 ในกรณีนี้ต้องตั้งค่าบิต SB ในรีจิสเตอร์สถานะ หากบิตนี้ถูกตั้งค่าไว้ เราจำเป็นต้องส่งที่อยู่ด้วยบิตโหมดอ่านหรือเขียน เพื่อให้ง่ายขึ้น คุณสามารถทำได้:

<<1) | mode)

ตอนนี้คุณสามารถเขียนตัวจัดการสถานะ EV5 ได้แล้ว:

ถ้า(sr1 & I2C_SR1_SB) ( I2C1->DR = I2C_ADDRESS(0x14,I2C_MODE_READ); )

เมื่อที่อยู่ถูกส่งและอุปกรณ์ทาสตอบสนองด้วยลำดับ ACK เหตุการณ์ EV6 และ EV8 พร้อมกันจะเกิดขึ้น: ธง ADDR และ TXE จะถูกตั้งค่า และภายในโหมดหลัก ADDR หมายความว่าที่อยู่ถูกส่งและยอมรับโดยอุปกรณ์ทาส และ TXE หมายความว่าบัฟเฟอร์มีอิสระในการป้อนข้อมูลสำหรับการส่งข้อมูลครั้งต่อไป ธง ADDR จะรีเซ็ตตัวเองทันทีที่เราอ่าน SR1 และ SR2 (ต้องอ่านทั้งคู่) และธง TXE จะถูกประมวลผลในบล็อกโค้ดที่แยกจากกัน ดังนั้นในความเป็นจริง มีเพียง EV5 และ EV8 เท่านั้นที่ต้องได้รับการประมวลผล EV6 จะแจ้งเฉพาะการมีอยู่ของทาสที่จำเป็นในบรรทัดเท่านั้น ในตัวจัดการ TXE สิ่งเดียวที่จำเป็นคือการถ่ายโอนข้อมูล เนื่องจากเราต้องการส่งเพียง 1 ไบต์ เราจะส่งลำดับ STOP ทันที:

ถ้า(sr1 & I2C_SR1_TXE) ( I2C1->DR = ข้อมูล; I2C1->CR1 |= I2C_CR1_STOP; )

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

หากจำเป็นต้องส่งข้อมูลมากกว่า 1 ไบต์ การเปลี่ยนแปลงโค้ดจะมีเพียงเล็กน้อย ตอนนี้ แทนที่จะเป็นข้อมูล uint8_t มาสร้างตัวแปรส่วนกลางต่อไปนี้:

Uint8_t ซ้ำ; ข้อมูล uint8_t = ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 );

เนื่องจากตัวแปรโกลบอลของประเภทจำนวนเต็มจะเป็นศูนย์ตามค่าเริ่มต้น จึงไม่จำเป็นต้องเริ่มต้นตัวแปร iter อย่างชัดเจน โดยทั่วไปการเปลี่ยนแปลงในตัวจัดการจะมีเพียงเล็กน้อย - คุณต้องเขียนบล็อกการประมวลผลเหตุการณ์ EV8 ใหม่:

ถ้า(sr1 & I2C_SR1_TXE) ( ถ้า(iter< 10) { I2C1->DR = ข้อมูล; ) อื่น ๆ ( I2C1->CR1 |= I2C_CR1_STOP; ) )

ดังนั้นเราจึงได้ฟังก์ชันตัวจัดการดังต่อไปนี้:

#define I2C_MODE_READ 1 #define I2C_MODE_WRITE 0 #define I2C_ADDRESS(addr, โหมด) ((addr<<1) | mode) uint8_t iter; uint8_t data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; void I2C1_EV_IRQHandler(void) { volatile uint32_t sr1 = module ->SR1, sr2 = โมดูล ->SR2; ถ้า (sr1 & I2C_SR1_SB) ( โมดูล -> DR = I2C_ADDRESS (0x14,I2C_MODE_READ); ) ถ้า (sr1 & I2C_SR1_TXE) ( ถ้า (iter< 10) { I2C1->DR = ข้อมูล; ) อื่น ๆ ( I2C1->CR1 |= I2C_CR1_STOP; ) ) )

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

โมฆะ I2C_handler (โมดูล I2C_TypeDef *, uint8_t addr, ข้อมูล uint8_t) (ระเหย uint32_t sr1 = โมดูล ->SR1, sr2 = โมดูล ->SR2; if(sr1 & I2C_SR1_SB) ( โมดูล ->DR = I2C_ADDRESS(addr,I2C_MODE_READ); ) ถ้า (sr1 & I2C_SR1_TXE) ( โมดูล -> DR = ข้อมูล; โมดูล ->CR1 |= I2C_CR1_STOP; ) )

ซึ่งจะทำให้สามารถใช้ฟังก์ชันเดียวกันในสองโมดูลพร้อมกันได้ ตัวอย่างเช่น เราสามารถแทรกได้ดังนี้:

โมฆะ I2C1_EV_IRQHandler(โมฆะ) ( I2C_handler(I2C1, 0x14, 0x10); ) โมฆะ I2C1_EV_IRQHandler(โมฆะ) ( I2C_handler(I2C1, 0x27, 0xFF); )

ฉันขอเตือนคุณอีกครั้งว่าที่อยู่อุปกรณ์ที่เราเห็นในเอกสารประกอบคือบิตจากไบต์ที่ส่งโดยโมดูล และบิต 0 คือโหมด ดังนั้นเมื่อระบุที่อยู่ 0x14 และโหมดการถ่ายโอนข้อมูลในอาร์กิวเมนต์ข้างต้นแล้ว ฉันจะได้รับไบต์ 0x29 สำหรับการส่ง เนื่องจากไม่มีการเช็คอินในมาโคร คุณจึงไม่ควรลืมว่าคุณสามารถส่งที่อยู่ 0x7F ลงไปได้เท่านั้น ไม่เช่นนั้นคุณจะก้าวกระโดด

สำหรับโหมดการอ่าน ทุกอย่างจะคล้ายกัน ดังที่เห็นได้จากแผนภาพ:

สำหรับการประมวลผลเราต้องการสถานะ EV5, EV6, EV7, EV7_1 สถานะ EV6 จะยังคงรีเซ็ตตัวเองหลังจากอ่านรีจิสเตอร์ SR1 และ SR2 และสถานะ EV7_1 สอดคล้องกับไบต์สุดท้ายที่ต้องการ เหล่านั้น. เมื่อเราได้รับไบต์สุดท้ายแล้ว เราต้องปิดการใช้งานการส่งข้อความ ACK ไปยังทาส เพื่อให้ไบต์ถัดไปเป็นไบต์สุดท้ายแล้ว ลองใช้โค้ดก่อนหน้าของเราและเพิ่มตัวจัดการเพิ่มเติมเช่นนี้เพื่อยอมรับข้อมูล 10 ไบต์:

ถ้า(sr1 & I2C_SR1_RXNE) ( ถ้า(rx_iter == 8) ( I2C1->CR1 &= ~I2C_CR1_ACK; ) อื่น ๆ ถ้า (rx_iter == 9) ( I2C1->< 10) { rx_data = I2C1->ดร.; -

ในกรณีนี้ จะต้องมีตัวแปรร่วม:

Uint8_t rx_iter; uint8_t rx_data;

ดังนั้นเราจึงได้รับตัวจัดการการขัดจังหวะต่อไปนี้สำหรับโมดูล I2C1:

#define I2C_MODE_READ 1 #define I2C_MODE_WRITE 0 #define I2C_ADDRESS(addr, โหมด) ((addr<<1) | mode) uint8_t iter; uint8_t data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; uint8_t rx_iter; uint8_t rx_data; uint8_t i2c_mode; void I2C1_EV_IRQHandler(void) { volatile uint32_t sr1 = I2C1->SR1, sr2 = I2C1->SR2; ถ้า(sr1 & I2C_SR1_SB) ( I2C1->DR = I2C_ADDRESS(0x14,i2c_mode); ) ถ้า(sr1 & I2C_SR1_TXE) ( ถ้า(iter< 10) { I2C1->DR = ข้อมูล; ) อื่น ๆ ( I2C1->CR1 |= I2C_CR1_STOP; ) ) ถ้า(sr1 & I2C_SR1_RXNE) ( ถ้า(rx_iter == 8) ( I2C1->CR1 &= ~I2C_CR1_ACK; ) อื่น ๆ ถ้า (rx_iter == 9) ( I2C1-> CR1 |= I2C_CR1_ACK ) ถ้า(rx_iter< 10) { rx_data = I2C1->ดร.; -

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

ยอดดูโพสต์: 318

เผยแพร่เมื่อ 10/26/2016

ในบทความที่แล้ว เรามาดูการทำงานของ STM32 ที่มีบัส I 2 C เป็น Master นั่นคือเขาเป็นผู้นำและสอบปากคำเซ็นเซอร์ ตอนนี้เรามาทำให้ STM32 เป็น Slave และตอบสนองต่อคำขอ ซึ่งก็คือ ตัวมันเองทำงานเป็นเซ็นเซอร์ เราจะจัดสรรหน่วยความจำ 255 ไบต์สำหรับการลงทะเบียนที่มีที่อยู่ตั้งแต่ 0 ถึง 0xFF และอนุญาตให้ Master เขียน/อ่านได้ และเพื่อทำให้ตัวอย่างไม่ง่ายนัก เรามาทำให้ STM32 ของเราเป็นตัวแปลงแอนะล็อกเป็นดิจิทัลที่มีอินเทอร์เฟซ I 2 C กันดีกว่า ADC จะประมวลผล 8 แชนเนล ตัวควบคุมจะให้ผลลัพธ์ของการแปลงแก่ Master เมื่ออ่านจากรีจิสเตอร์ เนื่องจากผลลัพธ์ของการแปลง ADC คือ 12 บิต เราจึงจำเป็นต้องมี 2 รีจิสเตอร์ (2 ไบต์) สำหรับแต่ละช่องสัญญาณ ADC

i2c_slave.hมีการตั้งค่า:

I2CSLAVE_ADDR– ที่อยู่ของอุปกรณ์ของเรา

ADC_ADDR_START– ที่อยู่เริ่มต้นของการลงทะเบียนที่รับผิดชอบผลลัพธ์ของการแปลง ADC

ในไฟล์ i2c_slave.cเราสนใจฟังก์ชั่นมากที่สุด get_i2c1_ramและ set_i2c1_ram- การทำงาน get_i2c1_ramมีหน้าที่อ่านข้อมูลจากรีจิสเตอร์ ส่งคืนข้อมูลจากที่อยู่ที่ระบุซึ่งมอบให้กับ Master ในกรณีของเรา ข้อมูลจะถูกอ่านจากอาเรย์ i2c1_ramแต่ถ้า Master ขอที่อยู่ลงทะเบียนจากช่วงที่จัดสรรสำหรับผลลัพธ์ ADC ข้อมูลการแปลง ADC จะถูกส่งไป

get_i2c1_ram:

Uint8_t get_i2c1_ram(uint8_t adr) ( //ข้อมูล ADC ถ้า ((ADC_ADDR_START<= adr) & (adr < ADC_ADDR_START + ADC_CHANNELS*2)) { return ADCBuffer; } else { // Other addresses return i2c1_ram; } }

การทำงาน set_i2c1_ram– เขียนข้อมูลที่ได้รับจาก Master ลงในรีจิสเตอร์ตามที่อยู่ที่ระบุ ในกรณีของเรา ข้อมูลจะถูกเขียนลงในอาร์เรย์ i2c1_ram- แต่นี่เป็นทางเลือก ตัวอย่างเช่น คุณสามารถเพิ่มเช็ค และเมื่อหมายเลขหนึ่งมาถึงที่อยู่หนึ่งๆ คุณก็ดำเนินการบางอย่างได้ วิธีนี้ทำให้คุณสามารถส่งคำสั่งต่างๆ ไปยังไมโครคอนโทรลเลอร์ได้

set_i2c1_ram:

เป็นโมฆะ set_i2c1_ram (uint8_t adr, uint8_t val) ( i2c1_ram = val; return; )

การเริ่มต้นนั้นค่อนข้างง่าย:

Int main(โมฆะ) ( SetSysClockTo72(); ADC_DMA_init(); I2C1_Slave_init(); ในขณะที่(1) ( ) )

ขั้นแรกเราตั้งค่าความถี่การทำงานสูงสุดของคอนโทรลเลอร์ ต้องใช้ความเร็วสูงสุดเมื่อจำเป็นต้องหลีกเลี่ยงความล่าช้าบนบัส I 2 C จากนั้นเราจะเริ่มการทำงานของ ADC โดยใช้ DMA เกี่ยวกับ . เกี่ยวกับ . และสุดท้าย เราก็เริ่มต้นบัส I 2 C เป็น ทาส- อย่างที่คุณเห็นไม่มีอะไรซับซ้อน

ตอนนี้เรามาเชื่อมต่อโมดูล STM32 ของเรากับ Raspberry Pi กัน มาเชื่อมต่อโพเทนชิโอมิเตอร์เข้ากับช่อง ADC และเราจะอ่านตัวบ่งชี้ ADC จากตัวควบคุมของเรา อย่าลืมว่าเพื่อให้บัส I 2 C ทำงาน คุณต้องติดตั้งตัวต้านทานแบบดึงขึ้นบนแต่ละบรรทัดของบัส

ในคอนโซล Raspberry ให้ตรวจสอบว่าอุปกรณ์ของเรามองเห็นได้บนบัส I 2 C หรือไม่ (ประมาณนั้น):

I2cdetect -y 1

อย่างที่คุณเห็นที่อยู่ของอุปกรณ์ 0x27แม้ว่าเราจะระบุ 0x4E ก็ตาม เมื่อคุณมีเวลา ลองคิดว่าเหตุใดจึงเกิดเหตุการณ์เช่นนี้

หากต้องการอ่านจากรีจิสเตอร์ของอุปกรณ์ I 2 C-Slave ให้ดำเนินการคำสั่ง:

I2cget -y 1 0x27 0x00

ที่ไหน:
0x27– ที่อยู่อุปกรณ์
0x00– ที่อยู่การลงทะเบียน (0x00…0xFF)

หากต้องการเขียนไปยังรีจิสเตอร์ของอุปกรณ์ I 2 C-Slave ให้ดำเนินการคำสั่ง:

I2cset -y 1 0x27 0xA0 0xDD

โดย:
0x27– ที่อยู่อุปกรณ์
0xA0– ที่อยู่ลงทะเบียน
0xDDข้อมูล -8 บิต (0x00…0xFF)

คำสั่งก่อนหน้านี้เขียนหมายเลข 0xDD ลงในรีจิสเตอร์ 0xA0(คุณสามารถเขียนถึง 16 การลงทะเบียนแรกได้ แต่ไม่มีประเด็น แต่สงวนไว้สำหรับ ADC) ตอนนี้เรามาอ่านกัน:

I2cget -y 1 0x27 0xA0

เพื่อให้กระบวนการอ่านข้อมูลช่อง ADC ง่ายขึ้น ฉันเขียนสคริปต์:

#!/usr/bin/env python นำเข้า smbus เวลานำเข้าบัส = smbus.SMBus (1) ที่อยู่ = 0x27 ในขณะที่ (1): ADC = (); สำหรับฉันในช่วง (0, 8): LBS = bus.read_byte_data(ที่อยู่, 0x00+i*2) MBS = bus.read_byte_data(ที่อยู่, 0x00+i*2+1) ADC[i] = MBS*256 + LBS พิมพ์ ADC time.sleep (0.2)

จะสำรวจและแสดงผลลัพธ์ของช่อง ADC ทั้ง 8 ช่องบนคอนโซล

ในทำนองเดียวกัน คุณสามารถรวมไมโครคอนโทรลเลอร์หลายตัวเข้าด้วยกันได้ หนึ่งในนั้นควรเป็น Master() และอีกหนึ่งทาส

ฉันขอให้คุณประสบความสำเร็จ!

คุณชอบบทความนี้หรือไม่? แบ่งปันกับเพื่อนของคุณ!
บทความนี้เป็นประโยชน์หรือไม่?
ใช่
เลขที่
ขอบคุณสำหรับคำติชมของคุณ!
มีข้อผิดพลาดเกิดขึ้นและระบบไม่นับคะแนนของคุณ
ขอบคุณ ข้อความของคุณถูกส่งแล้ว
พบข้อผิดพลาดในข้อความ?
เลือกคลิก Ctrl + เข้าสู่และเราจะแก้ไขทุกอย่าง!