การสื่อสารแบบอนุกรมทำงานบน Arduino อย่างไร


16

อ้างอิงถึง Arduino Uno, Mega2560, Leonardo และบอร์ดที่คล้ายกัน:

  • การสื่อสารแบบอนุกรมทำงานอย่างไร
  • ซีเรียสนั้นเร็วแค่ไหน?
  • ฉันจะเชื่อมต่อระหว่างผู้ส่งและผู้รับได้อย่างไร

โปรดทราบ: นี่เป็นคำถามอ้างอิง


คุณอาจพบว่าสิ่งนี้น่าสนใจเกี่ยวกับบัฟเฟอร์ทั้งสองด้านของนาโนที่เชื่อมต่อกับระบบ Raspian ที่ใช้ตัวบันทึกข้อมูล Python โดยใช้สายเคเบิลโปรแกรม USB ปกติระหว่างสองสาย: arduino.stackexchange.com/questions/11710/ …
SDsolar

คำตอบ:


16

การสื่อสารแบบอนุกรมแบบอะซิงโครนัส (โดยปกติจะเรียกว่าแบบอนุกรม) ใช้เพื่อส่งไบต์จากอุปกรณ์หนึ่งไปยังอุปกรณ์อื่น อุปกรณ์อาจเป็นหนึ่งในสิ่งต่อไปนี้:

  • Arduino
  • พีซี
  • จีพีเอส
  • เครื่องอ่านบัตร RFID
  • จอแสดงผล LCD
  • โมเด็ม
  • อื่น ๆ

อัตรานาฬิกาและการสุ่มตัวอย่างข้อมูล

ซึ่งแตกต่างจากการสื่อสารแบบอนุกรม SPI / USB / I2C ไม่มีสัญญาณนาฬิกา นาฬิกาตัวอย่างเป็นอัตราตัวอย่างที่ตกลงกันไว้ (เรียกว่าอัตรารับส่งข้อมูล) ทั้งผู้ส่งและผู้รับจะต้องได้รับการกำหนดค่าให้ใช้อัตราเดียวกันมิฉะนั้นผู้รับจะได้รับข้อมูลที่ไม่มีความหมาย (เนื่องจากบิตที่ไม่ได้ถูกสุ่มตัวอย่างในอัตราเดียวกันกับที่พวกเขาถูกส่ง)

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

Comms แบบอนุกรม - ส่งหนึ่งไบต์

ภาพด้านบนแสดงตัวอักษร 'F' ที่กำลังถูกส่ง ใน ASCII นี่คือ 0x46 (เป็นเลขฐานสิบหก) หรือ 0b01000110 (เป็นเลขฐานสอง) น้อยอย่างมีนัยสำคัญ (ต่ำลำดับ) 01100010บิตจะถูกส่งเป็นครั้งแรกดังนั้นในกราฟิกข้างต้นที่คุณเห็นบิตที่เดินทางมาถึงในลำดับที่:

เวลา "ไม่ได้ใช้งาน" ระหว่างไบต์จะถูกส่งเป็นบิต "1" ต่อเนื่อง (อย่างมีประสิทธิภาพบรรทัดการส่งจะถูกเก็บไว้อย่างต่อเนื่องสูง)

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

  • ข้ามบิตเริ่มต้น
  • ตัวอย่างครึ่งทางผ่านบิตถัดไป

หากอัตรา baud เป็น 9600 baud เช่นนั้นอัตราตัวอย่างจะเป็น1/9600 = 0.00010416วินาที (104.16 µs)

ดังนั้นที่ 9600 baud หลังจากได้รับบิตเริ่มต้นผู้รับจะรอ 156.25 ands แล้วสุ่มตัวอย่างทุก ๆ 104.16

เริ่มเวลาบิต

