稼働中

Raspberry Pi Pico(s_09)DS3231 用モジュール

DS3231 DS3231c.py

DS3231は温度補償されたreal-time clock (RTC)です。I2C通信で制御します。
※詳細はDS3231のデータシートを参照ください。
Raspberry Pi PicoでDS3231センサーモジュールを使う自作の’DS3231c.py’を作成しました。記事d_11を元に作成しています。
使い方だけを記載します。’DS3231c.py’は末尾にあります。

使い方

■ ファイル転送
Raspberry Pi Picoへ’DS3231c.py’を送った後でimportして使います。
※DS3231c.pyの末尾に、使用例(DS3231_ex.py)の## example以降をコピペ追加しても動作確認できます。
■ メソッド
‘DS3231c’をimportすると「write_Cal、read_Cal、write_Adata、read_Adata、disp_Adata、disp_Sdtim、disp_Mask、read_Aflg、reset_Aflag、read_Aie、set_Aie、read_Temp」のメソッドが使えるようになります。

DS3231(num=1,sda=10))で初期化します。
num:I2C0、I2C1の番号です。デフォルトは1(I2C1)
sda:SDAのGPIO番号です。デフォルトは10(GP10)

(01)write_Cal(l_data)
l_data:カレンダのリストデータです。※ここでは24H表示のみ対応しています。
2022/12/24 14:15:00 sat ( 0:sun 1:mon 2:tue 3:wed…)に設定するなら
l_cal=([2022, 12, 24, 17, 15, 0, 6])
write_Cal(l_cal)
とします。
(02)read_Cal()
現在の日時を返します。
(03)write_Adata(ldata)
アラーム設定をします。
ldataはマッチデータとアラームマスクデータのリストです。
例えば
ldata=[md1,md2,am1,am2,yt1,yt2]
md1,md2,am1,am2,yt1,yt2は以下のように設定します。
0x07~0x0dレジスタの日時等の一致データを設定します。
md1はアラーム1の一致リストデータで[秒,分,時,曜日/日付]
md2はアラーム1の一致リストデータで[分,時,曜日/日付]
で与えます。
例えば、
# mach data
md1=[59,59,23,31] # sec,min,hour,day/date(0-6/1-31)
md2=[52,23,31] # min,hour,day/date(0-6/1-31)
※以下のアラームマスクと無関係でも何か数値を設定しておきます。空にするとエラーになります。
# ALARM MASK BITS
0x07~0x0dレジスタのBit7にマスク設定します。
am1=[1,1,1,1] #alarm1 mask(bit7)0x07-0x0a
am2=[1,1,1] #alarm1 mask(bit7)0x0b-0x0d

DT DATE(日付 1-31)をセットする場合
0x0a、0x0dレジスタのBit6を設定します。”:不使用,’0′:曜日,’1’日付
yt1=[”] #day/date(曜日/日付) ”,’0′,’1′ 自動的に a1m=[0,0,0,0]になります。
yt2=[”] #day/date(曜日/日付) ”,’0′,’1’ 自動的に a2m=[0,0,0]になります。
※Alarm Mask Bitsの値など詳細はデータシートを参照して下さい。
(04)read_Adata()
アラームデータ(0x07~0x0dレジスタ)を返します。
(05)disp_Adata(r_reg)
アラームデータ(0x07~0x0dレジスタ)の値を表示します。
(06)disp_Sdtim(r_reg)
アラームデータ(0x07~0x0d)に設定されたマッチデータを表示します。
(07)disp_Mask(r_reg)
アラームデータ(0x07~0x0d)に設定されたマスクデータを表示します。
[am1,yt1][am2,yt2]のような表示をします。
(08)read_Aflg()
割込みの有無(アラームフラグ値)を調べます。|A2F|A1F|の値が返ります。
(09)reset_Aflag()
アラームフラグをリセットします。|A2F|A1F|を’0’にします。
(10)read_Aie()
割込みの許可の状態を調べます。|INTCN|A2IE|A1IE|の値が返ります。
(11)set_Aie()
割込みの許可を設定します。0Ehレジスタの|A2IE|A1IE|に値を与えます。デフォルトは両方割込み可です。
(12)read_Temp()
温度測定をします。温度計算後の値を返します。

