Mini Clock โดยใช้ LED Dot Matrix
จากบทความก่อนหน้านี้ที่แนะนำการใช้ LED Dot Matrix ไปบ้างแล้ว โดยเน้นไปที่ LED Dot Matrix ขนาด 8x8 ในบทความนี้ เราจะหาทางประยุกต์ใช้ในเรื่องอื่น โดยคราวนี้จะใช้ LED Dot Matrix ขนาด 8x32 ตามตัวอย่างในรูป
โดยในบทความนี้เราจะมาลองทำนาฬิกากัน เพราะน่าจะเป็นชิ้นงานที่นำไปใช้ได้ โดยบทความนี้จะยังคงใช้บอร์ด Arduino ในการควบคุม และเน้นที่การแสดงผลอย่างเดียว ซึ่งสามารถนำไปประยุกต์กับ ESP8266 เพื่อทำ Internet Clock หรือจะเพิ่ม RTC ก็ได้
หมายเหตุ ข้อมูลในบทความบางส่วนได้มาจาก https://123led.wordpress.com/mini-led-clock/
บอร์ดตามรูปยังคงใช้ MAX7219 ควบคุม โดยใน 1 โมดูลจะมีชิป 1 ตัว ดังนั้นในโมดูล 4 โมดูลตามรูปจึงมีชิปทั้งหมด 4 ตัว แต่เนื่องจาก MAX7219 ออกแบบให้สามารถเชื่อมต่อกันไปได้เรื่อยๆ โดยมีขา DOUT สำหรับส่งข้อมูลต่อไปยังโมดูลต่อไป ดังนั้นจึงสามารถใช้สายสัญญาณเพียงชุดเดียว 5 เส้น (เท่าเดิม) ในการเชื่อมต่อกับโมดูล LED ซึ่งถือว่าสะดวกสำหรับการใช้งานมากทีเดียว
หลายคนอาจสงสัยว่าในการส่งข้อมูลให้กับชิปแต่ละตัวมันทำงานยังไง ผมก็สงสัยเหมือนกัน ก็เลยไปค้นข้อมูล ก็พบว่าในการโปรแกรมลงในชิป ต้องโปรแกรมทีเดียว 4 ชิปพร้อมกันเลย จาก Timing Diagram ในรูปข้างล่างนี้ ในการโปรแกรมจะเริ่มจากการดึงขา CS/LOAD ให้เป็น Low เพื่อบอกกับโมดูลว่าจะโหลดข้อมูลแล้ว แต่เนื่องจากขา CS/LOAD ของแต่ละโมดูลจะต่อถึงกันหมด ดังนั้นเมื่อดึง CS/LOAD เป็น Low ทุกโมดูลก็จะพร้อมรับคำสั่งไปพร้อมกัน
จากนั้นจึงส่งข้อมูล CLK และ Din ของแต่ละคำสั่ง โดยจะส่งเข้าไปทีละบิต ในแบบอนุกรม ข้อมูลจะค่อยๆ เลื่อนเข้าจากโมดูล 1 ไปโมดูล 2 ไปโมดูล 3 จนมาถึงโมดูล 4 เนื่องจากแต่ละคำสั่งจะใช้ 16 clock ดังนั้นในการเลื่อนข้อมูลคำสั่งสำหรับ 4 โมดูล ก็จะใช้ทั้งหมด 64 clock โดยเมื่อข้อมูลเข้ามา 16 clock แรก ข้อมูลจะอยู่ในโมดูลที่ 1 แต่เมื่อ clock ที่ 17 ส่งมา ข้อมูลบิตแรก (D15) ที่อยู่ในโมดูล 1 ก็จะส่งต่อไปยังโมดูลที่ 2 โดยไม่เก็บข้อมูลเอาไว้ การทำงานก็จะเป็นแบบนี้ไปเรื่อย จนครบ 64 บิต และทุกโมดูลได้รับข้อมูล
สาเหตุที่การทำงานเป็นไปตามที่กล่าวมา เนื่องจากในขณะที่ขา CS/LOAD ถูกดึงให้เป็น Low ชิป MAX7219 จะยังไม่รับข้อมูลเข้าไปโปรแกรมลงในรีจิสเตอร์ จนกว่าขา CS/LOAD จะกลับมาเป็น High จึงเสมือนกับว่า MAX7219 อยู่ในโหมดโหลดข้อมูล จึงทำให้ข้อมูลสามารถจะเลื่อนผ่าน MAX7219 ไปยังโมดูลเป้าหมายได้ และเมื่อขา CS/LOAD กลับเป็น High เมื่อใด จึงค่อยนำข้อมูลไปใช้ ทำให้เราจะต่อ MAX7219 อนุกรมกันไปเรื่อยๆ กี่ตัวก็ได้
เมื่อเข้าใจการทำงานเรียบร้อย เราก็จะมาเข้าสู่การเขียนโปรแกรมกัน โดยเราจะยังใช้ไลบรารี LedControl เหมือนเดิม
เนื่องจากฟังก์ชัน setLed ของ LedControl มีพารามิเตอร์ 4 ตัว คือ Module,Row, Column และ T/F ซึ่งการจะระบุโมดูลในการเขียนโปรแกรม ทำให้ไม่สะดวก ในการนี้เราจึงสร้างฟังก์ชันใหม่สำหรับใช้กับ 4 โมดูล โดยจะมองเป็น LED matrix ขนาด 8x32 แทน
ฟังก์ชันแรกที่สร้าง คือ plot โดยจะทำหน้าที่แปลงตำแหน่ง x,y ที่เป็น 32x8 ลงไปยังโมดูล เพื่อให้ง่ายต่อการเขียนโปรแกรมมากขึ้น
ฟังก์ชัน clear_display ใช้สำหรับเคลียร์หน้าจอ ทั้ง 4 หน้าจอ
ฟังก์ชัน fede_down ทำหน้าที่ค่อยๆ หรี่จอลงจนดับ ทั้ง 4 หน้าจอเช่นกัน
เมื่อได้ฟังก์ชันพื้นฐานแล้ว คราวนี้ต่อไปก็เป็นการแสดงตัวอักษร โดยจะนำไฟล์ Font ของตัวอักษรมาจาก https://github.com/mrnick1234567/miniclock/blob/master/libraries/FontLEDClock/FontLEDClock.h โดยมีฟอนต์ 2 แบบ คือ อักษรตัวใหญ่ และ อักษรตัวเล็ก (หมายถึงขนาดตัวอักษร)
สำหรับอักษรตัวเล็กจะมีขนาด 3x5 จุด ซึ่งจะเล็กมากพอที่จะแสดง ตัวเลข ชั่วโมง นาที และ วินาที ใน LED Dot Matrix ขนาด 32x8 ได้ สำหรับอักษรตัวใหญ่จะมีขนาด 5x7 ซึ่งจะใช้แสดงตัวเลข ชั่วโมงและนาทีเท่านั้น
เมื่อได้ฟอนต์ ก็มาสร้างฟังก์ชันแสดงตัวอักษร โดยเป้าหมายคือ เมื่อเรียกฟังก์ชันจะสามารถแสดงตัวอักษรใดๆ ในตำแหน่งที่ต้องการได้
จากโปรแกรม ได้แยกเป็น 2 ฟังก์ชัน โดย print_tiny_char ใช้สำหรับแสดงผลอักษรตัวเล็ก และ print_normal_char ใช้สำหรับแสดงอักษรตัวใหญ่ โดยทั้งสองฟังก์ชันจะสามารถแสดงเลข 0–9 ตัวอักษร A-Z และอักขระ .\:> โดยฟังก์ชันแรกจะแสดงได้เฉพาะอักษรตัวใหญ่ ฟังก์ชันที่สองแสดงได้ทั้งตัวเล็กและตัวใหญ่
เมื่อครบทุกฟังก์ชัน ก็มาลองทดสอบโปรแกรมกัน โดยโปรแกรมตามข้างล่างนี้
โปรแกรมนี้จะแสดงตัวอักษร Hello และ World สลับกันไป โดยจะออกมาตามรูปนี้ครับ ตัวอักษรอาจจะไม่ค่อยสวยเท่าไร แต่ถ้าหาตัวอักษรสวยๆ มาได้ ก็จะสวยกว่านี้ แต่เนื่องจากเป้าหมายของโปรแกรมนี้คือ นาฬิกา ซึ่งแสดงตัวเลขเป็นหลัก ดังนั้นจะใช้ตัวอักษรแบบนี้ไปก่อน
เมื่อได้ฟังก์ชันพื้นฐานครบแล้ว ต่อไปก็เป็นเรื่องของเวลา ซึ่งหากใช้ RTC ก็ง่ายหน่อย สามารถอ่านมาแสดงได้เลย หรือหากใช้ ESP8266/32 ก็สามารถอ่าน Internet Time ได้ แต่เนื่องจากในงานนี้ผมจะใช้ Arduino อย่างเดียว (ไม่มี RTC) จึงต้องหาตัวช่วย ซึ่งตัวช่วยในที่นี้คือ Timer Interrupt นั่นเอง
ใน MPU ATmega328 ก็เช่นเดียวกับ MPU อื่นๆ ที่มักจะมี Timer/Counter มาให้อยู่ในตัว ซึ่งการสร้างพัลล์ PWM หรือตัวนับในคำสั่ง millis() หรือ delay() ก็อาศัยฐานเวลาพวกนี้ในการสร้างขึ้นมา สำหรับ ATmega328 ที่อยู่ใน Arduino ก็มี Timer อยู่ 3 ตัว โดยมีชื่อว่า Timer 0/1/2 โดย Timer 0 กับ 2 จะมีขนาด 8 บิต สำหรับ Timer1 จะพิเศษหน่อย เพราะมีขนาด 16 บิต
การทำงานของ Timer มีหลายแบบแต่ที่เราจะใช้ คือ Timer Interrupt โดยเราจะเลือกใช้ Timer1 โดยมีหลักการคือ จาก clock ของ MPU ที่วิ่งที่ 16 MHz เราสามารถกำหนดตัวหาร (เรียกว่า prescalar) โดยเลือกได้ 5 ค่า คือ 1, 8, 64, 256, 1024 เช่น ถ้ากำหนดให้หารด้วย 256 : 16000000/256 = 62500 นั่นหมายความว่าใน 1 วินาที Timer จะได้รับสัญญาณ clock จำนวน 62500 ครั้ง โดยเมื่อ Timer ได้รับสัญญาณ Clock ก็จะเพิ่มค่า Counter ขึ้น 1 และเมื่อเพิ่มจนถึง 255 หรือ 65535 (ขึ้นกับว่าเป็น 8 บิตหรือ 16 บิต) ก็จะเกิด Overflow และเกิด Interrupt ซึ่งเราสามารถเอา Interrupt ตัวนี้ไปใช้ได้
สำหรับโปรแกรมข้างล่างนี้จะเป็นโปรแกรมแสดงการทำงานของ Timer Interrupt อย่างง่าย โดยการทำงานโดยย่อ คือ กำหนดให้ Timer1 รับ clock ที่หาร 256 จาก CPU ได้เท่ากับ 62500 ครั้งต่อ 1 วินาที ซึ่ง ยังไม่ครบ 65535 ดังนั้นหากต้องการให้ Timer1 เกิด overflow (คือค่าเกิน 65535) ทุก 1 วินาที ก็ต้องให้เริ่มต้นนับที่ 3036 (65536–62500) เมื่อได้รับ clock ครบ 62500 ครั้ง ก็จะเป็นเวลา 1 วินาทีพอดี จากนั้นเราก็กำหนดให้ CPU อินเทอรัปต์เมื่อเกิด overflow
จากนั้นเราก็ไปกำหนดที่ ISR (Interrupt Service Routine) ของ Timer1 ซึ่งจะถูกเรียกมาทำงานทุก 1 วินาที ให้โหลดค่าเริ่มต้นใหม่ แล้วสั่งให้ไฟกระพริบ และส่งค่าออกทาง Serial ซึ่งถ้าเราไปเปิด Serial เราก็จะพบว่าค่าเพิ่มขึ้นทุก 1 วินาที ก็เป็นอันว่า เราได้ตัวนับวินาทีเรียบร้อย
ต่อไปเราก็จะมาเขียนโปรแกรมนาฬิกาง่ายๆ โดยเพื่อให้สั้น ผมจะไม่มีส่วนของการตั้งเวลา จะมีเพียงส่วนของ ISR และ การแสดงผลเวลาเท่านั้น โดยโปรแกรมจะมีดังนี้ครับ
ใครจะไปเพิ่มส่วนตั้งเวลา หรือ แสดงเดือนปี หรือจะให้มีส่วนของการตั้งเวลา เพื่อให้ครบฟังก์ชันของนาฬิกา ก็ตามสบายครับ