/* Jesper Hansen 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; 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. Project: CounterMeasures 40 MHz Frequency Counter ------------------------ CPU : At90S2313 Date: 2001-03-02 Author : Jesper Hansen Current consumption about 40-45 mA. Measures to > 50 MHz */ #include #include #include // PORT D bits // al counter control bits active low #define CLEAR PD6 #define OE_H PD5 #define OE_L PD4 // PD3..0 is lower data bus // PORT B // PB7..4 is high data bus // PB3 is OC1 output // PB2..0 is 74HC138 select bits for display common // constants/macros #define F_CPU 4000000 // 4MHz processor #define CYCLES_PER_US ((F_CPU+500000)/1000000) // cpu cycles per microsecond // display data #define SEG_a 0x01 #define SEG_b 0x02 #define SEG_c 0x04 #define SEG_d 0x08 #define SEG_e 0x10 #define SEG_f 0x20 #define SEG_g 0x40 #define SEG_dot 0x80 unsigned char digits[] = { (SEG_a|SEG_b|SEG_c|SEG_d|SEG_e|SEG_f), // 0 (SEG_b|SEG_c), // 1 (SEG_a|SEG_b|SEG_d|SEG_e|SEG_g), // 2 (SEG_a|SEG_b|SEG_c|SEG_d|SEG_g), // 3 (SEG_b|SEG_c|SEG_c|SEG_f|SEG_g), // 4 (SEG_a|SEG_c|SEG_d|SEG_f|SEG_g), // 5 (SEG_a|SEG_c|SEG_d|SEG_e|SEG_f|SEG_g), // 6 (SEG_a|SEG_b|SEG_c), // 7 (SEG_a|SEG_b|SEG_c|SEG_d|SEG_e|SEG_f|SEG_g), // 8 (SEG_a|SEG_b|SEG_c|SEG_d|SEG_f|SEG_g), // 9 (SEG_a), // mode 0 indicator (Hz) (SEG_g), // mode 1 indicator (kHz) (SEG_d), // mode 2 indicator (MHz) }; /****************************************************************************/ // timer 0 interrupt handles multiplex and refresh of the displays // timer is clocked at 62500 Hz #define TI0_L (256-125) // 500 Hz -> 2 mS volatile unsigned char active_led = 0; volatile unsigned long led_value = 0; // four BCD nibbles volatile unsigned char decimal_point = 0; volatile unsigned char mode_setting = 0; SIGNAL(SIG_OVERFLOW0) //timer 0 overflow { unsigned char a,b; // reload timer outp(TI0_L, TCNT0); // all displays off by setting all commons high outp(inp(PORTB) | 0x07, PORTB); if (active_led == 5) { b = digits[10 + mode_setting]; } else { a = led_value >> (( 4 - active_led ) * 4); b = digits[a & 0x0f]; if (decimal_point == (4 - active_led) ) b |= SEG_dot; } a = b & 0xf0; // hi part b = b & 0x0f; // lo part // set digit data on port outp( (inp(PORTB) & 0x0f) | a, PORTB); // high part outp( (inp(PORTD) & 0xf0) | b, PORTD); // low part // set common outp( (inp(PORTB) & 0xf8) | active_led, PORTB); active_led = (active_led+1) % 6; } /****************************************************************************/ /* helpers ****************************************************************/ /****************************************************************************/ 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++) {}; } // // read 16 bit counter value // unsigned int read_counters(void) { unsigned int counter_value; // stop display refresh while reading counters cli(); // turn off all segments outp(inp(PORTB) | 0x07, PORTB); // set high B port to input outp(0x0f,DDRB); // set low D port to input outp(0xf0,DDRD); // activate OE_H cbi(PORTD,OE_H); asm volatile("nop"); sbi(PORTD,OE_H); // one pulse to latch count asm volatile("nop"); cbi(PORTD,OE_H); asm volatile("nop"); // read hi counter_value = (inp(PINB) & 0xf0); // read lo counter_value |= (inp(PIND) & 0x0f); // deactivate OE_H sbi(PORTD,OE_H); counter_value <<= 8; // activate OE_L cbi(PORTD,OE_L); asm volatile("nop"); sbi(PORTD,OE_L); // one pulse to latch count asm volatile("nop"); cbi(PORTD,OE_L); asm volatile("nop"); // read hi counter_value |= (inp(PINB) & 0xf0); // read lo counter_value |= (inp(PIND) & 0x0f); // deactivate OE_L sbi(PORTD,OE_L); // set B port back to output outp(0xff,DDRB); // set D port back to output outp(0xff,DDRD); // re-enable display refresh sei(); return counter_value; } // // do a capture // void capture(unsigned int compare) { cbi(PORTD,CLEAR); // clear external counters asm volatile("nop"); sbi(PORTD,CLEAR); // remove clear outp(0,TCNT1H); // clear timer outp(0,TCNT1L); outp(compare >> 8,OCR1H); // set the compare1 register to the outp(compare,OCR1L); // required value outp(0x40,TCCR1A); // set OC1 bit to toggle on compare sbi(TIFR,OCF1A); // clear overflov/compare flags if (compare == 15625) outp(0x0C,TCCR1B); // start with fClk/256 (15625 Hz) and compare clear else outp(0x0A,TCCR1B); // start with fClk/8 (500 kHz) and compare clear while ( ! (unsigned char) ( inp(TIFR) & BV(OCF1A)) ); // wait for bit sbi(TIFR,OCF1A); // clear flags // counter input now enabled // for the specified time while ( ! (unsigned char) ( inp(TIFR) & BV(OCF1A)) ); // wait again for bit outp(0,TCCR1B); // stop timer // counter input disabled } /****************************************************************************/ /* main *******************************************************************/ /****************************************************************************/ int main(void) { int i,j; unsigned char dp,ms; unsigned long lv; unsigned int count; // set all PORTB as outputs outp(0xff,DDRB); // set all bits hi outp(0xff,PORTB); // set all PORTD as outputs outp(0xff,DDRD); // set all bits hi outp(0xff,PORTD); // setup timer 0 outp(0x03, TCCR0); // prescaler f/64 tPeriod = 1/62500 Hz -> 16 uS // enable timer 0 interrupt sbi(TIMSK, TOIE0); // start things running sei(); /* compare values at fclk/8 (500 kHz, 2 uS) : 500 = 1 mS 5000 = 10 mS 50000 = 100 mS at fclk/256 (15.625 kHz, 64 uS) : 15625 = 1 S */ // first make sure the OC1 pin is in a controlled state // we want it to be HIGH initially // There's no way to set/clear it directly, but it can be forced to // a defined state by a compare match, se by setting a low compare value // and start the timer, it can be forced into set state outp(0,TCNT1H); // clear timer outp(0,TCNT1L); outp(0,OCR1H); // set compare to 200 outp(200,OCR1L); outp(0xC0,TCCR1A); // set OC1 bit to set on compare // start timer and wait for one compare match outp(0x01,TCCR1B); // start with fClk/1 (4 MHz) while ( ! (unsigned char) ( inp(TIFR) & BV(OCF1A)) ); // wait for bit sbi(TIFR,OCF1A); // clear flags outp(0,TCCR1B); // stop timer // compare bit no HI, start // doing some useful work while (1) { // try a capture at min gate capture(500); // 1 mS // get the data count = read_counters(); dp = 3; // decimal point ms = 2; // indicate MHz if (count < 4096) // less than 4.096 MHz { // try a capture at next gate value capture(5000); // 10 mS // get the data count = read_counters(); dp = 4; // decimal point ms = 2; // indicate MHz if (count < 4096) // less than 409.6 kHz { // try a capture at next gate value capture(50000); // 100 mS // get the data count = read_counters(); dp = 3; // decimal point ms = 1; // indicate kHz if (count < 4096) // less than 40.96 kHz { // try a capture at next gate value capture(15625); // 1 S // get the data count = read_counters(); dp = 0; // decimal point ms = 0; // indicate Hz } } } // convert BINARY counter_value (int) to BCD in led_value (long) lv = 0; for (j=0;j<8;j++) { i = count % 10; lv >>= 4; lv |= ((unsigned long)i << 28); count /= 10; } // set display variables decimal_point = dp; mode_setting = ms; led_value = lv; } // loop }