การปรับปรุง โมดุล นาฬิกา RTC, Real Time Clock สำหรับ Micro Controller, Arduino, ESP8266 ให้เร็วแม่นยำ แก้ปัญหา lack

โดยปกติทั่วไปเวลาเราใช้ microcontroller ไม่ว่าจะเป็น Arduino หรือ ESP8266 หรืออะไรก็ตาม ในการทำ project ต่างๆที่เกี่ยวข้องกับเวลา ไม่ว่าจะเป็น Smart farm, ที่รดน้ำต้นไม้ เราจำเป็นต้องมีเวลามาเกี่ยวข้อง แม้ว่าในตัว MCU จะมีนาฬิกาแต่มันไม่สะดวก พอตัดไฟ ไฟดับ ไฟตก เวลาก็หายไป และต้องมาหาวันที่ วัน เดือนปี คศ แถมยังมีปีอธิกสุรทินอีก
ดังนั้นเรามักจะใช้ตัว module นาฬิกาเข้ามาเสริมจัดการตรงนี้ให้ง่ายๆแทน

ดูหมือนก็ไม่น่าจะมีปัญหาใช่ไหม ใช่ ถ้าเราทำโปรเจคที่เวลาเพี้ยนได้หลายร้อย millisecond แต่บางคนเริ่มเจอปัญหา

นั่นแสดงความยินดีด้วยเพราะแสดงว่าคุณเริ่มทำโปรเจคยากๆได้แล้ว ไม่ใช้มือใหม่อีกต่อไป เพราะคุณเห็นความเพี้ยนของเวลาเล็กๆได้

ปัญหามาจากไหน

อุปกรณ์พวกนี้ มันมีการสื่อสารข้อมูลระหว่าง Module RTC กับ MCU มันย่อมกินเวลา เวลาที่ระบบทำงานต้องหยุดรอ  โดยจะเห็นได้ชัดมากเวลาที่เราต้องแสดงผลอะไรแบบต่อเนื่องเช่น LED strip จะเห็นมันแสดงผลอืดมาก หากเราแก้ปัญหาโดยการให้มันอ่านค่าเวลาไม่บ่อยนานๆที ผลก็คือ เวลาควบคุมก็จะไม่ตรง realtime

กระทู้นี้เป็นการแชร์วิธีการแก้ปัญหาของผมเท่านั้น ครับ หากใครมี วิธีอื่นก็ต่อได้ตามสบายครับ  ไม่คาดหวังว่าใครจะมาอ่านแล้วรู้เรื่องเพราะมันเฉพาะทาง แต่เผื่อไว้ว่าอนาคตใครทำอะไรแล้วมีปัญหานี้ก็เอาไปใช้ได้

เรามาดู การใช้ RTC สมมุติว่าผมใช้ RTC DS3231 (หรือ module ก็ลองประยุกต์เอาเอง) บน Arduino Nano โปรแกรม Code การอ่านค่า RTC ทั่วไป จะเป็นแบบนี้

//***********************************************************************************

#include "Wire.h"

#include "DS3231.h"

DS3231 xclock;

RTCDateTime dt;

int s, m, h; //time sec, min, Hour

int D, M, Y; //Date, Month

long LoopTimeIn,LoopTimeOut;

void setup() {

Serial.begin(115200);

xclock.begin();

//xclock.setDateTime(2019, 7, 28, 13, 37, 0);// ใช้ตั้ง RTC เฉพาะ ครั้งแรก (ปี,เดือน,วัน, ชั่วโมง,นาที,วินาที)

RTCread();

}

void loop() {

LoopTimeIn=micros();

RTCread();

LoopTimeOut=micros();

Serial.print("Read Clock Time=");

Serial.print(LoopTimeOut-LoopTimeIn) ;

Serial.print(" microsec ,") ;

Serial.print("Time ");

Serial.print(h);

Serial.print(":");

Serial.print(m);

Serial.print(":");

Serial.print(s);

Serial.print(" ");

Serial.print(D);

Serial.print("/");

Serial.print(M);

Serial.print("/");

Serial.print(Y);

Serial.println("");

}

void RTCread()

{

dt = xclock.getDateTime();

s = dt.second;

m = dt.minute;

h = dt.hour;

xclock.forceConversion();

D = dt.day;

M = dt.month;

Y = dt.year;

}

//***********************************************************************************

เวลาที่ใช้ในการอ่านค่า เวลาคือประมาณ 135,050 microsec หรือ 135 microsec นานมาก

ซึ่งเยอะมากพอดูและสังเกตเห็นได้ในบาง Project ว่าเลขวินาทีที่แสดงผลมันเขย่ง เร็ว ช้าแบบสังเกตดีๆรู้สึกได้
หรือมองอีกมุมหนึ่งคือ Project นี้ไม่สามารถ Refresh เวลา ในช่วงเวลาได้ต่ำกว่า 135 ms เลย 