จุดประสงค์ของStop Bitคือเพื่อให้แน่ใจว่ามี 1 บิตระหว่างแต่ละไบต์ หากไม่มีไบต์หยุดหากไบต์สิ้นสุดลงที่ศูนย์ดังนั้นจะเป็นไปไม่ได้ที่ฮาร์ดแวร์จะสามารถบอกความแตกต่างระหว่างสิ่งนั้นกับบิตเริ่มต้นของไบต์ต่อไป

เพื่อผลิตผลลัพธ์ข้างต้นใน Uno คุณสามารถเขียนรหัสนี้:

void setup()
  {
      Serial.begin(9600);
      Serial.print("F");
  }

void loop ()
  {
  }

จำนวนบิตข้อมูล

เพื่อที่จะประหยัดเวลาในการส่งข้อมูล (ในสมัยก่อน heh) คุณได้รับอนุญาตให้ระบุจำนวนบิตข้อมูลที่แตกต่างกัน ฮาร์ดแวร์ AtMega รองรับบิตข้อมูลที่มีหมายเลขตั้งแต่ 5 ถึง 9 บิตยิ่งน้อยกว่าบิตข้อมูลที่คุณสามารถส่งได้น้อยลง แต่ยิ่งเร็วเท่าไหร่


พาริตี้บิต

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

ตัวอย่างเช่นสำหรับตัวอักษร "F" (หรือ 0x46 หรือ 0b01000110) คุณจะเห็นว่ามี 3 ตัว (ใน 01000110) ดังนั้นเราจึงมีความเท่าเทียมกันแปลก ดังนั้นบิตพาริตีจะเป็นดังนี้:

  • ไม่มีความเท่าเทียมกัน: ละเว้น
  • Even parity: a 1 (3 + 1 is even)
  • คี่พาริตี้: a 0 (3 + 0 คี่)

บิตพาริตี (ถ้ามี) จะปรากฏขึ้นหลังจากบิตข้อมูลสุดท้าย แต่ก่อนบิตหยุด

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

ระบบเริ่มต้นบางระบบใช้พาริตี "ทำเครื่องหมาย" (โดยที่บิตพาริตีคือ 1 โดยไม่คำนึงถึงข้อมูล) หรือพาริตี "ช่องว่าง" (โดยที่บิตพาริตีเป็น 0 เสมอโดยไม่คำนึงถึงข้อมูล)


ส่ง 9 บิต

อุปกรณ์สื่อสารบางอย่างใช้ข้อมูล 9 บิตดังนั้นในกรณีนี้บิตพาริตี้จะเปลี่ยนเป็นบิตที่ 9 มีเทคนิคพิเศษสำหรับการส่งบิตที่ 9 นี้ (รีจิสเตอร์เป็นเรจิสเตอร์ 8 บิตดังนั้นบิตที่ 9 จะต้องใส่ที่อื่น)


จำนวนบิตหยุด

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

หากผู้รับไม่ได้รับตรรกะ 1 เมื่อบิตควรจะหยุดที่เรียกว่า "ข้อผิดพลาดในการกำหนดกรอบ" มันแสดงว่ามีปัญหาบางอย่าง ค่อนข้างเป็นไปได้ว่าผู้ส่งและผู้รับได้รับการกำหนดค่าให้ใช้อัตรารับส่งข้อมูล (บิต) ที่แตกต่างกัน


เอกสาร

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

9600/8-N-1

นี่คือการบอกเรา:

  • 9600 บิตต่อวินาที
  • 8 บิตข้อมูล
  • ไม่มีความเท่าเทียมกัน (คุณอาจเห็นแทน: E = คู่, O = คี่)
  • 1 stop bit

เป็นเรื่องสำคัญที่ผู้ส่งและผู้รับจะต้องยอมรับในข้างต้นมิฉะนั้นการสื่อสารก็ไม่น่าจะสำเร็จ


Pin-ลึกหนาบาง

Arduino Uno มีพินดิจิตอล 0 และ 1 สำหรับซีเรียลฮาร์ดแวร์:

Arduino Uno ขาอนุกรม

