Ansteuerung der ICs AT24C32 und DS1307 mit Python

Auf Ebay, Amazon oder Aliexpress kann man sehr billig dieses Real-Time-Clock Modul für den RaspberryPi (oder Arduino und Co.) kaufen: Amazon

rtcmodulAuf diesem Modul sind nun zwei interessante ICs drauf. Und zwar (natürlich) eine Realtime-Clock DS1307 (inklusive Uhrenquarz) und ein AT24c32 EEPROM. Beide ICs können über den I2C-Bus angeseteurt werden.

Damit das Modul sauber mit den 3.3 Volt des Raspberry PIs läuft müssen wir aber vorher die Widerstände R2 und R3 auslöten.

Sollte man nun noch eine normale CS2032 Batterie verwenden wollen (die nicht aufladbar ist!) muss noch entfernt werden: R4 + R5 + R6 + D1.
Sodann müssen die Pads von R6 kurzgeschlossen werden.
(Das ganze ist nötig da die mitgelieferte Batterie des Typs LIR2032 (die aufladbare wäre) leider meistens kaputt ist!)

Nun müssen wir nur noch den Pi vorbereiten. Im Programm ‚raspi-config‘ müssen wir unter ‚Advanced Options‘ anschalten dass wir das I2C-Interface automatisch laden wollen.
Nach einem Neustart installieren wir noch die SMBUS Library für Python3.
Leider ist diese nicht direkt in den Python3 Paketquellen enthalten, aber zum Glück ist das installieren nicht schwer:

- sudo -i
- apt-get install python3-dev
- apt-get install libi2c-dev
- cd /tmp
- wget http://ftp.de.debian.org/debian/pool/main/i/i2c-tools/i2c-tools_3.1.0.orig.tar.bz2 # download Python 2 source
- tar xavf i2c-tools_3.1.0.orig.tar.bz2
- cd i2c-tools-3.1.0/py-smbus
- mv smbusmodule.c smbusmodule.c.orig # backup
- wget https://gist.githubusercontent.com/sebastianludwig/c648a9e06c0dc2264fbd/raw/2b74f9e72bbdffe298ce02214be8ea1c20aa290f/smbusmodule.c # download patched (Python 3) source
- python3 setup.py build
- python3 setup.py install

Und nun noch das verbinden des Moduls mit dem Pi:

  • VCC vom Modul nach 5V vom Pi (Pin2)
  • GND vom Modul nach GND vom Pi (Pin6)
  • SDA vom Modul nach SDA1 vom Pi (Pin3)
  • SCL vom Modul nach SCL1 vom Pi (Pin5)

Wenn man nun per ‚apt-get install i2c-tools‘ die i2c-Tools installiert hat und das Kommando ‚i2cdetect -y 1‘ ausführt sollte man beide ICs finden.
Der DS1307 hat die Adresse 0x68 und der AT24c32 die Adresse 0x50:

     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: 50 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

Das ansteuern in Python ist nun einfach, einmal das EEPROM:

#!/usr/bin/python3
import smbus
import time
import random

# I2C Address of AT24C32 EEPROM
at24c32addr = 0x50

# Set current adress of eeprom
def at24c32_set_addr(addr):
	upperbyte = (addr & 0b1111111100000000) >> 8
	lowerbyte = addr & 0b0000000011111111
	i2c.write_i2c_block_data(at24c32addr, upperbyte, [lowerbyte])
	time.sleep(0.1)

# Valid addr for RAM: 0 - 4096
def at24c32_get_ram(addr):
	at24c32_set_addr(addr)
	return i2c.read_byte(at24c32addr)	

# Valid addr for RAM: 0 - 4096
def at24c32_set_ram(addr, val):
	upperbyte = (addr & 0b1111111100000000) >> 8
	lowerbyte = addr & 0b0000000011111111
	i2c.write_i2c_block_data(at24c32addr, upperbyte, [lowerbyte, val])	
	time.sleep(0.1)