วิธีการแก้ปัญหาที่จะนำเสนอต่อไปนี้คือการรวม เอาเวลาจาก RTC และ clock ใน MCU มาใช้ด้วยกันแบบ Hybrid โดย เวลา RTC ซึ่งมีข้อดีคือเวลาไม่หายเวลาไฟดับ ไม่ต้องตั้งใหม่ เวลาเปิดใหม่ และ Clock ใน MCU ที่อ่านเร็วมากไม่เสียเวลา

หลักการคือเราจะอ่านเวลาจาก RTC ครั้งแรกและอ่านอีกเป็นช่วงๆ (แต่ไม่เกิน 30 วันในแต่ละช่วง แต่ไม่ควรนานขนาดนั้นแค่ 1-2 นาทีก็พอแล้ว) เป็นค่าเริ่มต้นและนำไปบวกกับเวลาที่เคลื่อนไปจากตำแหน่งที่อ่าน ซึ่งมาจาก clock ของ MCU เอาเวลาที่ได้มาคำนวณทดเวลา นาที ชั่วโมงไป ก็จะได้เวลาจริง ณ จุดใดๆ ดังโปรแกรมนี้

//***********************************************************************************

#include “Wire.h”

#include “DS3231.h”

DS3231 xclock;

RTCDateTime dt;

int s, m, h; //time sec, min, Hour

int D, M, Y; //Date, Month

unsigned long HybridTimeStart;

int HybridRefresh = 120; //in sec to refresh hybrid time to real RTC time

int hF, mF, sF;//Fix h,m,s

long LoopTimeIn,LoopTimeOut;

void setup() {

Serial.begin(115200);

xclock.begin();

//xclock.setDateTime(2019, 7, 28, 13, 37, 0);

HybridTimeStart = millis();

RTCread();

}

void loop() {

LoopTimeIn=micros();

GetTime();

//RTCread();

LoopTimeOut=micros();

Serial.print("Read Clock Time=");

Serial.print(LoopTimeOut-LoopTimeIn) ;

Serial.print(" microsec ,") ;

Serial.print("Time ");

Serial.print(h);

Serial.print(":");

Serial.print(m);

Serial.print(":");

Serial.print(s);

Serial.print(" ");

Serial.print(D);

Serial.print("/");

Serial.print(M);

Serial.print("/");

Serial.print(Y);

Serial.println("");

}

void GetTime() {//RTC Time

static unsigned long TotalSec;

if ((HybridTimeStart + (HybridRefresh * 1000)) < millis())

{ RTCread();

HybridTimeStart = millis();

Serial.print("Hybrid Update");

}

s = sF;

m = mF;

h = hF;

long xtrasec;

xtrasec = byte((millis() - HybridTimeStart) / 1000);

TotalSec = h * 3600L + m * 60 + s + xtrasec;

s = TotalSec % 60;

h = TotalSec / 3600;

m = ((TotalSec - ((TotalSec / 3600) * 3600)) / 60);

if (h >= 24)h = h - 24;

}

void RTCread()

{

dt = xclock.getDateTime();

s = dt.second;

m = dt.minute;

h = dt.hour;

xclock.forceConversion();

D = dt.day;

M = dt.month;

Y = dt.year;

sF = s; //fix sec for hybrid use

mF = m;

hF = h;

}

//***********************************************************************************

โดยเราเพิ่ม HybridRefresh เป็นช่วงเวลาที่เราอ่าน RTC ในตัวอย่างคือทุก 120 วินาที ผลคือ

เวลาที่อ่านนาฬิกาเหลือ 176 microsec ไม่ถึง 0.2 ของ millisec ซึ่งเร็วกว่าเก่ามากๆ หลักการง่ายๆแต่ได้ผล
ทำให้เราสามารถ update เวลาเราได้บ่อยมากๆ วินาทีละพันครั้งยังได้ และเวลาที่แสดงผลก็จะตรงกับเวลาจริงมาก

ขอจบแค่นี้แหละครับหวังว่าคงได้ประโยชน์บ้าง อนึ่งหากต้องการมันแม่นเข้าไปอีกก็ลบ เวลาที่อ่านได้ด้วยเวลาที่ใช้ไปจากการอ่าน แต่ผมมองว่ามันไม่จำเป็นหรอก เพราะช่วงตั้งเวลาก็เพี้ยนกว่านั้นแล้ว

หมายเหตุ: หากใช้ ESP8266 และมี Internet เราไม่ต้องใช้ RTC เลยครับอ่านเวลาจาก NTP server แทนเลย

ใครจะวิจารณ์ หรือเสนอเพิ่มอะไรก็ต่อด้านล่างตามสบายครับ
แก้ไขข้อความเมื่อ
แสดงความคิดเห็น
โปรดศึกษาและยอมรับนโยบายข้อมูลส่วนบุคคลก่อนเริ่มใช้งาน อ่านเพิ่มเติมได้ที่นี่