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