So I am trying to build a small watch project, I cant really go so much into detail as I am just prototyping. I am not exactly "great" in C, but know my way around the basics.
However, timer interrupts or fuse settings continue to boggle me.
At this point I have gotten counting and LEDs to light up on my board, good signs. Once that went good, I moved onto timing the danged thing. I am basing my code heavily off of sparkfuns "bigTime" watch code (on their website if you need it). Basically the clock is running off of a 32.768kHz crystal, as sort of an RTC. This is timed by generating interrupts on the clock pins. Unfortunately, my code doesnt seem to work. I have LEDs that light up, and my button interrupts work, but I CANNOT get the external crystal to trigger the interrupt.
Here is my code:
9/8/12
#include <avr/sleep.h> //Needed for sleep_mode
#include <avr/power.h> //Needed for powering down perihperals such as the ADC/TWI and Timers
#define TRUE 1
#define FALSE 0
//Set this variable to change how long the time is shown on the watch face. In milliseconds so 1677 = 1.677 seconds
int show_time_length = 2000;
int show_the_time = FALSE;
//You can set always_on to true and the display will stay on all the time
//This will drain the battery in about 15 hours
int always_on = FALSE;
long seconds = 54;
int minutes = 24;
int hours = 3;
#define RED 1
int systemColor = RED;
int display_brightness = 15000; //A larger number makes the display more dim. This is set correctly below.
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//Pin definitions
int hour8 = A4; //Hour 8
int hour4 = A2; //Hour 4
int hour2 = 12; //Hour 2
int hour1 = 10; //Hour 1
int minute32 = A0; //Minute 32
int minute16 = A3; //Minute 16
int minute8 = A1; //Minute 8
int minute4 = 13; //Minute 4
int minute2 = 11; //Minute 2
int minute1 = 9; //Minute 1
int theButton = 2;
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//The very important 32.686kHz interrupt handler
SIGNAL(TIMER2_OVF_vect){
seconds += 8; //We sleep for 8 seconds instead of 1 to save more power
//seconds++; //Use this if we are waking up every second
if(seconds > 59){
minutes++;
seconds = seconds - 59;
}
if(minutes > 59){
hours++;
minutes = minutes - 59;
}
/*
//Update the minutes and hours variables
minutes += seconds / 60; //Example: seconds = 2317, minutes = 58 + 38 = 96
seconds %= 60; //seconds = 37
hours += minutes / 60; //12 + (96 / 60) = 13
minutes %= 60; //minutes = 36
*/
if(hours > 12) hours -= 12;
}
//The interrupt occurs when you push the button
SIGNAL(INT0_vect){
//When you hit the button, we will need to display the time
//if(show_the_time == FALSE)
show_the_time = TRUE;
}
void setup() {
//To reduce power, setup all pins as inputs with no pullups
for(int x = 1 ; x < 18 ; x++){
pinMode(x, INPUT);
digitalWrite(x, LOW);
}
pinMode(theButton, INPUT); //This is the main button, tied to INT0
digitalWrite(theButton, HIGH); //Enable internal pull up on button
//These pins are used to control the display
pinMode(hour8, OUTPUT);
.....
pinMode(minute1, OUTPUT);
//Power down various bits of hardware to lower power usage
set_sleep_mode(SLEEP_MODE_PWR_SAVE);
sleep_enable();
//Shut off ADC, TWI, SPI, Timer0, Timer1
ADCSRA &= ~(1<<ADEN); //Disable ADC
ACSR = (1<<ACD); //Disable the analog comparator
DIDR0 = 0x3F; //Disable digital input buffers on all ADC0-ADC5 pins
DIDR1 = (1<<AIN1D)|(1<<AIN0D); //Disable digital input buffer on AIN1/0
power_twi_disable();
power_spi_disable();
power_usart0_disable();
//power_timer0_disable(); //Needed for delay_ms
power_timer1_disable();
//power_timer2_disable(); //Needed for asynchronous 32kHz operation
//Setup TIMER2
TCCR2A = 0x00;
TCCR2B = (1<<CS22)|(1<<CS20); //Set CLK/128 or overflow interrupt every 1s
// TCCR2B = (1<<CS22)|(1<<CS21)|(1<<CS20); //Set CLK/1024 or overflow interrupt every 8s
ASSR = (1<<AS2); //Enable asynchronous operation
TIMSK2 = (1<<TOIE2); //Enable the timer 2 interrupt
//Setup external INT0 interrupt
EICRA = (1<<ISC01); //Interrupt on falling edge
EIMSK = (1<<INT0); //Enable INT0 interrupt
//System clock futzing
//CLKPR = (1<<CLKPCE); //Enable clock writing
//CLKPR = (1<<CLKPS3); //Divid the system clock by 256
//Display brightness changes based on color
if(systemColor == RED) {
display_brightness = 4500; //The higher the number, the lower the brightness
}
else {
display_brightness = 1;
}
showTime(); //Show the current time for a few seconds
sei(); //Enable global interrupts
}
void loop() {
if(always_on == FALSE)
sleep_mode(); //Stop everything and go to sleep. Wake up if the Timer2 buffer overflows or if you hit the button
if(show_the_time == TRUE || always_on == TRUE) {
showTime(); //Show the current time for a few seconds
//If you are STILL holding the button, then you must want to adjust the time
if(digitalRead(theButton) == LOW){ setTime();
}
show_the_time = FALSE; //Reset the button variable
}
}
void showTime() {
//Now show the time for a certain length of time
long startTime = millis();
while( (millis() - startTime) < show_time_length) {
lightLeds();
if(display_brightness > 0){ //PWM only if brightness is not full
delayMicroseconds(display_brightness);
}
}
}
//Holding the button down will increase the time (accelerates)
void setTime(void) {
//Now show the time for a certain length of time
long startTime = millis();
while( (millis() - startTime) < show_time_length) {
lightLeds();
if(display_brightness > 0){ //PWM only if brightness is not full
delayMicroseconds(display_brightness);
}
}
int idleMiliseconds = 0;
//This is the timeout counter. Once we get to ~2 seconds of inactivity, the watch
//will exit the setTime function and return to normal operation
int buttonHold = 0;
//This counts the number of times you are holding the button down consecutively
//Once we notice you're really holding the button down a lot, we will speed up the minute counter
while(idleMiliseconds < 2000) {
cli(); //We don't want the interrupt changing values at the same time we are!
if(seconds > 59){
minutes++;
seconds = seconds - 59;
}
if(minutes > 59){
hours++;
minutes = minutes - 59;
}
if(hours > 12) hours -= 12;
sei(); //Resume interrupts
for(int x = 0 ; x < 20 ; x++) {
lightLeds(); //Each call takes about 8ms, display the colon for about 100ms
if(display_brightness > 0) {delayMicroseconds(display_brightness); //Wait before we paint the display again
}
}
//If you're still hitting the button, then increase the time and reset the idleMili timeout variable
if(digitalRead(theButton) == LOW) {
idleMiliseconds = 0;
buttonHold++;
if(buttonHold < 10)
minutes++; //Advance the minutes
else {
//Advance the minutes faster because you're holding the button for 10 seconds
//Start advancing on the tens digit. Floor the single minute digit.
minutes /= 10; //minutes = 46 / 10 = 4
minutes *= 10; //minutes = 4 * 10 = 40
minutes += 10; //minutes = 40 + 10 = 50
}
}
else
buttonHold = 0;
idleMiliseconds += 200;
}
}
void lightLeds() {
//Turns correct outputs on
switch(hours) {
...
}
switch(minutes){
...
}
if(display_brightness > 0){ //Only PWM if brightness is not full
delayMicroseconds(display_brightness);
digitalWrite(hour8, LOW); //write pins low to get "dimming" effect
....
}
}
/*
ARTHUR: What?
TIM: There he is!
ARTHUR: Where?
TIM: There!
ARTHUR: What, behind the rabbit?
TIM: It is the rabbit!
(\_/)
(o.O) ----Grrrrrrrrrrr.
(>< )
/_|_\
*/
Sorry its pretty long, but most of the stuff is in the top part. The rest is just variables
Things i have tried: different crystal, random bits of the "same" code off of google searches, etc. Not much, but Ive been searching for a few days on this and come to nothing. Grr.
I am uploading using arduino 1.01 and an AVRISP MKII !CLONE! but that seems to work perfectly. Even in AVR studio! shameless plug, cost me $12 for a kit to build it and it WORKS! happy dance
THANKS so much for any help you can give. At this point its kind of exceeding my limits...
Stupid forum limits... had to take out half my code