From 60d611bf78d10510c33cde10b139bba5c7a1103c Mon Sep 17 00:00:00 2001 From: Doug Szumski Date: Thu, 4 Feb 2016 19:52:37 +0000 Subject: [PATCH] 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. --- examples/board_ds1338/Makefile | 50 +++++++++++ examples/board_ds1338/atmega32_ds1338.c | 93 +++++++++++++++++++ examples/board_ds1338/ds1338.c | 114 ++++++++++++++++++++++++ examples/board_ds1338/ds1338.h | 105 ++++++++++++++++++++++ examples/board_ds1338/ds1338demo.c | 83 +++++++++++++++++ 5 files changed, 445 insertions(+) create mode 100644 examples/board_ds1338/Makefile create mode 100644 examples/board_ds1338/atmega32_ds1338.c create mode 100644 examples/board_ds1338/ds1338.c create mode 100644 examples/board_ds1338/ds1338.h create mode 100644 examples/board_ds1338/ds1338demo.c 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 +# Copyright 2008-2011 Michel Pollet +# +# 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 . + +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 + + 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 . + */ + +#include +#include +#include +#include + +#include + +#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 + + 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 + */ + +#include + +#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 + + 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 + */ + +/* + * 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 + Copyright 2011 Michel Pollet + + 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 . + */ + +#include +#include +#include + +#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); +} -- 2.39.5