使用例

スクリプトで以下のDS3231の動作を確認しています。
2022/12/30 20:10:54 friにカレンダ設定します。
現在の日時を表示します。
アラームのマッチデータを56秒、分ごとに設定します。
アラームデータを読み込んで、
アラームデータ、マッチデータ、マスクデータを順次表示します。
割込みの許可の状態を表示します。
割込みの許可を設定します。|A2IE|A1IE|両方許可します。
アラームフラグを調べます。
アラームフラグをリセットします。
アラームフラグを調べます。
温度測定をします。
アラームフラグをリセット後にアラームフラグの状態を調べマッチ状態を確認します。
両方のフラグ(56秒、分ごとでマッチ)で終了します。


DS3231c_ex.py
#!/usr/bin/env python
# -*- coding: utf-8 -*
# DS3231 extremely accurate  I2C real-time  clock  (RTC)
from machine import I2C,Pin
import time
from DS3231c import DS3231
## exapmle -----------------------
## Callender set,read
#instance
a=DS3231(1, 10)         #init(num=1,sda=10)
# Callender 24H disp
# 2022/12/30 20:10:54 fri ( 0:sun 1:mon 2:tue 3:wed...)
l_cal=([2022, 12, 30, 20, 10, 54, 5])

a.write_Cal(l_cal)              #日時セット

print('Callender',a.read_Cal()) #日時確認

## Alarm set,read
# mach data 
md1=[56,59,23,31]   # sec,min,hour,day/date(0-6/1-31)
md2=[50,23,31]      # min,hour,day/date

# ALARM MASK BITS 飛び設定は出来ない[1,0,1,0]はNG
# [A1M1,,A1M4] per_sec[1,1,1,1]sec[0,1,1,1]min[0,0,1,1]hour[0,0,0,1]の4PTN
am1=[0,1,1,1] #alarm1 mask(bit7)
# [A2M2,,A2M4] per_min[1,1,1] min[0,1,1]hour[0,0,1]の3PTN
am2=[1,1,1]   #alarm1 mask(bit7)
# DY DAY(曜日 1-7 便宜上0-6にしている)
# wday=['sun','mon','tue','wed','thu','fri','sat']
# DT DATE(日付 1-31)
yt1=[''] #day/date '','0','1' 自動的に a1m=[0,0,0,0] 曜日/日付
yt2=[''] #day/date '','0','1' 自動的に a2m=[0,0,0]   

set_ldata=[md1,md2,am1,am2,yt1,yt2]
# wirte mach mask Adata
a.write_Adata(set_ldata)        #アラームデータ設定

# read Adata
a_dat=a.read_Adata()            #アラームデータ取得

# disp Adata
print('Adata-------')
a.disp_Adata(a_dat)             #アラームデータ表示

print('Sdtim-------')
a.disp_Sdtim(a_dat)             #マッチデータ表示

print('Mask-------')
a.disp_Mask(a_dat)             #マスクデータ表示
print('')

# read AIE > set AIE > read AIE
print('read_AIE |INTCN|A2IE|A1IE|=',bin(a.read_Aie()))  #割込み許可設定表示
a.set_Aie(3)                                            #割込み許可設定(3)
print('set_AIE  |INTCN|A2IE|A1IE|=',bin(a.read_Aie()))  #割込み許可設定表示

# read Aflg > reset Aflag > read Aflg
print('read_Aflag  |A2F|A1F|=',bin(a.read_Aflg()))      #フラグ確認
a.reset_Aflag()                                         #フラグリセット
print('reset_Aflag |A2F|A1F|=',bin(a.read_Aflg()))      #フラグ確認

#a.set_Aie(0)
print("")

## Read Temp
temp=a.read_Temp()                  #温度測定
print('Temp=%6.2f'%temp,'℃')
print('')

