ผลต่างระหว่างรุ่นของ "การสื่อสารผ่านพอร์ทอนุกรม"
Chaiporn (คุย | มีส่วนร่วม) |
Chaiporn (คุย | มีส่วนร่วม) |
||
(ไม่แสดง 30 รุ่นระหว่างกลางโดยผู้ใช้คนเดียวกัน) | |||
แถว 1: | แถว 1: | ||
: ''วิกินี้เป็นส่วนหนึ่งของรายวิชา [[01204223]]'' | : ''วิกินี้เป็นส่วนหนึ่งของรายวิชา [[01204223]]'' | ||
− | ไมโครคอนโทรลเลอร์ส่วนใหญ่ (รวมถึงเบอร์ ATMega168 ที่ใช้ในรายวิชา) มีความสามารถในการรับส่งข้อมูลระหว่างอุปกรณ์อื่นและคอมพิวเตอร์ผ่านพอร์ทอนุกรม ([http://en.wikipedia.org/wiki/Serial_port serial port]) เราจึงสามารถนำคุณสมบัตินี้มาใช้ในการแสดงผลลัพธ์การทำงานของบอร์ดไมโครคอนโทรลเลอร์ที่ให้รายละเอียดมากกว่าการแสดงผลผ่าน LED เพียงอย่างเดียวได้ อีกทั้งสภาพแวดล้อมของ Arduino ยังมีคำสั่งจำพวก < | + | == แนะนำพอร์ทอนุกรมบนไมโครคอนโทรลเลอร์ == |
+ | ไมโครคอนโทรลเลอร์ส่วนใหญ่ (รวมถึงเบอร์ ATMega168/328 ที่ใช้ในรายวิชา) มีความสามารถในการรับส่งข้อมูลระหว่างอุปกรณ์อื่นและคอมพิวเตอร์ผ่านพอร์ทอนุกรม ([http://en.wikipedia.org/wiki/Serial_port serial port]) เราจึงสามารถนำคุณสมบัตินี้มาใช้ในการแสดงผลลัพธ์การทำงานของบอร์ดไมโครคอนโทรลเลอร์ที่ให้รายละเอียดมากกว่าการแสดงผลผ่าน LED เพียงอย่างเดียวได้ อีกทั้งสภาพแวดล้อมของ Arduino ยังมีคำสั่งจำพวก <tt>[http://arduino.cc/en/Serial/Print Serial.print]</tt> ที่นำไปใช้ในการส่งข้อความมาแสดงผลบนหน้าจอคอมพิวเตอร์ได้ทันที การสื่อสารแบบอนุกรมนั้นต้องการใช้สายสัญญาณเพียงเส้นเดียวต่อการส่งสัญญาณหนึ่งทิศทาง ดังนั้นการเชื่อมต่ออุปกรณ์เข้าด้วยกันจึงต้องการสายไฟเพียง 3 เส้น เป็นสายสัญญาณสองเส้นเพื่อรับส่งข้อมูลสองทิศทาง และสายอ้างอิงศักย์ไฟฟ้า (หรือกราวนด์) อีกหนึ่งเส้น ตามภาพ | ||
[[Image:Serial.png|800px|center|thumb|การเชื่อมต่อกับอุปกรณ์อื่นผ่านการสื่อสารแบบอนุกรม]] | [[Image:Serial.png|800px|center|thumb|การเชื่อมต่อกับอุปกรณ์อื่นผ่านการสื่อสารแบบอนุกรม]] | ||
แถว 9: | แถว 10: | ||
[[Image:DE-9.jpg|200px|center|thumb|อะแดปเตอร์ USB-Serial ที่มีหัวเชื่อมต่อแบบ DE-9 ตัวผู้]] | [[Image:DE-9.jpg|200px|center|thumb|อะแดปเตอร์ USB-Serial ที่มีหัวเชื่อมต่อแบบ DE-9 ตัวผู้]] | ||
− | + | ในรายวิชา 01204223 เราจะใช้อะแดปเตอร์ USB-Serial ชนิดที่รับข้อมูลจากพอร์ท USB แล้วแปลงเป็นสัญญาณแบบ TTL โดยตรง ทำให้นำไปใช้เชื่อมต่อกับบอร์ดไมโครคอนโทรลเลอร์ได้ทันทีโดยไม่ต้องผ่านหัวเชื่อมต่อแบบ DE-9 ดังภาพ | |
[[Image:dongles.jpg|200px|center|thumb|USB-Serial Dongle ที่ใช้ในรายวิชา]] | [[Image:dongles.jpg|200px|center|thumb|USB-Serial Dongle ที่ใช้ในรายวิชา]] | ||
− | == | + | == การติดตั้งไดรเวอร์สำหรับอะแดปเตอร์ == |
เครื่องที่ใช้ระบบปฏิบัติการลินุกซ์จะมีไดรเวอร์สำหรับอะแดปเตอร์ติดตั้งมาให้พร้อมใช้งานอยู่แล้ว สำหรับระบบปฏิบัติการอื่น ๆ ให้ดาวน์โหลดและติดตั้งไดรเวอร์จากเว็บ [http://www.silabs.com/products/mcu/pages/usbtouartbridgevcpdrivers.aspx http://www.silabs.com/products/mcu/pages/usbtouartbridgevcpdrivers.aspx] | เครื่องที่ใช้ระบบปฏิบัติการลินุกซ์จะมีไดรเวอร์สำหรับอะแดปเตอร์ติดตั้งมาให้พร้อมใช้งานอยู่แล้ว สำหรับระบบปฏิบัติการอื่น ๆ ให้ดาวน์โหลดและติดตั้งไดรเวอร์จากเว็บ [http://www.silabs.com/products/mcu/pages/usbtouartbridgevcpdrivers.aspx http://www.silabs.com/products/mcu/pages/usbtouartbridgevcpdrivers.aspx] | ||
− | ==การเชื่อมอะแดปเตอร์เข้ากับบอร์ดไมโครคอนโทรลเลอร์== | + | == การเชื่อมอะแดปเตอร์เข้ากับบอร์ดไมโครคอนโทรลเลอร์ == |
ตามหลักการแล้วการเชื่อมต่ออุปกรณ์สองชิ้นเข้าด้วยกันผ่านการสื่อสารแบบอนุกรมนั้นทำได้โดยการเชื่อมขา TX (Transmit) เข้ากับขา RX (Receive) ของอุปกรณ์ตรงข้ามให้ครบทั้งสองทิศทางดังรูปด้านบน กรณีที่เราต้องการส่งข้อมูลจากบอร์ดไมโครคอนโทรลเลอร์ไปยังคอมพิวเตอร์เพียงทิศทางเดียวนั้นจะใช้สายเชื่อมเพียงสองเส้น คือ | ตามหลักการแล้วการเชื่อมต่ออุปกรณ์สองชิ้นเข้าด้วยกันผ่านการสื่อสารแบบอนุกรมนั้นทำได้โดยการเชื่อมขา TX (Transmit) เข้ากับขา RX (Receive) ของอุปกรณ์ตรงข้ามให้ครบทั้งสองทิศทางดังรูปด้านบน กรณีที่เราต้องการส่งข้อมูลจากบอร์ดไมโครคอนโทรลเลอร์ไปยังคอมพิวเตอร์เพียงทิศทางเดียวนั้นจะใช้สายเชื่อมเพียงสองเส้น คือ | ||
แถว 24: | แถว 25: | ||
สังเกตว่าขา Tx และ Rx ของไมโครคอนโทรลเลอร์เป็นขาเดียวกับ PD1 และ PD0 ตามลำดับ จึงต้องมีการบัดกรีคอนเน็คเตอร์แบบ 5x2 ลงไปที่ตำแหน่งพอร์ท D ก่อนจึงจะใช้งานได้ | สังเกตว่าขา Tx และ Rx ของไมโครคอนโทรลเลอร์เป็นขาเดียวกับ PD1 และ PD0 ตามลำดับ จึงต้องมีการบัดกรีคอนเน็คเตอร์แบบ 5x2 ลงไปที่ตำแหน่งพอร์ท D ก่อนจึงจะใช้งานได้ | ||
− | [[Image:dongle-diagram-blue.jpg|center| | + | [[Image:dongle-diagram-blue.jpg|600px|center|thumb|ผังภาพการเชื่อมต่อบอร์ดไมโครคอนโทรลเลอร์เข้ากับอะแดปเตอร์ USB-Serial (รุ่นสีดำ/น้ำเงิน)]] |
− | อย่างไรก็ตาม <span style="color: red;">อะแดปเตอร์รุ่นสีแดง</span>จะมีการใช้ขา Tx เป็นขารับสัญญาณ จึงต้องต่อขา Tx (PD1) จากบอร์ดไมโครคอนโทรลเลอร์เข้ากับขา Tx ของอะแดปเตอร์แทน ดังรูป | + | อย่างไรก็ตาม <span style="color: red;">อะแดปเตอร์รุ่นสีแดง<u>บางรุ่น</u></span>จะมีการใช้ขา Tx เป็นขารับสัญญาณ จึงต้องต่อขา Tx (PD1) จากบอร์ดไมโครคอนโทรลเลอร์เข้ากับขา Tx ของอะแดปเตอร์แทน ดังรูป |
− | [[Image:dongle-diagram-red.jpg|center| | + | [[Image:dongle-diagram-red.jpg|600px|center|thumb|ผังภาพการเชื่อมต่อบอร์ดไมโครคอนโทรลเลอร์เข้ากับอะแดปเตอร์ USB-Serial (รุ่นสีแดง)]] |
จากนั้นเสียบทั้งบอร์ดไมโครคอนโทรลเลอร์และอะแดปเตอร์ USB-Serial เข้ากับพอร์ท USB ของคอมพิวเตอร์ดังภาพ | จากนั้นเสียบทั้งบอร์ดไมโครคอนโทรลเลอร์และอะแดปเตอร์ USB-Serial เข้ากับพอร์ท USB ของคอมพิวเตอร์ดังภาพ | ||
แถว 34: | แถว 35: | ||
[[Image:dongle-connect.jpg|250px|center|thumb|ตัวอย่างการเชื่อมต่อบอร์ดไมโครคอนโทรลเลอร์เข้ากับอะแดปเตอร์ USB-Serial]] | [[Image:dongle-connect.jpg|250px|center|thumb|ตัวอย่างการเชื่อมต่อบอร์ดไมโครคอนโทรลเลอร์เข้ากับอะแดปเตอร์ USB-Serial]] | ||
− | ==โค้ดเฟิร์มแวร์สำหรับส่งข้อมูลสู่พอร์ทอนุกรม== | + | == โค้ดเฟิร์มแวร์สำหรับส่งข้อมูลสู่พอร์ทอนุกรม == |
− | อุปกรณ์สองฝั่งของการเชื่อมต่อจะสามารถสื่อสารกันได้นั้นต้องมีการกำหนดความเร็วในการรับส่งข้อมูลให้สอดคล้องกัน | + | อุปกรณ์สองฝั่งของการเชื่อมต่อจะสามารถสื่อสารกันได้นั้นต้องมีการกำหนดความเร็วในการรับส่งข้อมูลให้สอดคล้องกัน ในงานทั่วไปที่ไม่ต้องการการแลกเปลี่ยนข้อมูลในปริมาณมากมักจะมีการกำหนดความเร็วไว้ที่ 9,600 บิตต่อวินาที เฟิร์มแวร์ที่พัฒนาด้วยภาษาซีตรง ๆ ต้องมีการตั้งค่าให้กับรีจีสเตอร์ที่เกี่ยวข้องได้แก่ UBRR0H, UBRR0L และ UCSR0C แล้วจึงค่อยป้อนข้อมูลที่จะส่ง<u>ทีละไบท์</u>ให้กับรีจีสเตอร์ UDR0 รายละเอียดสามารถดูเพิ่มเติมได้จากดาต้าชีตของไมโครคอนโทรลเลอร์เบอร์ ATMega168 |
− | + | เพื่อความสะดวกผู้สอนได้เตรียมโค้ดเบื้องต้นเอาไว้สำหรับส่งข้อความออกทางพอร์ท UART ด้วยฟังก์ชัน <tt>printf()</tt> ดาวน์โหลดโค้ดได้จากลิ้งค์ https://www.cpe.ku.ac.th/~cpj/204223/avr-serial.zip แตกไฟล์และนำไฟล์ <tt>serial.c</tt> มาคอมไพล์ร่วมกับโค้ดเฟิร์มแวร์ โค้ดตัวอย่างด้านล่างจะส่งข้อความ <tt>Hello, Serial</tt> ออกไปยังพอร์ทอนุกรมพร้อมกับ LED สีเขียวบนบอร์ดหลักกระพริบทุก ๆ ครึ่งวินาที | |
+ | |||
+ | <syntaxhighlight lang="c"> | ||
+ | #include <avr/io.h> | ||
+ | #include <util/delay.h> | ||
+ | |||
+ | #include "serial.h" | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | serial_init(); | ||
+ | DDRD |= (1<<PD3); // ให้ PD3 เป็นเอาท์พุท | ||
+ | |||
+ | for (;;) | ||
+ | { | ||
+ | printf("Hello, Serial\n"); | ||
+ | PORTD ^= (1<<PD3); // ใช้ตัวดำเนินการ XOR เพื่อสลับลอจิกของขา PD3 | ||
+ | _delay_ms(500); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | กรณีที่พัฒนาเฟิร์มแวร์ด้วย Arduino เราสามารถเรียกใช้คำสั่ง <tt>Serial.begin</tt> ในการตั้งอัตราการส่งข้อมูล และคำสั่ง <tt>Serial.print</tt> เพื่อพิมพ์ข้อความที่ต้องการออกทางพอร์ท UART ตัวอย่างด้านล่างให้พฤติกรรมเดียวกับกับโค้ดภาษาซีข้างต้น | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
− | |||
− | |||
void setup() | void setup() | ||
{ | { | ||
แถว 51: | แถว 72: | ||
{ | { | ||
Serial.println("Hello, Serial"); | Serial.println("Hello, Serial"); | ||
− | PORTD ^= (1<<PD3); | + | PORTD ^= (1<<PD3); // ใช้ตัวดำเนินการ XOR เพื่อสลับลอจิกของขา PD3 |
delay(500); | delay(500); | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | + | ทดลองคอมไพล์และอัพโหลดเฟิร์มแวร์เข้าสู่ไมโครคอนโทรลเลอร์ เชื่อมสายสัญญาณจากบอร์ดไมโครคอนโทรลเลอร์เข้ากับอะแดปเตอร์ USB-Serial และเสียบอะแดปเตอร์เข้ากับพอร์ท USB ของเครื่องคอมพิวเตอร์ จากนั้นเลือกเมนู Tools → Serial Port และเลือกชื่อพอร์ทที่ปรากฏขึ้นมา (บนลินุกซ์มักปรากฏเป็นชื่อ <tt>/dev/ttyUSB0</tt>) | |
[[Image:serial-mon-open.jpg|center|291px|thumb|การเลือกพอร์ทอนุกรมที่ต้องการเชื่อมต่อใน Arduino IDE]] | [[Image:serial-mon-open.jpg|center|291px|thumb|การเลือกพอร์ทอนุกรมที่ต้องการเชื่อมต่อใน Arduino IDE]] | ||
แถว 64: | แถว 85: | ||
[[Image:serial-mon-port.jpg|center|262px|thumb|การเปิดโปรแกรม Serial Monitor]] | [[Image:serial-mon-port.jpg|center|262px|thumb|การเปิดโปรแกรม Serial Monitor]] | ||
− | ให้แน่ใจว่าอัตราการส่งข้อมูลถูกตั้งไว้ที่ 9600 baud (หมายถึง 9600 บิตต่อวินาที) หน้าจอ Serial Monitor ควรปรากฏผลลัพธ์เป็นข้อความ < | + | ให้แน่ใจว่าอัตราการส่งข้อมูลถูกตั้งไว้ที่ 9600 baud (หมายถึง 9600 บิตต่อวินาที) หน้าจอ Serial Monitor ควรปรากฏผลลัพธ์เป็นข้อความ <tt>Hello, Serial</tt> ทุก ๆ ครึ่งวินาที พร้อมกับ LED สีเขียวบนบอร์ดหลักกระพริบเป็นจังหวะเดียวกัน |
[[Image:serial-mon.jpg|center|295px|thumb|ผลลัพธ์จากหน้าจอของ Serial Monitor]] | [[Image:serial-mon.jpg|center|295px|thumb|ผลลัพธ์จากหน้าจอของ Serial Monitor]] | ||
− | ==ปัญหาที่อาจพบ== | + | == ปัญหาที่อาจพบ == |
* '''Arduino IDE บน Ubuntu ไม่แสดงรายการพอร์ทอนุกรมในเมนู Tools → Serial Port''' | * '''Arduino IDE บน Ubuntu ไม่แสดงรายการพอร์ทอนุกรมในเมนู Tools → Serial Port''' | ||
: สาเหตุมักเกิดจากผู้ใช้ไม่ได้รับสิทธิ์ในการเข้าใช้พอร์ทอนุกรม แก้ไขโดยการระบุให้บัญชีผู้ใช้อยู่ในกลุ่ม dialout โดยใช้คำสั่ง | : สาเหตุมักเกิดจากผู้ใช้ไม่ได้รับสิทธิ์ในการเข้าใช้พอร์ทอนุกรม แก้ไขโดยการระบุให้บัญชีผู้ใช้อยู่ในกลุ่ม dialout โดยใช้คำสั่ง | ||
แถว 80: | แถว 101: | ||
* '''สายไฟที่ใช้เชื่อมบอร์ดไมโครคอนโทรลเลอร์กับอะแดปเตอร์ยาวไม่พอ เนื่องจากพอร์ท USB อยู่ห่างกันเกินไป''' | * '''สายไฟที่ใช้เชื่อมบอร์ดไมโครคอนโทรลเลอร์กับอะแดปเตอร์ยาวไม่พอ เนื่องจากพอร์ท USB อยู่ห่างกันเกินไป''' | ||
− | : | + | : มีทางเลือกในการแก้ปัญหาดังนี้ |
+ | :* ใช้สาย USB ที่ปลายด้านหนึ่งเป็นตัวผู้และอีกปลายเป็นตัวเมียเพื่อช่วยเสริมความยาว | ||
+ | :* ใช้อุปกรณ์ [http://en.wikipedia.org/wiki/USB_hub USB hub] | ||
+ | :* ในกรณีฉุกเฉินให้ถอดบอร์ดไมโครคอนโทรลเลอร์ออกหลังจากอัพโหลดเฟิร์มแวร์ แล้วเสียบอะแดปเตอร์แทนที่ในพอร์ท USB เดียวกัน จากนั้นป้อนไฟเลี้ยงให้กับบอร์ดไมโครคอนโทรลเลอร์ผ่านทางอะแดปเตอร์ USB-serial โดยเชื่อมขา VCC ของอุปกรณ์ทั้งคู่เข้าด้วยกัน ถอดอะแดปเตอร์ออกและเสียบบอร์ดไมโครคอนโทรลเลอร์กลับที่เดิมเมื่อต้องการอัพโหลดเฟิร์มแวร์ใหม่ | ||
+ | |||
+ | [[Image:serial-vcc.png|600px|center|thumb|การจ่ายไฟเลี้ยงให้กับบอร์ดไมโครคอนโทรลเลอร์ผ่านอะแดปเตอร์ USB-serial]] | ||
+ | |||
+ | == โปรแกรมทางฝั่งคอมพิวเตอร์ == | ||
+ | |||
+ | โปรแกรมที่ใช้รับข้อมูลผ่านพอร์ทอนุกรมทางฝั่งคอมพิวเตอร์สามารถเขียนขึ้นได้จากภาษาใดก็ได้ที่มีไลบรารีสำหรับเชื่อมต่อกับพอร์ทอนุกรม ในวิกินี้จะยกตัวอย่างสองภาษาคือภาษาจาวาและภาษาไพทอน | ||
+ | |||
+ | === ตัวอย่างโปรแกรมภาษาจาวา === | ||
+ | |||
+ | ภาษาจาวาไม่ได้มีไลบรารีสำหรับสื่อสารกับพอร์ทอนุกรมมาให้ตั้งแต่แรก จึงต้องมีการติดตั้งเพิ่มเติม ไลบรารีที่นิยมใช้กันได้แก่ [http://rxtx.qbang.org/wiki/index.php/Main_Page RXTX] โค้ดตัวอย่างด้านล่างรอรับข้อมูลจากพอร์ทอนุกรมที่ระบุไ้ว้ในคอมมานด์ไลน์อาร์กิวเมนต์ และพิมพ์อักขระที่รับมาได้ทั้งหมดออกมาทางหน้าจอ | ||
+ | |||
+ | <syntaxhighlight lang="cpp"> | ||
+ | import java.io.InputStream; | ||
+ | import gnu.io.*; | ||
+ | |||
+ | public class SerialMonitor { | ||
+ | |||
+ | public static void main(String[] args) throws Exception | ||
+ | { | ||
+ | if (args.length != 1) | ||
+ | { | ||
+ | System.err.println("Usage: java SerialMonitor <serial-dev>"); | ||
+ | System.exit(1); | ||
+ | } | ||
+ | |||
+ | String dev = args[0]; | ||
+ | |||
+ | System.out.println("Monitoring serial stream on " + dev); | ||
+ | CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(dev); | ||
+ | if (portIdentifier.isCurrentlyOwned()) | ||
+ | { | ||
+ | System.out.println( "Error: Port is currently in use" ); | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | int timeout = 2000; | ||
+ | CommPort commPort = portIdentifier.open("serial",timeout); | ||
+ | |||
+ | if (commPort instanceof SerialPort) | ||
+ | { | ||
+ | SerialPort serialPort = (SerialPort) commPort; | ||
+ | serialPort.setSerialPortParams(9600, | ||
+ | SerialPort.DATABITS_8, | ||
+ | SerialPort.STOPBITS_1, | ||
+ | SerialPort.PARITY_NONE ); | ||
+ | |||
+ | InputStream in = serialPort.getInputStream(); | ||
+ | while (true) | ||
+ | { | ||
+ | int data = in.read(); | ||
+ | if (data < 0) break; | ||
+ | System.out.print((char)data); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ศึกษาขั้นตอนการติดตั้งไลบรารี RXTX ได้จากวิกิ [[การติดตั้งไลบรารี RXTX]] | ||
+ | |||
+ | === ตัวอย่างโปรแกรมภาษาไพทอน === | ||
+ | เช่นเดียวกับภาษาจาวา ภาษาไพทอนไม่ได้มีไลบรารีสำหรับสื่อสารกับพอร์ทอนุกรมมาให้เช่นกัน ใช้คำสั่ง <tt>apt-get</tt> หรือ <tt>pip</tt> เพื่อติดตั้งไลบรารี <tt>pyserial</tt> ดังนี้ | ||
+ | |||
+ | sudo apt-get install python-serial | ||
+ | |||
+ | หรือ | ||
+ | |||
+ | sudo pip install pyserial | ||
+ | |||
+ | โค้ดตัวอย่างด้านล่าง (สำหรับไพทอน 3.x) รอรับข้อมูลจากพอร์ทอนุกรมที่ระบุผ่านคอมมานด์ไลน์อาร์กิวเมนต์ และพิมพ์อักขระที่รับมาได้ทั้งหมดออกมาทางหน้าจอ | ||
+ | |||
+ | <syntaxhighlight lang="python"> | ||
+ | import sys | ||
+ | import serial | ||
+ | |||
+ | if len(sys.argv) != 2: | ||
+ | print("Usage: %s <serial-dev>" % sys.argv[0], file=sys.stderr) | ||
+ | exit(1) | ||
+ | |||
+ | dev = sys.argv[1] | ||
+ | print("Monitoring serial input stream on %s" % dev) | ||
+ | ser = serial.Serial(dev) | ||
+ | while True: | ||
+ | data = ser.read() | ||
+ | print(data.decode("utf8"),end="") | ||
+ | sys.stdout.flush() # force output to display | ||
+ | </syntaxhighlight> | ||
− | [ | + | == ข้อมูลเพิ่มเติม == |
+ | * [https://appelsiini.net/2011/simple-usart-with-avr-libc/ Simple Serial Communications With AVR libc] |
รุ่นแก้ไขปัจจุบันเมื่อ 11:21, 2 เมษายน 2565
- วิกินี้เป็นส่วนหนึ่งของรายวิชา 01204223
เนื้อหา
แนะนำพอร์ทอนุกรมบนไมโครคอนโทรลเลอร์
ไมโครคอนโทรลเลอร์ส่วนใหญ่ (รวมถึงเบอร์ ATMega168/328 ที่ใช้ในรายวิชา) มีความสามารถในการรับส่งข้อมูลระหว่างอุปกรณ์อื่นและคอมพิวเตอร์ผ่านพอร์ทอนุกรม (serial port) เราจึงสามารถนำคุณสมบัตินี้มาใช้ในการแสดงผลลัพธ์การทำงานของบอร์ดไมโครคอนโทรลเลอร์ที่ให้รายละเอียดมากกว่าการแสดงผลผ่าน LED เพียงอย่างเดียวได้ อีกทั้งสภาพแวดล้อมของ Arduino ยังมีคำสั่งจำพวก Serial.print ที่นำไปใช้ในการส่งข้อความมาแสดงผลบนหน้าจอคอมพิวเตอร์ได้ทันที การสื่อสารแบบอนุกรมนั้นต้องการใช้สายสัญญาณเพียงเส้นเดียวต่อการส่งสัญญาณหนึ่งทิศทาง ดังนั้นการเชื่อมต่ออุปกรณ์เข้าด้วยกันจึงต้องการสายไฟเพียง 3 เส้น เป็นสายสัญญาณสองเส้นเพื่อรับส่งข้อมูลสองทิศทาง และสายอ้างอิงศักย์ไฟฟ้า (หรือกราวนด์) อีกหนึ่งเส้น ตามภาพ
คอมพิวเตอร์รุ่นใหม่โดยเฉพาะอย่างยิ่งคอมพิวเตอร์แบบโน้ตบุ๊กในปัจจุบันมักไม่มีพอร์ทอนุกรมติดมาให้ จึงต้องอาศัยอุปกรณ์เสริมที่เรียกว่า USB to serial adapter หรือ USB-Serial dongle ที่เพิ่มพอร์ทอนุกรมให้กับคอมพิวเตอร์ ตามมาตรฐานการสื่อสารผ่านพอร์ทอนุกรมนั้นมีการใช้หัวเชื่อมต่อแบบ DE-9 ที่มีลักษณะดังภาพ
ในรายวิชา 01204223 เราจะใช้อะแดปเตอร์ USB-Serial ชนิดที่รับข้อมูลจากพอร์ท USB แล้วแปลงเป็นสัญญาณแบบ TTL โดยตรง ทำให้นำไปใช้เชื่อมต่อกับบอร์ดไมโครคอนโทรลเลอร์ได้ทันทีโดยไม่ต้องผ่านหัวเชื่อมต่อแบบ DE-9 ดังภาพ
การติดตั้งไดรเวอร์สำหรับอะแดปเตอร์
เครื่องที่ใช้ระบบปฏิบัติการลินุกซ์จะมีไดรเวอร์สำหรับอะแดปเตอร์ติดตั้งมาให้พร้อมใช้งานอยู่แล้ว สำหรับระบบปฏิบัติการอื่น ๆ ให้ดาวน์โหลดและติดตั้งไดรเวอร์จากเว็บ http://www.silabs.com/products/mcu/pages/usbtouartbridgevcpdrivers.aspx
การเชื่อมอะแดปเตอร์เข้ากับบอร์ดไมโครคอนโทรลเลอร์
ตามหลักการแล้วการเชื่อมต่ออุปกรณ์สองชิ้นเข้าด้วยกันผ่านการสื่อสารแบบอนุกรมนั้นทำได้โดยการเชื่อมขา TX (Transmit) เข้ากับขา RX (Receive) ของอุปกรณ์ตรงข้ามให้ครบทั้งสองทิศทางดังรูปด้านบน กรณีที่เราต้องการส่งข้อมูลจากบอร์ดไมโครคอนโทรลเลอร์ไปยังคอมพิวเตอร์เพียงทิศทางเดียวนั้นจะใช้สายเชื่อมเพียงสองเส้น คือ
- ขา Tx จากบอร์ดไมโครคอนโทรลเลอร์ (ขาเดียวกับ PD1) เชื่อมกับขา Rx ของอะแดปเตอร์
- ขา GND จากบอร์ดไมโครคอนโทรลเลอร์ เชือมกับขา GND ของอะแดปเตอร์
สังเกตว่าขา Tx และ Rx ของไมโครคอนโทรลเลอร์เป็นขาเดียวกับ PD1 และ PD0 ตามลำดับ จึงต้องมีการบัดกรีคอนเน็คเตอร์แบบ 5x2 ลงไปที่ตำแหน่งพอร์ท D ก่อนจึงจะใช้งานได้
อย่างไรก็ตาม อะแดปเตอร์รุ่นสีแดงบางรุ่นจะมีการใช้ขา Tx เป็นขารับสัญญาณ จึงต้องต่อขา Tx (PD1) จากบอร์ดไมโครคอนโทรลเลอร์เข้ากับขา Tx ของอะแดปเตอร์แทน ดังรูป
จากนั้นเสียบทั้งบอร์ดไมโครคอนโทรลเลอร์และอะแดปเตอร์ USB-Serial เข้ากับพอร์ท USB ของคอมพิวเตอร์ดังภาพ
โค้ดเฟิร์มแวร์สำหรับส่งข้อมูลสู่พอร์ทอนุกรม
อุปกรณ์สองฝั่งของการเชื่อมต่อจะสามารถสื่อสารกันได้นั้นต้องมีการกำหนดความเร็วในการรับส่งข้อมูลให้สอดคล้องกัน ในงานทั่วไปที่ไม่ต้องการการแลกเปลี่ยนข้อมูลในปริมาณมากมักจะมีการกำหนดความเร็วไว้ที่ 9,600 บิตต่อวินาที เฟิร์มแวร์ที่พัฒนาด้วยภาษาซีตรง ๆ ต้องมีการตั้งค่าให้กับรีจีสเตอร์ที่เกี่ยวข้องได้แก่ UBRR0H, UBRR0L และ UCSR0C แล้วจึงค่อยป้อนข้อมูลที่จะส่งทีละไบท์ให้กับรีจีสเตอร์ UDR0 รายละเอียดสามารถดูเพิ่มเติมได้จากดาต้าชีตของไมโครคอนโทรลเลอร์เบอร์ ATMega168
เพื่อความสะดวกผู้สอนได้เตรียมโค้ดเบื้องต้นเอาไว้สำหรับส่งข้อความออกทางพอร์ท UART ด้วยฟังก์ชัน printf() ดาวน์โหลดโค้ดได้จากลิ้งค์ https://www.cpe.ku.ac.th/~cpj/204223/avr-serial.zip แตกไฟล์และนำไฟล์ serial.c มาคอมไพล์ร่วมกับโค้ดเฟิร์มแวร์ โค้ดตัวอย่างด้านล่างจะส่งข้อความ Hello, Serial ออกไปยังพอร์ทอนุกรมพร้อมกับ LED สีเขียวบนบอร์ดหลักกระพริบทุก ๆ ครึ่งวินาที
#include <avr/io.h>
#include <util/delay.h>
#include "serial.h"
int main()
{
serial_init();
DDRD |= (1<<PD3); // ให้ PD3 เป็นเอาท์พุท
for (;;)
{
printf("Hello, Serial\n");
PORTD ^= (1<<PD3); // ใช้ตัวดำเนินการ XOR เพื่อสลับลอจิกของขา PD3
_delay_ms(500);
}
}
กรณีที่พัฒนาเฟิร์มแวร์ด้วย Arduino เราสามารถเรียกใช้คำสั่ง Serial.begin ในการตั้งอัตราการส่งข้อมูล และคำสั่ง Serial.print เพื่อพิมพ์ข้อความที่ต้องการออกทางพอร์ท UART ตัวอย่างด้านล่างให้พฤติกรรมเดียวกับกับโค้ดภาษาซีข้างต้น
void setup()
{
pinMode(PIN_PD3, OUTPUT);
Serial.begin(9600);
}
void loop()
{
Serial.println("Hello, Serial");
PORTD ^= (1<<PD3); // ใช้ตัวดำเนินการ XOR เพื่อสลับลอจิกของขา PD3
delay(500);
}
ทดลองคอมไพล์และอัพโหลดเฟิร์มแวร์เข้าสู่ไมโครคอนโทรลเลอร์ เชื่อมสายสัญญาณจากบอร์ดไมโครคอนโทรลเลอร์เข้ากับอะแดปเตอร์ USB-Serial และเสียบอะแดปเตอร์เข้ากับพอร์ท USB ของเครื่องคอมพิวเตอร์ จากนั้นเลือกเมนู Tools → Serial Port และเลือกชื่อพอร์ทที่ปรากฏขึ้นมา (บนลินุกซ์มักปรากฏเป็นชื่อ /dev/ttyUSB0)
เลือกเมนู Tools → Serial Monitor ดังแสดง
ให้แน่ใจว่าอัตราการส่งข้อมูลถูกตั้งไว้ที่ 9600 baud (หมายถึง 9600 บิตต่อวินาที) หน้าจอ Serial Monitor ควรปรากฏผลลัพธ์เป็นข้อความ Hello, Serial ทุก ๆ ครึ่งวินาที พร้อมกับ LED สีเขียวบนบอร์ดหลักกระพริบเป็นจังหวะเดียวกัน
ปัญหาที่อาจพบ
- Arduino IDE บน Ubuntu ไม่แสดงรายการพอร์ทอนุกรมในเมนู Tools → Serial Port
- สาเหตุมักเกิดจากผู้ใช้ไม่ได้รับสิทธิ์ในการเข้าใช้พอร์ทอนุกรม แก้ไขโดยการระบุให้บัญชีผู้ใช้อยู่ในกลุ่ม dialout โดยใช้คำสั่ง
sudo adduser <ชื่อบัญชี> dialout
- จากนั้นให้ล็อกเอาท์และล็อกอินกลับเข้ามาใหม่
- Arduino IDE บน MAC OS X ไม่แสดงรายการพอร์ทอนุกรมในเมนู Tools → Serial Port
- ติดตั้งไดรเวอร์สำหรับอะแดปเตอร์ USB-serial จากเว็บ http://www.silabs.com/products/mcu/pages/usbtouartbridgevcpdrivers.aspx
- สายไฟที่ใช้เชื่อมบอร์ดไมโครคอนโทรลเลอร์กับอะแดปเตอร์ยาวไม่พอ เนื่องจากพอร์ท USB อยู่ห่างกันเกินไป
- มีทางเลือกในการแก้ปัญหาดังนี้
- ใช้สาย USB ที่ปลายด้านหนึ่งเป็นตัวผู้และอีกปลายเป็นตัวเมียเพื่อช่วยเสริมความยาว
- ใช้อุปกรณ์ USB hub
- ในกรณีฉุกเฉินให้ถอดบอร์ดไมโครคอนโทรลเลอร์ออกหลังจากอัพโหลดเฟิร์มแวร์ แล้วเสียบอะแดปเตอร์แทนที่ในพอร์ท USB เดียวกัน จากนั้นป้อนไฟเลี้ยงให้กับบอร์ดไมโครคอนโทรลเลอร์ผ่านทางอะแดปเตอร์ USB-serial โดยเชื่อมขา VCC ของอุปกรณ์ทั้งคู่เข้าด้วยกัน ถอดอะแดปเตอร์ออกและเสียบบอร์ดไมโครคอนโทรลเลอร์กลับที่เดิมเมื่อต้องการอัพโหลดเฟิร์มแวร์ใหม่
โปรแกรมทางฝั่งคอมพิวเตอร์
โปรแกรมที่ใช้รับข้อมูลผ่านพอร์ทอนุกรมทางฝั่งคอมพิวเตอร์สามารถเขียนขึ้นได้จากภาษาใดก็ได้ที่มีไลบรารีสำหรับเชื่อมต่อกับพอร์ทอนุกรม ในวิกินี้จะยกตัวอย่างสองภาษาคือภาษาจาวาและภาษาไพทอน
ตัวอย่างโปรแกรมภาษาจาวา
ภาษาจาวาไม่ได้มีไลบรารีสำหรับสื่อสารกับพอร์ทอนุกรมมาให้ตั้งแต่แรก จึงต้องมีการติดตั้งเพิ่มเติม ไลบรารีที่นิยมใช้กันได้แก่ RXTX โค้ดตัวอย่างด้านล่างรอรับข้อมูลจากพอร์ทอนุกรมที่ระบุไ้ว้ในคอมมานด์ไลน์อาร์กิวเมนต์ และพิมพ์อักขระที่รับมาได้ทั้งหมดออกมาทางหน้าจอ
import java.io.InputStream;
import gnu.io.*;
public class SerialMonitor {
public static void main(String[] args) throws Exception
{
if (args.length != 1)
{
System.err.println("Usage: java SerialMonitor <serial-dev>");
System.exit(1);
}
String dev = args[0];
System.out.println("Monitoring serial stream on " + dev);
CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(dev);
if (portIdentifier.isCurrentlyOwned())
{
System.out.println( "Error: Port is currently in use" );
}
else
{
int timeout = 2000;
CommPort commPort = portIdentifier.open("serial",timeout);
if (commPort instanceof SerialPort)
{
SerialPort serialPort = (SerialPort) commPort;
serialPort.setSerialPortParams(9600,
SerialPort.DATABITS_8,
SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE );
InputStream in = serialPort.getInputStream();
while (true)
{
int data = in.read();
if (data < 0) break;
System.out.print((char)data);
}
}
}
}
}
ศึกษาขั้นตอนการติดตั้งไลบรารี RXTX ได้จากวิกิ การติดตั้งไลบรารี RXTX
ตัวอย่างโปรแกรมภาษาไพทอน
เช่นเดียวกับภาษาจาวา ภาษาไพทอนไม่ได้มีไลบรารีสำหรับสื่อสารกับพอร์ทอนุกรมมาให้เช่นกัน ใช้คำสั่ง apt-get หรือ pip เพื่อติดตั้งไลบรารี pyserial ดังนี้
sudo apt-get install python-serial
หรือ
sudo pip install pyserial
โค้ดตัวอย่างด้านล่าง (สำหรับไพทอน 3.x) รอรับข้อมูลจากพอร์ทอนุกรมที่ระบุผ่านคอมมานด์ไลน์อาร์กิวเมนต์ และพิมพ์อักขระที่รับมาได้ทั้งหมดออกมาทางหน้าจอ
import sys
import serial
if len(sys.argv) != 2:
print("Usage: %s <serial-dev>" % sys.argv[0], file=sys.stderr)
exit(1)
dev = sys.argv[1]
print("Monitoring serial input stream on %s" % dev)
ser = serial.Serial(dev)
while True:
data = ser.read()
print(data.decode("utf8"),end="")
sys.stdout.flush() # force output to display