ในการเชื่อมต่อ Arduinos สองอันคุณต้องสลับ Tx และ Rx ดังนี้:

เชื่อมต่อ Arduinos สองแห่งเข้าด้วยกัน


ความเร็ว

รองรับความเร็วที่หลากหลาย (ดูกราฟิคด้านล่าง) ความเร็ว "มาตรฐาน" มักจะเป็นหลายเท่าของ 300 baud (เช่น 300/600/1200/2400 เป็นต้น)

ความเร็ว "ที่ไม่ได้มาตรฐาน" อื่น ๆ สามารถจัดการได้โดยการตั้งค่าการลงทะเบียนที่เหมาะสม คลาส HardwareSerial ใช้สำหรับคุณ เช่น.

Serial.begin (115200);  // set speed to 115200 baud

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

ดังนั้นที่ 9600 baud คุณสามารถส่ง 960 bytes ( 9600 / 10 = 960) ต่อวินาที


อัตราการรับส่งข้อมูลผิดพลาด

อัตราการรับส่งข้อมูลของ Atmega นั้นสร้างขึ้นโดยการแบ่งเวลาของระบบนาฬิกาจากนั้นนับเป็นจำนวนที่ตั้งไว้ล่วงหน้า ตารางนี้จากแผ่นข้อมูลแสดงค่าการลงทะเบียนและเปอร์เซ็นต์ความผิดพลาดสำหรับนาฬิกา 16 MHz (เช่นหนึ่งใน Arduino Uno)

อัตราการรับส่งข้อมูลผิดพลาด

บิต U2Xn ส่งผลกระทบต่อตัวหารอัตรานาฬิกา (0 = หารด้วย 16, 1 = หารด้วย 8) การลงทะเบียน UBRRn มีจำนวนโปรเซสเซอร์ที่นับได้สูงสุด

จากตารางด้านบนเราเห็นว่าเราได้รับ 9600 baud จากนาฬิกา 16 MHz ดังนี้:

16000000 / 16 / 104 = 9615

เราหารด้วย 104 และไม่ใช่ 103 เพราะตัวนับนั้นเป็นศูนย์สัมพันธ์ ดังนั้นข้อผิดพลาดที่นี่15 / 9600 = 0.0016ซึ่งอยู่ใกล้กับสิ่งที่ตารางข้างต้นพูดว่า (0.02%)

คุณจะสังเกตเห็นว่าอัตราการรับส่งข้อมูลบางอย่างมีจำนวนข้อผิดพลาดสูงกว่าอัตราอื่น ๆ

ตามแผ่นข้อมูลเปอร์เซ็นต์ความผิดพลาดสูงสุดสำหรับ 8 บิตข้อมูลอยู่ในช่วง 1.5% ถึง 2.0% (ดูแผ่นข้อมูลสำหรับรายละเอียดเพิ่มเติม)


Arduino เลโอนาร์โด

Arduino Leonardo และ Micro มีวิธีการสื่อสารแบบอนุกรมที่แตกต่างกันเนื่องจากพวกเขาเชื่อมต่อโดยตรงผ่าน USB เข้ากับโฮสต์คอมพิวเตอร์ไม่ใช่ผ่านพอร์ตอนุกรม

ด้วยเหตุนี้คุณต้องรอให้ Serial เป็น "พร้อม" (เนื่องจากซอฟต์แวร์สร้างการเชื่อมต่อ USB) โดยมีบรรทัดเพิ่มเติมสองบรรทัดเช่นนี้

void setup()
  {
      Serial.begin(115200);
      while (!Serial)
      {}  // wait for Serial comms to become ready
      Serial.print("Fab");
  }

void loop ()
  {
  }

อย่างไรก็ตามหากคุณต้องการสื่อสารผ่านพิน D0 และ D1 จริง ๆ (แทนที่จะใช้สายเคเบิล USB) คุณต้องใช้ Serial1 แทน Serial คุณทำเช่นนี้:

void setup()
  {
      Serial1.begin(115200);
      Serial1.print("Fab");
  }

void loop ()
  {
  }

ระดับแรงดันไฟฟ้า

โปรดทราบว่า Arduino ใช้ระดับ TTL สำหรับการสื่อสารแบบอนุกรม ซึ่งหมายความว่าคาดว่า:

  • บิต "ศูนย์" คือ 0V
  • บิต "one" คือ + 5V

อุปกรณ์อนุกรมรุ่นเก่าที่ออกแบบมาเพื่อเสียบเข้ากับพอร์ตอนุกรมของพีซีอาจใช้ระดับแรงดันไฟฟ้า RS232 ซึ่ง ได้แก่ :

  • บิต "ศูนย์" คือ +3 ถึง +15 โวลต์
  • บิต "หนึ่ง" คือ −3 ถึง −15 volts

ไม่เพียง แต่เป็น "ฤvertedษี" ที่เกี่ยวกับระดับ TTL ("หนึ่ง" เป็นลบมากกว่า "ศูนย์"), Arduino ไม่สามารถจัดการแรงดันไฟฟ้าเชิงลบบนหมุดอินพุตของมัน (หรือบวกมากกว่า 5V)

ดังนั้นคุณต้องมีวงจรอินเตอร์เฟสสำหรับการสื่อสารกับอุปกรณ์ดังกล่าว สำหรับอินพุต (ไปยัง Arduino) เท่านั้นทรานซิสเตอร์ธรรมดาไดโอดและตัวต้านทานสองตัวจะทำ:

บัฟเฟอร์อินเวอร์เตอร์

สำหรับการสื่อสารสองทางคุณต้องสามารถสร้างแรงดันไฟฟ้าเชิงลบได้ดังนั้นจึงจำเป็นต้องใช้วงจรที่ซับซ้อนมากขึ้น ตัวอย่างเช่นชิป MAX232 จะทำเช่นนั้นร่วมกับตัวเก็บประจุ 1 µF สี่ตัวเพื่อทำหน้าที่เป็นวงจรปั๊มประจุ


ซอฟต์แวร์อนุกรม

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


Mega2560

Arduino "Mega" มีพอร์ตอนุกรมฮาร์ดแวร์เพิ่มเติม 3 พอร์ต พวกเขาถูกทำเครื่องหมายบนกระดานเป็น Tx1 / Rx1, Tx2 / Rx2, Tx3 / Rx3 ควรใช้ซอฟต์แวร์เหล่านี้ตามความต้องการของ SoftwareSerial ในการเปิดพอร์ตอื่น ๆ เหล่านั้นให้ใช้ชื่อ Serial1, Serial2, Serial3 เช่นนี้:

Serial1.begin (115200);  // start hardware serial port Tx1/Rx1
Serial2.begin (115200);  // start hardware serial port Tx2/Rx2
Serial3.begin (115200);  // start hardware serial port Tx3/Rx3

ขัดจังหวะ

ทั้งการส่งและรับโดยใช้ไลบรารี HardwareSerial ใช้การขัดจังหวะ

การส่ง

เมื่อคุณทำ a Serial.printข้อมูลที่คุณพยายามพิมพ์จะถูกวางในบัฟเฟอร์ "ส่ง" ภายใน หากคุณมี 1024 ไบต์หรือมากกว่า RAM (เช่นใน Uno) คุณจะได้รับบัฟเฟอร์ 64- มิฉะนั้นคุณจะได้รับบัฟเฟอร์ 16 ไบต์ หากบัฟเฟอร์มีที่ว่างแล้วSerial.printจะส่งคืนทันทีจึงไม่ทำให้รหัสของคุณล่าช้า หากไม่มีที่ว่างก็จะ "บล็อก" รอให้บัฟเฟอร์ว่างเปล่าพอที่จะมีที่ว่าง