## Match Alarm Test
a.reset_Aflag()                 #フラグリセット
while True:
    print(a.read_Cal())         #現在時刻表示
    flag=a.read_Aflg()          #フラグ確認
    print(bin(flag))
    time.sleep(1)
    if flag==0b11:              #両方マッチ後に終了
        break

実行結果

実行すると以下のようになりました。※Thonnyのshellに表示されます。


>>> %Run -c $EDITOR_CONTENT
Callender ['2022', '12', '30', '20', '10', '54', 'fri']
Adata-------
0x7 0b01010110 0x56         # 番地 データ hex値
0x8 0b11011001 0xd9
0x9 0b10100011 0xa3
0xa 0b10110001 0xb1
0xb 0b11010000 0xd0
0xc 0b10100011 0xa3
0xd 0b10110001 0xb1
Sdtim-------
['56', '59', '23', '31']
['50', '23', '31']
Mask-------
[0, 1, 1, 1, 0]     #秒でマッチ(56秒)
[1, 1, 1, 0]        #分ごとでマッチ

read_AIE |INTCN|A2IE|A1IE|= 0b111
set_AIE  |INTCN|A2IE|A1IE|= 0b111
read_Aflag  |A2F|A1F|= 0b11
reset_Aflag |A2F|A1F|= 0b0

Temp= 19.50 ℃

['2022', '12', '30', '20', '10', '54', 'fri']
0b0
['2022', '12', '30', '20', '10', '55', 'fri']
0b0
['2022', '12', '30', '20', '10', '56', 'fri']   #56秒でマッチ|A2F|A1F|= 0b01
0b1
['2022', '12', '30', '20', '10', '57', 'fri']
0b1
['2022', '12', '30', '20', '10', '58', 'fri']
0b1
['2022', '12', '30', '20', '10', '59', 'fri']
0b1
['2022', '12', '30', '20', '11', '0', 'fri']    #分ごとでマッチ|A2F|A1F|= 0b11
0b11
>>> 

DS3231用モジュール


