ZS-042, czyli DS3231(RTC) i AT24C32(EEPROM) w jednym module... (część 1)
Posted: 31 October 2016, 17:52 - Mon
Dziś chciałbym przedstawić dość ciekawy moduł, który zawiera w swojej strukturze 2 układy, precyzyjny zegar czasu rzeczywistego(+bateria!) oraz pamięć EEPROM o pojemności 32Kb. Moduł dostępny jest za ok 1,5$. Oba układy sterowane są oczywiście magistralą I2C. Kilka słów o DS3231 jest to następca bardzo popularnego DS1302, który w swojej strukturze zawiera dodatkowo datownik, podwójny alarm, miernik temperatury z kompensacją. Dodatkowo nasz DS3231 może być źródłem sygnału 32KHz (lub jego podzielnych) oraz źródłem przerwań (np w celu wybudzenia naszego Cubie zgodnie z zaprogramowanym alarmem... Warto w tym miejscu wspomnieć również o bliźniaczym układzie DS3232, który prócz wszystkiego opisanego powyżej posiada również dodatkową pamięć SRAM (235 komórek). My skupimy się a wersji DS3231 ale nasze biblioteki mogą być wykorzystane dla obu układów. Dodatkowy układ, AT24C32 jest chyba jednym z najpopularniejszych pamięci typu EPPROM jakie występują w większości sprzętu RTV/SAT (np. jako pamięć nastaw programów). W zasadzie ten rodzaj układu mógłby leżeć poza polem naszego zainteresowania (wszak Cubietruck posiada 8GB pamięci FLASH) ale spójrzmy na to w taki sposób jakbyśmy musieli np zapisywać nastawy naszych eksperymentów a następnie korzystać z nich za pomocą innych urządzeń.. np mikrokontrolera AVR...
Real Time Clock
Masz moduł podłączamy standardowo do zasilania 3V3 i magistrali I2C. Układy zgłaszają się odpowiednio:
Do obsługi naszego RTC napisałem kilka funkcji, za pomocą których możemy ustawić i odczytać czas i datę, dzień tygodnia oraz zapisać i odczytać komórki pamięci SRAM (DS3232).
Program korzysta z biblioteki I2C.py, którą można pobrać jako załącznik za końcu tutoriala. Z braku czasu nie napisałem funkcji do obsługi alarmu.. ale kto ich używa?
Obsługa biblioteki:
Ustawienie czasu:
gdzie:
gdzie:
gdzie podajemy liczbę z zakresu 1-7 (odpowiednio: Sun,...,Sat)
Odczyt temperatury:
Temperatura zwracana jest w postaci tablicy dwóch elementów: [stopnie_ze_znakiem,setne_części_stopnia]
Pamięć EEPROM AT24C32
Z uwagi na fakt, że nasza biblioteka do obsługi I2C nie chciała ze mną współpracować gdy dopisałem kilka funkcji, zdecydowałem sie dodać obsługę EEPROM w programie głównym. Konsekwencją tego jest potrzeba osobnej inicjalizacji układu AT24C32 jak również brak możliwośći używania klasy Device. (Swoją drogą, czas pomyśleć nad osobnymi bibliotekami dla poszczególnych urządzeń). Za obsługę EEPROM-u odpowiada poniższa część kodu:
Wywołanie jest następujące:
gdzie:
gdzie:
Real Time Clock
Masz moduł podłączamy standardowo do zasilania 3V3 i magistrali I2C. Układy zgłaszają się odpowiednio:
- DS3231 - 0x68
AT24C32 - 0x57
Code: Select all
root@ctdev:~/ds3231# i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- 57 -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
root@ctdev:~/ds3231#
Code: Select all
from I2C import Device
import smbus
import time
#Registers
TIME_SECSREG = 0x00
TIME_MINSREG = 0x01
TIME_HOURREG = 0x02
TIME_DAYWREG = 0x03
TIME_DAYMREG = 0x04
TIME_MONTREG = 0x05
TIME_YEARREG = 0x06
ALM1_SESCREG = 0x07
ALM1_MINSREG = 0x08
ALM1_HOURREG = 0x09
ALM1_DATEREG = 0x0a
ALM2_MINSREG = 0x0b
ALM2_HOURREG = 0x0c
ALM2_DATEREG = 0x0d
CONTROL_REG = 0x0e
CTRLSTAT_REG = 0x0f
AGINGOFFSET_REG = 0x10
TEMP_MSB_REG = 0x11
TEMP_LSB_REG = 0x12
# Bits in the Control register
DS3231_EOSC = 0x80
DS3231_BBSQW = 0x40
DS3231_CONV = 0x20
DS3231_RS2 = 0x10
DS3231_RS1 = 0x08
DS3231_INTCN = 0x04
DS3231_A2IE = 0x02
DS3231_A1IE = 0x01
DS3231_RS_1HZ = 0x00
DS3231_RS_1024HZ = 0x08
DS3231_RS_4096HZ = 0x10
DS3231_RS_8192HZ = 0x18
# Bits in the Status register
DS3231_OSF = 0x80
DS3231_BB33KHZ = 0x40
DS3231_CRATE1 = 0x20
DS3231_CRATE0 = 0x10
DS3231_EN33KHZ = 0x08
DS3231_BSY = 0x04
DS3231_A2F = 0x02
DS3231_A1F = 0x01
DS3231_CRATE_64 = 0x00
DS3231_CRATE_128 = 0x10
DS3231_CRATE_256 = 0x20
DS3231_CRATE_512 = 0x30
# DS3231 Address:
ADDRESSds = 0x68
ADDRESSat = 0x57
# I2C BUS:
BUS = 1
# initialize
ds=Device(ADDRESSds, BUS)
at=smbus.SMBus(BUS)
# program variables:
hours = 23
minutes = 59
seconds = 57
flag24 = True
year = 16
month = 10
day = 31
dayofweek = 1 # 1-Sun,2-Mon,.....,7-Sat
century = False
# functions:
def read_SECONDS ():
""" Read seconds from DS3231 """
raw = ds.readU8(TIME_SECSREG)
secs = ((raw >> 4) & 0x07)*10 + (raw & 0x0f)
return secs
def read_MINUTES ():
""" Read minutes from DS3231 """
raw = ds.readU8(TIME_MINSREG)
mins = ((raw >> 4) & 0x07)*10 + (raw & 0x0f)
return mins
def read_HOURS ():
""" Read hours from DS3231 """
raw = ds.readU8(TIME_HOURREG)
test12_24 = raw & 0b01000000 #if bit no 6 is set means that we using 12 AM/PM conversion
if test12_24:
hours = ((raw >> 4) & 0x01)*10 + (raw & 0x0f)
else:
hours = ((raw >> 4) & 0x02)*10 + (raw & 0x0f)
return hours
def read_DAYOFWEEK ():
""" Read day of the month from DS3231 """
days = ['', 'Sun',' Mon', 'Tue',' Wed', 'Thu', 'Fri','Sat']
raw = ds.readU8(TIME_DAYWREG)
dayofweek = (raw & 0x07)
return days[dayofweek]
#return dayofweek
def read_DAY ():
""" Read day of week from DS3231 """
raw = ds.readU8(TIME_DAYMREG)
dayofmonth = ((raw >> 4) & 0x0f)*10 + (raw & 0x0f)
return dayofmonth
def read_MONTH ():
""" Read month from DS3231 """
raw = ds.readU8(TIME_MONTREG)
month = ((raw >> 4) & 0x07)*10 + (raw & 0x0f)
return month
def read_YEAR ():
""" Read month from DS3231 """
raw = ds.readU8(TIME_YEARREG)
year = ((raw >> 4) & 0x0f)*10 + (raw & 0x0f)
return year
def set_TIME (hours, minutes, seconds, flag24):
""" Set time on ds3231 """
raw = (( seconds / 10)) << 4 | ( seconds - (( seconds / 10) *10))
ds.write8(TIME_SECSREG,raw)
raw = (( minutes / 10)) << 4 | ( minutes - (( minutes / 10) *10))
ds.write8(TIME_MINSREG,raw)
if flag24:
raw = (( hours / 10)) << 4 | ( hours - (( hours / 10) *10))
raw = raw & 0b10111111
ds.write8(TIME_HOURREG,raw)
if flag24 == False:
if hours > 12:
hours = hours -12
raw = (( hours / 10)) << 4 | ( hours - (( hours / 10) *10))
raw = raw | 0b01100000 # enable 12H mode
raw = raw & 0b11111111 # and set PM
ds.write8(TIME_HOURREG,raw)
else:
raw = (( hours / 10)) << 4 | ( hours - (( hours / 10) *10))
raw = raw | 0b01000000 # enable 12H mode
raw = raw & 0b11011111 # and set AM
ds.write8(TIME_HOURREG,raw)
def set_DATE (year, month, day, century):
""" Set date on ds3231 """
raw = ((( day / 10)) << 4 | ( day - (( day / 10) *10))) & 0x3f
ds.write8(TIME_DAYMREG,raw)
raw = (( month / 10)) << 4 | ( month - (( month / 10) *10))
if century == False:
raw = raw & 0b00011111 # clear century bit
ds.write8(TIME_MONTREG,raw)
else:
raw = ((raw | 0b10000000) & 0b10011111) # set century bit
ds.write8(TIME_MONTREG,raw)
raw = (( year / 10)) << 4 | ( year - (( year / 10) *10))
ds.write8(TIME_YEARREG,raw)
def set_DAY (dayofweek):
""" Set day of week """
raw = dayofweek & 0x07
ds.write8(TIME_DAYWREG,raw)
def write_MEMORY (register, data):
""" Write value to SRAM memory - DS3232 only! """
ds.write8(register,data)
def read_MEMORY (register):
""" Read value from SRAM memory - DS3232 only! """
return ds.readU8(register)
def read_TEMPERATURE ():
""" Temperature """
temp_raw1 = ds.readU8(TEMP_MSB_REG)
temp_raw2 = ds.readU8(TEMP_LSB_REG)
if (temp_raw1 & 0b10000000):
temp_raw1 = temp_raw1 - (2 * temp_raw1)
temp_raw2 = (temp_raw2 >> 6) * 25
return [temp_raw1,temp_raw2]
def set_AT24C32ADDRESS(address):
""" Set current address on AT24C32 """
upperbyte = (address & 0b1111111100000000) >> 8
lowerbyte = address & 0b0000000011111111
at.write_i2c_block_data(ADDRESSat, upperbyte, [lowerbyte])
def read_AT24C32MEMORY(address):
""" Read value from memory address on AT24C32 """
set_AT24C32ADDRESS(address)
return at.read_byte(ADDRESSat)
def write_AT24C32MEMORY(address, value):
""" Write value for memory address on AT24C32 """
upperbyte = (address & 0b1111111100000000) >> 8
lowerbyte = address & 0b0000000011111111
at.write_i2c_block_data(ADDRESSat, upperbyte, [lowerbyte, value])
time.sleep(0.1)
# program:
set_TIME(hours, minutes, seconds, flag24)
set_DATE(year, month, day, century)
set_DAY(7)
eeprom_addr = 1000
value = 123
write_AT24C32MEMORY(eeprom_addr,value)
#write_MEMORY(0xf0,128) # DS3232 only
while 1:
print read_HOURS(), read_MINUTES(), read_SECONDS(), read_DAYOFWEEK(), read_YEAR(), read_MONTH(), read_DAY(), read_TEMPERATURE(), "EEPROM[", eeprom_addr, "]:", read_AT24C32MEMORY(eeprom_addr)
Obsługa biblioteki:
Ustawienie czasu:
Code: Select all
set_TIME(hours, minutes, seconds, flag24)
- hours: 0-23
minutes: 0-59
seconds: 0-59
flag24: False/True lub 0/1 - wykorzystywane do określenia rodzaju zegara 12H AM/PM lub 24H
Code: Select all
set_DATE(year, month, day, century)
- year: 0-99
month: 1-12
day: 1-31
century: True/False lub 1/0 - właściwie mało przydatne... chyba że planujemy "powrót do przeszłości"
Code: Select all
set_DAY(7)
Odczyt temperatury:
Code: Select all
read_TEMPERATURE()
Pamięć EEPROM AT24C32
Z uwagi na fakt, że nasza biblioteka do obsługi I2C nie chciała ze mną współpracować gdy dopisałem kilka funkcji, zdecydowałem sie dodać obsługę EEPROM w programie głównym. Konsekwencją tego jest potrzeba osobnej inicjalizacji układu AT24C32 jak również brak możliwośći używania klasy Device. (Swoją drogą, czas pomyśleć nad osobnymi bibliotekami dla poszczególnych urządzeń). Za obsługę EEPROM-u odpowiada poniższa część kodu:
Code: Select all
def set_AT24C32ADDRESS(address):
""" Set current address on AT24C32 """
msb = (address & 0b1111111100000000) >> 8
lsb = address & 0b0000000011111111
at.write_i2c_block_data(ADDRESSat, msb, [lsb])
def read_AT24C32MEMORY(address):
""" Read value from memory address on AT24C32 """
set_AT24C32ADDRESS(address)
return at.read_byte(ADDRESSat)
def write_AT24C32MEMORY(address, value):
""" Write value for memory address on AT24C32 """
msb = (address & 0b1111111100000000) >> 8
lsb = address & 0b0000000011111111
at.write_i2c_block_data(ADDRESSat, msb, [lsb, value])
time.sleep(0.1)
Code: Select all
write_AT24C32MEMORY(address, value)
- address: 0-4095
value: 0-255
Code: Select all
read_AT24C32MEMORY(address)
- address: 0-4095