ผลการเรียนรู้ (Learning Outcomes)
- อธิบายได้ว่า UART คืออะไร ต่างจาก USART อย่างไร และทำไมเรียกว่าเป็นการสื่อสารแบบ asynchronous (ไม่มีสายนาฬิกา)
- อ่านและออกแบบ frame format ของ UART ได้ครบทุกส่วน: start bit, data bits, parity bit, stop bit และบอกได้ว่าส่ง LSB ก่อน
- แยกความหมายของ bit rate กับ baud rate และคำนวณ overhead ของการส่งแบบ UART ได้
- เปรียบเทียบสัญญาณระดับ TTL กับ RS-232 ได้ว่าต่างกันเรื่องแรงดันและกลับขั้วลอจิกอย่างไร และรู้ว่าต้องต่อ TX↔RX ไขว้กัน
- ตั้งค่า (config) UART ได้ครบ: จำนวน data bit, parity (none/odd/even), จำนวน stop bit และเลือก baud rate ที่เหมาะสม
- เขียนโปรแกรมรับส่งข้อมูลผ่าน USART3 บน NUCLEO-F767ZI ด้วย HAL ได้ โดยเข้าใจบทบาทของ register TDR/RDR
§5.1 UART คืออะไร และ asynchronous หมายถึงอะไร
UART ย่อมาจาก Universal Asynchronous Receiver / Transmitter คือวงจรสำหรับรับและส่งข้อมูลแบบอนุกรม (serial) หนึ่งทิศทางใช้สายเพียงเส้นเดียว. ในโลก Arduino เรามักเรียกการสื่อสารแบบนี้สั้น ๆ ว่า “Serial”.
คำว่า asynchronous (ไม่ประสานเวลา) เป็นหัวใจของ UART. มันแปลว่าเรา “ไม่ได้ส่งสายนาฬิกา (clock) ไปด้วย”. ฝั่งส่งกับฝั่งรับต่างคนต่างใช้นาฬิกาของตัวเอง แล้วตกลงกันไว้ล่วงหน้าว่าจะส่งเร็วเท่าไร ฝั่งรับจึงรู้ว่าต้องไปอ่านค่าบนสายตรงจังหวะไหน.
ถ้าเพิ่มสายนาฬิกาเข้าไปด้วย วงจรจะกลายเป็น USART (Universal Synchronous/Asynchronous Receiver/Transmitter) ซึ่งเลือกได้ว่าจะใช้ clock หรือไม่ใช้. การมี clock ช่วยให้ส่งข้อมูลได้เร็วและแม่นยำขึ้น. ฮาร์ดแวร์ของ STM32 ส่วนใหญ่เป็น USART แต่ถ้าเราไม่ใช้สาย clock มันก็ทำงานเป็น UART ธรรมดา.
ข้อสำคัญ: UART เป็นเพียงข้อตกลงเรื่อง “รูปแบบเฟรม (frame format)” เท่านั้น มันไม่ได้กำหนดชั้นกายภาพ (physical layer) ว่าต้องใช้แรงดันหรือสายแบบไหน. ตามแบบจำลอง OSI ถือว่า UART อยู่ใน Data Link layer (เลเยอร์ 2). เพราะไม่ได้ผูกกับชั้นกายภาพ เราจึงนำเฟรมของ UART ไปวิ่งบนสายมาตรฐานอื่น เช่น RS-232 ได้.
เพราะวงจรไม่ซับซ้อน (ใช้สายส่งเส้นเดียวต่อทิศทาง ไม่ต้องมี clock) ไมโครคอนโทรลเลอร์แทบทุกเบอร์จึงมี UART มาให้อย่างน้อย 1 ชุด. UART เหมาะกับงานทั่วไปที่ไม่ต้องการความเร็วสูง ส่วนงานเร็ว ๆ มักไปใช้ SPI หรือ I2C แทน.
UART = serial + asynchronous → มีแค่สายข้อมูล ไม่มีสาย clock (กำลังแสดงกรณีนี้)
UART = serial + asynchronous: มีแค่สายข้อมูล ไม่มีสาย clock เชื่อมสองฝั่ง. ลองสลับ Async/Sync เพื่อดูสาย clock ปรากฏหรือหายไป.
§5.2 โครงสร้างเฟรม (Frame Format) ของ UART
ข้อมูล 1 เฟรมของ UART ประกอบด้วย 4 ส่วน เรียงกันตามเวลา: บิตเริ่ม (start bit) → บิตข้อมูล (data bits) → บิตพาริตี (parity bit, มีหรือไม่ก็ได้) → บิตหยุด (stop bit).
ตอนไม่มีการส่ง สายจะอยู่สถานะว่าง (idle) ซึ่งเป็นลอจิก 1. เมื่อจะเริ่มส่ง ฝั่งส่งดึงสายลงเป็นลอจิก 0 หนึ่งบิต นี่คือ start bit ที่บอกฝั่งรับว่า “เฟรมใหม่กำลังมา” ให้เริ่มจับเวลาอ่านบิตถัดไป.
data bits: เลือกได้ว่าจะมีกี่บิต เช่น 6, 7, 8 หรือ 9 บิต ขึ้นกับว่าฮาร์ดแวร์ของ MCU รองรับเท่าไร. ที่นิยมที่สุดคือ 8 บิต (ส่ง 1 ไบต์ = 1 ตัวอักษร). UART ส่ง LSB (บิตต่ำสุด) ออกไปก่อน แล้วไล่ไปจน MSB (เรียงแบบ little-endian).
parity bit: เป็น overhead ตัวหนึ่งไว้ตรวจความถูกต้องของข้อมูลแบบง่าย ๆ. ถ้าเลือก even parity ให้เติม parity ให้จำนวนบิต 1 รวมทั้งเฟรมเป็นเลขคู่; ถ้าเลือก odd parity ให้รวมเป็นเลขคี่; ถ้าเลือก none ก็ไม่มี parity เลย. ฝั่งรับนับบิต 1 แล้วเทียบ ถ้าไม่ตรงแปลว่าข้อมูลเพี้ยนระหว่างทาง.
ข้อควรระวังเฉพาะของ STM32: ช่องความกว้าง data ที่ตั้งไว้ (เช่น 8 บิต) จะ “รวม” parity เข้าไปด้วย. ถ้าตั้ง 8 บิตแล้วเปิด parity จะเหลือข้อมูลจริงแค่ 7 บิต + parity 1 บิต. ถ้าต้องการข้อมูลจริง 8 บิตพร้อม parity ต้องตั้งความกว้างเป็น 9 บิต.
stop bit: ปิดท้ายเฟรมด้วยลอจิก 1 (เหมือนคืนสายกลับสู่ idle) เป็นเวลาอย่างน้อย 1 บิต. UART ส่วนใหญ่ใช้ 1 stop bit แต่เลือกใช้ 1.5 หรือ 2 บิตก็ได้ เพื่อให้วงจรฝั่งรับมีเวลาตั้งตัวก่อนเฟรมถัดไป. การตั้งค่าแบบ 8 data bit, no parity, 1 stop bit เขียนย่อว่า 8N1 เป็นค่าที่พบบ่อยที่สุด.
| ส่วน | ลอจิก/ค่า | หน้าที่ |
|---|---|---|
| Idle | 1 | สถานะว่าง ไม่มีการส่ง |
| Start bit | 0 | บอกว่าเฟรมใหม่เริ่มแล้ว (1 บิตเสมอ) |
| Data bits | 6/7/8/9 บิต | ข้อมูลจริง ส่ง LSB ก่อน (little-endian) |
| Parity bit | 0 หรือ 1 (เลือกได้) | ตรวจความถูกต้อง: even=คู่, odd=คี่, none=ไม่มี |
| Stop bit | 1 | ปิดท้ายเฟรม ≥1 บิต (นิยม 1 บิต) |
ประกอบเฟรม UART หนึ่งเฟรม: idle(1) → start(0) → data (LSB ออกก่อน) → parity → stop(1). ปรับ config แล้ว waveform กับการคำนวณ parity จะวาดใหม่ทันที.
§5.3 Bit rate, Baud rate และ Overhead
Bit rate = จำนวนบิตที่ส่งได้ใน 1 วินาที หน่วยเป็น bit per second (bps). เช่น 9600 bps หมายถึงส่งได้ 9600 บิตต่อวินาที.
Baud rate = จำนวน “สัญลักษณ์ (symbol)” ที่ส่งได้ใน 1 วินาที. ในระบบ UART ที่ 1 สัญลักษณ์เท่ากับ 1 บิตพอดี ค่าของ baud rate กับ bit rate จึง “เท่ากัน” และใช้หน่วยเดียวกันได้ (คนจึงมักเรียกสลับกัน). แต่ในระบบที่ 1 สัญลักษณ์แทนได้หลายบิต สองค่านี้จะไม่เท่ากัน จึงต้องแยกให้ชัด.
Overhead = ทุกบิตที่เราต้องส่งไปแต่ไม่ใช่ข้อมูลจริง ได้แก่ start bit, parity bit และ stop bit. มันทำให้ความเร็ว “ข้อมูลจริง” ต่ำกว่า baud rate เสมอ.
ตัวอย่าง: ส่งแบบ 8N1 (8 data, no parity, 1 stop) 1 เฟรมมี 1 (start) + 8 (data) + 1 (stop) = 10 บิต แต่เป็นข้อมูลจริงแค่ 8 บิต. overhead = 2/10 = 20%. ที่ baud rate 9600 จึงส่งข้อมูลจริงได้จริงราว 9600 × 8/10 = 7680 bps.
การเลือก baud rate: ยิ่งสูงยิ่งส่งเร็ว แต่ต้องระวังความคลาดเคลื่อนของนาฬิกาสองฝั่ง เพราะเป็น asynchronous ถ้าเร็วเกินไปฝั่งรับอาจอ่านบิตผิดจังหวะ. ค่ายอดนิยมคือ 9600 และ 115200 bps.
ฝั่งรับอ่านค่าด้วยการ oversampling คือสุ่มอ่านที่ความถี่ 16 เท่าของ baud rate เพื่อหาจุดกึ่งกลางของแต่ละบิตให้แม่นยำ หลังจากนั้นจึงตัด start bit กับ stop bit ทิ้ง เหลือแต่ data bits.
| การตั้งค่า | บิตต่อเฟรม | ข้อมูลจริง | overhead |
|---|---|---|---|
| 8N1 (no parity, 1 stop) | 10 | 8 | 20% |
| 8E1 / 8O1 (parity, 1 stop) | 11 | 8 | 27% |
| 8N2 (no parity, 2 stop) | 11 | 8 | 27% |
baud rate คืออัตราสัญลักษณ์ ไม่ใช่ bit rate ของข้อมูลล้วน. ฝั่งรับ oversample 16× แล้วตัดสินค่าที่จุดกึ่งกลางบิต; start/parity/stop คือ overhead ที่กินแบนด์วิดท์.
§5.4 ระดับสัญญาณ: TTL เทียบกับ RS-232 และการต่อสาย
เฟรมของ UART เป็นแค่ลำดับลอจิก 0/1 แต่เวลาวิ่งบนสายจริง ต้องแปลงเป็นแรงดัน. ที่ขาของ MCU โดยตรงจะเป็นระดับ TTL คือลอจิก 1 = ไฟบวก (เช่น 3.3V) และลอจิก 0 = 0V. สัญญาณ TTL ส่งได้ระยะสั้น เพราะแรงดันต่ำและถูกรบกวนง่าย.
RS-232 เป็นมาตรฐานชั้นกายภาพที่ออกแบบมาให้ส่งได้ไกลขึ้น โดย (1) ใช้แรงดันสูงกว่า และ (2) กลับขั้วลอจิก: ลอจิก 1 เป็นไฟลบ และลอจิก 0 เป็นไฟบวก. เพราะส่งไปไกลสัญญาณจะมีการสูญเสีย (loss) ฝั่งส่งจึงดันแรงดันให้สูง ส่วนฝั่งรับตั้งเกณฑ์ให้ต่ำลงมาเพื่อยังอ่านได้.
ตัวเลขแรงดันของ RS-232 (ค่าที่พบบ่อย): ฝั่งส่งใช้ประมาณ ±3 ถึง ±15 V ส่วนฝั่งรับยอมรับ +3 ถึง +15 V เป็นลอจิก 0 และ −3 ถึง −15 V เป็นลอจิก 1. ช่วง −3 ถึง +3 V ถือเป็นเขตคลุมเครือ (ไม่นับเป็นค่าแน่นอน).
เพราะ TTL กับ RS-232 ใช้แรงดันและขั้วต่างกัน จะต่อตรงกันไม่ได้ ต้องมีวงจรแปลงระดับ (level shifter) เช่นชิป MAX232 คั่นกลาง. สาย RS-232 แบบเก่าใช้หัวต่อ DB9; โน้ตบุ๊กสมัยใหม่ไม่มีพอร์ตนี้แล้ว จึงต้องใช้ตัวแปลง USB-to-Serial (ภายในเป็น IC แปลง) เสียบเข้า USB แทน.
เรื่องต่อสายที่พลาดบ่อย: ต้องต่อ TX ของฝั่งหนึ่งเข้ากับ RX ของอีกฝั่ง และ RX เข้ากับ TX (ต่อไขว้กัน) เพราะขาส่งของเราต้องไปเข้าขารับของเขา. ยกเว้นกรณีมีวงจรสลับให้อัตโนมัติ จึงไม่ต้องไขว้เอง. และอย่าลืมต่อสาย Ground ร่วมกันด้วย.
| หัวข้อ | TTL | RS-232 |
|---|---|---|
| ลอจิก 1 | ไฟบวก (เช่น 3.3V) | ไฟลบ (−3 ถึง −15V) |
| ลอจิก 0 | 0V | ไฟบวก (+3 ถึง +15V) |
| แรงดัน | ต่ำ | สูง |
| ระยะส่ง | สั้น | ไกลกว่า |
| ต่อเข้า MCU ตรง | ได้ | ต้องมี level shifter (เช่น MAX232) |
RS-232 กลับขั้วลอจิกและสวิงแรงดันกว้างกว่า TTL จึงต้องมี level shifter (เช่น MAX232) และต่อสาย TX↔RX ไขว้. เลื่อนเมาส์หรือโฟกัสที่คอลัมน์บิตเพื่อดูแรงดันทั้งสองมาตรฐาน.
§5.5 UART บน NUCLEO-F767ZI และการเขียนโปรแกรม
ชิป STM32F767ZI มีโมดูล USART/UART ทั้งหมด 8 โมดูล. ช่องที่เราใช้บ่อยที่สุดคือ USART3 เพราะมันถูกเดินสายเชื่อมเข้ากับ ST-LINK บนบอร์ด ซึ่ง ST-LINK ทำหน้าที่เป็น Virtual COM Port (VCP) ให้ — เราจึงคุยกับ PC ได้ผ่านสาย USB เส้นเดียวกับที่โปรแกรมบอร์ด ไม่ต้องต่อสายเพิ่ม.
ขาของ USART3 บน NUCLEO-F767ZI: PD8 ทำหน้าที่เป็นขาส่ง (TX) และ PD9 เป็นขารับ (RX) ซึ่งทั้งสองต่อไปยังฝั่ง VCP ของ ST-LINK เรียบร้อยแล้ว.
ภายในฮาร์ดแวร์ UART มี register สำคัญคือ shift register สำหรับเลื่อนบิตเข้าออก และ data register สำหรับพักข้อมูล. บน STM32 (HAL) data register ฝั่งส่งชื่อ TDR (Transmit Data Register) และฝั่งรับชื่อ RDR (Receive Data Register) พร้อมบิตควบคุม baud rate ต่าง ๆ.
การตั้งค่าใน STM32CubeMX: เลือก baud rate (เช่น 115200), จำนวน word length (8 บิต), parity (none), และ stop bit (1) — รวมเป็น 115200 8N1. จากนั้น generate code.
การส่ง: ใช้ HAL_UART_Transmit โดยระบุ handle ของ UART, ที่อยู่ของข้อมูล, จำนวนไบต์ และ timeout. การรับ: ใช้ HAL_UART_Receive รับมาเก็บในตัวแปร. ฟังก์ชันแบบนี้เป็น blocking คือจะรอจนครบหรือจน timeout. ถ้ารับเป็นตัวอักษร (character) ให้ประกาศ char แล้วส่งที่อยู่ของมัน แต่ต้อง cast เป็น uint8_t* เพราะ HAL รับเป็น pointer ของ uint8_t.
ตัวอย่างการใช้งานจริง: ทำเมนูสั่งหุ่นยนต์ผ่าน UART — รับตัวอักษร 1 ตัวจาก PC แล้ว if เทียบ เช่น 'W' = เดินหน้า, 'S' = ถอยหลัง, 'A' = เลี้ยวซ้าย, 'D' = เลี้ยวขวา.
// ส่งข้อความออกไปที่ PC (blocking, timeout 100 ms)
char msg[] = "Hello from Nucleo\r\n";
HAL_UART_Transmit(&huart3, (uint8_t *)msg, sizeof(msg) - 1, 100);
// รับ 1 ตัวอักษรจาก PC มาเก็บใน rx
char rx;
if (HAL_UART_Receive(&huart3, (uint8_t *)&rx, 1, 1000) == HAL_OK) {
// สั่งงานตามตัวอักษรที่รับมา (เมนูหุ่นยนต์)
if (rx == 'W') go_forward();
else if (rx == 'S') go_backward();
else if (rx == 'A') turn_left();
else if (rx == 'D') turn_right();
}ไบต์เดินทาง: โค้ด → USART3 (TDR/RDR) → PD8/PD9 → ST-LINK (Virtual COM Port) → USB → PC. สาย USB เส้นเดียวกับที่ใช้โปรแกรมบอร์ดก็คุยกับ PC ได้.
แบบฝึกหัด
12 ข้อ — แต่ละข้อมีชนิดและระดับความยากกำกับ กดปุ่ม "ดูเฉลย" เพื่อดูคำตอบและคำอธิบาย
- 1.ปรนัยง่าย
คำว่า asynchronous ใน UART หมายความว่าอะไร
- 2.ปรนัยง่าย
ในเฟรม UART ส่วนใดที่บอกฝั่งรับว่าเฟรมใหม่กำลังเริ่ม
- 3.ปรนัยกลาง
ความต่างหลักระหว่าง UART กับ USART คืออะไร
- 4.ปรนัยกลาง
UART ส่งบิตข้อมูลออกไปตามลำดับใด
- 5.ปรนัยยาก
บน STM32 ถ้าตั้ง word length = 8 บิตและเปิด parity ด้วย ข้อมูลจริง (data) จะเหลือกี่บิต
- 6.ถูก/ผิดกลาง
ในระบบ UART ค่า baud rate กับ bit rate มีค่าเท่ากัน
- 7.ถูก/ผิดกลาง
ใน RS-232 ลอจิก 1 แทนด้วยแรงดันไฟบวก และลอจิก 0 แทนด้วยไฟลบ
- 8.ถูก/ผิดง่าย
การต่อ UART ระหว่างสองอุปกรณ์ต้องต่อ TX ของฝั่งหนึ่งเข้ากับ RX ของอีกฝั่ง (ต่อไขว้)
- 9.เติมคำกลาง
การตั้งค่า 8 data bit, no parity, 1 stop bit เขียนย่อว่า ____ และ 1 เฟรมมีทั้งหมด ____ บิต
- 10.เติมคำง่าย
บน NUCLEO-F767ZI ช่อง USART ที่ต่อกับ ST-LINK เป็น Virtual COM Port คือ USART____ โดยใช้ขา PD8 เป็น ____ และ PD9 เป็น ____
- 11.วิเคราะห์ยาก
ส่งข้อมูลแบบ 8N1 ที่ baud rate 9600 bps ถ้าต้องส่งข้อความ 100 ตัวอักษร (100 ไบต์) จะใช้เวลาประมาณเท่าใด และ overhead เป็นกี่เปอร์เซ็นต์ อธิบายวิธีคิด
- 12.วิเคราะห์ยาก
ต่อ UART ระหว่างบอร์ด STM32 (ขา TTL 3.3V) กับอุปกรณ์ที่มีพอร์ต RS-232 โดยตรงโดยไม่ผ่านอะไรเลย ผลจะเป็นอย่างไร และต้องแก้ด้วยอะไร