DS3231c.py
#!/usr/bin/env python
# -*- coding: utf-8 -*
from machine import I2C,Pin
import time
# DS3231 extremely accurate  I2C real-time  clock  (RTC)
class DS3231:
    def __init__(self,num=1,sda=10):
        self.adr=0x68    # 0x50(80)~0x57(87)
        self.i2c=I2C(num, scl=Pin(sda+1), sda=Pin(sda), freq=100_000)        
    # zero padding 8-12bit
    def z_padd(self,dat):  # int
        dat=bin(dat)  # str
        value='0b'+'0'*(8-len(dat[2:])) + dat[2:]      # str 8bit
        #value='0'*(12-len(dat[2:])) + dat[2:]         # str 12bit
        return value
    # read num register
    def read_reg(self,reg,num):
        buf=bytearray(1)
        buf[0]=reg
        self.i2c.writeto(self.adr,buf)
        time.sleep_ms(5)
        r_reg=self.i2c.readfrom(self.adr,num)
        time.sleep_ms(5)
        return r_reg
    # separate upddata書込むデータを上位下位に分ける hex表示にすると読視できる
    def s_dat(self,dat):
        s_dat=(dat//10 << 4) | (dat%10)
        return s_dat

    ### read-write Calender data --------------------------------------
    def write_Cal(self, l_data): #l_data=calender list-setdata
        dat=l_data 
        s_reg=bytearray(1)
        s_reg[0]=0x00        # write start regi-num
        # Convert from data list to data for writing
        # 無理矢理for文でもできるが見た目が分かり難いので止めた
        buf=bytearray(7)
        # 0x00 seconds 
        buf[0]= self.s_dat(dat[5])
        # 0x01 minutes 
        buf[1]= self.s_dat(dat[4])
        # 0x02 hour 
        buf[2]= self.s_dat(dat[3])
        # 0x03 day of week
        buf[3]= self.s_dat(dat[6])
        # 0x04 date
        buf[4]= self.s_dat(dat[2])
        # 0x05  month  over century 
        buf[5]= self.s_dat(dat[1])
        # 0x06  year
        buf[6]= self.s_dat(dat[0]-2000)

        # writing to registers
        s_buf=bytearray(8)
        s_buf=s_reg+buf      # bytearrayなら足し算できる 開始レジスタ+データ
        #print(s_buf)
        self.i2c.writeto(self.adr,s_buf) #**change

    def read_Cal(self):
        # read real time
        wday=['sun','mon','tue','wed','thu','fri','sat']
        s_reg=bytearray(1)
        s_reg[0]=0x00
        self.i2c.writeto(self.adr,s_reg)   #**change
        r_reg=self.i2c.readfrom(self.adr,7)#**change
        #print(r_reg)
        
        r_data=[]
        for i in range(7):
            a=hex(r_reg[i])[2:]  #a='0x36' a[2:]='36' 0xを除く文字列に
            if i==3:             # 0x03 曜日に
                a=wday[int(a)]
            if i==6:
                a=str(2000+int(a))        # 0x06 西暦に
            #print(i,a)
            r_data.append(a)
        # [::-1]逆ソート
        # [::-1][0:3] [::-1][4:7] [::-1][3:4] 逆ソートしてから並べ替え
        ss=r_data[::-1][0:3]+r_data[::-1][4:7]+r_data[::-1][3:4]
        #print(ss)
        return ss

    ### read-write Alarm set data ----------------------------------
    def write_Adata(self,ldata):
        a1= ldata[0]
        a2= ldata[1]
        a1m=ldata[2]
        a2m=ldata[3]
        a1d=ldata[4]
        a2d=ldata[5]
        
        # write alarm data set-regi 0x07-0xd
        s_reg=bytearray(1)
        s_reg[0]=0x07              # write start regi-num

        buf=bytearray(7)
        for i in range(4):
            buf[i]=self.s_dat(a1[i])    #一致データを上位下位に変換する
            #print('a1 mach-data',hex(buf[i]))
            # mask bit
            if a1d[0]=='':
                buf[i]=buf[i] | a1m[i]<< 7
            else:
                buf[i]=buf[i] & 0x7f  # AM bit7= '0'
               
                if a1d[0]=='1':
                    print('a1d=1')
                    buf[3]=buf[3] | 0x40  # DY/DT bit wo '1'
                    
            #print('a1m add-mask',hex(buf[i]))
            
        for i in range(3):
            buf[i+4]=self.s_dat(a2[i]) #設定データを上位下位に変換する
            #print('a2 mach-data',hex(buf[i+4]))
            # mask bit 
            if a2d[0]=='':
                buf[i+4]=buf[i+4] | a2m[i]<< 7
            else:
                buf[i+4]=buf[i+4] & 0x7f  # AM bit7 '0'
               
            if a2d[0]=='1':
                print('a2d=1')
                buf[6]=buf[6] | 0x40  # DY/DT bit wo '1'
                
            #print('a2m add-mask',hex(buf[i+4]))

        # regi-addres,write-bytearray
        s_buf=s_reg+buf      # bytearrayなら足し算できる 開始レジスタ+データ
        #print(s_buf)
        self.i2c.writeto(self.adr,s_buf) # write mach data

    # read Alarm data -------------------------0x07から7bytes
    def read_Adata(self):
        s_reg=bytearray(1)
        s_reg[0]=0x07
        self.i2c.writeto(self.adr,s_reg)
        r_reg=self.i2c.readfrom(self.adr,7)
        #print(r_reg)
        return r_reg

    # Disp Alarm regi-data
    def disp_Adata(self,r_reg):
        for i in range(7):    
            print(hex(7+i),self.z_padd(r_reg[i]),hex(r_reg[i]))

    # Disp set_data dy/dt-hour-min-sec 
    def disp_Sdtim(self,r_reg):
        DAT1=[] #sec,min,hour,DY/DT    
        for i in range(4):
            a=hex(r_reg[i]& 0b0111_1111)[2:]  #a='0x36' a[2:]='36' 0xを除く文字列に
            if i==3:
                a=hex(r_reg[i]& 0b0011_1111)[2:]     
            DAT1.append(a)        
        print(DAT1)

        DAT2=[] #min,hour,DY/DT    
        for i in range(3):
            a=hex(r_reg[i+4]& 0b0111_1111)[2:]  #a='0x36' a[2:]='36' 0xを除く文字列に
            if i==2:
                a=hex(r_reg[i+4]& 0b0011_1111)[2:]     
            DAT2.append(a)        
        print(DAT2)

    # Disp mask data
    def disp_Mask(self,r_reg):
        ALM1=[] #ALM1=[A1M1,A1M2,A1M3,A1M4,DY/DT]
        for i in range(4):
            ALM1.append((r_reg[i]>>7)) 
        ALM1.append((r_reg[3] & 0b0100_0000)>>6) #最後にDY/DTを加える
        print(ALM1)

        ALM2=[] #ALM2=[A2M2,A2M3,A1M4,DY/DT]
        for i in range(3):
            ALM2.append((r_reg[i+4]>>7)) 
        ALM2.append((r_reg[6] & 0b0100_0000)>>6)   
        print(ALM2)

    ## Alarm Flag(read,reset) Ctrl-AIE(read,set)
    # read Alarm Flag |A2F|A1F|
    def read_Aflg(self):
        a_flag=self.read_reg(0x0f,1)[0]& 0x03
        #print('Aflg=',bin(a_flag))
        return a_flag

    # reset Alarm Flag |A2F|A1F|
    def reset_Aflag(self):
        r_reg=self.read_reg(0x0f,1)
        buf=bytearray(2)
        buf[0]=0x0f
        buf[1]=r_reg[0] & 0xfc   # 0b_1111_1100
        self.i2c.writeto(self.adr,buf)
        #print('reset 0x0f=',buf[1],bin(buf[1]))

    ## INTCON Alarm INT Enabel
    # read |INTCN|A2IE|A1IE| Control/Status(0x0e)
    def read_Aie(self):
        a_ie= self.read_reg(0x0e,1)[0] & 0x07
        #print('INT-AIE=',bin(a_ie))
        return a_ie

    # write |A2IE|A1IE| Control/Status(0x0e)
    def set_Aie(self,s_num=3):
        r_ie=self.read_reg(0x0e,1)[0] & 0xfc #|A2IE|A1IE| reset site kara kakikomu

        aie=s_num # 00,01,10,11(0,1,2,3)
        buf=bytearray(2)
        buf[0]=0x0e
        buf[1]=r_ie | aie
        self.i2c.writeto(self.adr,buf)

    ## TEMP ----------------------------------
    ## temp read regi-0x11,0x12 Sign 10bit
    # 2'complement
    def s_comp(self,value):
        bits=10
        a = value[2:]
        b_dat = '0'*(bits-len(a)) + a
        conv_value = -int(b_dat[0]) << (bits-1) | int(b_dat,2)
        return conv_value

    # MSB(8)+LSB(2) 10bit 
    def add_bit(self,msb, lsb):
        m = msb[2:]
        msb_dat = '0'*(8-len(m)) + m
        msb_8bit='0b'+msb_dat
        l = lsb[2:]
        lsb_dat = '0'*(8-len(l)) + l
        lsb_2bit=lsb_dat[:2]
        # 10bit data = MSB a_8bit+LSB b_2bit 
        x_reg = msb_8bit + lsb_2bit
        return x_reg

    # temp calc 
    def read_Temp(self):
        # Temp Reg 0x11,0x12
        r_reg=self.read_reg(0x11,2)
        # 10bit data 
        t_bit=self.add_bit(bin(r_reg[0]),bin(r_reg[1]))
        # 2'complement
        t_dat=self.s_comp(t_bit)
        #print(t_bit,t_dat)
        t=0.25*t_dat
        return t