[TH] RTC DS1302 and Micropython

บทความนี้อธิบายการเขียนโปรแกรมเพื่อใช้งาน DS1302 (ภาพที่ 1) ซึ่งเป็นไอซีที่ทำหน้าที่เป็น RTC (Real-Time Clock) อีกตัวหนึ่ง (ก่อนหน้านี้ได้เขียนถึง PCF8583 ด้วยภาษาไพธอนและ Arduino C++) และเป็นโมดูลที่นิยมใช้ในการเริ่มต้นศึกษาเนื่องจากเป็นโมดูลในชุดเรียนรู้ทั้งของ Arduino, IoT, 37-Sensors หรือ 45 Sensors เป็นต้น

ภาพที่ 1 ตัวอย่างการต่อใช้โมดูล ds1302

DS1302

คุณสมบัติของ DS1302 เป็นดังนี้

  1. เก็บค่า วินาที นาที ชั่วโมง วันในเดือน เดือน วันในสัปดาห์ ปี
  2. ค่าปีมีความถูกต้องเริ่มต้นจนถึงปี ค.ศ. 2100
  3. มีหน่วยความจำแรม (RAM) ขนาด 8 บิต จำนวน 31 ไบต์ที่สำรองข้อมูลด้วยแบตเตอรี
  4. ทำงานที่ 2.0VDC ถึง 5.5VDC
  5. ใช้กระแสน้อยกว่า 300nA เมื่อใช้แรงดัน 2VDC
  6. ทำงานในอุณหภูมิ -40°C ถึง 85°C

การจัดเรียงขาของตัวถังไอซีเป็นดังภาพที่ 2 จะพบว่ามีขาทั้งสิ้น 8 ขา ได้แก่ Vcc1, Vcc2, X1, X2, GND, CE, SCLK และ I/O ซึ่งขา X1,X2 เป็นขาที่ต่อกับคริสตัล และขาสำหรับเชื่อมต่อกับไมโครคอนโทรลเลอร์ คือ Vcc, GND, SCLK, CE (สำหรับรีเซ็ต) และ I/O ดังภาพที่ 3

ภาพที่ 2 การจัดเรียงขาของไอซี DS1302
ที่มา DS1302 Datasheet
ภาพที่ 3 โมดูล DS1302 และขาเชื่อมต่อกับไมโครคอนโทรลเลอร์

ตำแหน่งของหน่วยความจำภายใน DS1302 เป็นดังภาพที่ 4

ภาพที่ 4 ตำแหน่งหน่วยความจำภายใน DS1302
ที่มา DS1302 Datasheet

รูปแบบของคำสั่งที่ส่งให้กับ RTC มีขนาด 8 บิต โดยแต่ละบิตมีความหมายดังภาพที่ 5

ภาพที่ 5 ไบต์คำสั่ง/ตำแหน่งของ DS1302
ที่มา DS1302 Datasheet

จากภาพที่ 5 จะได้ว่าบิตต่าง ๆ มีความหมายดังนี้

  1. RAM หรือ CK/
  2. A0-A4 ค่าตำแหน่งที่ต้องการเข้าถึง
  3. RD หรือ WR/

รูปแบบของการส่งข้อมูลในการสื่อสารระหว่างไมโครคอนโทรลเลอร์กับ DS1302 เป็นดังภาพที่ 6

ภาพที่ 6 รูปแบบการรับ/ส่งข้อมูลระหว่างไมโครคอนโทรลเลอร์กับ DS1302
ที่มา ds1302 Datasheet

ผังลำดับเวลา (time-diagram) ของการเขียนและอ่านข้อมูลของ DS1302 เป็นดังภาพที่ 7 และ 8 ตามลำดับ

ภาพที่ 7 ผังลำดับเวลาของการเขียนข้อมูล
ที่มา ds1302 datasheet
ภาพที่ 8 ผังลำดับเวลาของการอ่านข้อมูล
ที่มา ds1302 datasheet

จากภาพรูปแบบของการส่งข้อมูลและรับข้อมูลจะได้ว่า ซึ่งมีข้อสังเกตว่าก่อนส่งบิตต้องเปลี่ยนสถานะของ SCLK จาก 0 เป็น 1 และเมื่อส่งเสร็จต้องเปลี่ยนจาก 1 เป็น 0 และสลับกันไปจนกว่าจะส่งข้อมูลหมด

  1. เปลี่ยนสถานะของขา CE จาก 0 เป็น 1
  2. ส่งบิต R/W’ เพื่อระบุการรับส่ง
  3. ส่งบิต A0, A1, A2, A3 และ A4 เพื่อระบุตำแหน่ง
  4. ส่งบิต R/C’
  5. ปิดท้ายด้วยบิต 1
  6. ส่งข้อมูล 8 บิต เรียงจาก D0, D1, D2, … , D7
  7. เปลี่ยนสถานะของขา CE จาก 1 มาเป็น 0

ตัวอย่างโปรแกรม

ตัวอย่างโปรแกรมสำหรับตั้งค่าและแสดงเวลา โดยการเชื่อมต่อระหว่างบอร์ด ESP32  กับโมดูล DS1302  เป็นดังภาพที่ 9

ภาพที่ 9 ตัวอย่างการเชื่อมต่อกับ ESP32
################################################
# 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

ภาพที่ 10 ตัวอย่างผลการทำงานของโปรแกรม

สรุป

จากบทความนี้จะพบว่า การเขียนโปรแกรมภาษาไพธอนสามารถเข้าถึงการทำงานของฮาร์ดแวร์ได้เช่นเดียวกับภาษา C++ เพียวแต่ความเร็วในการทำงานอาจจะแตกต่างกัน แต่อย่างไรก็ดี ด้วยความง่ายในการอ่านโค้ด และความสะดวกในการย้ายโค้ดไปรันบนระบบอื่นทำให้ภาษานี้น่าสนใจ สุดท้ายนี้ทางทีมงานหวังว่าบทความนี้คงมีประโยชน์บ้างไม่มากก็น้อย และขอให้สนุกกับการเขียนโปรแกรมครับ

ท่านใดอยากพูดคุยสามารถคอมเมนท์ไว้ได้เลยครับ

แหล่งอ้างอิง

  1. DS1302 Datasheet

(C) 2020-2021, โดย อ.ดนัย เจษฎาฐิติกุล/อ.จารุต บุศราทิจ
ปรับปรุงเมื่อ 2021-07-07, 2021-10-16