def main():
	if "i2c" not in vars():
		# open I2C Bus 1
		i2c = smbus.SMBus(1)

	# Set Data in EEPROM
	rannum = int(random.random()*100)
	print("Setting EEPROM to 'Hello World X' where X is a random number.")
	print("Random Number is: " + str(rannum))
	at24c32_set_ram(0, ord('H'))
	at24c32_set_ram(1, ord('e'))
	at24c32_set_ram(2, ord('l'))
	at24c32_set_ram(3, ord('l'))
	at24c32_set_ram(4, ord('o'))
	at24c32_set_ram(5, ord(' '))
	at24c32_set_ram(6, ord('W'))
	at24c32_set_ram(7, ord('o'))
	at24c32_set_ram(8, ord('r'))
	at24c32_set_ram(9, ord('l'))
	at24c32_set_ram(10, ord('d'))
	at24c32_set_ram(11, ord(' '))
	at24c32_set_ram(12, rannum)

	# Get Data from EEPROM
	data = []
	print("Reading EEPROM:")
	for i in range(13):
		data.append(at24c32_get_ram(i))

	# Print data as String
	for i in range(12):
		print(str(chr(data[i])), end='')
	print(str(data[12]))
	
# open I2C Bus 1 -> Everytime
i2c = smbus.SMBus(1)
	
# Only run when you are the main program. Not when you're importes as a module:
if __name__ == '__main__':
	main()

Und einmal die DS1307:

#!/usr/bin/python3
import smbus
import datetime
import random

# I2C Address of DS1307 RTC
ds1307addr = 0x68

# Functions for getting and setting according to the DS1307 Datasheet
def ds1307_get_seconds():
	retbyte = i2c.read_byte_data(ds1307addr, 0x00)
	lower4bits = retbyte & 0b00001111
	upper4bits = ((retbyte & 0b01110000) >> 4)
	returnstr = str(upper4bits) + str(lower4bits)
	return int(returnstr)
	
def ds1307_get_minutes():
	retbyte = i2c.read_byte_data(ds1307addr, 0x01)
	lower4bits = retbyte & 0b00001111
	upper4bits = ((retbyte & 0b01110000) >> 4)
	returnstr = str(upper4bits) + str(lower4bits)
	return int(returnstr)	

# Returns in 24 hours mode
def ds1307_get_hours():
	retbyte = i2c.read_byte_data(ds1307addr, 0x02)
	lower4bits = retbyte & 0b00001111
	# Check if 24 Hour Clock
	mode12 = ((retbyte & 0b01000000) >> 6)
	if mode12 == 0:
		# 24-hour-mode
		upper4bits = ((retbyte & 0b00110000) >> 4)
		returnstr = str(upper4bits) + str(lower4bits)
		return int(returnstr)	
	else:
		# 12-hour-mode
		upper4bits = ((retbyte & 0b00010000) >> 4)
		returnstr = str(upper4bits) + str(lower4bits)
		pm_am = ((retbyte & 0b00100000) >> 5)
		if pm_am == 1:
			# pm - we return in 24-hour-mode
			return int(returnstr) + 12
		else:
			# am
			return int(returnstr)

def ds1307_get_day():
	retbyte = i2c.read_byte_data(ds1307addr, 0x03)
	lower3bits = retbyte & 0b00000111
	returnstr = str(lower3bits)
	return int(returnstr)		

def ds1307_get_date():
	retbyte = i2c.read_byte_data(ds1307addr, 0x04)
	lower4bits = retbyte & 0b00001111
	upper4bits = ((retbyte & 0b00110000) >> 4)
	returnstr = str(upper4bits) + str(lower4bits)
	return int(returnstr)		

def ds1307_get_month():
	retbyte = i2c.read_byte_data(ds1307addr, 0x05)
	lower4bits = retbyte & 0b00001111
	upper4bits = ((retbyte & 0b00010000) >> 4)
	returnstr = str(upper4bits) + str(lower4bits)
	return int(returnstr)		

def ds1307_get_year():
	retbyte = i2c.read_byte_data(ds1307addr, 0x06)
	lower4bits = retbyte & 0b00001111
	upper4bits = ((retbyte & 0b11110000) >> 4)
	returnstr = str(upper4bits) + str(lower4bits)
	returnval = int(returnstr)	
	if returnval > 60:
		# year 60 - 99 must be 1900
		return returnval + 1900
	else:
		# year 00 - 60 must be 2000
		return returnval + 2000

# Returns: Clock stopped, 12-hour-mode, outputmode, squarewaveenabled, frequency as a five value tuple
def ds1307_get_control():
	retbyte1 = i2c.read_byte_data(ds1307addr, 0x00)
	retbyte2 = i2c.read_byte_data(ds1307addr, 0x02)
	retbyte3 = i2c.read_byte_data(ds1307addr, 0x07)
	clockstopped = ((retbyte1 & 0b10000000) >> 7)
	mode12 = ((retbyte2 & 0b01000000) >> 6)
	out = ((retbyte3 & 0b10000000) >> 7)
	sqwe = ((retbyte3 & 0b00010000) >> 4)
	rs0 = ((retbyte3 & 0b00000001) >> 0)
	rs1 = ((retbyte3 & 0b00000010) >> 1)
	if rs1 == 0 and rs0 == 0:
		freq = 1
	if rs1 == 0 and rs0 == 1:
		freq = 4096000
	if rs1 == 1 and rs0 == 0:
		freq = 8192000
	if rs1 == 1 and rs0 == 1:
		freq = 32768000
	return clockstopped, mode12, out, sqwe, freq
	
