📄 timer.c
字号:
//++
//timer.c - basic timer and time keeping functions
//
// Copyright (C) 2005 by Spare Time Gizmos. All rights reserved.
//
// This file is part of the Spare Time Gizmos' MP3 Player firmware.
//
// This firmware 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 2 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, write to the Free Software Foundation, Inc., 59 Temple
// Place, Suite 330, Boston, MA 02111-1307 USA.
//
//DESCRIPTION:
// This routine uses the 8051's timer 2 to generate a periodic 20ms (that's
// 50Hz!) interrupt, and from that it derives several basic time keeping and
// delay functions. Not all that exciting, really...
//
// One thing which you might not expect, however, is that the timer interrupt
// also drives the user interface. All push buttons and switches are polled by
// the timer interrupt code - this gives us a convenient way to debounce mech-
// anical switches (the 20ms timer tick interval automatically imposes a debounce
// interval), and it also makes it easy to differentiate short presses ("clicks")
// from a button that's held down for a while.
//
//REVISION HISTORY:
// dd-mmm-yy who description
// 30-May-05 RLA New file.
// 12-Oct-05 RLA Change TimerInterrupt() to PUBLIC for SDCC.
//--
// Include files...
#include <stdio.h> // needed so DBGOUT(()) can find printf!
#include "standard.h" // standard types - BYTE, WORD, BOOL, etc
#include "reg66x.h" // declarations for Philips 89C66x processors
#include "player.h" // project wide (hardware configuration) declarations
#include "timer.h" // declarations for this module
// Local variables for this module...
PRIVATE volatile BYTE m_bOnceASecond;
PRIVATE volatile BYTE m_bDelayCounter;
// This module implements a very simple clock in the form of a word that's
// incremented once a second. It can count up to a maximum of 65536 seconds,
// or a little over 1000 minutes and is used to display the running time of
// the current MP3 file. The g_fTimerRunning can be used to pause the timer
// - when g_fTimerRunning is FALSE the clock holds its current value.
//
// Interacting with this clock/timer is so simple that there are no sub-
// routines for manipulating it; instead there are several macros in timer.h
// that look like subroutine calls.
PUBLIC volatile WORD g_wSecondsTimer;
PUBLIC bit g_fTimerRunning, g_fTimerChanged;
PUBLIC void TimerInterrupt (void) interrupt TIMER2_VECTOR
{
// The m_nDelayCounter is used by the WaitMS() routine to implement a
// timed delay. Anytime it is non-zero, we count it down...
if (m_bDelayCounter != 0) --m_bDelayCounter;
// We decrement the once-a-second counter on every interrupt, and when
// it reaches zero we can perform (what else?) the once a second code...
if (--m_bOnceASecond == 0) {
// If the seconds clock/timer is running then increment it now. Notice
// that we're careful never to let this timer overflow and wrap around -
// when it reaches the maximum count it simply stops there.
if (g_fTimerRunning)
if (g_wSecondsTimer != 0xFFFF) ++g_wSecondsTimer, g_fTimerChanged=TRUE;
m_bOnceASecond = HERTZ;
}
// The hardware doesn't clear the timer flag automatically, even in auto
// reload mode, so if we don't do it we'll never come here again!
TF2 = 0;
}
//++
// This routine implements a programmable delay which is independent of the
// CPU speed (at least it will be if we've programmed the timer correctly!).
// The argument specifies the delay in milliseconds, however the resolution
// of the delay is the same as our timebase - HERTZ. Thus a request for a
// 1 millisecond delay will actually wait for 20ms, assuming HERTZ == 50.
// A request for 105ms would wait for 120, and so on. Note that the actual
// delay count we use is a BYTE value, so the maximum possible delay is
// 255*(1/HERTZ) or about 5.1 seconds at 50 HERTZ.
//--
PUBLIC void DelayMS (WORD wDelay)
{
ASSERT( (wDelay <= (255*(1000/HERTZ))), ("DelayMS: argument too large %u\n", wDelay) );
// Since m_bDelayCounter is a single byte value, there's really no need
// to turn off interrupts here. No matter how many instructions it may
// take to evaluate this expression, the final step of assigning a value
// to m_bDelayCounter is always atomic.
m_bDelayCounter = (BYTE) ((wDelay+HERTZ-1)/HERTZ);
// Now just wait, with interrupts on, until the timer ISR counts m_bDelayCounter
// down to zero...
while (m_bDelayCounter != 0) ;
}
//++
// This routine will configure timer 2 for periodic 20ms interrupts. Timer
// two is especially convenient for this usage because it has a 16 bit auto-
// reload capability - such a long interval is beyond the range of timer 0 or 1
// with an automatic hardware reload. You can reload timer 0 or 1 with software
// in the ISR, of course, but this is susceptible to timing deviations caused
// by interrupt latency. Since timer 2 is reloaded automatically by hardware,
// the interrupt can be delayed by an aribtrary time (so long as it occurs before
// the next tick!) without affecting the accuracy.
//
// This routine enables timer 2 interrupts, but it does NOT set the global
// interrupt enable. The clock will start running as soon as EA is set.
//--
PUBLIC void InitializeTimer (void)
{
// Initialize module variables first...
m_bOnceASecond = HERTZ; m_bDelayCounter = 0;
g_wSecondsTimer = 0; g_fTimerRunning = FALSE; g_fTimerChanged = FALSE;
// Initialize RCAP2 with the timer reload value (this is calculated from the
// CPU oscillator frequency and the constant HERTZ) and the timer control reg-
// isters. Note that the "classic" 8051 timer counts once for every machine
// cycle, which is 1/12 the CPU clock frequency. The Philips high speed parts
// are the similar, however in this case a machine cycle is 1/6th the clock.
RCAP2H = HIBYTE(-(CPU_CLOCK/CPU_CLOCKS_PER_CYCLE/HERTZ));
RCAP2L = LOBYTE(-(CPU_CLOCK/CPU_CLOCKS_PER_CYCLE/HERTZ));
// Every standard 8051 puts the interrupt enable bit for timer 2 in IE.5,
// however for some reason Philips decided to use this bit for ES1 (second
// serial port interrupt enable). Philips parts have a second interrupt
// enable register, IE1, which holds the timer 2 enable in IE1.0. Fortunately
// we don't have to worry about this since the REG66x.H header takes care of
// defining ET2 correctly, but it's something to keep in mind if you port this
// module to another processor.
T2CON = 0; T2MOD = 0; TR2 = 1; ET2 = 1;
DBGOUT(("Timer initialized at %buHz ...\n", (BYTE) HERTZ));
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -