From: Doug Szumski <d.s.szumski@gmail.com>
Date: Thu, 4 Feb 2016 19:52:37 +0000 (+0000)
Subject: examples: take the DS1338 virtual real time clock for a test drive
X-Git-Tag: v1.4~25^2~1
X-Git-Url: https://git.htl-mechatronik.at/public/?a=commitdiff_plain;h=60d611bf78d10510c33cde10b139bba5c7a1103c;p=sx%2Fsimavr.git

examples: take the DS1338 virtual real time clock for a test drive

This demo consists of a DS1338 RTC connected via the TWI bus of an
atmega32. The square wave output of the DS1338 is enabled, with the
tick-rate set to 1HZ. This is then fed to pin D3 on the atmega32
which is configured to generate an interrupt on a rising edge. When
the interrupt fires the time is read from the DS1338.
---

diff --git a/examples/board_ds1338/Makefile b/examples/board_ds1338/Makefile
new file mode 100644
index 0000000..275eabc
--- /dev/null
+++ b/examples/board_ds1338/Makefile
@@ -0,0 +1,50 @@
+#	Copyright 2014 Doug Szumski <d.s.szumski@gmail.com>
+#	Copyright 2008-2011 Michel Pollet <buserror@gmail.com>
+#
+#	This file is part of simavr.
+#
+#	simavr is free software: you can redistribute it and/or modify
+#	it under the terms of the GNU General Public License as published by
+#	the Free Software Foundation, either version 3 of the License, or
+#	(at your option) any later version.
+#
+#	simavr is distributed in the hope that it will be useful,
+#	but WITHOUT ANY WARRANTY; without even the implied warranty of
+#	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#	GNU General Public License for more details.
+#
+#	You should have received a copy of the GNU General Public License
+#	along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+
+target=	ds1338demo
+firm_src = ${wildcard at*${board}.c}
+firmware = ${firm_src:.c=.axf}
+simavr = ../../
+
+IPATH = .
+IPATH += ../parts
+IPATH += ${simavr}/include
+IPATH += ${simavr}/simavr/sim
+
+VPATH = .
+VPATH += ../parts
+
+LDFLAGS += -lpthread
+
+all: obj atmega32_ds1338.axf ${target}
+
+atmega32_ds1338.axf: atmega32_ds1338.c ds1338.c twimaster.c
+
+include ${simavr}/Makefile.common
+
+board = ${OBJ}/${target}.elf
+
+${board} : ${OBJ}/ac_input.o
+${board} : ${OBJ}/ds1338_virt.o
+${board} : ${OBJ}/${target}.o
+
+${target}: ${board}
+	@echo $@ done
+
+clean: clean-${OBJ}
+	rm -rf *.hex *.a *.axf ${target} *.vcd .*.swo .*.swp .*.swm .*.swn
diff --git a/examples/board_ds1338/atmega32_ds1338.c b/examples/board_ds1338/atmega32_ds1338.c
new file mode 100644
index 0000000..034bcec
--- /dev/null
+++ b/examples/board_ds1338/atmega32_ds1338.c
@@ -0,0 +1,93 @@
+/*
+ atmega32_ds1338.c
+
+ Copyright 2014 Doug Szumski <d.s.szumski@gmail.com>
+
+ This file is part of simavr.
+
+ simavr is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ simavr is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <avr/io.h>
+#include <avr/sleep.h>
+#include <avr/interrupt.h>
+#include <util/delay.h>
+
+#include <stdio.h>
+
+#undef F_CPU
+#define F_CPU 7380000
+
+#include "avr_mcu_section.h"
+AVR_MCU(F_CPU, "atmega32");
+
+#include "ds1338.h"
+#include "i2cmaster.h"
+
+volatile uint8_t update_flag;
+
+/*
+ * This demo consists of a DS1338 RTC connected via the TWI bus
+ * of an atmega32. The square wave output of the DS1338 is
+ * enabled, with the tick-rate set to 1HZ. This is then fed
+ * to pin D3 on the atmega32 which is configured to generate
+ * an interrupt on a rising edge. When the interrupt fires the
+ * time is read from the DS1338.
+ */
+
+int
+main()
+{
+	i2c_init();
+
+	/*
+	 * Demonstrate the virtual part functionality.
+	 */
+	ds1338_init();
+	ds1338_time_t time = {
+		.date = 31,
+		.day = 6,
+		.hours = 23,
+		.minutes = 59,
+		.month = 12,
+		.seconds = 56,
+		.year = 14,
+	};
+	ds1338_set_time(&time);
+
+	// Setup pin change interrupt for the square wave output
+	cli();
+	DDRD &= ~(1 << PD3);
+	// Fire INT1 on the rising edge
+	MCUCR |= (1 << ISC11) | (1 << ISC10);
+	GICR |= (1 << INT1);
+	sei();
+
+	while(time.seconds != 2)
+	{
+		if (update_flag) {
+			ds1338_get_time(&time);
+			update_flag = 0;
+		}
+	}
+
+	cli();
+	sleep_mode();
+
+}
+
+ISR (INT1_vect)
+{
+	update_flag = 1;
+}
diff --git a/examples/board_ds1338/ds1338.c b/examples/board_ds1338/ds1338.c
new file mode 100644
index 0000000..76d49a8
--- /dev/null
+++ b/examples/board_ds1338/ds1338.c
@@ -0,0 +1,114 @@
+/*
+ ds1338.c
+
+ DS1338 / DS1307 I2C clock module driver
+
+ Copyright 2012, 2014 Doug Szumski <d.s.szumski@gmail.com>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+#include <stdint.h>
+
+#include "ds1338.h"
+#include "i2cmaster.h"
+
+static void
+ds1338_write(const uint8_t reg, const uint8_t data)
+{
+	i2c_start(DS1338_TWI_ADR + I2C_WRITE);
+	i2c_write(reg);
+	i2c_write(data);
+	i2c_stop();
+}
+
+static uint8_t
+ds1338_read(const uint8_t reg)
+{
+	i2c_start(DS1338_TWI_ADR + I2C_WRITE);
+	i2c_write(reg);
+	i2c_rep_start(DS1338_TWI_ADR + I2C_READ);
+	uint8_t data = i2c_readNak();
+	i2c_stop();
+
+	return data;
+}
+
+void
+ds1338_init(void)
+{
+	ds1338_write(DS1338_CONTROL, DS1338_CONTROL_SETTING);
+	// Set the CS bit to zero to enable clock
+	uint8_t data = ds1338_read(DS1338_SECONDS);
+	data &= ~(1 << DS1338_CH);
+	ds1338_write(DS1338_SECONDS, data);
+}
+
+void
+ds1338_get_time(ds1338_time_t * const time)
+{
+	uint8_t data = ds1338_read(DS1338_SECONDS);
+	time->seconds = 10 * ((data & 0b01110000) >> 4) + (data & 0x0F);
+
+	data = ds1338_read(DS1338_MINUTES);
+	time->minutes = UNPACK_BCD(data);
+
+	data = ds1338_read(DS1338_HOURS);
+	if (data & (1 << DS1338_12_24_HR))
+	{
+		// 12 hour mode
+		time->hours = 10 * ((data & 0b00010000) >> 4) + (data & 0x0F);
+		time->is_pm = data & (1 << DS1338_AM_PM);
+	} else {
+		// 24 hour mode
+		time->hours = 10 * ((data & 0b00110000) >> 4) + (data & 0x0F);
+	}
+
+	data = ds1338_read(DS1338_DAY);
+	// Shift day so that it's 0-6, not 1-7
+	time->day = data - 1;
+
+	data = ds1338_read(DS1338_DATE);
+	time->date = UNPACK_BCD(data);
+
+	data = ds1338_read(DS1338_MONTH);
+	time->month = UNPACK_BCD(data);
+
+	data = ds1338_read(DS1338_YEAR);
+	time->year = UNPACK_BCD(data);
+}
+
+void
+ds1338_set_time(const ds1338_time_t * const time)
+{
+	// Should always write zero to the CS bit to enable the clock
+	ds1338_write(DS1338_SECONDS, TO_BCD(time->seconds));
+	ds1338_write(DS1338_MINUTES, TO_BCD(time->minutes));
+
+	uint8_t hour_reg = ds1338_read(DS1338_HOURS);
+	// Wipe everything apart from the 12/24 hour bit
+	hour_reg &= ~(0b00111111);
+	if ((hour_reg & (1 << DS1338_12_24_HR)) && (time->is_pm)) {
+		// 12 hour mode and it's PM
+		hour_reg |= (1 << DS1338_AM_PM);
+	}
+	hour_reg |= TO_BCD(time->hours);
+	ds1338_write(DS1338_HOURS, hour_reg);
+
+	// Shift back day from 0-6 to 1-7.
+	ds1338_write(DS1338_DAY, time->day + 1);
+	ds1338_write(DS1338_DATE, TO_BCD(time->date));
+	ds1338_write(DS1338_MONTH, TO_BCD(time->month));
+	ds1338_write(DS1338_YEAR, TO_BCD(time->year));
+}
diff --git a/examples/board_ds1338/ds1338.h b/examples/board_ds1338/ds1338.h
new file mode 100644
index 0000000..3698ed1
--- /dev/null
+++ b/examples/board_ds1338/ds1338.h
@@ -0,0 +1,105 @@
+/*
+ ds1338.h
+
+ DS1338 / DS1307 I2C clock module driver
+
+ Copyright 2012, 2014 Doug Szumski <d.s.szumski@gmail.com>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+/*
+ * A simple driver to interface with the DS1338. Should work with
+ * the pin compatible DS1307 too.
+ *
+ * TODO:
+ *
+ * > Read/write registers sequentially to/from an array.
+ * > Add a function to check the 'oscillator had a problem flag'
+ * > Add a function to configure the square wave output
+ * > Add a function to allow writing to additional NVRAM
+ * > Use bit fields to save RAM when storing time
+ */
+
+#ifndef DS1338_H
+#define DS1338_H
+
+/*
+ * Internal registers. Time is in BCD.
+ * See p10 of the DS1388 datasheet.
+ */
+#define DS1338_TWI_ADR		0xD0
+#define DS1338_SECONDS		0x00
+#define DS1338_MINUTES		0x01
+#define DS1338_HOURS		0x02
+#define DS1338_DAY		0x03
+#define DS1338_DATE		0x04
+#define DS1338_MONTH		0x05
+#define DS1338_YEAR		0x06
+#define DS1338_CONTROL		0x07
+
+/*
+ * Seconds register flag - oscillator is enabled when
+ * this is set to zero. Undefined on startup.
+ */
+#define DS1338_CH		7
+
+/*
+ * 12/24 hour select bit. When high clock is in 12 hour
+ * mode and the AM/PM bit is operational. When low the
+ * AM/PM bit becomes part of the tens counter for the
+ * 24 hour clock.
+ */
+#define DS1338_12_24_HR		6
+
+/*
+ * AM/PM flag for 12 hour mode. PM is high.
+ */
+#define DS1338_AM_PM		5
+
+// Control register settings: 1Hz square wave out
+#define DS1338_CONTROL_SETTING 0b10010000
+// 4kHz
+//#define DS1338_CONTROL_SETTING 0b10010001
+// 8 kHz
+//#define DS1338_CONTROL_SETTING 0b10010010
+// 32kHz
+//#define DS1338_CONTROL_SETTING 0b10010011
+
+// Generic BCD conversion. Don't use on seconds or hours.
+#define UNPACK_BCD(x) (((x) & 0x0F) + ((x) >> 4) * 10)
+#define TO_BCD(x) ((((x) / 10) << 4) + (x) % 10)
+
+typedef struct ds1338_time_t
+{
+	uint8_t seconds;
+	uint8_t minutes;
+	uint8_t hours;
+	uint8_t is_pm;
+	uint8_t day;		// Runs from 0-6
+	uint8_t date;
+	uint8_t month;
+	uint8_t year;
+} ds1338_time_t;
+
+void
+ds1338_init (void);
+
+void
+ds1338_get_time (ds1338_time_t * const time);
+
+void
+ds1338_set_time (const ds1338_time_t * const time);
+
+#endif //DS1338_H
diff --git a/examples/board_ds1338/ds1338demo.c b/examples/board_ds1338/ds1338demo.c
new file mode 100644
index 0000000..565d02f
--- /dev/null
+++ b/examples/board_ds1338/ds1338demo.c
@@ -0,0 +1,83 @@
+/*
+ ds1338_demo.c
+
+ Copyright 2014 Doug Szumski <d.s.szumski@gmail.com>
+ Copyright 2011 Michel Pollet <buserror@gmail.com>
+
+ This file is part of simavr.
+
+ simavr is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ simavr is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <libgen.h>
+
+#include "sim_avr.h"
+#include "avr_twi.h"
+#include "sim_elf.h"
+#include "sim_gdb.h"
+#include "ds1338_virt.h"
+
+avr_t * avr = NULL;
+ds1338_virt_t ds1338_virt;
+
+int main(int argc, char *argv[])
+{
+	elf_firmware_t f;
+	const char * fname =  "atmega32_ds1338.axf";
+
+	printf("Firmware pathname is %s\n", fname);
+	elf_read_firmware(fname, &f);
+
+	printf("firmware %s f=%d mmcu=%s\n", fname, (int)f.frequency, f.mmcu);
+
+	avr = avr_make_mcu_by_name(f.mmcu);
+	if (!avr) {
+		fprintf(stderr, "%s: AVR '%s' not known\n", argv[0], f.mmcu);
+		exit(1);
+	}
+	avr_init(avr);
+	avr_load_firmware(avr, &f);
+
+	// Initialise our 'peripheral'
+	ds1338_virt_init(avr, &ds1338_virt);
+
+	// Hook up the TWI bus
+	ds1338_virt_attach_twi(&ds1338_virt, AVR_IOCTL_TWI_GETIRQ(0));
+
+	// Connect the square wave output
+	ds1338_pin_t wiring = {
+		.port = 'D',
+		.pin = 3
+	};
+	ds1338_virt_attach_square_wave_output (&ds1338_virt, &wiring);
+
+	// Even if not setup at startup, activate gdb if crashing
+	avr->gdb_port = 1234;
+	if (0) {
+		avr->state = cpu_Stopped;
+		avr_gdb_init(avr);
+	}
+
+	printf( "\nDS1338 demo launching:\n");
+
+	// Enable debug info
+	// TODO: Convert to logger?
+	ds1338_virt.verbose = 1;
+
+	int state = cpu_Running;
+	while ((state != cpu_Done) && (state != cpu_Crashed))
+		state = avr_run(avr);
+}