// File Name : timer.c
/*! \file timer.c \brief System Timer function library. */
//*****************************************************************************
//
// File Name : 'timer.c'
// Title : System Timer function library
// Author : Pascal Stang - Copyright (C) 2000-2002
// Created : 11/22/2000
// Revised : 5/6/2002
// Version : 1.1
// Target MCU : Atmel AVR Series
// Editor Tabs : 4
//
// This code is distributed under the GNU Public License
// which can be found at http://www.gnu.org/licenses/gpl.txt
//
//*****************************************************************************
#ifndef WIN32
#include
#include
#include
#include
#endif
#include "global.h"
#include "timer.h"
#include "lcd.h"
extern RtcTimeType gRtcTime;
extern PumpInfoType gPumpInfo;
extern RtcTimeType gAlarmTime;
extern SysStateType gSysState;
extern void timer0OutForButtonClick(void);
extern void Timer1CmpA_Isr(void);
extern u08 NeedSpeakerWork(void);
// Program ROM constants
// the prescale division values stored in 2^n format
// STOP, CLK, CLK/8, CLK/64, CLK/256, CLK/1024
short __attribute__ ((progmem)) TimerPrescaleFactor[] = {0,0,3,6,8,10};
// Program ROM constants
static char __attribute__ ((progmem)) MonthDayTable[] = {31,28,31,30,31,30,31,31,30,31,30,31};
// Note every 4 years, Feb has 29 days, as in 2004, 2008, 2012, ...
// Global variables
// time registers
volatile unsigned long Timer0PauseReg;
volatile unsigned long Timer0Reg0;
volatile unsigned long Timer0Reg1;
volatile unsigned long Timer2Reg0;
volatile unsigned long Timer2Reg1;
typedef void (*voidFuncPtr)(void);
volatile static voidFuncPtr TimerIntFunc[TIMER_NUM_INTERRUPTS];
// delay for a minimum of microseconds
// the time resolution is dependent on the time the loop takes
// e.g. with 4Mhz and 5 cycles per loop, the resolution is 1.25 us
void delay(unsigned short us)
{
unsigned short delay_loops;
register unsigned short i;
delay_loops = (us+3)/5*CYCLES_PER_US; // +3 for rounding up (dirty)
// one loop takes 5 cpu cycles
for (i=0; i < delay_loops; i++) {};
}
void timerInit(void)
{
u08 intNum;
// detach all user functions from interrupts
for(intNum=0; intNum number of milliseconds
// calculate delay for [pause_ms] milliseconds
u16 prescaleDiv = 1<<(PRG_RDB(TimerPrescaleFactor+inp(TCCR0)));
u32 pause = (pause_ms*(F_CPU/(prescaleDiv*256)))/1000;
Timer0PauseReg = 0;
while(Timer0PauseReg < pause);
}
void timer0ClearOverflowCount(void)
{
// clear the timer overflow counter registers
Timer0Reg0 = 0; // initialize time registers
Timer0Reg1 = 0; // initialize time registers
}
long timer0GetOverflowCount(void)
{
// return the current timer overflow count
// (this is since the last timer0ClearOverflowCount() command was called)
return Timer0Reg0;
}
void timer2ClearOverflowCount(void)
{
// clear the timer overflow counter registers
Timer2Reg0 = 0; // initialize time registers
Timer2Reg1 = 0; // initialize time registers
}
long timer2GetOverflowCount(void)
{
// return the current timer overflow count
// (this is since the last timer2ClearOverflowCount() command was called)
return Timer2Reg0;
}
void timer1PWMInit(u08 bitRes)
{
// configures timer1 for use with PWM output
// on OC1A and OC1B pins
// enable timer1 as 8,9,10bit PWM
if(bitRes == 9)
{ // 9bit mode
sbi(TCCR1A,PWM11);
cbi(TCCR1A,PWM10);
}
else if( bitRes == 10 )
{ // 10bit mode
sbi(TCCR1A,PWM11);
sbi(TCCR1A,PWM10);
}
else
{ // default 8bit mode
cbi(TCCR1A,PWM11);
sbi(TCCR1A,PWM10);
}
// set clear-timer-on-compare-match
sbi(TCCR1B,CTC1);
// clear output compare value A
outp(0, OCR1AH);
outp(0, OCR1AL);
// clear output compare value B
outp(0, OCR1BH);
outp(0, OCR1BL);
}
void timer1PWMOff(void)
{
// turn off timer1 PWM mode
cbi(TCCR1A,PWM11);
cbi(TCCR1A,PWM10);
// clear (disable) clear-timer-on-compare-match
cbi(TCCR1B,CTC1);
// set PWM1A/B (OutputCompare action) to none
timer1PWMAOff();
timer1PWMBOff();
}
void timer1PWMAOn(void)
{
// turn on channel A (OC1A) PWM output
// set PWM1A as non-inverted PWM
sbi(TCCR1A,COM1A1);
cbi(TCCR1A,COM1A0);
}
void timer1PWMBOn(void)
{
// turn on channel B (OC1B) PWM output
// set PWM1B as non-inverted PWM
sbi(TCCR1A,COM1B1);
cbi(TCCR1A,COM1B0);
}
void timer1PWMAOff(void)
{
// turn off channel A (OC1A) PWM output
// set PWM1A (OutputCompare action) to none
cbi(TCCR1A,COM1A1);
cbi(TCCR1A,COM1A0);
}
void timer1PWMBOff(void)
{
// turn off channel B (OC1B) PWM output
// set PWM1B (OutputCompare action) to none
cbi(TCCR1A,COM1B1);
cbi(TCCR1A,COM1B0);
}
void timer1PWMASet(u16 pwmDuty)
{
// set PWM (output compare) duty for channel A
// this PWM output is generated on OC1A pin
// NOTE: pwmDuty should be in the range 0-255 for 8bit PWM
// pwmDuty should be in the range 0-511 for 9bit PWM
// pwmDuty should be in the range 0-1023 for 10bit PWM
outp( (pwmDuty>>8), OCR1AH); // set the high 8bits of OCR1A
outp( (pwmDuty&0x00FF), OCR1AL); // set the low 8bits of OCR1A
}
void timer1PWMBSet(u16 pwmDuty)
{
// set PWM (output compare) duty for channel B
// this PWM output is generated on OC1B pin
// NOTE: pwmDuty should be in the range 0-255 for 8bit PWM
// pwmDuty should be in the range 0-511 for 9bit PWM
// pwmDuty should be in the range 0-1023 for 10bit PWM
outp( (pwmDuty>>8), OCR1BH); // set the high 8bits of OCR1B
outp( (pwmDuty&0x00FF), OCR1BL); // set the low 8bits of OCR1B
}
//! Interrupt handler for tcnt0 overflow interrupt
SIGNAL(SIG_OVERFLOW0)
{
Timer0Reg0++; // increment low-order counter
if(!Timer0Reg0) // if low-order counter rollover
Timer0Reg1++; // increment high-order counter
// increment pause counter
Timer0PauseReg++;
// if a user function is defined, execute it too
if(TimerIntFunc[TIMER0OVERFLOW_INT])
TimerIntFunc[TIMER0OVERFLOW_INT]();
}
//! Interrupt handler for tcnt1 overflow interrupt
SIGNAL(SIG_OVERFLOW1)
{
// if a user function is defined, execute it
if(TimerIntFunc[TIMER1OVERFLOW_INT])
TimerIntFunc[TIMER1OVERFLOW_INT]();
}
//! Interrupt handler for tcnt2 overflow interrupt
SIGNAL(SIG_OVERFLOW2)
{
Timer2Reg0++; // increment low-order counter
if(!Timer2Reg0) // if low-order counter rollover
Timer2Reg1++; // increment high-order counter
// if a user function is defined, execute it
if(TimerIntFunc[TIMER2OVERFLOW_INT])
TimerIntFunc[TIMER2OVERFLOW_INT]();
}
//! Interrupt handler for CutputCompare1A match (OC1A) interrupt
SIGNAL(SIG_OUTPUT_COMPARE1A)
{
// if a user function is defined, execute it
if(TimerIntFunc[TIMER1OUTCOMPAREA_INT])
TimerIntFunc[TIMER1OUTCOMPAREA_INT]();
}
//! Interrupt handler for OutputCompare1B match (OC1B) interrupt
SIGNAL(SIG_OUTPUT_COMPARE1B)
{
// if a user function is defined, execute it
if(TimerIntFunc[TIMER1OUTCOMPAREB_INT])
TimerIntFunc[TIMER1OUTCOMPAREB_INT]();
}
//! Interrupt handler for InputCapture1 (IC1) interrupt
SIGNAL(SIG_INPUT_CAPTURE1)
{
// if a user function is defined, execute it
if(TimerIntFunc[TIMER1INPUTCAPTURE_INT])
TimerIntFunc[TIMER1INPUTCAPTURE_INT]();
}
//! Interrupt handler for OutputCompare2 match (OC2) interrupt
SIGNAL(SIG_OUTPUT_COMPARE2)
{
// if a user function is defined, execute it
if(TimerIntFunc[TIMER2OUTCOMPARE_INT])
TimerIntFunc[TIMER2OUTCOMPARE_INT]();
}
//=================================================================================
// The following functions are written by Gang Xie for the project.
//
// Courtesy of Pascal Stang: some segments/functions are directly from Pascal.
//=================================================================================
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void InitializeRtc(void)
{
// initializae the real-time clock Timer2: 32768/128/2^8 = 1Hz
outp((1< 59) // check seconds overflow
{
gRtcTime.second = 0;
gRtcTime.minute++; // increment minutes
gSysState.update_rtc_minute = 1;
if(gRtcTime.minute > 59) // check minutes overflow
{
gRtcTime.minute = 0;
gRtcTime.hour++; // increment hours
gSysState.update_rtc_hour = 1;
if(gRtcTime.hour > 23) // check hours overflow
{
gRtcTime.hour = 0;
gRtcTime.day++; // increment days
gRtcTime.sunsat++;
if (gRtcTime.sunsat>6)
gRtcTime.sunsat = 0;
gSysState.update_rtc_day = 1;
gSysState.update_rtc_sunsat = 1;
days = CalcDaysInMonth(gRtcTime.month, gRtcTime.year);
if(gRtcTime.day > days) // check days overflow
{
gRtcTime.day = 1;
gRtcTime.month++; // increment months
gSysState.update_rtc_month = 1;
if(gRtcTime.month >= 13) // check months overflow
{
gRtcTime.month = 1;
gRtcTime.year++; // increment years
if (gRtcTime.year>=10000)
gRtcTime.year = 0;
gSysState.update_rtc_year = 1;
}
}
}
}
}
//alarm
if (gSysState.alarmOn)
{
if ((gRtcTime.hour==gAlarmTime.hour) &&
(gRtcTime.minute==gAlarmTime.minute) &&
(gRtcTime.second==gAlarmTime.second))
gSysState.alarmCount = kAlarmTimeout + 1;
}
if (gSysState.alarmCount > 0) //safer to be here
gSysState.alarmCount--;
// change frequency
if (NeedSpeakerWork())
{
gSysState.spkNoteNum++;
if (gSysState.spkNoteNum >= 8)
gSysState.spkNoteNum = 0;
}
//pump
if (gSysState.pumpActivated)
{
if (CompareTwoTimes(gRtcTime, gPumpInfo.next)==1)
{
gPumpInfo.duration.year = ((u16)gPumpInfo.duration.minute)*60;
gPumpInfo.duration.year += ((u16)gPumpInfo.duration.second);
gPumpInfo.totDurInSeconds = gPumpInfo.duration.year;
gPumpInfo.duration.year ++;
}
}
if (gPumpInfo.duration.year > 0)
{
if (gSysState.pumpActivated)
sbi(SPK_PORTO, PUMP_POSPIN); //set 1=>begin bumping
gPumpInfo.duration.year--;
if (gPumpInfo.duration.year==0)
{
cbi(SPK_PORTO, PUMP_POSPIN); //clear 0=>stop bumping
CalcNextPumpTime();
}
gSysState.update_info = 1;
}
//switch to normal LCD page
if (gSysState.displayTimeout > 0)
gSysState.displayTimeout--;
//info
if (gSysState.displayPage == kPage0_Norm)
{
gSysState.update_info = 1;
gSysState.update_info_num++;
if (gSysState.update_info_num>21)
gSysState.update_info_num = 0;
}
}
//---------------------------------------------------------------------------------
// return
// 0: one < two, i.e., one is slower than two
// 1: = equal
// 2: one > two, i.e., one is faster than two
//---------------------------------------------------------------------------------
u08 CompareTwoTimes(RtcTimeType one, RtcTimeType two)
{
if (one.year>two.year)
return 2;
if (one.yeartwo.month)
return 2;
if (one.monthtwo.day)
return 2;
if (one.daytwo.hour)
return 2;
if (one.hourtwo.minute)
return 2;
if (one.minutetwo.second)
return 2;
if (one.secondminute + gap->minute;
while (tmp > 59)
{
tmp -= 60;
aid ++;
}
t->minute = tmp;
// hour
tmp = t->hour + gap->hour + aid;
aid = 0;
while (tmp > 23)
{
tmp -= 24;
aid ++;
}
t->hour = tmp;
// day
tmp = t->day + gap->day + aid;
yeas = t->year;
mons = t->month;
days = CalcDaysInMonth(mons, yeas);
while (tmp > days)
{
tmp -= days;
mons ++;
if (mons == 13)
{
mons = 1;
yeas ++;
}
days = CalcDaysInMonth(mons, yeas);
}
t->day = tmp;
t->month = mons;
t->year = yeas;
}
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void CalcNextPumpTime(void)
{
u08 rlt;
rlt = CompareTwoTimes(gPumpInfo.next, gRtcTime);
while (rlt<2)
{
AddTimeGap(&(gPumpInfo.next), &(gPumpInfo.frequency));
rlt = CompareTwoTimes(gPumpInfo.next, gRtcTime);
}
}
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
u16 AdjustYear(u16 val, u08 isInc)
{
u16 rlt;
if (isInc)
{
rlt = val + 1;
if (rlt>9999)
rlt = 0;
}
else
{
if (val == 0)
val = 10000;
rlt = val - 1;
}
return rlt;
}
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
u08 Adjust2DgtNumber(u08 val, u08 isInc, u08 minv, u08 maxv)
{
u08 rlt;
// In the middle of computations, there should not be negative
if (isInc)
{
if (val>=maxv)
rlt = minv;
else
rlt = val + 1;
}
else
{
if (val <= minv)
rlt = maxv;
else
rlt = val - 1;
}
return rlt;
}