Commit 60d611bf78d10510c33cde10b139bba5c7a1103c
authorDoug Szumski <d.s.szumski@gmail.com>
Thu, 4 Feb 2016 19:52:37 +0000 (19:52 +0000)
committerDoug Szumski <d.s.szumski@gmail.com>
Thu, 4 Feb 2016 22:16:25 +0000 (22:16 +0000)
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.

5 files changed:
examples/board_ds1338/Makefile [new file with mode: 0644]
examples/board_ds1338/atmega32_ds1338.c [new file with mode: 0644]
examples/board_ds1338/ds1338.c [new file with mode: 0644]
examples/board_ds1338/ds1338.h [new file with mode: 0644]
examples/board_ds1338/ds1338demo.c [new file with mode: 0644]

diff --git a/examples/board_ds1338/Makefile b/examples/board_ds1338/Makefile
new file mode 100644 (file)
index 0000000..275eabc
--- /dev/null
@@ -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 (file)
index 0000000..034bcec
--- /dev/null
@@ -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 (file)
index 0000000..76d49a8
--- /dev/null
@@ -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 (file)
index 0000000..3698ed1
--- /dev/null
@@ -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 (file)
index 0000000..565d02f
--- /dev/null
@@ -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);
+}