บทความนี้อธิบายการเขียนโปรแกรมเพื่อใช้งาน DS1302 (ภาพที่ 1) ซึ่งเป็นไอซีที่ทำหน้าที่เป็น RTC (Real-Time Clock) อีกตัวหนึ่ง (ก่อนหน้านี้ได้เขียนถึง PCF8583 ด้วยภาษาไพธอนและ Arduino C++) และเป็นโมดูลที่นิยมใช้ในการเริ่มต้นศึกษาเนื่องจากเป็นโมดูลในชุดเรียนรู้ทั้งของ Arduino, IoT, 37-Sensors หรือ 45 Sensors เป็นต้น
DS1302
คุณสมบัติของ DS1302 เป็นดังนี้
- เก็บค่า วินาที นาที ชั่วโมง วันในเดือน เดือน วันในสัปดาห์ ปี
- ค่าปีมีความถูกต้องเริ่มต้นจนถึงปี ค.ศ. 2100
- มีหน่วยความจำแรม (RAM) ขนาด 8 บิต จำนวน 31 ไบต์ที่สำรองข้อมูลด้วยแบตเตอรี
- ทำงานที่ 2.0VDC ถึง 5.5VDC
- ใช้กระแสน้อยกว่า 300nA เมื่อใช้แรงดัน 2VDC
- ทำงานในอุณหภูมิ -40°C ถึง 85°C
การจัดเรียงขาของตัวถังไอซีเป็นดังภาพที่ 2 จะพบว่ามีขาทั้งสิ้น 8 ขา ได้แก่ Vcc1, Vcc2, X1, X2, GND, CE, SCLK และ I/O ซึ่งขา X1,X2 เป็นขาที่ต่อกับคริสตัล และขาสำหรับเชื่อมต่อกับไมโครคอนโทรลเลอร์ คือ Vcc, GND, SCLK, CE (สำหรับรีเซ็ต) และ I/O ดังภาพที่ 3
ตำแหน่งของหน่วยความจำภายใน DS1302 เป็นดังภาพที่ 4
รูปแบบของคำสั่งที่ส่งให้กับ RTC มีขนาด 8 บิต โดยแต่ละบิตมีความหมายดังภาพที่ 5
จากภาพที่ 5 จะได้ว่าบิตต่าง ๆ มีความหมายดังนี้
- RAM หรือ CK/
- A0-A4 ค่าตำแหน่งที่ต้องการเข้าถึง
- RD หรือ WR/
รูปแบบของการส่งข้อมูลในการสื่อสารระหว่างไมโครคอนโทรลเลอร์กับ DS1302 เป็นดังภาพที่ 6
ผังลำดับเวลา (time-diagram) ของการเขียนและอ่านข้อมูลของ DS1302 เป็นดังภาพที่ 7 และ 8 ตามลำดับ
จากภาพรูปแบบของการส่งข้อมูลและรับข้อมูลจะได้ว่า ซึ่งมีข้อสังเกตว่าก่อนส่งบิตต้องเปลี่ยนสถานะของ SCLK จาก 0 เป็น 1 และเมื่อส่งเสร็จต้องเปลี่ยนจาก 1 เป็น 0 และสลับกันไปจนกว่าจะส่งข้อมูลหมด
- เปลี่ยนสถานะของขา CE จาก 0 เป็น 1
- ส่งบิต R/W’ เพื่อระบุการรับส่ง
- ส่งบิต A0, A1, A2, A3 และ A4 เพื่อระบุตำแหน่ง
- ส่งบิต R/C’
- ปิดท้ายด้วยบิต 1
- ส่งข้อมูล 8 บิต เรียงจาก D0, D1, D2, … , D7
- เปลี่ยนสถานะของขา CE จาก 1 มาเป็น 0
ตัวอย่างโปรแกรม
ตัวอย่างโปรแกรมสำหรับตั้งค่าและแสดงเวลา โดยการเชื่อมต่อระหว่างบอร์ด ESP32 กับโมดูล DS1302 เป็นดังภาพที่ 9
################################################
# demo_ds1302.py
# (C) 2021, JarutEx (https://www.jarutex.com
################################################
from machine import Pin
import time
# setting
class DS1302:
REG_SECOND = const(0x80)
REG_MINUTE = const(0x82)
REG_HOUR = const(0x84)
REG_DAY = const(0x86)
REG_MONTH = const(0x88)
REG_WEEKDAY= const(0x8A)
REG_YEAR = const(0x8C)
REG_WP = const(0x8E)
REG_CTRL = const(0x90)
REG_RAM = const(0xC0)
def __init__(self, _sclk=12, _io=13, _ce=14):
self.pinClk = Pin( _sclk, Pin.OUT )
self.pinIO = Pin( _io )
self.pinCE = Pin( _ce, Pin.OUT )
self.pinCE.value(0) # disable
self.pinClk.value(0)
self.rtc = [0,0,0,0,0,0,0] # y/m/d/dw/h/mi/s
def dec2bcd(self, n):
return ((n // 10 * 16) + (n % 10))
def bcd2dec(self, n):
return ((n // 16 * 10) + (n % 16))
def write(self, data):
self.pinIO.init(Pin.OUT)
for i in range(8):
bit = (data & 0x01)
data = (data >> 1)
self.pinIO.value(bit)
self.pinClk.value(1)
self.pinClk.value(0)
def read(self):
self.pinIO.init( Pin.IN )
byte = 0
for i in range(8):
bit = self.pinIO.value() & 0x01
bit = (bit << i)
byte = (byte | bit)
self.pinClk.value(1)
self.pinClk.value(0)
return byte
def set(self, reg, data):
self.pinCE.value(1)
self.write( reg )
self.write( data )
self.pinCE.value(0)
def get(self, reg):
self.pinCE.value(1)
self.write( reg+1 )
data = self.read()
self.pinCE.value(0)
return data
def now(self):
self.rtc[0] = self.bcd2dec(self.get(REG_YEAR))+2000
self.rtc[1] = self.bcd2dec(self.get(REG_MONTH))
self.rtc[2] = self.bcd2dec(self.get(REG_DAY))
self.rtc[3] = self.bcd2dec(self.get(REG_WEEKDAY))
self.rtc[4] = self.bcd2dec(self.get(REG_HOUR))
self.rtc[5] = self.bcd2dec(self.get(REG_MINUTE))
self.rtc[6] = self.bcd2dec(self.get(REG_SECOND))
def adjust(self, day, month, year, dow, hour, minute, second ):
# convert
year = year - 2000
year = self.dec2bcd(year)
month = self.dec2bcd(month)
day = self.dec2bcd(day)
dow = self.dec2bcd(dow)
minute = self.dec2bcd(minute)
hour = hour & 0x1F
hour = self.dec2bcd(hour)
second = self.dec2bcd(second)
# adjust
self.set(REG_YEAR, year)
self.set(REG_MONTH, month)
self.set(REG_DAY, day)
self.set(REG_WEEKDAY, dow)
self.set(REG_HOUR, hour)
self.set(REG_MINUTE, minute)
self.set(REG_SECOND, second)
def show(self):
print("{} {}-{}-{} {}:{}:{}".format(
self.rtc[3],
self.rtc[0], self.rtc[1], self.rtc[2],
self.rtc[4], self.rtc[5], self.rtc[6]
))
# main program
rtc = DS1302()
rtc.adjust(7,7,2021, 3, 17,57,00)
while True:
rtc.now()
rtc.show()
time.sleep_ms(1000)
ตัวอย่างผลการทำงานเป็นดังภาพที่ 10
สรุป
จากบทความนี้จะพบว่า การเขียนโปรแกรมภาษาไพธอนสามารถเข้าถึงการทำงานของฮาร์ดแวร์ได้เช่นเดียวกับภาษา C++ เพียวแต่ความเร็วในการทำงานอาจจะแตกต่างกัน แต่อย่างไรก็ดี ด้วยความง่ายในการอ่านโค้ด และความสะดวกในการย้ายโค้ดไปรันบนระบบอื่นทำให้ภาษานี้น่าสนใจ สุดท้ายนี้ทางทีมงานหวังว่าบทความนี้คงมีประโยชน์บ้างไม่มากก็น้อย และขอให้สนุกกับการเขียนโปรแกรมครับ
ท่านใดอยากพูดคุยสามารถคอมเมนท์ไว้ได้เลยครับ
แหล่งอ้างอิง
(C) 2020-2021, โดย อ.ดนัย เจษฎาฐิติกุล/อ.จารุต บุศราทิจ
ปรับปรุงเมื่อ 2021-07-07, 2021-10-16