/************************************************************************** * * Copyright (C) 2007 Steve Karg * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * *********************************************************************/ #include #include #include /* global variable counts milliseconds */ volatile unsigned long Timer_Milliseconds; /* MS/TP Silence Timer */ static volatile int SilenceTime; /* counts ticks */ volatile unsigned long Timer_Milliseconds; #define RTC_CMD_ADDR 0x70 /* RTC internal register offset goes here */ #define RTC_DAT_ADDR 0x71 /* RTC internal register R/W access here */ static uint8_t RTC_RS_Convert( uint16_t hertz) { uint8_t RS = 0; /* from DS12887A datasheet SELECT BITS tPI PERIODIC REGISTER A INTERRUPT SQW OUTPUT RS3 RS2 RS1 RS0 RATE FREQUENCY --- --- --- --- ------------ ---------- 0 0 0 0 None 0Hz 0 0 0 1 3.90625ms 256Hz 0 0 1 0 7.8125ms 128Hz 0 0 1 1 122.070µs 8192Hz 0 1 0 0 244.141µs 4096Hz 0 1 0 1 488.281µs 2048Hz 0 1 1 0 976.5625µs 1024Hz 0 1 1 1 1.953125ms 512Hz 1 0 0 0 3.90625ms 256Hz 1 0 0 1 7.8125ms 128Hz 1 0 1 0 15.625ms 64Hz 1 0 1 1 31.25ms 32Hz 1 1 0 0 62.5ms 16Hz 1 1 0 1 125ms 8Hz 1 1 1 0 250ms 4Hz 1 1 1 1 500ms 2Hz */ /* FIXME: create a clever formula to replace switch */ switch (hertz) { case 8192: RS = 3; break; case 4096: RS = 4; break; case 2048: RS = 5; break; case 1024: RS = 6; break; case 512: RS = 7; break; case 256: RS = 8; break; case 128: RS = 9; break; case 64: RS = 10; break; case 32: RS = 11; break; case 16: RS = 12; break; case 8: RS = 13; break; case 4: RS = 14; break; case 2: RS = 15; break; default: break; } return RS; } /* setting for 8192 interrupts per second which is an interrupt every 122uS. */ #define INT_FREQ 8192 static void interrupt Timer_Interrupt_Handler( void) { static uint16_t Timer_Ticks = 0; static uint16_t Elapsed_Milliseconds = 0; uint16_t milliseconds = 0; uint16_t diff = 0; uint8_t temp_reg; Timer_Ticks++; milliseconds = (Timer_Ticks * 1000) / INT_FREQ; diff = milliseconds - Elapsed_Milliseconds; if (diff >= 1) { Elapsed_Milliseconds = milliseconds; Timer_Milliseconds++; if (SilenceTime < 60000) SilenceTime++; } /* max resolution */ if (Timer_Ticks >= INT_FREQ) { Timer_Ticks = 0; Elapsed_Milliseconds = 0; } /* clear interrupt */ outportb(RTC_CMD_ADDR, 0x0C); /* select RTC register C */ temp_reg = inportb(RTC_DAT_ADDR); /* read RTC register C */ /* signal end of interrupt to slave PIC */ outportb(0xA0, 0x20); /* signal end of interrupt to master PIC */ outportb(0x20, 0x20); } /* previous interrrupt vector */ static void interrupt( *OldVector) ( ); void Timer_Cleanup( void) { setvect(0x70, OldVector); } void Timer_Init( void) { uint8_t RC = RTC_RS_Convert(INT_FREQ); /* get old interrupt vector to re-install on exit */ OldVector = getvect(0x70); /* disable interrupts */ disable(); /* set RTC int. vector for our routine */ setvect(0x70, Timer_Interrupt_Handler); /* set register B with PIE enabled */ outportb(RTC_CMD_ADDR, 0x0B); outportb(RTC_DAT_ADDR, 0x42); /* set register A to our frequency */ outportb(RTC_CMD_ADDR, 0x0A); outportb(RTC_DAT_ADDR, (0x20 | (RC & 0x0F))); /* re-enable system interrupts */ enable(); atexit(Timer_Cleanup); } int Timer_Silence( void) { uint16_t time_value; disable(); time_value = SilenceTime; enable(); return time_value; } void Timer_Silence_Reset( void) { disable(); SilenceTime = 0; enable(); }