จากนั้นเมื่อแต่ละไบต์ถูกส่งโดยฮาร์ดแวร์อินเตอร์รัปต์จะถูกเรียก (อินเทอร์รัปต์ "USART, Data Register Empty") และรูทีนการขัดจังหวะจะส่งไบต์ถัดไปจากบัฟเฟอร์ออกจากพอร์ตอนุกรม

การได้รับ

เมื่อได้รับข้อมูลขาเข้าแล้วรูทีนการขัดจังหวะจะถูกเรียก (การขัดจังหวะ "USART Rx Complete") และไบต์ขาเข้าจะถูกวางลงในบัฟเฟอร์ "รับ" (ขนาดเดียวกับบัฟเฟอร์การส่งสัญญาณที่กล่าวถึงข้างต้น)

เมื่อคุณโทรSerial.availableหาคุณจะทราบว่ามีกี่ไบต์ในบัฟเฟอร์ "รับ" นั้น เมื่อคุณเรียกSerial.readไบต์จะถูกลบออกจากบัฟเฟอร์การรับและส่งกลับไปที่รหัสของคุณ

ใน Arduinos ที่มีหน่วยความจำ 1,000 ไบต์ขึ้นไปไม่ต้องรีบลบข้อมูลออกจากบัฟเฟอร์รับหากคุณไม่ปล่อยให้มันเติม หากกรอกข้อมูลแล้วข้อมูลขาเข้าเพิ่มเติมจะถูกยกเลิก

โปรดทราบว่าเนื่องจากขนาดของบัฟเฟอร์นี้ไม่มีจุดรอให้จำนวนไบต์ที่มาถึงมากตัวอย่างเช่น:

while (Serial.available () < 200)
  { }  // wait for 200 bytes to arrive

สิ่งนี้จะไม่ทำงานเนื่องจากบัฟเฟอร์ไม่สามารถเก็บได้มาก


เคล็ดลับ

  • ก่อนอ่านควรตรวจสอบให้แน่ใจว่ามีข้อมูลอยู่เสมอ ตัวอย่างเช่นนี่ผิด

    if (Serial.available ())
      {
          char a = Serial.read ();
          char b = Serial.read ();  // may not be available
      }

    การSerial.availableทดสอบทำให้คุณมั่นใจว่าคุณมีหนึ่งไบต์ แต่รหัสพยายามอ่านสองตัว มันอาจทำงานได้หากมีสองไบต์ในบัฟเฟอร์ถ้าไม่คุณจะได้รับ -1 คืนซึ่งจะมีลักษณะเช่น 'ÿ' ถ้าพิมพ์

  • ใช้ความระมัดระวังในการส่งข้อมูล ดังที่กล่าวไว้ข้างต้นที่ 9600 บอดคุณส่งเพียง 960 ไบต์ต่อวินาทีดังนั้นพยายามส่ง 1,000 อ่านจากพอร์ตอะนาล็อกที่ 9600 บอดจะไม่ประสบความสำเร็จมาก


อ้างอิง


ในกราฟิกที่ 1: ด้วยลูกศรดูเหมือนว่าบิตหยุดส่งผ่านก่อน หากคุณแลกเปลี่ยน Rx / Tx และทิศทางของลูกศรฉันคิดว่ามันสับสนน้อยกว่า
ott--

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

ตกลงกับคำอธิบายออสซิลโลสโคปที่ฉันซื้อ :-)
ott--

อย่างไรก็ตามฉันคิดว่าประเด็นของคุณสมเหตุสมผลแล้ว คนอื่นคิดอย่างไร มันจะชัดเจนขึ้นหรือไม่ถ้าลูกศรกลับด้านและฉันแลกเปลี่ยน Rx / Tx หรือไม่
Nick Gammon

1
@ linhartr22 ฉันแก้ไขให้อ่าน "ข้อมูลไร้ความหมาย" ซึ่งอาจใกล้กว่า
Nick Gammon
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.