So, I am trying to make a wrist watch and at first I had decided to use a standalone RTC module for it but someone one here suggested that AVR can keep time in low power modes. So, I looked in to it and turns out AVR's Asynchronous timers are optimized to be used with 32.768kHz oscillator. And software RTC can be implemented with power saver sleep mode.
Here's the documentation for it
http://www.atmel.com/images/atmel-1259-real-time-clock-rtc-using-the-asynchronous-timer_ap-note_avr134.pdfBut the example code is written for Atmega128. But ATmega32 has different Timer/Counter that works with external crystal. So, I changed few things and ported it to work with Atmega32.
#include "atmel_start.h"
#include <avr/sleep.h>
static char not_leap(void);
static void init(void);
typedef struct {
unsigned char second;
unsigned char minute;
unsigned char hour;
} time;
time t;
int main(void)
{
/* Configure all eight pins of port B as outputs */
system_init();
/* Initialize registers and configure RTC. */
init();
while (1) {
/* Enter sleep mode.
* (Will wake up from timer overflow interrupt) */
sleep_mode();
/*The MCU should always be awake for a minimum of one
* TOSC cycle, before re-entering sleep mode. Otherwise,
* the interrupt flag will not be cleared, and the MCU
* will stay in sleep mode until it is reset. We solve
* this by doing a dummy write to the TCCR0 register
* and waiting until the ASSR register reports that TC0
* has been updated, before going to sleep*/
/* Dummy write the desired prescaler */
TCCR2 = (1 << CS20) | (1 << CS22);
/* Wait until TC0 is updated */
while (ASSR & ((1 << TCN2UB) | (1 << OCR2UB) | (1 << TCR2UB))) {
}
}
}
static void init(void)
{
/* Wait for external clock crystal to stabilize */
for (uint8_t i = 0; i < 0x40; i++) {
for (int j = 0; j < 0xFFFF; j++) {
/* Do a nop instruction to keep the empty
* loop from being optimized away */
asm volatile("nop");
}
}
/* Make sure all TC0 interrupts are disabled */
TIMSK &= ~((1 << TOIE2) | (1 << OCIE2));
/* set Timer/counter0 to be asynchronous from the CPU clock.
* This will clock TC0 from the external 32,768 kHz crystal. */
ASSR |= (1 << AS2);
/* Reset timer */
TCNT2 = 0;
/* Prescale the timer to be clock source/128 to make */
/* TC0 overflow precisely once every second. */
TCCR2 = (1 << CS20) | (1 << CS22);
/* Wait until TC0 is updated */
while (ASSR & ((1 << TCN2UB) | (1 << OCR2UB) | (1 << TCR2UB))) {
}
/* Enable Timer/Counter0 Overflow Interrupts */
TIMSK |= (1 << TOIE2);
/* Set the Global Interrupt Enable Bit */
sei();
/* Setting the sleep mode to be used to power save mode. */
set_sleep_mode(SLEEP_MODE_PWR_SAVE);
/* Enabling sleep mode */
sleep_enable();
}
/* keep track of time, date, month, and year */
ISR(TIMER2_OVF_vect)
{
if (++t.second == 60) {
t.second = 0;
if (++t.minute == 60) {
t.minute = 0;
if (++t.hour == 24) {
t.hour = 0;
}
What this code does is, wakes up microcontroller when the counter overflows and then increments the seconds variable in timer ISR and then goes back to sleep.
This can keep track of Hours , Minutes and Seconds using small amount of power. Now I need a single button interface so when the button is pressed once the watch displays time and when the button is pressed twice, you can set the minutes and hour variables to set time. At first I thought, I would use a hardware interrupt to wake the microcontroller if it is in sleep mode and then display the time. But then I don't know how I would implement setting time functionality. Can you use Interrupt pin in ISR as I/O pin? So, when I press interrupt, MC wakes up goes into hardware ISR and then looks for button press on the same pin, if the button is pressed once more it ecexutes the 'time setting code'.
How do I do it?