/* vim: set sw=8 ts=8 si : */ /************************************************************************* Title: linuxfocus frequency counter Author: guido socher <guido(at)linuxfocus.org> Copyright: GPL **************************************************************************/ #include <io.h> #include <progmem.h> #include <interrupt.h> #include <string-avr.h> #include <stdlib.h> #include <sig-avr.h> #include "lcd.h" /* these are the prototypes and defines for lcd.c */ #include "avr-util.h" #include "uart.h" /* Input Push Buttons */ #define BUTTON0 PIND6 #define BUTTON0DDR DDRD #define BUTTON0PIN PIND #define BUTTON1 PIND3 #define BUTTON1DDR DDRD #define BUTTON1PIN PIND /* reset line to 74hc390 */ #define CCLEAR PC4 #define CCLEARDDR DDRC #define CCLEARPORT PORTC /* LED0=green */ #define LED0 PC5 #define LED0DDR DDRC #define LED0PORT PORTC static char uart_outbuf[UART_RX_BUFFER_SIZE+1]; static unsigned char clock64Hz; static unsigned char hflag; static unsigned char mode; /* 0=cout up, 1=freq 1Hz resultion, 2=freq 64Hz resolution */ static unsigned char dmode; /* add 2 zeros behind the display output */ static unsigned char clocklimit; /* our counter is 16bit wide but we multiply in the fast mode * with 64 (gate frequency) therefore we need a 24 bit variable */ static unsigned char counterval[3]; /* 24 bit */ static unsigned char counterval_cp[3]; /* 24 bit */ static unsigned char overflowflag; #define reply_ok() uart_sendstr_P("ok\n") #define reply_err() uart_sendstr_P("err\n") /* devide the 24 bit integer unsigned char i[3] by 10 and return * the value in i[3] */ void divby10(unsigned char *i,unsigned char *rest){ unsigned char loop; unsigned int tmp; // 16 bit loop=3; /* start with highest byte */ *rest=0; while(loop>0){ tmp=*rest * 256 + i[loop-1]; i[loop-1]=tmp / 10; *rest=tmp % 10; loop--; } } /* convert a 3 time 8 bit interger (24 bit=i[3]) to decimal ascii and * print it using the printfun. */ void longtoascii(void (*printfun)(char),unsigned char *i,unsigned char dodot) { char str[14]; unsigned char pos=14; unsigned char dot=0; if (dmode==1){ /* take the 2 zeros that will be added into account */ dot=2; } do{ /* write a "." every 1000 and 1000000 to make the * number more readable. 1234 will be 1.234 */ if (dot==3 && dodot==1){ dot=0; str[pos-1]='.'; pos--; }else{ divby10(i,&str[pos-1]); str[pos-1]+= '0'; pos--; dot++; } }while((i[0]|i[1]|i[2]) && pos > 0); /* now reverse the string and print */ while(pos<14){ (*printfun)(str[pos]); pos++; } if (dmode==1){ (*printfun)('0'); (*printfun)('0'); } } /* setup the T1 (PD5) counter to get ready for counting. * You must call once sei() in the main program */ void cnt_readyforstart(void) { /* add: set nand gates */ /* timer should generate interrupt on compate match */ sbi(TIMSK,TOIE1); /* write high byte first */ outp(0x0,TCNT1H); /* set counter to zero*/ outp(0x0,TCNT1L); /* set counter to zero*/ /* write high byte first */ outp(0xFF,OCR1H); /* set internal compare register to max */ outp(0xFF,OCR1L); /* set internal compare register to max */ outp(0,TCCR1A); /* Timer/Counter1 disconnected from output pin OC1 */ outp(0,TCCR1B); /* off */ } /* write info about mode to display */ void displaymode(void) { lcd_gotoxy(0,1); if (mode==0){ lcd_puts_P("gate:cont "); } if (mode==1){ lcd_puts_P("gate:1sec "); } if (mode==2){ lcd_puts_P("gate:1/64sec"); } } /* the following function will be called when the counter * is out of range. This is a 16bit counter! */ SIGNAL(SIG_OVERFLOW1) { /* add: set nand gates */ overflowflag=1; outp(0x0,TCNT1H); /* set counter to zero*/ outp(0x0,TCNT1L); /* set counter to zero*/ outp(0,TCCR1B); // stop counter } /* we can not use large functions in * an interrupt handle. Therefore the interrupt routine reads only * the counter values and the rest is done later via this function * which is called from the main program */ void handlecounterresult(void){ /* display the result value e.g c:123 */ uart_sendchar('c'); uart_sendchar(':'); lcd_clrscr(); if (overflowflag){ /* overflow */ uart_sendchar('-'); uart_sendchar('1'); lcd_puts_P("overflow"); }else{ if (mode==2) { /* mult by 64 */ counterval[2]=counterval[1]>>2; counterval[1]=((counterval[0]>>2)|(counterval[1]<<6)); counterval[0]=counterval[0]<<6; } /* make a copy of the counter value */ counterval_cp[2]=counterval[2]; counterval_cp[1]=counterval[1]; counterval_cp[0]=counterval[0]; /* longtoascii sets counterval to zero */ longtoascii(lcd_putc,counterval_cp,1); longtoascii(uart_sendchar,counterval,0); if (mode){ /* not for cont count */ lcd_puts_P(" Hz "); } if (dmode==1){ /* indicate that the dispaly has 2 additional zeros */ lcd_puts_P("x100"); } } uart_sendchar('\n'); displaymode(); clock64Hz=0; } /* this function will be called in 64Hz intervals if * a 4.194304 MHz crystal is used */ SIGNAL(SIG_OVERFLOW0) { outp(0,TCNT0); /* clear 64Hz timer value */ if (clock64Hz == clocklimit){ /* read counter */ /* stop 64Hz timer */ outp(0,TCCR0); //if (mode==1){ /* use this to calibrate the counter result * for 1 sec gate */ // delay_us(700); //}else{ /* calibrate for 1/64 sec gate: */ // delay_us(10); //} outp(0,TCCR1B); // stop counter counterval[0]=inp(TCNT1L); /* read low first !! */ counterval[1]=inp(TCNT1H); hflag=1; /* call the handlecounterresult function */ sbi(LED0PORT,LED0); }else{ if (clock64Hz==0){ overflowflag=0; cbi(LED0PORT,LED0); /* start counter */ /* write high byte first */ outp(0x0,TCNT1H); /* set counter to zero*/ outp(0x0,TCNT1L); /* set counter to zero*/ outp(7,TCCR1B); /* enable 16bin counter from T1 pin */ } } clock64Hz++; } void changemode(void) { outp(0x0,TCNT1H); /* set counter to zero*/ outp(0x0,TCNT1L); /* set counter to zero*/ hflag=0; clock64Hz=0; overflowflag=0; /* clear the 74hc390 divider, clear = 5V */ sbi(CCLEARPORT,CCLEAR); counterval[0]=0; counterval[1]=0; counterval[2]=0; outp(0,TCCR1B); // stop counter /* LED off */ sbi(LED0PORT,LED0); if (mode==0){ /* continious up counting */ outp(0,TCCR0); /* stop 64Hz timer */ outp(0,TCNT0); /* clear timer value */ outp(7,TCCR1B); /* enable counter from T1 pin */ /* LED on */ cbi(LED0PORT,LED0); } if (mode==1){ /* one sec gate */ clocklimit=64; /* start 64Hz timer, clock/256 overflow0 every 256 */ outp(4,TCCR0); } if (mode==2){ clocklimit=1; /* start 64Hz timer, clock/256 overflow0 every 256 */ outp(4,TCCR0); } displaymode(); /* enable counting for the 74hc390 divider, clear=0V */ cbi(CCLEARPORT,CCLEAR); } int main(void) { unsigned char i,rxresult,status; unsigned int ignorebutton,pausetodisplay; char cmd; char *val; ignorebutton=0; pausetodisplay=0; /* initialize display, cursor off */ lcd_init(LCD_DISP_ON); /* initialize LED as output */ sbi(LED0DDR,LED0); /* initialize clear line to 74hc390 as output */ sbi(CCLEARDDR,CCLEAR); /* clear line off */ cbi(CCLEARPORT,CCLEAR); /* button as digital input */ cbi(BUTTON0DDR,BUTTON0); cbi(BUTTON1DDR,BUTTON1); /* initialize rs232 */ uart_init(); /* setup the counter */ cnt_readyforstart(); /* setup the 64Hz timer */ sbi(TIMSK,TOIE0); /* enable T0 overflow0 interrupt */ outp(0,TCNT0); /* start value */ mode=1; dmode=0; changemode(); sei(); /* enable interrupt */ lcd_clrscr(); while(1){ if (hflag==1){ handlecounterresult(); hflag=2; pausetodisplay=65000; // wait a bit for the LCD to show result } if (hflag==2 && pausetodisplay==0){ /* prevent flickering of LCD display */ hflag=0; /* start 64Hz timer */ outp(4,TCCR0); } /* for countup mode display result in regular intervalls */ if (mode==0 && pausetodisplay==0){ pausetodisplay=65000; counterval[0]=inp(TCNT1L); /* read low first !! */ counterval[1]=inp(TCNT1H); handlecounterresult(); } rxresult=uart_getrxbufnl(uart_outbuf); if (ignorebutton==0){ /* button 0 is the mode button */ if (bit_is_clear(BUTTON0PIN,BUTTON0)){ mode++; if (mode>2) mode=0; changemode(); ignorebutton=55000; // debounce, ignore button for a while } /* button 1 is the clear button * press twice to add 00 behind the numbers */ if (bit_is_clear(BUTTON1PIN,BUTTON1)){ /* call changemode not to change the mode * but to clear the 74hc390 counter */ changemode(); /* the second function of this clear button * is to show 00 on the display */ dmode++; if (dmode > 1){ dmode=0; } outp(0x0,TCNT1H); /* set counter to zero*/ outp(0x0,TCNT1L); /* set counter to zero*/ ignorebutton=55000; // debounce, ignore button for a while outp(7,TCCR1B); /* enable counter from T1 pin, needed in case there was an overflow */ overflowflag=0; } } if (ignorebutton) ignorebutton--; if (pausetodisplay) pausetodisplay--; if (rxresult==0){ continue; } /* valid command are: * */ /* now parse the comands and act on them */ if (strlen(uart_outbuf) < 3) { reply_err(); continue; } if (uart_outbuf[1] != '='){ reply_err(); continue; } val=&(uart_outbuf[2]); cmd=uart_outbuf[0]; status=0; /* act on the commands */ if (cmd=='h'){ if (*val =='1'){ dmode=1; status=1; } if (*val =='0'){ dmode=0; status=1; } if (*val == '?'){ uart_sendstr_P("h:"); uart_sendchar('0'+ dmode); uart_sendchar('\n'); continue; } } if (cmd=='c' && *val =='1'){ /* call changemode not to change the mode * but to clear the 74hc390 counter */ changemode(); outp(0x0,TCNT1H); /* set counter to zero*/ outp(0x0,TCNT1L); /* set counter to zero*/ status=1; } if (cmd=='m'){ i=*val; if (i == '?'){ uart_sendstr_P("m:"); uart_sendchar('0'+ mode); uart_sendchar('\n'); continue; } if (i < '3' && i >= '0'){ mode= i - '0'; // convert to integer; changemode(); status=1; } } /* command handling done. now return status */ if (status==1){ reply_ok(); } if (status==0){ reply_err(); } } }mirror server hosted at Truenetwork, Russian Federation.