/********************************************* Project : RPM counter Version : 1.0 Date : 19-08-2004 Author : Jan Thogersen Email : jan@future-design.dk Company : Comments: The project here measures the time between two pulses and calculates the RPM from a average of several measurements. The project uses a 16 bit timer to measure the time between pulses, however, to extend the RPM range the 16 bit hardware timer is extended with an extra software layer. The end product is a 32 bit timer. Which makes the measurement very accurate and with a large range. To minimize the software footprint the timer is running in Indput Capture mode. So the essential of this project is done 100% in hardware. Chip type : AT90S2313 Clock frequency : 8,000000 MHz Memory model : Tiny Internal SRAM size : 128 External SRAM size : 0 Data Stack size : 32 *********************************************/ #include #include #include // Here is the definition of which port pin is used for which LED in the matrix. // The following defines is the ROW in the matrix. // These values can be changed to fit the PCB layout. #define A 4 #define B 128 #define C 32 #define D 2 #define E 1 #define F 16 #define G 64 #define Dod 8 // The following defines is the COL in the matrix. // These values can be changed to fit the PCB layout. #define COL_A 0x1C #define COL_B 0x2C #define COL_C 0x34 #define COL_D 0x38 // Definition of the 7 segment numbers // A // ----- // F | G | B // |----| // E | | C // -----o // D Dod const unsigned char kucDigi[] = { (A+B+C+D+E+F), // 0 (B+C), // 1 (A+B+G+E+D), // 2 (A+B+C+D+G), // 3 (B+C+F+G), // 4 (A+C+D+F+G), // 5 (A+C+D+E+F+G), // 6 (A+B+C), // 7 (A+B+C+D+E+F+G), // 8 (A+B+C+D+F+G), // 9 (0) // blank }; const unsigned char kucCOLUMS[] = {COL_A, COL_B, COL_C, COL_D}; #define NumMeasures 2 // The output is a average of the X readings unsigned char ucLEDS[4], ucLED_POS; unsigned char ucCiffer[4]; unsigned int uiTempDigi, uiOutput; unsigned long ulMeasuredTime; unsigned char ucOutputOutOfRange; unsigned long ulMeasured[NumMeasures]; unsigned char ucMeasuredPos; // Timer 0 overflow interrupt service routine //interrupt [TIM0_OVF] void timer0_ovf_isr(void) ISR(TIMER0_OVF0_vect){ // Place your code here // Here is the 7 segment matrix scanner. TCNT0=0xF0; // This value can be changes to scan faster or slower. PORTD = kucCOLUMS[ucLED_POS]; PORTB = ucLEDS[ucLED_POS]; ucLED_POS++; if (ucLED_POS == 4) ucLED_POS = 0; } // Timer 1 overflow interrupt service routine //interrupt [TIM1_OVF] void timer1_ovf_isr(void) ISR(TIMER1_OVF1_vect){ // Place your code here // When we receive a Timer overflood interrupt // then we add 1 to the high 16 bit of the // ulMeasuredTime. Why? // The timer is 16 bit. Thats not enough so we add // another 16 bit, however this time in software. // The end product is a 32 bit unsigned long ulMeasuredTime. // The low 16 bit is for the hardware value from the timer, the // high 16 bit is for the software timer part. // So a the overflood we simply add 1 to the software part. // Later when we have the Input Capture Interrupt we can complete // the process by adding the hardware value from the timer // to the low 16 bit of the ulMeasuredTime. ulMeasuredTime += 0x10000; // The ucOutputOutOfRange is a variable used to // verify that we have an input or not. It's a form // of timeout. When there is a overflood like now, then // we decrease the value by one. // If the value reaches zero, then we make the // assumption that there is no signal. if (ucOutputOutOfRange > 0) ucOutputOutOfRange--; } // Timer 1 input capture interrupt service routine //interrupt [TIM1_CAPT] void timer1_capt_isr(void) ISR(TIMER1_CAPT1_vect){ // Place your code here if (ucOutputOutOfRange < 16) ucOutputOutOfRange+=4; // Place the hardware timer value into the 32 bit accumulation. ulMeasuredTime += ICR1; TCNT1 = 0; // The output is based on an average, so we save the measurements. // This is not the best way to do it. I know that. But it was fast to code // And I had enough RAM to do it. But a waste I know. ulMeasured[ucMeasuredPos] = ulMeasuredTime; ucMeasuredPos++; if (ucMeasuredPos >= NumMeasures) ucMeasuredPos = 0; ulMeasuredTime = 0; } // Declare your global variables here char cTempCnt; unsigned long ulSum; int main(void) { // Declare your local variables here // Input/Output Ports initialization // Port B initialization // Func0=Out Func1=Out Func2=Out Func3=Out Func4=Out Func5=Out Func6=Out Func7=Out // State0=1 State1=1 State2=1 State3=1 State4=1 State5=1 State6=1 State7=1 PORTB=0xFF; DDRB=0xFF; // Port D initialization // Func0=Out Func1=Out Func2=Out Func3=Out Func4=In Func5=In Func6=In // State0=1 State1=1 State2=1 State3=1 State4=T State5=T State6=T PORTD=0x3C; DDRD=0x3C; // Timer/Counter 0 initialization // Clock source: System Clock // Clock value: 31,250 kHz TCCR0=0x04; TCNT0=0x00; // Timer/Counter 1 initialization // Clock source: System Clock // Clock value: 125,000 kHz // Mode: Normal top=FFFFh // OC1 output: Discon. // Noise Canceler: On // Input Capture on Faling Edge TCCR1A=0x00; TCCR1B=0x83; TCNT1H=0x00; TCNT1L=0x00; OCR1H=0x00; OCR1L=0x00; // External Interrupt(s) initialization // INT0: Off // INT1: Off GIMSK=0x00; MCUCR=0x00; // Timer(s)/Counter(s) Interrupt(s) initialization TIMSK=0x8A; // Analog Comparator initialization // Analog Comparator: Off // Analog Comparator Input Capture by Timer/Counter 1: Off // Analog Comparator Output: Off ACSR=0x80; // Global enable interrupts sei(); uiOutput = 0; while (1) { ulSum = 0; // Calculate the average for (cTempCnt = 0; cTempCnt < NumMeasures; cTempCnt++) ulSum += ulMeasured[cTempCnt]; uiOutput = (long)ulSum / NumMeasures; // Calculate the RPM from the average time between pulses. uiTempDigi = (long)(125000*60*NumMeasures) / ulSum; // Place your code here if (ucOutputOutOfRange > 4) { // > 4 then Ok Else to many overfloads... // Check if the RPM is higner than we can show in the display else show "out of range" in the display. if (uiTempDigi < 10000) { // A simple binary to BCD converter. for (cTempCnt = 4;cTempCnt > 0;cTempCnt--) { ucCiffer[4 - cTempCnt] = (uiTempDigi % 10); uiTempDigi = uiTempDigi / 10; } // Swap a leading zero with "blank" for (cTempCnt = 3;(ucCiffer[cTempCnt] == 0) && (cTempCnt > 0);cTempCnt--) { ucCiffer[cTempCnt] = 0x0A; } // convert the BCD calue to port values. for (cTempCnt = 0;cTempCnt < 4;cTempCnt++) ucLEDS[cTempCnt] = ~kucDigi[ucCiffer[cTempCnt]]; // Flash the comma in the last digi if there is a signal input if (PIND6 == 0) ucLEDS[0] &= ~(Dod); } else { // Show out of range in display if (PIND6 == 0) ucLEDS[0] = ~(A+D+E+F+Dod); else ucLEDS[0] = ~(A+D+E+F); ucLEDS[1] = 0xFF; ucLEDS[2] = 0xFF; ucLEDS[3] = ~(A+B+C+D); } } else { // If we had to many overfloods (No signal) then we show that in the display // so it is visible for the user that there is no signal. if (PIND6 == 0) ucLEDS[0] = ~(G+Dod); else ucLEDS[0] = ~(G); ucLEDS[1] = ~G; ucLEDS[2] = ~G; ucLEDS[3] = ~G; } }; }