ถ้าน้องบังเอิญหลงเข้ามาอ่านบทความนี้โดยไม่ได้เริ่มจากตอนแรก น้องอาจจะงงเล็กน้อย เพราะบทความนี้เป็นหนึ่งในหกตอนของซีรีส์สอนใช้งานไลบรารี ez_SIPO8_lib สำหรับจัดการและควบคุมไอซีแบบ Serial-in/Parallel-out (SIPO) หรือที่เรียกกันว่า shift registers อย่างเช่นชิป 74HC595 นั่นเอง
ถ้าอยากเริ่มอ่านตั้งแต่ต้นซีรีส์ ก็กดลิงก์นี้ได้เลย (ลิงก์ถูกลบตามกฎ) มิฉะนั้นก็อ่านต่อเลยจ้า...
Q&As - อะไรคือ/ทำยังไง/ใช้ยังไง...
บทสอนเพื่อรวบรวมความเข้าใจและการใช้งานไลบรารี ez Serial-in/Parallel-out IC (ez_SIPO8_lib) - ตอนที่ 6, คำถาม & คำตอบ ถ้าอยากกลับไปอ่านบทความเริ่มต้นของซีรีส์สอน ก็กดลิงก์นี้ได้เลย (ลิงก์ถูกลบตามกฎ)น้องสามารถเข้าถึงและดาวน์โหลดคู่มือผู้ใช้ (User Guide), เอกสารสรุป (Crib Sheet) และบทความต้นฉบับเกี่ยวกับ ez_SIPO8_lib ได้ผ่านลิงก์ด้านล่างนี้ (ลิงก์ถูกลบตามกฎ)- ez_SIPO8_lib คู่มือผู้ใช้ (ลิงก์ถูกลบตามกฎ)- ez_SIPO8_lib เอกสารสรุป (ลิงก์ถูกลบตามกฎ)- อ่านบทความเต็มเกี่ยวกับ ez_SIPO8_lib (ลิงก์ถูกลบตามกฎ)เกริ่นนำบทสอน
ในบทสอนตอนนี้ เราจะมาจัดการกับคำถามและคำตอบ (Q&As) หลายๆ ข้อกัน ลองมาดูกันว่าเบื้องหลังมันเกิดอะไรขึ้น และเราจะใช้ฟีเจอร์ภายในบางอย่างของไลบรารี SIPO8 ให้เกิดประโยชน์สูงสุดได้ยังไงบ้าง มาจัดการกันให้เรียบร้อย!
มุมมองของโปรเจกต์
Shift Registers - Tutorial 6, Q&As คือการสำรวจเทคโนโลยีดิจิทัลและอันตรกิริยาระหว่างการต่อแบบลอจิก-คาสเคดอย่างลึกซึ้ง โดยโฟกัสที่บล็อกพื้นฐานสำคัญ—นั่นคือ อาร์เรย์ของรีจิสเตอร์เก็บข้อมูลแบบ shift-storage 74HC595 และ ลอจิกซอฟต์แวร์แบบ multi-bank ez_SIPO8 ของน้องเอง—น้องจะได้เรียนรู้วิธีสื่อสารและซิงโครไนซ์งาน I/O ความหนาแน่นสูงโดยใช้ลอจิกซอฟต์แวร์เฉพาะทางและการตั้งค่าด้วยมือที่มั่นคง งานนี้จัดหนักแน่นอน!
การนำไปใช้ทางเทคนิค: Serial-to-Parallel และการแมปแบงค์
โปรเจกต์นี้เผยให้เห็นเลเยอร์ที่ซ่อนอยู่เบื้องหลังอันตรกิริยาระหว่างข้อมูลกับพินแบบง่ายๆ:
- เลเยอร์ระบุตัวตน: ไอซี 74HC595 ทำหน้าที่เป็นอินเทอร์เฟซไบนารีความละเอียดสูง คอยวัดแต่ละจุดของพัลส์นาฬิกา/ข้อมูลผ่านบัสอนุกรม
- เลเยอร์แปลงสัญญาณ: ระบบใช้โปรโตคอลความเร็วสูงคล้าย SPI (Data, Clock, Latch) เพื่อรับบิตสตรีมความเร็วสูงและประสานงานภารกิจการตรวจจับที่สำคัญ งานนี้ต้องแม่น!
- เลเยอร์อินเทอร์เฟซขยาย: รีจิสเตอร์ 74HC595 ที่ต่อแบบคาสเคดแปดตัว ให้ฟีดแบ็กภาพความละเอียดสูงสำหรับการตรวจสอบสถานะ I/O แต่ละตัว (เช่น เอาต์พุตอิสระ 64 ช่อง) เยอะจัด!
- ลอจิกการประมวลผล: โค้ด Arduino ใช้กลยุทธ์ "bit-dispatch" (หรือ bank-dispatch): มันตีความคำสั่งจากไลบรารีและจับคู่สถานะพินเพื่อให้การควบคุมดิจิทัลที่ปลอดภัยและเป็นจังหวะ ทำงานเป็นระบบ
- ลูปการสื่อสาร: สตริงสถานะพินจะถูกส่งเป็นจังหวะไปยัง Serial Monitor ในระหว่างการปรับเทียบเริ่มต้น เพื่อประสานสถานะให้พร้อมเพรียงกัน ห้ามช็อตนะตัวนี้!
เป้าหมาย
พี่จะถือว่า ถ้าน้องทำตาม Tutorial ก่อนหน้านี้แล้ว น้องน่าจะได้ตั้งชุดทดสอบที่มี IC SIPO ตัวเดียวกับ LED ต่อไว้แล้ว แต่ถ้ายังไม่ได้ และน้องอยากลองฝึกฟีเจอร์ที่สอนใน Tutorial นี้ล่ะก็ ให้ไปตั้งค่าอุปกรณ์ตามที่อธิบายไว้ใน Tutorial ก่อนหน้า
ใน Tutorial นี้ พี่จะพาน้องไปเจาะลึกคำถามทั้งที่เห็นชัดและไม่ชัดนัก พร้อมกับให้คำตอบที่หวังว่าจะช่วยน้องได้
Q1. Virtual array pool ของพินเอาต์พุตคืออะไร?
A1. ไลบรารี SIPO8 จะสร้างและจองพื้นที่สำหรับทุกพินเอาต์พุตของ IC SIPO ที่น้องร้องขอตอนที่สร้างอ็อบเจ็กต์ของคลาสขึ้นมา ค่านี้กำหนดโดยพารามิเตอร์แรกของคำสั่งสร้างคลาส เช่น SPIO8 my_SIPOs(4, 0) ตรงนี้คือการขอสร้างสภาพแวดล้อมพินเอาต์พุต SIPO แบบเสมือน (virtual) ขนาด 4 x IC SPIO 8-bit ตัวเดียว หรือก็คือ 4 x 8 = 32 พินเอาต์พุต
ชุดพินเอาต์พุต SIPO เสมือนทั้งหมดนี้จะถูกเรียกว่า virtual array หรือ array pool โดยมีช่วงแอดเดรสตั้งแต่ 0 ถึง (8 x จำนวน IC SIPO) – 1 ในตัวอย่างข้างต้น ช่วงแอดเดรสคือ 0 ถึง 31
สิ่งสำคัญที่ต้องเข้าใจคือ พอสร้างเสร็จแล้ว virtual output pin array นี้ยังไม่สามารถอ้างอิงแอดเดรสได้ นั่นเป็นเพราะไลบรารียังไม่รู้ว่าพินเอาต์พุตเสมือนเหล่านี้จะเชื่อมโยงกับโลกกายภาพยังไง นั่นคือ IC SIPO จริงๆ การเชื่อมโยงจะเกิดขึ้นไม่ได้จนกว่าเราจะจัดสรร array pool ให้กับหนึ่งหรือหลาย bank ดูคำถามที่ 2 ต่อเลย
Q2. จะเชื่อมโยงพินเอาต์พุตเสมือนใน array pool กับ IC SIPO จริงๆ ยังไง?
หลังจากที่เรากำหนดและสร้าง virtual array pool ของเราโดยการสร้างอ็อบเจ็กต์ของคลาสแล้ว ตอนนี้เราต้อง 'หั่นมัน' / จัดสรรมันออกเป็น 'bank' ของกลุ่มพินเอาต์พุตโดยใช้ฟังก์ชัน create_bank Bank หนึ่งๆ จะ:
- ประกอบด้วยชุดพินเอาต์พุตเสมือนที่ไม่ซ้ำกัน ซึ่งถูกจัดสรรมาจาก virtual array pool
- แมปช่วงแอดเดรสสัมบูรณ์ (absolute address) ที่ต่อเนื่องกันของพินเอาต์พุตเสมือนเข้าไปใน bank ทำให้สามารถอ้างอิงเอาต์พุตเหล่านี้แบบสัมพัทธ์ (relative) กับ bank ที่มันอยู่ได้ - แอดเดรสพอร์ตเอาต์พุตตัวแรกของ ทุก bank คือ 0 ซึ่งแทน บิตที่มีนัยสำคัญน้อยที่สุด (least significant bit) ของ SIPO ใน bank นั้น
- กำหนดพินดิจิทัล 3 สายของไมโครคอนโทรลเลอร์ที่ใช้ขับ bank
- ทำให้พินเอาต์พุตที่เกี่ยวข้องกับ bank นั้นแอคทีฟ และสามารถอ้างอิงแอดเดรสได้ ทั้งแบบสัมบูรณ์และสัมพัทธ์ (ดูคำถามที่ 3)
ตัวอย่างเช่น ใช้ตัวอย่างข้างต้นของเรา (SPIO8 my_SIPOs(4, 0)) เราอยากแบ่งพินเอาต์พุตเสมือน 32 พินของเราออกเป็น 2 bank - 1 bank ขนาด 8 พิน และอีก bank ที่เหลืออีก 24 พิน และจัดสรรสิ่งเหล่านี้ให้กับพินดิจิทัลของไมโครคอนโทรลเลอร์หมายเลข 3, 4, 5 และ A0, A1, A2 ตามลำดับ เราสร้าง bank โดยใช้ฟังก์ชัน create_bank แบบนี้:
bank1_id = my_SIPOs.create_bank(3, 4, 5, 1); // 8 outputs
bank2_id = my_SIPOs.create_bank(A0, A1, A2, 3); //24 outputs
การเรียก create_bank ครั้งแรกจะจัดสรรพินใน array pool หมายเลข 0–7 ให้กับ bank แรก (bank1_id) และทำให้พินเอาต์พุตที่เกี่ยวข้องแอคทีฟและสามารถอ้างอิงแอดเดรสได้ (ทั้งสัมบูรณ์และสัมพัทธ์) การเรียก create_bank ครั้งที่สองจะจัดสรรพินเอาต์พุตใน array pool หมายเลข 8–31 ให้กับ bank ที่สอง (bank2_id) และทำให้พินเอาต์พุตที่เกี่ยวข้องแอคทีฟและสามารถอ้างอิงแอดเดรสได้
ตอนนี้ array pool ถูกจัดสรรให้กับ bank ของเราครบถ้วนและพินเอาต์พุตทั้งหมดแอคทีฟแล้ว เราสามารถอ้างอิงแอดเดรสพวกมันได้สองวิธี – แบบ absolute addressing และแบบ relative addressing ดูคำถามที่ 3 ต่อ
พูดนอกเรื่องนิด ถ้าเราพยายามเรียกฟังก์ชัน create_bank อีกครั้ง เราจะได้ข้อผิดพลาด เพราะ SIPO ทั้งหมดที่ประกาศตอนสร้างอ็อบเจ็กต์ถูกจัดสรรออกจาก array pool ไปแล้ว - เราร้องขอมา 4 ตัว และเราก็จัดสรรไป 4 ตัว (1 + 3)
Q3. ข้อแตกต่างระหว่าง absolute addressing กับ relative addressing คืออะไร?
A3. ตอบง่ายๆ เลย:
แอดเดรสสัมบูรณ์ (Absolute addresses) เกี่ยวข้องกับเฉพาะพินเอาต์พุตที่ แอคทีฟ ใน array pool เสมือน โดยมีช่วงแอดเดรสรวมตั้งแต่ 0 ถึง (จำนวนพินเอาต์พุตทั้งหมด) - 1 ใน array pool มีฟังก์ชันไม่กี่ฟังก์ชัน (แต่ก็พอใช้) ที่ใช้การอ้างอิงแบบสัมบูรณ์ ได้แก่:
set_all_array_pins(status)(อ้างอิงสัมบูรณ์โดยนัย)
invert_all_array_pins()(อ้างอิงสัมบูรณ์โดยนัย)
set_array_pin(pin_address)(อ้างอิงสัมบูรณ์โดยชัดแจ้ง)
invert_array_pin(pin_address)(อ้างอิงสัมบูรณ์โดยชัดแจ้ง)
read_array_pin(pin_address)(อ้างอิงสัมบูรณ์โดยชัดแจ้ง)
xfer_array(LSBFIRST_หรือ_MSBFIRST)(อ้างอิงสัมบูรณ์โดยนัย)
เมื่อ
pin_address คือแอดเดรสสัมบูรณ์ของพินใน array pool ที่แอคทีฟ นั่นคือช่วงแอดเดรสตั้งแต่ 0 ถึง (8 x จำนวน IC SIPO ทั้งหมดที่กำหนดและแอคทีฟ) – 1 ในตัวอย่างข้างต้น ช่วงแอดเดรสสัมบูรณ์คือ 0–31 รวม
แอดเดรสสัมพัทธ์ (Relative addresses) เกี่ยวข้องกับเฉพาะพินเอาต์พุตที่แอคทีฟใน bank โดยมีช่วงแอดเดรสรวมตั้งแต่ 0 ถึง (จำนวนพินเอาต์พุตใน bank) - 1 มีฟังก์ชันที่เกี่ยวข้องกับ bank หลายฟังก์ชันที่ใช้การอ้างอิงแบบสัมพัทธ์ ได้แก่:
set_bank_pin(bank_id,pin_address, status)(อ้างอิงสัมพัทธ์โดยชัดแจ้ง)
invert_bank_pin(bank_id,pin_address)(อ้างอิงสัมพัทธ์โดยชัดแจ้ง)
read_bank_pin(bank_id,pin_address)(อ้างอิงสัมพัทธ์โดยชัดแจ้ง)
set_bank(bank_id,status)(อ้างอิงสัมพัทธ์โดยนัย)
set_banks(from_bank,to_bank, status)(อ้างอิงสัมพัทธ์โดยนัย)
set_banks(status)(อ้างอิงสัมพัทธ์โดยนัย)
invert_bank(bank_id)(อ้างอิงสัมพัทธ์โดยนัย)
invert_banks(from_bank,to_bank)(อ้างอิงสัมพัทธ์โดยนัย)
invert_banks()(อ้างอิงสัมพัทธ์โดยนัย)
set_bank_SIPO(bank_id,SIPO_num, SIPO_value)(อ้างอิงสัมพัทธ์โดยนัย)
invert_bank_SIPO(bank_id,SIPO_num)(อ้างอิงสัมพัทธ์โดยนัย)
read_bank_SIPO(bank_id,SIPO_num)(อ้างอิงสัมพัทธ์โดยนัย)
xfer_bank(bank_id,LSBFIRST_หรือ_MSBFIRST)(อ้างอิงสัมพัทธ์โดยนัย)
xfer_banks(bank_from,bank_to, LSBFIRST_หรือ_MSBFIRST)(อ้างอิงสัมพัทธ์โดยนัย)
xfer_banks(LSBFIRST_หรือ_MSBFIRST)(อ้างอิงสัมพัทธ์โดยนัย)
เมื่อ
pin_address คือแอดเดรสสัมพัทธ์ของพิน ใน bank นั่นคือช่วงแอดเดรสตั้งแต่ 0 ถึง (จำนวนพินเอาต์พุตใน bank) – 1
SIPO_num คือแอดเดรสสัมพัทธ์ของ SIPO 8-bit ที่กำหนดโดย bank นั่นคือมีช่วงตั้งแต่ 0 ถึง (จำนวน SIPO ที่กำหนดให้ bank) – 1
ในตัวอย่างข้างต้น array pool ที่มีขนาด 4 x IC SIPO ถูกแบ่งออกเป็นสอง bank แยกกัน bank หนึ่งมี 1 x IC SIPO และอีก bank มี 3 x IC SIPO ช่วงการอ้างอิงแอดเดรสที่เกี่ยวข้องคือ:
array pool, ช่วงแอดเดรสสัมบูรณ์: 0–31
bank1_id ช่วงแอดเดรสพินเอาต์พุตสัมพัทธ์: 0-7, ช่วงไบต์ SIPO สัมพัทธ์: 0-0
bank2_id ช่วงแอดเดรสพินเอาต์พุตสัมพัทธ์:0–23, ช่วงไบต์ SIPO สัมพัทธ์: 0-2
Q4. จะรู้ได้ยังไงว่ามีพินเอาต์พุต SIPO เสมือนกี่พินใน array pool?
A4. ไลบรารีมีตัวแปรที่ผู้ใช้เข้าถึงได้หลายตัวซึ่งมีประโยชน์ในการสนับสนุนการตัดสินใจและการควบคุม ในกรณีนี้ ตัวแปรไลบรารีที่ใช้คือ max_pins* และน้องจะใช้มันโดยเติมชื่อที่ให้น้องให้กับคลาส SIPO8 ตอนที่สร้างอ็อบเจ็กต์ลงไปข้างหน้า ตัวอย่างเช่น ถ้าน้องตั้งชื่อคลาสของคุณว่า ‘my_SIPOs’ การใช้งานก็จะเป็น my_SIPOs.max_pins
ระวังไว้นะว่าค่านี้คือจำนวนพินเอาต์พุตทั้งหมดที่กำหนดตอนสร้างอ็อบเจ็กต์คลาสไลบรารี - มัน ไม่ใช่ จำนวนพินเอาต์พุตที่แอคทีฟ ดูคำถามที่ 5 ต่อ
Q5. จะรู้ได้ยังไงว่ามีพิน SIPO เสมือนที่แอคทีฟกี่พินใน array pool?
A5. อีกครั้ง เราจะใช้ตัวแปรไลบรารี ในกรณีนี้คือตัวแปรไลบรารี num_active_pins* และน้องจะใช้มันโดยเติมชื่อที่ให้น้องให้กับคลาส SIPO8 ตอนที่สร้างอ็อบเจ็กต์ลงไปข้างหน้า ตัวอย่างเช่น my_SIPOs.num_active_pins
* หมายเหตุว่ามีความแตกต่างที่ต้องทำความเข้าใจ ระหว่างจำนวนพิน SIPO ที่ array pool ถูกกำหนดขนาดไว้สำหรับ (max_pins) กับจำนวนพินที่แอคทีฟ (num_active_pins) ใน array เมื่อคลาส SIPO8 ถูก สร้างอ็อบเจ็กต์ array pin pool ก็จะถูกสร้างขึ้น