# Valid addr for RAM: 0 - 55
def ds1307_get_ram(addr):
	# RAM starts at Address 0x08
	addr = addr + 0x08	
	return i2c.read_byte_data(ds1307addr, addr)	
	
def ds1307_set_seconds(val):
	if len(str(val)) == 1:
		lower4bits = int(str(val))
		upper4bits = 0
	else:
		lower4bits = int(str(val)[1:])
		upper4bits = int(str(val)[:1]) & 0b00000111
	setstr = "0b" + str("{0:04b}".format(upper4bits)) + str("{0:04b}".format(lower4bits))
	i2c.write_byte_data(ds1307addr, 0x00, int(setstr, 2))

def ds1307_set_minutes(val):
	if len(str(val)) == 1:
		lower4bits = int(str(val))
		upper4bits = 0
	else:
		lower4bits = int(str(val)[1:])
		upper4bits = int(str(val)[:1]) & 0b00000111
	setstr = "0b" + str("{0:04b}".format(upper4bits)) + str("{0:04b}".format(lower4bits))
	i2c.write_byte_data(ds1307addr, 0x01, int(setstr, 2))

# Sets in 24 hours mode
def ds1307_set_hours(val):
	if len(str(val)) == 1:
		lower4bits = int(str(val))
		upper4bits = 0
	else:
		lower4bits = int(str(val)[1:])
		upper4bits = int(str(val)[:1]) & 0b00000011
	setstr = "0b00" + str("{0:02b}".format(upper4bits)) + str("{0:04b}".format(lower4bits))
	i2c.write_byte_data(ds1307addr, 0x02, int(setstr, 2))
	
def ds1307_set_day(val):
	lower3bits = int(str(val)[:1]) & 0b00000111
	setstr = "0b0000" + str("{0:04b}".format(lower3bits))
	i2c.write_byte_data(ds1307addr, 0x03, int(setstr, 2))

def ds1307_set_date(val):
	if len(str(val)) == 1:
		lower4bits = int(str(val))
		upper4bits = 0
	else:
		lower4bits = int(str(val)[1:])
		upper4bits = int(str(val)[:1]) & 0b00000011
	setstr = "0b" + str("{0:04b}".format(upper4bits)) + str("{0:04b}".format(lower4bits))
	i2c.write_byte_data(ds1307addr, 0x04, int(setstr, 2))

def ds1307_set_month(val):
	if len(str(val)) == 1:
		lower4bits = int(str(val))
		upper4bits = 0
	else:
		lower4bits = int(str(val)[1:])
		upper4bits = int(str(val)[:1]) & 0b00000001
	setstr = "0b" + str("{0:04b}".format(upper4bits)) + str("{0:04b}".format(lower4bits))
	i2c.write_byte_data(ds1307addr, 0x05, int(setstr, 2))

def ds1307_set_year(val):
	if len(str(val)) == 1:
		lower4bits = int(str(val))
		upper4bits = 0
	elif len(str(val)) == 2:
		lower4bits = int(str(val)[1:])
		upper4bits = int(str(val)[:1]) & 0b00001111		
	else:
		val = str(val)[2:]
		lower4bits = int(str(val)[1:])
		upper4bits = int(str(val)[:1]) & 0b00001111
	setstr = "0b" + str("{0:04b}".format(upper4bits)) + str("{0:04b}".format(lower4bits))
	i2c.write_byte_data(ds1307addr, 0x06, int(setstr, 2))

