EEPROM เป็นหน่วยความจำถาวร ที่สามารถอ่าน และเขียนซ้ำได้ประมาณ 1 แสนครั้ง ซึ่งในบอร์ด Arduino แต่ละบอร์ดก็จะมีขนาดพื้นที่ EEPROM แตกต่างกันไป อย่างในบอร์ด Arduino Uno R3 , Pro Mini , Nano จะมีพื้นที่ให้ใช้งานทั้งหมด 1KB (1,024 ตัวอักษร) ส่วน Arduino Mega 2560 R3 มีพื้นที่ 4KB (4,096 ตัวอักษร) พื้นที่ EEPROM ส่วนใหญ่แล้ว จะนำไปใช้ในการเก็บค่าคอนฟิกต่างๆที่ได้ทำการตั้งค่าไว้ เมื่อหยุดจ่ายไฟจะไม่ทำให้ข้อมูลหายไป
การที่จะทำให้ Arduino จำข้อมูลก่อนที่ไฟจะดับ สามารถทำได้อยู่ 2 วิธี
วิธีที่ 1 ทันทีที่ข้อมูลมีความเปลี่ยนแปลง ให้บันทึกข้อมูลใหม่ทับไปเลย - วิธีนี้มีข้อเสียตรงที่หากมีการบันทึกข้อมูลใหม่เข้าไปซ้ำๆเรื่อยๆ จะทำให้ EEPROM เต็มรอบการเขียน ส่งผลให้ EEPROM เสื่อม และไม่สามารถบันทึกข้อมูลได้อีก
วิธีที่ 2 ตรวจวัดแรงดัน เมื่อแรงดันลดลงให้ทำการบันทึกข้อมูลปัจจุบันทันที - วิธีนี้ต้องใช้อุปกรณ์อิเล็กทรอนิกส์ร่วมกับซอฟแวร์ ใช้อินเตอร์รัพท์วนตรวจสอบแรงดันไฟเลี้ยงปัจจุบัน เมื่อแรงดันไฟเลี้ยงลดลงกว่าที่กำหนด ค่อยทำการบันทึกข้อมูลลง EEPROM วิธีนี้จะช่วยยืดอายุการใช้งาน EEPROM ลงได้มากเมื่อนำมาใช้ในโรงงานอุตสาหกรรม สมมุติให้ใน 1 ปี มีไฟดับ 100 ครั้ง หมายความว่าอุปกรณ์จะสามารถใช้งานได้นาน 100,000 / 100 = 1,000 ปี (แต่ตามจริงแล้วอุปกรณ์อิเล็กทรอนิกส์อื่นๆจะเสื่อมก่อน)
ในบทความนี้จะเลือกใช้วิธีที่ 2 เนื่องจากมีข้อดีกว่าวิธีที่ 1 แม้จะยุ่งยากกว่าก็ตามครับ
สามารถทำได้โดยต่อเข้า A0 แล้วใช้คำสั่ง analogRead() อ่านค่าออกมาได้เลย . . . แต่เดี๋ยวก่อน เราต้องมาทำความเข้าใจการใช้งาน ADC แบบลึกกว่าปกติเล็กน้อยกันก่อนครับ
การวัดค่าอนาล็อก จะมีสิ่งที่เรียกว่า แรงดันอ้างอิง (Voltage Reference) อยู่ด้วย เพื่อเป็นตัวกำหนดว่า ให้ค่าเต็มสเกล (ใน Arduino คือ 1023) นั้นเทียบได้กับแรงดันเท่าไร ตัวอย่าง กำหนดให้แรงดันอ้างอิงเป็น 1V นั้นหมายความว่าหากใช้คำสั่ง analogRead() แล้วได้ค่าเต็มสเกล (1023) จะหมายถึงวัดแรงดันได้ 1V พอดี แต่หากวัดได้ต่ำกว่านั้น เช่น ครึ่งหนึ่งของสเกล (512) จะหมายถึงวัดแรงดันได้ 0.5V นั้นเองครับ
การกำหนดค่าแรงดันอ้างอิงสามารถทำได้ 2 วิธี ดังนี้
วิธีที่ 1 เลือกใช้แรงดันอ้างอิงจากภายนอก
วิธีนี้เราสามารถกำหนดแรงดันอ้างอิงได้อย่างอิสระ โดยการจ่ายไฟเข้าไปที่ขา AREF บนบอร์ด Arduino แรงดันที่ได้จากขา AREF จะอ้างเป็นแรงดันเต็มสเกล
การเลือกแรงดันที่นำมาใส่ในขา AREF ควรเป็นแรงดันที่สามารถรักษาระดับแรงดันไฟฟ้าได้คงที่ตลอด เช่น แรงดันที่ได้จากเรกกูเลเตอร์ ซึ่งมีค่าคงที่และเทียงตรงตลอดแม้แรงดันอินพุตของเรกกูเเตอร์จะเปลี่ยนไปก็ตาม
ไม่ควรใช้แรงดันที่ได้จากวงจรแบ่งแรงดัน (Voltage Divider) มาจ่ายให้ขา AREF เนื่องจากเมื่อแรงดันอินพุตเปลี่ยนไป ก็จะทำให้แรงดันเอาต์พุตเปลี่ยนตามไปด้วย
การจัดวงจรจ่ายแรงดันอ้างอิง
จัดแรงดันให้คงที่ด้วยซีเนอร์ไดโอด วงจรจะง่าย และราคาถูก เนื่องจากซีเนอร์ไดโอดมีแรงดันให้เลือกมากมาย ทำให้สามารถเลือกแรงดันอ้างอิงให้เหมาะสมกับแรงดันที่ต้องการจะวัดได้
* แก้ไข 22/5/2559 ขอบคุณ ครูประภาส ที่แจ้งเข้ามาครับ
จัดแรงดันให้คงที่ด้วยเรกกูเลเตอร์ วงจรจะง่ายเช่นเดียวกัน แต่มีราคาแพงกว่าเนื่องจากใช้ไอซีในการคุมแรงดันให้คงที่ และมีข้อเสียงตรงที่ต้องจ่ายแรงดันอินพุต ให้มากกว่าแรงดันเอาต์พุตประมาณ 2V - 3V ไอซีเรกกูเลเตอร์มีหลายเบอร์ด้วยกัน แนะนำให้เลือกเบอร์ที่ทนกระแสต่ำๆจะได้ราคาถูก เนื่องจากแรงดันที่นำไปจ่ายให้กับขา AREF นั้นใช้กระแสต่ำมากๆ ไม่ถึง 1mA ตัวอย่างวงจรด้านล่างนี้เลือกใช้เบอร์ 78L33 คุมแรงดันเอาต์พุตคงที่ 3.3V
วิธีที่ 2 เลือกใช้แรงดันอ้างอิงจากภายใน
ใน Arduino มีให้เลือกแรงดันอินพุตจากภายในได้โดยเลือกได้ว่าจะใช้แรงดันอ้างอิงจาก VCC หรือจะใช้ 1.1V ซึ่งไม่มีผลกับแรงดัน VCC ไม่ว่าแรงดัน VCC จะมีค่าเท่าไร แรงดันอ้างอิงจะยังคงเป็น 1.1V อยู่คงเดิม
แบบไหนเรียกสูงกว่าแรงดันอ้างอิงละ ? ก็เช่น กำหนดแรงดันอ้างอิงไว้ 5V แต่ว่าต้องการจะวันแรงดันที่สูงกว่า 5V เช่น 12V 24V เป็นต้น
การวัดแรงดันที่สูงกว่าแรงดันอ้างอิงสามารถทำได้โดยใช้ วงจรแบ่งแรงดัน (Voltage Divider) เป็นวงจรที่จะช่วยลดแรงดันสูง ลงมาเป็นแรงดันต่ำแบบเป็นเชิงเส้น วงจรแบ่งแรงดันจะใช้ตัวต้านทาน 2 ตัวต่อเป็นวงจรอนุกรม ดังรูปด้านล่างนี้
เมื่อเห็นตัวต้านทานต่อกันเป็นวงจรอนุกรมแบบนี้ ให้นึกถึงสูตร EIR ได้เลย และนิยามที่ว่า Es = Er1 + Er2 (กฏของเคอร์ชอฟฟ์ แรงดันในแต่ละโหนดรวมกันจะเท่ากับแหล่งจ่าย)
ตัวอย่าง กำหนดให้ R1 = 10KE, R2 = 10KE แรงดันอินพุต 5V แรงดันเอาต์พุตจะเป็นเท่าไร ?
จากสูตร EIR
เนื่องจากแรงดันเอาต์พุตจะมีค่าเท่ากับแรงดันที่ตกคร่อม R2 ดังนั้น
Er2 = R2 * I Es / (R1 + R2) มาแทนลงใน I
Er2 = R2 * (Es / (R1 + R2))
Er2 = (R2 * Es) / (R1 + R2)
แทนค่า R1 = 10KE, R2 = 10KE, Es = 5V
Er2 = (10KE * 5V) / (10KE + 10KE)
Er2 = (10 * 5) / 20
Er2 = 50 / 20
Er2 = 5 / 2
Er2 = 2.5V
เมื่อ Er2 = OUTPUT ดังนั้นแรงดันเอาต์พุตที่ได้จึงเป็น 2.5V
สรุป สูตรของวงจรแบ่งแรงดัน คือ OUTPUT = (R2 * INPUT) / (R1 + R2)
ทีนี้เมื่อเรานำไปใช้งานในการวัดแรงดัน เราไม่ต้องการแรงดันเอาต์พุต แต่ว่าเราต้องการแรงดันอินพุตสูงสุดที่สามารถวัดได้ ที่เมื่อนำไปเข้าวงจรแบ่งแรงดันแล้ว เอาต์พุตจะได้เท่ากับแรงดันอ้างอิงพอดี
หากเราพิจารณาดูดีๆแล้ว สัดส่วนของแรงดันเอาต์พุตต่อแรงดันเอาต์พุต (OUTPUT / INPUT) จะมีค่าคล้ายๆกับสัดส่วนของ R1 และ R2 ในวงจรแบ่งแรงดัน
OUTPUT / INPUT = R2 / R1 + R2
ดังนั้นจึงสามารถสมมุติแรงดันอินพุตที่ต้องการ และกำหนดแรงดันเอาพ์พุตที่ต้องการได้เลย
ตัวอย่าง ต้องการให้แรงดันอินพุต 30V และแรงดันเอาต์พุต 5V จะต้องใช้ R1 และ R2 เท่าไร
5V / 30V = R2 / R1 + R2
5V / 30V = 5 / R1 + 5
30V - 5V = 15V
5V / 30V = 5 / 25 + 5
5V / 30V = 5 / 30
เนื่องจากจะใช้ค่าเป็นโอห์มไม่ได้ จึงเติม K เข้าไป จึงได้ค่า R1 = 25KE , R2 = 5KE แต่ตัวต้านทานค่า 25KE หาได้ยาก จึงใช้ค่า 27KE แทนใน R1
คำนวนแรงดันอินพุตสูงสุดใหม่ได้จากสูตรวงจรแบ่งแรงดัน (หาแรงดันอินพุตจากแรงดันเอาต์พุตที่ต้องการ 5V)
OUTPUT = (R2 * INPUT) / (R1 + R2)
OUTPUT / R2 / (R1 + R2) = INPUT
OUTPUT * (R1 + R2) / R2 = INPUT
5V * (27KE + 5KE) / 5KE = INPUT
5 * 32 / 5 = INPUT
32V = INPUT
ดังนั้นในวงจรแบ่งแรงดันนี้ ที่แรงดันเอาต์เอาต์พุต 5V จะมีแรงดันอินพุต 32V ซึ่งเป็นแรงดันอินพุตสูงสุดที่นำไปใช้กับการวัดแรงดันไฟฟ้าได้
นำไปใช้งานจริงร่วมกับ Arduino
เมื่อคำนวน R1 R2 ได้แล้ว นำค่าที่หาได้นำไปแทนลงวงจร ทำให้วงจรเป็นไปดังรูปด้านล่างนี้ ตรงส่วน INPUT เป็นส่วนที่นำไปวัดแรงดัน สามารถวัดได้สูงสุด 32V และทางด้านเอาต์พุต ต่อเข้าขา A0 - A5 ของบอร์ด Arduino (ขาใดขาหนึ่ง)
เมื่อได้ทราบข้อมูลใน 2 หัวข้อด้านบนแล้ว ก็มาถึงช่วงที่วัดแรงดัน VCC กันแล้วครับ โดยการประยุกต์ใช้การกำหนดแรงดันอ้างอิง และการวัดแรงดันที่สูงกว่าแรงดันอ้างอิงครับ
เริ่มที่วงจรก่อนเลย จะมีเพียงวงจรแบ่งแรงดันมาต่อเข้า INPUT ที่ 5V และเอาต์พุตก็ต่อเข้าขา A0 ดังรูปด้านล่าง โดย R1 ผมเลือกใช้ 5KE และ R2 เลือกใช้ค่า 1KE เมื่อมีแรงดันอินพุต 5V แรงดันเอาต์พุตจะได้ 0.83V (ผมใช้แรงดันอ้างอิงจากภายในที่ 1.1V)
ต่อมาก็เขียนโค้ดกันครับ โดยเริ่มจากจากฟังก์ชั่น analogReference(type) ที่ใช้กำหนดแหล่งแรงดันอ้างอิง ซึ่งสามารถกำหนดได้ดังนี้
ซึ่งผมเลือกใช้ analogReference(INTERNAL); แรงดันอ้างอิงจากภายใน 1.1V
การอ่านค่าแรงดันสามารถใช้คำสั่ง analogRead() แบบปกติได้เลย แต่ต้องมาทำความเข้าใจเรื่องค่าที่อ่านได้กันก่อน
ค่าที่ได้จากคำสั่ง analogRead() เป็นค่าตั้งแต่ 0 - 1023 (10 บิต) ซึ่งจะเป็นค่าที่ต้องอ้างอิงกับแรงดันอ้างอิง ตัวอย่างเช่น ใช้คำสั่ง analogRead() แล้วได้ค่า 1023 นั่นหมายถึงวัดแรงดันได้เท่ากับแรงดันอ้างอิง (ผมได้กำหนดแรงดันอ้างอิงไว้ที่ 1.1V ดังนั้นค่า 1023 จึงเท่ากับ 1.1V) เราสามารถใช้การเทียบบัญญัติไตรยางค์ได้เลย ซึ่งจะได้ออกมาเป็นสูตร
ค่าทีอ่านได้ * แรงดันอ้างอิง / 1023 หรือ ค่าที่อ่านได้ / 1023 * แรงดันอ้างอิง
เนื่องจากเราเลือกใช้แรงดันอ้างอิงที่ 1.1V ดังนั้นเมื่อนำไปเขียนโค้ดอ่านแรงดันจะได้
float volt = analogRead() * 1.1 / 1023;
แต่เดี๋ยวก่อน เนื่องจากเราใช้แรงดันอ้างอิงที่ 1.1V ดังนั้นเราจึงไม่สามารถนำไปวัดแรงดัน 5V ได้โดยตรง ต้องผ่านวงจรแบ่งแรงดันก่อน ซึ่งเราก็ได้ต่อไว้แล้วในวงจรจากรูปที่แล้ว ที่ใช้ R1 = 5KE , R2 = 1KE
จะเห็นว่าตัวแปร volt ที่ได้มานั้น คือวัดแรงดันเอาต์พุตได้แล้ว ดังนั้นจึงสามารถคำนวนหาแรงดันอินพุตได้จากสูตรวงจรแบ่งแรงดัน (ที่ได้ผ่านมาแล้ว)
OUTPUT * (R1 + R2) / R2 = INPUT
volt * (5KE + 1KE) / 1KE = INPUT
volt * 6 = INPUT
นำไปเขียนโค้ดต่อจากเดิม จะได้
float volt = analogRead() * 1.1 / 1023;
volt *= 6;
ดังนั้นโค้ดเต็มๆจึงเป็นดังนี้ ลองก๊อบไปใส่ Arduino IDE อัพโหลด แล้วดูผลได้เลย
ผลที่ได้จะเป็นดังนี้
แต่จากการคำนวนด้านบน เราสามารถลดงานลงได้ (ลดสมการ) โดยเมื่อพิจารณาดีๆแล้ว สูตร ค่าทีอ่านได้ * แรงดันอ้างอิง / 1023 นั้นสามารถนำมาใช้ได้กรณีที่มีวงจรแบ่งแรงดันเช่นเดียวกัน โดยให้พิจารณาเป็น ค่า 1.1V = แรงดันสูงสุดที่สามารถวัดได้ ดังนั้นแรงดันสูงสุดที่สามารถวัดได้ก็คือค่า INPUT สูงสุดของวงจรแบ่งแรงดันนั่นเอง
จากสูตรที่ผ่านมา
OUTPUT * (R1 + R2) / R2 = INPUT
1.1V * (5KE + 1KE) / 1KE = INPUT
1.1V * 6 = INPUT
6.6V = INPUT
จากโค้ดเดิม ก็แก้ใหม่เป็นดังนี
ลองอัพโหลดเข้าไปใหม่ ผลที่ได้เป็นไปตามรูปนี้
โดยใช้ไลบารี่สำเร็จรูป TimeOne ซึ่งใช้สำหรับให้เรียกใช้งานฟังก์ชั่นใดๆก็ได้ในเวลาที่กำหนดอย่างต่างเนื่อง ซึ่งในที่นี้จะใช้สำหรับการวัดแรงดัน VCC ทุกๆ 50mS
ก่อนอื่นให้ไปโหลดไลบารี่ TimeOne ที่ลิ้ง https://code.google.com/archive/p/arduino-timerone/downloads ติดตั้งให้เรียบร้อย
ฟังก์ชั่นที่เราจะใช้งานกันในบทความนี้ มีดังนี้
อัพโหลดโค้ดตัวอย่างด้านล่างนี้
ผลที่ได้จะเป็นดังนี้ ฟังก์ชั่น echoISR จะถูกเรียกขึ้นมาทุกๆ 500mS
มีอยู่ด้วยกัน 2 ฟังก์ชั่น คือ อ่าน และเขียน
(byte) EEPROM.read(int address);
พารามิเตอร์
ค่าส่งกลับ
ส่งกลับข้อมูลที่อ่านได้ 8 บิต (1 ไบต์)
void EEPROM.write(int address, byte value)
พารามิเตอร์
ค่าส่งกลับ
ไม่มี
การใช้ EEPROM บันทึกข้อมูลที่เป็น Int (ขนาด 16 บิต หรือ 2 ไบต์)
ทำได้โดยการเก็บข้อมูลบิต 16 - 9 แยกกันกับบิต 8 - 1 ตัวอย่างเช่น
int num = 1000;
EEPROM.write(0, num>>8); // เขียนข้อมูลบิตที่ 16 - 9 ลงใน EEPROM จำแหน่งที่ 0
EEPROM.write(1, num&0xFF); // เขียนข้อมูลบิตที่ 8 - 1 ลงใน EEPROM ตำแหน่งที่ 1
การอ่านสามารถทำได้โดย
int num = 0;
num |= EEPROM.read(0)<<8;
num |= EEPROM.read(1)&0xFF;
ทดลองอัพโหลดโค้ดด้านล่างนี้ลงไป
เมื่อเปิดขึ้นมา จะเห็นได้ว่ามีการเขียนเลขที่ถูกสุ่มลงไปให้เรียบร้อยแล้ว
สุดท้ายแล้วสิ่งที่กล่าวมาทั้งหมด จะถูกนำมาใช้งานเพื่อสิ่งนี้สิ่งเดียว ยังไงการเขียนโปรแกรมก็เหมือนกับการต่อจิกซอ เรียนรู้คำสั่ง แล้วนำมารวมกันเพื่อให้ทำงานได้ถูกต้องตามที่ต้องการ
เริ่มต้นด้วยการต่อวงจร
เมื่อหยุดจ่ายไฟให้กับบอร์ด Arduino แล้ว วินาทีถัดไปคือ Arduino บันทึกข้อมูลลง EEPROM แล้วทำอย่างไรถึงจะยื้อชีวิตให้มีนาทีต่อไปได้ละ ? ต้องใช้ตัวเก็บประจุเข้ามาช่วย ตัวเก็บประจุจะสามารถจ่ายไฟได้ระยะเวลาหนึ่ง ขึ้นอยู่กับค่าของตัวเก็บประจุเอง ซึ่งผมเลือกใช้ค่า 1,000uF 16V
อัพโหลดโค้ดต่อไปนี้ลงไป ซึ่งในแต่ละบรรทัดของโค้ดจะมีคอมเม้นอธิบายโค้ดอยู่แล้ว รายละเอียดของทุกฟังก์ชั่นสามารถย้อนกลับไปอ่านด้านบนได้ (ใครที่อ่านด้านบนแล้ว จะเข้าใจโค้ดได้ทันทีแม้เห็นโค้ดครั้งแรก)
เมื่ออัพโหลดโค้ดแล้ว จะได้ผลลัพท์ดังนี้
มีการนับตัวเลขเพิ่มขึ้นเรื่อยๆ จากนั้นถอดสาย USB ออก ค่าที่นับล่าสุดคือ 16830
เสียบสาย USB กลับเข้าไปใหม่ จะเห็นว่ามีการนับค่าต่อจากเดิม แสดงว่าค่าถูกบันทึกไว้ในช่วงวินาทีสุดท้ายหลังตัดไฟเลี้ยงวงจรแล้ว (ถอดสาย USB)
จากรูปด้านบนจะเห็นว่าโค้ดสามารถทำงานได้ตามปกติ แต่ว่ามีบัคตรงที่หากเราต้องการจะรีเซ็คค่าให้กลับเป็น 0 จะทำไม่ได้ เพราะไม่ได้พิมพ์โค้ดเผื่อไว้ ยังไงก็ขอให้โค้ดในบทความนี้เป็นแนวทางไปประยุกต์ใช้งานจริงละกันครับ
กรณีที่ Arduino มีอุปกรณ์อื่นๆต่อร่วมอยู่ เมื่อตัดไฟไปแล้ว อุปกรณ์เหล่านั้นจะมาช่วยแย่งใช้ไฟจากตัวเก็บประจุ ดังนั้นจึงอาจเกิดปัญหาไฟหมดก่อนที่จะบันทึกค่าลง EEPROM สำเร็จ ควรที่จะใช้ไดโอดมาต่อกั้นไว้ เพื่อให้ไฟสามารถไหลเข้ามาที่ไอซีไมโครคอนโทรลเลอร์และตัวเก็บประจุได้ แต่ไม่สามารถไหลออกไปจ่ายให้อุปกรณ์อื่นๆได้ ส่วนอุปกรณ์อื่นๆที่ต่อกับบอร์ดโดยตรง ก็เลี่ยงไปใช้อุปกรณ์อื่นมาช่วยไดร์ หรือแยกแหล่งจ่ายไปเลยกว่าครับ
จริงๆแล้ววิธีที่จะเก็บค่าไว้ขณะไฟดับยังมีวืธีอื่นๆนอกจากใช้ EEPROM ด้วย นั้นคือการต่อ แรม ภายนอก แล้วใช้ถ่าน ในการจ่ายไฟเลี้ยงแบบเดียวกับ RTC ที่ใช้การจดจำค่าโดยการบันทึกไว้บนแรม แล้วใช้ถ่านเป็นแหล่งจ่ายพลังงานให้นับเวลาได้ถูกต้อง ทำให้สามารถจำค่าเวลาได้
*** จบ ***