ZS-042, czyli DS3231(RTC) i AT24C32(EEPROM) w jednym module... (część 1)

Moderator: bbiernat

Post Reply
User avatar
pancio
Administrator
Posts: 63
Joined: 18 September 2013, 23:02 - Wed
Location: SILESIA

ZS-042, czyli DS3231(RTC) i AT24C32(EEPROM) w jednym module... (część 1)

Post by pancio »

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...
zs042.jpg
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# 

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).

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)
	


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? :D

Obsługa biblioteki:

Ustawienie czasu:

Code: Select all

set_TIME(hours, minutes, seconds, flag24)
gdzie:
  • 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
Ustawienie daty:

Code: Select all

set_DATE(year, month, day, century)
gdzie:
  • 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" :lol:
Ustawienie dnai tygodnia:

Code: Select all

set_DAY(7)
gdzie podajemy liczbę z zakresu 1-7 (odpowiednio: Sun,...,Sat)

Odczyt temperatury:

Code: Select all

read_TEMPERATURE()
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:

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)
Wywołanie jest następujące:

Code: Select all

write_AT24C32MEMORY(address, value)
gdzie:
  • address: 0-4095
    value: 0-255

Code: Select all

read_AT24C32MEMORY(address)
gdzie:
  • address: 0-4095
I to by było na tyle ...
Attachments
ds3231_at24c32.zip
(3 KiB) Downloaded 1544 times
AT24CXX.pdf
AT24C32 2-Wire Serial EEPROM 32K (4096 x 8)
(193.58 KiB) Downloaded 1526 times
ds3231.zip
(2.79 KiB) Downloaded 1484 times
DS3231.pdf
Extremely Accurate I2C-Integrated RTC/TCXO/Crystal
(292.08 KiB) Downloaded 1549 times
DS3232.pdf
Extremely Accurate I 2 C RTC with Integrated Crystal and SRAM
(422.15 KiB) Downloaded 1572 times

Post Reply