def ds1307_set_control(clockstopped, mode12, outputmode, squarewaveenabled, frequency):
	if clockstopped == 1:
		setstr = "0b1" + str("{0:07b}".format(i2c.read_byte_data(ds1307addr, 0x00) & 0b01111111))
		i2c.write_byte_data(ds1307addr, 0x00, int(setstr, 2))
	else:
		setstr = "0b0" + str("{0:07b}".format(i2c.read_byte_data(ds1307addr, 0x00) & 0b01111111))
		i2c.write_byte_data(ds1307addr, 0x00, int(setstr, 2))		
	if mode12 == 1:
		setstr = "0b01" + str("{0:06b}".format(i2c.read_byte_data(ds1307addr, 0x02) & 0b00111111))
		i2c.write_byte_data(ds1307addr, 0x02, int(setstr, 2))	
	else:
		setstr = "0b00" + str("{0:06b}".format(i2c.read_byte_data(ds1307addr, 0x02 & 0b00111111)))
		i2c.write_byte_data(ds1307addr, 0x02, int(setstr, 2))	
	if outputmode == 1:
		setstr = "0b1"
	else:
		setstr = "0b0"
	if squarewaveenabled == 1:
		setstr += "001"
	else:
		setstr += "000"
	if frequency == 32768000:
		setstr += "0011"
	elif frequency == 8192000:
		setstr += "0010"
	elif frequency == 4096000:
		setstr += "0001"
	else:
		setstr += "0000"	
	i2c.write_byte_data(ds1307addr, 0x07, int(setstr, 2))

# Valid addr for RAM: 0 - 55
def ds1307_set_ram(addr, val):
	# RAM starts at Address 0x08
	addr = addr + 0x08
	i2c.write_byte_data(ds1307addr, addr, val)	
	
def main():
	# Set RTC Control
	# -> Set Clock to running
	# -> Set 24-hour-mode
	# -> Output (when SquareWave disabled) 0
	# -> Square Wave enabled
	# -> Frequency 1 Hertz
	ds1307_set_control(0, 0, 0, 1, 1)

	# Set RTC Time
	now = datetime.datetime.now()

	ds1307_set_seconds(int(now.second))
	ds1307_set_minutes(int(now.minute))
	ds1307_set_hours(int(now.hour))
	ds1307_set_day(int(now.strftime("%w")))
	ds1307_set_date(int(now.day))
	ds1307_set_month(int(now.month))
	ds1307_set_year(int(now.year))

	# read RTC
	seconds = ds1307_get_seconds()
	minutes = ds1307_get_minutes()
	hours = ds1307_get_hours()
	day = ds1307_get_day()
	date = ds1307_get_date()
	month = ds1307_get_month()
	year = ds1307_get_year()
	clockstopped, mode12hour, output, squarewavean, freq = ds1307_get_control()

	# Print time
	print("Time is: " + str("{0:02}".format(date)) + "." + str("{0:02}".format(month)) + "." + str("{0:04}".format(year)) + " " + str("{0:02}".format(hours)) + ":" + str("{0:02}".format(minutes)) + ":" + str("{0:02}".format(seconds)))
	print("Day of week: " + str(day))
	print("Clock stopped: " + str(clockstopped))
	print("12-hour-mode: " + str(mode12hour))
	print("Output when SquareWave disabled: " + str(output))
	print("Square-Wave Enabled: " + str(squarewavean))
	print("Frequency of Output: " + str(freq) + " Hz")

	# Set Data in RAM
	rannum = int(random.random()*100)
	print("Setting RAM to 'Hello World X' where X is a random number.")
	print("Random Number is: " + str(rannum))
	ds1307_set_ram(0, ord('H'))
	ds1307_set_ram(1, ord('e'))
	ds1307_set_ram(2, ord('l'))
	ds1307_set_ram(3, ord('l'))
	ds1307_set_ram(4, ord('o'))
	ds1307_set_ram(5, ord(' '))
	ds1307_set_ram(6, ord('W'))
	ds1307_set_ram(7, ord('o'))
	ds1307_set_ram(8, ord('r'))
	ds1307_set_ram(9, ord('l'))
	ds1307_set_ram(10, ord('d'))
	ds1307_set_ram(11, ord(' '))
	ds1307_set_ram(12, rannum)

	# Get Data from RAM
	data = []
	print("Reading RAM:")
	for i in range(13):
		data.append(ds1307_get_ram(i))

	# Print data as String
	for i in range(12):
		print(str(chr(data[i])), end='')
	print(str(data[12]))
	
# open I2C Bus 1 -> Everytime
i2c = smbus.SMBus(1)
	
# Only run when you are the main program. Not when you're importes as a module:
if __name__ == '__main__':
	main()

 Runterladen kann man die Codes natürlich auch, entweder hier oder über meinen Github-Account.

Und warum das ganze, wo es doch genug Anleitungen gibt wie man diese RTC direkt in den Linux Kernel einbaut mit einem fertigen Modul?
Ganz einfach: Wenn mans von Hand macht lernt man deutlich mehr 🙂

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.