PIC16f690を使った周波数測定器と発振回路
2009年9月19日
色々と考えるところ有って、PIC16f690を用いて、1 Hzから50 MHzまで測定できる簡易周波数測定器と、その動作検証のための発振回路を作成した。
まず、発振回路のプログラムから。RA3とRA5の2つの入力を調整することで、1 Hzから2 MHzまでの周波数で方形波を発生することができる。どの周波数が発生できるかは、プログラム中のコメントを参照。
この装置については多分回路図を描くまでも無い。RA3とRA5にHかLの入力を与え、RC0-7とRB4-7に出力されるという簡単なもの。ちなみに最初のほう、長々とコメントがあるのはPIC16f690のCONFIGビットの設定の仕方について。
次は、周波数測定器のプログラム。内部クロックを用いている。このクロックがどれくらい正確なのかによるが、誤差は多くても数パーセント。自作コンピュータのチェックなどには十分な性能のはずである。
表示は基本的に、KHz単位。10 MHzを超える場合は、MHz単位で点滅して表示される。プログラムでは、10 kHz以下、10-100 kHz、100 kHz - 1 MHz、1 - 10 Mhz、10 Mhz以上の5つの場合に分けて、TMR0のプリスケーラの使用方法や測定の仕方などを変化させるようにした。色々と実験を行って、PIC16f690の理論的に可能な速度である50 MHzでも問題なく測定できるプログラムにしてある。
信号は、RA2から入力する。回路は4つの7セグメントを用いたもので、プロトタイプになりそう。
090921追記:
先日の記事において、回路図の一部に誤りがあったのを修正。
<%media(20090922-hz_0022.zip|プログラムと回路図のソースコードはここ。)%>
まず、発振回路のプログラムから。RA3とRA5の2つの入力を調整することで、1 Hzから2 MHzまでの周波数で方形波を発生することができる。どの周波数が発生できるかは、プログラム中のコメントを参照。
#include <pic.h> /* // Oscillator #define EXTCLK 0x3FFF // External RC Clockout #define EXTIO 0x3FFE // External RC No Clock #define INTCLK 0x3FFD // Internal RC Clockout #define INTIO 0x3FFC // Internal RC No Clock #define EC 0x3FFB // EC #define HS 0x3FFA // HS #define XT 0x3FF9 // XT #define LP 0x3FF8 // LP // Watchdog Timer #define WDTEN 0x3FFF // On #define WDTDIS 0x3FF7 // Off // Power Up Timer #define PWRTDIS 0x3FFF // Off #define PWRTEN 0x3FEF // On // Master Clear Enable #define MCLREN 0x3FFF // MCLR function is enabled #define MCLRDIS 0x3FDF // MCLR functions as IO // Code Protect #define UNPROTECT 0x3FFF // Code is not protected #define CP 0x3FBF // Code is protected #define PROTECT CP //alternate // Data EE Read Protect #define UNPROTECT 0x3FFF // Do not read protect EEPROM data #define CPD 0x3F7F // Read protect EEPROM data // Brown Out Detect #define BORDIS 0x3CFF // BOD and SBOREN disabled #define SWBOREN 0x3DFF // SBOREN controls BOR function (Software control) #define BORXSLP 0x3EFF // BOD enabled in run, disabled in sleep, SBOREN disabled #define BOREN 0x3FFF // BOD Enabled, SBOREN Disabled // Internal External Switch Over Mode #define IESOEN 0x3FFF // Enabled #define IESODIS 0x3BFF // Disabled // Monitor Clock Fail-safe #define FCMEN 0x3FFF // Enabled #define FCMDIS 0x37FF // Disabled */ __CONFIG(INTCLK & WDTDIS & PWRTDIS & MCLRDIS & UNPROTECT & BORDIS & IESODIS & FCMDIS); unsigned char g_tmr0=0; void interrupt int_function (void) { if (!T0IF) return; TMR0=36; T0IF=0; g_tmr0=1; } void main(){ START: // Disable all interrupt events INTCON=0; // all output except for RA3 and RA5 TRISA=0x28; TRISB=0; TRISC=0; // Non-analog mode ANSEL=0x00; ANSELH=0x00; /* Fastest mode (RA3=1, RA5=0) RA4: 2 MHz RC0: 250 kHz RC1: 125 kHz RC2: 62500 Hz RC3: 31250 Hz RC4: 15625 Hz RC5: 7813 Hz RC6: 3906 Hz RC7: 1953 Hz */ OSCCON=0x70;// CLK=8 MHz PORTC=0x00; #asm LOOP1: INCF 0x7,F BTFSC 0x5,3 GOTO LOOP1 // loop if RA3=1 #endasm /* Fast mode (RA3=0, RA5=1) RA4: 500 kHz RC0: 62500 Hz RC1: 31250 Hz RC2: 15625 Hz RC3: 7813 Hz RC4: 3906 Hz RC5: 1953 Hz RC6: 977 Hz RC7: 488 Hz */ OSCCON=0x50;// CLK=2 MHz PORTC=0x00; #asm LOOP2: INCF 0x7,F BTFSC 0x5,5 GOTO LOOP2 // loop if RA5=1 #endasm /* Slow mode (RA3=0, RA5=0) RA4: 1 MHz RC0: 2048 Hz RC1: 1024 Hz RC2: 512 Hz RC3: 256 Hz RC4: 128 Hz RC5: 64 Hz RC6: 32 Hz RC7: 16 Hz RB4: 8 Hz RB5: 4 Hz RB6: 2 Hz RB7: 1 Hz */ OSCCON=0x60;// CLK=4 MHz GIE=1; T0CS=0; // Use internal clock T0IF=0; // Clear flag T0IE=1; // Interrupt by timer PSA=1; // Don't use prescaler for TMR0 unsigned char rb=0,rc=0; while((PORTA&0x28)==0) { for(g_tmr0=0;!g_tmr0;/* wait until intteruption */); PORTC=++rc; if (rc==0) { rb+=0x10; PORTB=rb; } } goto START; }
この装置については多分回路図を描くまでも無い。RA3とRA5にHかLの入力を与え、RC0-7とRB4-7に出力されるという簡単なもの。ちなみに最初のほう、長々とコメントがあるのはPIC16f690のCONFIGビットの設定の仕方について。
次は、周波数測定器のプログラム。内部クロックを用いている。このクロックがどれくらい正確なのかによるが、誤差は多くても数パーセント。自作コンピュータのチェックなどには十分な性能のはずである。
#include <pic.h> __CONFIG(INTIO & WDTDIS & PWRTDIS & MCLRDIS & UNPROTECT & BORDIS & IESODIS & FCMDIS); unsigned char led7(unsigned char num){ unsigned char ret=0x80; if (num & 0x10) ret=0x00; switch (num & 0x0F) { case 0: return ret | 0x08; case 1: return ret | 0x3B; case 2: return ret | 0x41; case 3: return ret | 0x21; case 4: return ret | 0x32; case 5: return ret | 0x24; case 6: return ret | 0x04; case 7: return ret | 0x38; case 8: return ret | 0x00; case 9: return ret | 0x20; default: return 0xff; } } // 10000 cycles @ 8 MHz / 4 == 5 ms #define WAIT_CYCLE (10000-23) #define TMR1H_INIT ((unsigned char)(((unsigned short)(65536-WAIT_CYCLE))>>8)) #define TMR1L_INIT ((unsigned char)(((unsigned short)(65536-WAIT_CYCLE))&0x00ff)) unsigned char g_ledp=0; unsigned short g_num=0; unsigned char g_count=0; void interrupt int_function (void) { if (!TMR1IF) return; TMR1H=TMR1H_INIT; TMR1L=TMR1L_INIT; TMR1IF=0; g_count++; static unsigned char ledp=0x10; if (!(ledp=ledp<<1)) ledp=0x10; unsigned short i=g_num; unsigned char j; for (j=0x80;j!=ledp;j=j>>1) i=i/10; i=i % 10 + (ledp==g_ledp?0x10:0x00); PORTB=0; PORTC=led7(i); PORTB=ledp; } void main(){ // CLK=8 MHz OSCCON=0x70; // all output TRISA=0; TRISB=0; TRISC=0; // Non-analog mode ANSEL=0x00; ANSELH=0x00; // Timer0 is used as a counter. TRISA=TRISA | 0x04; // RA2 is for input. T0CS=1; // counter mode T0SE=0; // increment when L -> H PSA=1; // Prescaler not used for TMR0, first TMR0=0; // Scaler 1:1 T1CON=0x00; // Reset timer TMR1H=0x00; TMR1L=0x00; // Clear flag TMR1IF=0; // Enable interrupt TMR1IE=1; PEIE=1; GIE=1; // Start timer TMR1ON=1; unsigned long i; unsigned char mhz=0; while(1){ if (i<10000) { mhz=0; PSA=1; // Prescaler not used for TMR0 i=0; TMR0=g_count=0; T0IF=0; TMR1H=TMR1H_INIT; TMR1L=TMR1L_INIT; while(g_count<200){ if (T0IF){ T0IF=0; i++; } } i=(i<<8)+(unsigned long)TMR0; } else if (i<100000) { mhz=0; PSA=0; // Prescaler used for TMR0 PS2=PS0=0; // Prescaler; 1:8 PS1=1; // Prescaler; 1:8 i=0; TMR0=g_count=0; T0IF=0; TMR1H=TMR1H_INIT; TMR1L=TMR1L_INIT; while(g_count<200){ if (T0IF){ T0IF=0; i++; } } i=(i<<8)+(unsigned long)TMR0; i=i<<3; } else if (i<1000000) { mhz=0; PSA=0; // Prescaler used for TMR0 PS2=PS0=1; // Prescaler; 1:64 PS1=0; // Prescaler; 1:64 i=0; TMR0=g_count=0; T0IF=0; TMR1H=TMR1H_INIT; TMR1L=TMR1L_INIT; while(g_count<200){ if (T0IF){ T0IF=0; i++; } } i=(i<<8)+(unsigned long)TMR0; i=i<<6; } else if (i<9500000 || (i<10000000 && mhz==0)) { mhz=0; PSA=0; // Prescaler used for TMR0 PS2=PS1=PS0=1; // Prescaler; 1:256 i=0; TMR0=g_count=0; T0IF=0; TMR1H=TMR1H_INIT; TMR1L=TMR1L_INIT; while(g_count<200){ if (T0IF){ T0IF=0; i++; } } i=(i<<8)+(unsigned long)TMR0; i=i<<8; } else { mhz=1; PSA=0; // Prescaler used for TMR0 PS2=PS1=PS0=1; // Prescaler 1:256 i=0; TMR0=g_count=0; T0IF=0; GIE=0; // Disable interrupt PORTB=0; TMR1H=0xB; TMR1IF=0; TMR1L=0xDC; //0xBDC = 65536-62500 while(!TMR1IF){ if (T0IF){ T0IF=0; i++; } } i=(i<<8)+(unsigned long)TMR0; i=i<<8; i=i<<5; // 2000000/62500 = 32 TMR1IF=0; GIE=1; // Enable interrupt while(g_count<200); } if (mhz) i=i/1000; if (i<10000) { g_num=i; g_ledp=0x10; } else if (i<100000) { g_num=i/10; g_ledp=0x20; } else if (i<1000000) { g_num=i/100; g_ledp=0x40; } else if (i<10000000) { g_num=i/1000; g_ledp=0x80; } else { g_ledp=0x00; g_num=8888; } if (mhz) i=i*1000; } }
表示は基本的に、KHz単位。10 MHzを超える場合は、MHz単位で点滅して表示される。プログラムでは、10 kHz以下、10-100 kHz、100 kHz - 1 MHz、1 - 10 Mhz、10 Mhz以上の5つの場合に分けて、TMR0のプリスケーラの使用方法や測定の仕方などを変化させるようにした。色々と実験を行って、PIC16f690の理論的に可能な速度である50 MHzでも問題なく測定できるプログラムにしてある。
信号は、RA2から入力する。回路は4つの7セグメントを用いたもので、プロトタイプになりそう。
090921追記:
先日の記事において、回路図の一部に誤りがあったのを修正。
<%media(20090922-hz_0022.zip|プログラムと回路図のソースコードはここ。)%>