นอกจากจะเขียนโค้ด Arduino ด้วย C/C++ แล้ว พี่ยังมีสกิลเขียนโปรแกรม Visual Basic 6 ได้ด้วยนะ (เก่าแต่ยังแจ๋ว!)
หลายปีก่อน พี่เคยทำโปรแกรมแสดงเวลาบน LED Bar ไว้ด้วย ดูรูปนี้เลย:

อย่างที่เห็นนะ เวลาคือ 19 นาฬิกา LED สีฟ้าแทนเลขนี้ด้วย LED 1 ดวง และ LED 9 ดวง
นาทีคือ 39 : แทนด้วย LED สีเขียว 3 ดวง และ 9 ดวง และวินาที 42: แทนด้วย LED สีแดง 4 ดวง และ 2 ดวง
พี่เลยเอาไอเดียนี้มาทำเป็นนาฬิกาจริงๆ พร้อมฟังก์ชันเพิ่มเติมอีกนิดหน่อย
จะเห็นว่าแนวคิดเหมือนกันเลย: ชั่วโมงคือสองแถบซ้าย (ติด LED 1 และ 6 ดวง) นาทีคือแถบที่ 3 และ 4 (ติด LED 4 และ 8 ดวง) และวินาทีคือสองแถบขวา (ติด LED 4 และ 9 ดวง)
LED ดวงที่ 5 ในทุกคอลัมน์ (ยกเว้นคอลัมน์แรก) เป็นสีแดง
ดูรูปเพิ่มเติมได้เลย:
มุมมองของโปรเจกต์
นาฬิกาไบนารี/ทศนิยมนี้คือ "เครื่องฝึกตรรกะ" (Logic Trainer) แบบใหม่ที่เชื่อมโยงพื้นฐานอิเล็กทรอนิกส์เข้ากับการพัฒนาสมัยใหม่ โดยเน้นไปที่บล็อกพื้นฐานสำคัญ เช่น การคำนวณจัดการบิต (bit-manipulation arithmetic) และการเชื่อมต่อกับฮาร์ดแวร์ คุณจะได้เรียนรู้วิธีทำให้การบอกเวลาทำงานอัตโนมัติโดยใช้ตรรกะซอฟต์แวร์เฉพาะทางและการตั้งค่าฮาร์ดแวร์ที่แข็งแกร่ง
การนำไปใช้ทางเทคนิค: เลขคณิตบิตและการขับเมทริกซ์
โปรเจกต์นี้เผยให้เห็นเลเยอร์ที่ซ่อนอยู่ของการโต้ตอบระหว่างเลขฐานสองกับแสง ซึ่งสร้างขึ้นจากเลเยอร์หลักหลายชั้น:
- เลเยอร์ระบุตัวตน: โมดูล DS3231 RTC ทำหน้าที่เป็นตัวอ้างอิงเวลาความละเอียดสูง โดยให้เวลาที่แม่นยำผ่านคริสตัลภายใน
- เลเยอร์ตรรกะประมวลผล: โค้ด Arduino ใช้กลยุทธ์ "การให้น้ำหนักบิต" (ทศนิยมเป็นไบนารี) มันตีความค่าจาก RTC และจับคู่สถานะของ LED เพื่อให้รูปแบบการแสดงเวลาที่ปลอดภัยและเป็นจังหวะ ตรรกะหลักเกี่ยวข้องกับการแยกเลขฐานสองจากค่าของเวลาเพื่อควบคุมคอลัมน์ LED
- เลเยอร์อินเทอร์เฟซภาพ: แถบ LED และจอแสดงผล 7-Segment ให้ผลตอบรับภาพที่ชัดเจนสำหรับ "สถานะเวลา" (เช่น คอลัมน์ = ชั่วโมง/นาที/วินาทีในรูปแบบทศนิยมหรือไบนารี)
- เลเยอร์การสื่อสารและการควบคุม: ระบบใช้ชิฟต์เรจิสเตอร์ (SN74595) เพื่อจัดการ LED และเซ็กเมนต์แสดงผลจำนวนมากได้อย่างมีประสิทธิภาพ LED ถูกควบคุมผ่านการมัลติเพล็กซ์ ในขณะที่จอแสดงผลไม่ได้เป็นเช่นนั้น ตามที่แสดงในแผนผังและรูปภาพแกลเลอรีด้านบน
[!TIP] ในโค้ด Arduino ของคุณ ใช้ฟังก์ชัน
bitRead()เพื่อแยกสถานะไบนารีสำหรับแต่ละคอลัมน์ LED ได้ง่ายๆ โดยไม่ต้องคำนวณหารให้ยุ่งยาก! จัดไปวัยรุ่น!
โครงสร้างฮาร์ดแวร์นาฬิกา
โครงสร้างทางกายภาพประกอบด้วยชิ้นส่วนหลักหลายอย่าง ซึ่งเห็นได้จากแกลเลอรี่โปรเจกต์:
- ATmega328 (Arduino-compatible): เป็น "สมอง" ของโปรเจกต์นี้ จัดการการคำนวณเวลา, การสลับโหมด, และประสานงานกับชิฟต์เรจิสเตอร์
- SN74595 Shift Registers: ใช้ทั้งหมด 9 ตัว (6 ตัวสำหรับจอ 7-เซกเมนต์, 3 ตัวสำหรับแถบ LED) เพื่อขยายจำนวนเอาต์พุตและควบคุมไฟแสดงผลทั้งหมด
- DS3231 RTC: ให้ความสามารถในการจับเวลาที่แม่นยำและเสถียร
- Custom PCB/Breadboard: เป็นวิธีจัดระเบียบการเชื่อมต่อชิ้นส่วนทั้งหมด ตามที่เห็นในภาพมุมด้านหลัง
การทำงานอัตโนมัติและการโต้ตอบของนาฬิกา
การทำงานของนาฬิกากลางอยู่ที่สวิตช์ 4 ตัว ซึ่งให้ฟังก์ชันการทำงานที่แตกต่างกันถึง 16 แบบ ลูปหลักจะตรวจสอบสถานะของสวิตช์เหล่านี้ตลอดเวลาเพื่อกำหนดโหมดที่กำลังทำงานอยู่
ฟังก์ชันหลัก:
- ฟังก์ชัน 0000: LED แสดงเวลาในรูปแบบทศนิยม; จอแสดงผลแสดงเวลา
- ฟังก์ชัน 0001: LED แสดงเวลาในรูปแบบทศนิยม (โหมดบาร์); จอแสดงผลแสดงวันที่
- ฟังก์ชัน 0010: LED แสดงเวลา—แต่ละคอลัมน์ในรูปแบบไบนารี; จอแสดงผลแสดงเวลา
- ฟังก์ชัน 0100: LED ปิด; จอแสดงผลแสดงเวลา (พร้อมโหมดกลางคืนที่ปิดจอระหว่าง 0-8 AM)
- ฟังก์ชัน 1001: LED ปิด; จอแสดงผลแสดงเมนูปรับตั้งเวลา, วันที่, และ RTC
- ฟังก์ชัน 1100/1101/1110/1111: LED แสดงเวลา; จอแสดงผลแสดงข้อมูลตำแหน่งดวงอาทิตย์ (ขึ้น, ตก, ความสูงสุด, ความสูงปัจจุบัน) สำหรับละติจูดที่ตั้งไว้
อธิบายโค้ด
ลูปหลักใช้คำสั่ง switch โดยอิงจากตำแหน่งของสวิตช์ (Test)
switch (Test) {
case 0:// 0000
if (ddd[0] == 1) {
Tijd(); // อัพเดทแถบ LED สำหรับเวลา
dddInstellen(0);
}
TijdDig1(); // อัพเดทจอ 7-เซกเมนต์สำหรับเวลา
break;
เมื่อตำแหน่งสวิตช์ = 0 ส่วนนี้ของรูทีนจะทำงาน คำสั่ง if ป้องกันไม่ให้ LED กะพริบ; รูทีน Tijd() จะทำงานแค่ครั้งเดียวต่อรอบการอัพเดท ในขณะที่รูทีนสำหรับจอ TijdDig1() จะทำงานบ่อยเท่าที่ต้องการ dddInstellen(0) จะรีเซ็ตตัวแปรแฟล็ก ddd[0] กลับเป็น 0 โดยแฟล็กนี้จะถูกเซ็ตกลับเป็น 1 หนึ่งครั้งต่อวินาทีโดยอินเตอร์รัปต์ เพื่อให้จอแสดงผลสามารถอัพเดทได้
อินเตอร์รัปต์ 1 วินาที:
if (secondsInterrupt == 1) { // ถูกเรียกโดยสัญญาณ 1 Hz SQW จาก RTC บนพินอินเตอร์รัปต์ 2
readtime(); // อ่านเวลาจาก RTC
secondsInterrupt = 0; // รีเซ็ตแฟล็ก ISR
dddInstellen(16); // รีเซ็ตตัวแปร ddd[x] กลับเป็น 1 เพื่อเปิดใช้งานการอัพเดท
}
การอัพเดทจอแสดงผลอย่างมีประสิทธิภาพ:
ฟังก์ชัน Tijd() แสดงให้เห็นถึงการจัดการข้อมูลอย่างมีประสิทธิภาพ มันใช้ฟังก์ชัน DDP() เพื่อส่งข้อมูลเวลาไปยังจอแสดงผลในบรรทัดเดียว
void Tijd() { // time
DDP(LED1[a], LED1[b], LED1[c], LED1[d], LED2[e], LED1[f]);
}
ตัวแปร a และ b แทนวินาที, c และ d แทนนาที, และ e กับ f แทนชั่วโมง โปรแกรมจะค้นหารูปแบบเซกเมนต์ที่ตรงกันจากอาร์เรย์ที่กำหนดไว้ล่วงหน้า (LED1[], LED2[]) และส่งรหัสทั้งหกไปยังชิฟต์เรจิสเตอร์พร้อมกันทีเดียวเลย
เกริ่นนำและบันทึกการสร้าง
เมื่อก่อนพี่เคยสร้างนาฬิกาแบบนี้ด้วยไอซีดิจิตอลแบบแยกส่วน (ตัวนับ BCD, ดีโค้ดเดอร์) และตัวต้านทาน (Resistor) 48 ตัว—ตัวละหนึ่งจุดต่อเซ็กเมนต์ของจอเลย ไม่มี RTC ด้วย ตัวเวอร์ชั่นปัจจุบันนี้มันง่ายขึ้นและเจ๋งขึ้นเยอะเลย พี่ใช้จอเดิม แต่ตอนนี้ขับมันด้วยชิฟต์เรจิสเตอร์ (Shift Register) 6 ตัว ส่วนไฟ LED นั้นขับด้วยชิฟต์เรจิสเตอร์อีก 3 ตัวโดยใช้วิธีมัลติเพล็กซ์ (Multiplexing) พี่ยังเขียนโค้ดระดับล่างเพื่อคุยกับ DS3231 RTC โดยตรงอีกด้วย เลยไม่ต้องพึ่งไลบรารีภายนอกแล้ว
ไอเดียสำหรับการต่อยอดในอนาคต
- OLED Dashboard: เอาจอ OLED ขนาดเล็กมาติดด้านหลังเพื่อแสดง "โหมดปัจจุบัน" หรือข้อมูลสถานะอื่นๆ
- Wireless Control: ใส่ปุ่มกดหรือโรตารีเอ็นโค้ดเดอร์ (Rotary Encoder) เพื่อให้สลับโหมดและเลื่อนเมนูได้ง่ายขึ้น
- Cloud Interface: เพิ่ม WiFi หรือ Bluetooth เพื่อซิงค์เวลาผ่าน NTP หรือบันทึกประวัติเวลาขึ้นไปที่เว็บดาชบอร์ด
- Ambient Light Sensor: ทำระบบปรับความสว่างอัตโนมัติสำหรับไฟ LED และจอ ตามความสว่างของห้อง
นาฬิกาไบนารี/ดิจิตอลเรือนนี้เป็นโปรเจกต์ที่เพอร์เฟกต์สำหรับสายอิเล็กทรอนิกส์ที่กำลังมองหาเครื่องมือลอจิกแบบอินเทอร์แอคทีฟและน่าสนใจ ที่ผสมผสานการออกแบบฮาร์ดแวร์เข้ากับการควบคุมซอฟต์แวร์ระดับสูง