sd2snes/mk1-src/timer.c
2010-09-06 23:51:48 +02:00

136 lines
3.8 KiB
C

/* sd2snes - SD card based universal cartridge for the SNES
Copyright (C) 2009-2010 Maximilian Rehkopf <otakon@gmx.net>
This file was adapted from sd2iec, written by Ingo Korb.
Original copyright header follows:
*/
/* sd2iec - SD/MMC to Commodore serial bus interface/controller
Copyright (C) 2007-2009 Ingo Korb <ingo@akana.de>
Inspiration and low-level SD/MMC access based on code from MMC2IEC
by Lars Pontoppidan et al., see sdcard.c|h and config.h.
FAT filesystem access based on code from ChaN and Jim Brain, see ff.c|h.
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; version 2 of the License only.
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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
timer.c: System timer (and button debouncer)
*/
#include "config.h"
#include <avr/interrupt.h>
#include <avr/io.h>
#include "avrcompat.h"
#include "led.h"
#include "timer.h"
volatile tick_t ticks;
// Logical buttons
volatile uint8_t active_keys;
// Physical buttons
uint8_t buttonstate;
tick_t lastbuttonchange;
/* Called by the timer interrupt when the button state has changed */
static void buttons_changed(void) {
/* Check if the previous state was stable for two ticks */
if (time_after(ticks, lastbuttonchange+2)) {
if (active_keys & IGNORE_KEYS) {
active_keys &= ~IGNORE_KEYS;
} else if (!(buttonstate & (BUTTON_PREV|BUTTON_NEXT))) {
/* Both buttons held down */
active_keys |= KEY_HOME;
} else if (!(buttonstate & BUTTON_NEXT) &&
(BUTTON_PIN & BUTTON_NEXT)) {
/* "Next" button released */
active_keys |= KEY_NEXT;
} else if (!(buttonstate & BUTTON_PREV) &&
(BUTTON_PIN & BUTTON_NEXT)) {
active_keys |= KEY_PREV;
}
}
lastbuttonchange = ticks;
buttonstate = BUTTON_PIN & BUTTON_MASK;
}
/* The main timer interrupt */
ISR(TIMER1_COMPA_vect) {
uint8_t tmp = BUTTON_PIN & BUTTON_MASK;
if (tmp != buttonstate) {
buttons_changed();
}
ticks++;
#ifdef SINGLE_LED
if (led_state & LED_ERROR) {
if ((ticks & 15) == 0)
DIRTY_LED_PORT ^= DIRTY_LED_BIT();
} else {
if ((led_state & LED_BUSY) || (led_state & LED_DIRTY)) {
DIRTY_LED_ON();
} else {
DIRTY_LED_OFF();
}
}
#else
if (led_state & LED_ERROR)
if ((ticks & 15) == 0)
DIRTY_LED_PORT ^= DIRTY_LED_BIT();
#endif
/* Sleep button triggers when held down for 2sec */
if (time_after(ticks, lastbuttonchange+2)) {
if (!(buttonstate & BUTTON_NEXT) &&
(buttonstate & BUTTON_PREV) &&
time_after(ticks, lastbuttonchange+2*HZ) &&
!key_pressed(KEY_SLEEP)) {
/* Set ignore flag so the release doesn't trigger KEY_NEXT */
active_keys |= KEY_SLEEP | IGNORE_KEYS;
/* Avoid triggering for the next two seconds */
lastbuttonchange = ticks;
}
}
#if CONFIG_RTC_VARIANT == 1
increment_rtc();
#endif
#ifdef CONFIG_REMOTE_DISPLAY
/* Check if the display wants to be queried */
if (!(SOFTI2C_PIN & _BV(SOFTI2C_BIT_INTRQ))) {
active_keys |= KEY_DISPLAY;
}
#endif
}
void timer_init(void) {
/* Count F_CPU/8 in timer 0 */
TCCR0B = _BV(CS01);
/* Set up a 100Hz interrupt using timer 1 */
OCR1A = 1249;
TCNT1 = 0;
TCCR1A = 0;
TCCR1B = _BV(WGM12) | _BV(CS10) | _BV(CS11);
TIMSK1 |= _BV(OCIE1A);
/* Buttons */
BUTTON_DDR &= (uint8_t)~BUTTON_MASK;
BUTTON_PORT |= BUTTON_MASK;
}