PIC16F1827を使って、周波数カウンタを作ってみました。
これまで、PIC16F88,PIC16F628Aなどで製作した「電子交差etc」というサイトで紹介された「周波数カウンタV7」と基本的には同じアルゴリズムです。
残念ながら「電子工作etc」というサイトは既に見えなくなっているようです。
PIC 1個で、どのようにして周波数カウンタを作るかを簡単に説明します。
周波数カウンタを作るには、周波数を測定するカウンタとカウント時間(普通は1秒)を発生するタイムベースが必要です。
PICは、内部に複数のタイマー(カウンタ)を持っています。そのうち16bitタイマーであるTimer1(TMR1)を周波数を測定するカウンタに使い、8bitタイマーTimer2(TMR2)を使って1秒を発生させます。
TMR2を利用したタイムベースからの1秒の合図で、TMR1へのゲートを開けたり閉めたりして、周波数をカウントします。
詳細は、JH7UBCホームページのここを見てください。
回路図です。
Image may be NSFW.
Clik here to view.
Clik here to view.

入力信号は、2SC1815GRの簡単なアンプを通して、T1CKI(RB6)に加えます。
クロックは、正確に測定するために20MHzのクリスタルオシレータを使い、外部クロック入力CLKIN(RA7)に入れます。
周波数表示は、I2Cアダプタ付きのLCD1602を使いました。
動作確認のために、TMR1のゲートが開いている間点灯するLEDをつけました。
ブレッドボードです。自作SGからの1MHz(1000000Hz)の信号を測定しています。
Image may be NSFW.
Clik here to view.
Clik here to view.

プログラムです。
------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#include <xc.h>
// CONFIG1
#pragma config FOSC = ECH //外部クロック CLKIN (4-32MHz)
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config MCLRE = OFF
#pragma config CP = OFF
#pragma config CPD = OFF
#pragma config BOREN = ON
#pragma config CLKOUTEN = OFF
#pragma config IESO = OFF
#pragma config FCMEN = OFF
#pragma config FOSC = ECH //外部クロック CLKIN (4-32MHz)
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config MCLRE = OFF
#pragma config CP = OFF
#pragma config CPD = OFF
#pragma config BOREN = ON
#pragma config CLKOUTEN = OFF
#pragma config IESO = OFF
#pragma config FCMEN = OFF
// CONFIG2
#pragma config WRT = OFF
#pragma config PLLEN = OFF
#pragma config STVREN = ON
#pragma config BORV = HI
#pragma config LVP = OFF
#pragma config WRT = OFF
#pragma config PLLEN = OFF
#pragma config STVREN = ON
#pragma config BORV = HI
#pragma config LVP = OFF
#define _XTAL_FREQ 20000000 //クロック20MHz
#define LED RA4
#define LED RA4
//LCD関係定義
#define LCD_EN 0b00000100 //Enable
#define LCD_BL 0b00001000 //Back Light
#define LCD_CMD 0x00
#define LCD_CHR 0x01
#define LCD_LINE1 0x80
#define LCD_LINE2 0xC0
#define LCD_addr 0x4E //0x27<<1
#define LCD_EN 0b00000100 //Enable
#define LCD_BL 0b00001000 //Back Light
#define LCD_CMD 0x00
#define LCD_CHR 0x01
#define LCD_LINE1 0x80
#define LCD_LINE2 0xC0
#define LCD_addr 0x4E //0x27<<1
/* I2C 初期化*/
void I2C_init(){
SSP1CON1 = 0x28; //SSPEN = 1,I2C Master Mode
SSP1STATbits.SMP = 1; //標準速度モード(100KHz)
SSP1ADD = 0x31; //Fosc/(4*Clock)-1 Clock=100kHz,Fosc=20MHz
}
void I2C_init(){
SSP1CON1 = 0x28; //SSPEN = 1,I2C Master Mode
SSP1STATbits.SMP = 1; //標準速度モード(100KHz)
SSP1ADD = 0x31; //Fosc/(4*Clock)-1 Clock=100kHz,Fosc=20MHz
}
/* スタートコンディション */
void I2C_start(){
SSP1CON2bits.SEN = 1;
while(SSP1CON2bits.SEN);
}
void I2C_start(){
SSP1CON2bits.SEN = 1;
while(SSP1CON2bits.SEN);
}
/* ストップコンディション */
void I2C_stop(){
SSP1IF = 0;
SSP1CON2bits.PEN = 1;
while(SSP1CON2bits.PEN);
SSP1IF = 0;
}
void I2C_stop(){
SSP1IF = 0;
SSP1CON2bits.PEN = 1;
while(SSP1CON2bits.PEN);
SSP1IF = 0;
}
/* I2Cに1byte送信 */
void I2C_write(unsigned char dat){
SSP1IF = 0;
SSP1BUF = dat;
while(!SSP1IF);
}
void I2C_write(unsigned char dat){
SSP1IF = 0;
SSP1BUF = dat;
while(!SSP1IF);
}
void Write_data(unsigned char data){
I2C_start();
I2C_write(LCD_addr);
I2C_write(data | LCD_EN | LCD_BL);
I2C_write(data | LCD_BL);
I2C_stop();
__delay_us(100);
}
I2C_start();
I2C_write(LCD_addr);
I2C_write(data | LCD_EN | LCD_BL);
I2C_write(data | LCD_BL);
I2C_stop();
__delay_us(100);
}
void LCD_write(unsigned char bits,unsigned char mode){
//send High 4bits
Write_data((bits & 0xF0) | mode);
//send Low 4bits
Write_data(((bits << 4) & 0xF0) | mode);
}
//send High 4bits
Write_data((bits & 0xF0) | mode);
//send Low 4bits
Write_data(((bits << 4) & 0xF0) | mode);
}
void LCD_init(){
LCD_write(0x33,LCD_CMD);
LCD_write(0x32,LCD_CMD);
LCD_write(0x06,LCD_CMD);
LCD_write(0x0C,LCD_CMD);
LCD_write(0x28,LCD_CMD);
LCD_write(0x01,LCD_CMD);
__delay_ms(1);
}
void LCD_clear(){
LCD_write(0x01,LCD_CMD);
__delay_ms(1);
}
LCD_write(0x01,LCD_CMD);
__delay_ms(1);
}
void LCD_home(){
LCD_write(0x02,LCD_CMD);
__delay_ms(1);
}
LCD_write(0x02,LCD_CMD);
__delay_ms(1);
}
void LCD_cursor(unsigned char x,unsigned char y){
if (y == 0){
LCD_write(LCD_LINE1+x,LCD_CMD);
}
if (y == 1){
LCD_write(LCD_LINE2+x,LCD_CMD);
}
}
if (y == 0){
LCD_write(LCD_LINE1+x,LCD_CMD);
}
if (y == 1){
LCD_write(LCD_LINE2+x,LCD_CMD);
}
}
void putch(unsigned char ch){
LCD_write(ch,LCD_CHR);
}
LCD_write(ch,LCD_CHR);
}
static unsigned int MeassuremmentCnt;
/*TMR2のオーバーフロー割り込み*/
void interrupt isr(){
TMR2IF = 0;//TMR2割り込みフラッグクリア
MeassuremmentCnt --;
if (MeassuremmentCnt == 0){
TMR1ON = 0;//ゲートを閉める。
TMR2ON = 0;//TMR2を停止する。
}
}
void interrupt isr(){
TMR2IF = 0;//TMR2割り込みフラッグクリア
MeassuremmentCnt --;
if (MeassuremmentCnt == 0){
TMR1ON = 0;//ゲートを閉める。
TMR2ON = 0;//TMR2を停止する。
}
}
/*周波数測定*/
unsigned long FreqMeassurement(){
static unsigned long freq;
/*TIMERの初期化*/
TMR1IF = 0; //TMR1割り込みフラッグクリア
TMR1L = 0; //TMR1クリア
TMR1H = 0;
/*TMR2の初期化*/
TMR2IF = 0; //TMR2割り込みフラッグクリア
MeassuremmentCnt = 1221;
TMR2 = 0x4C;
/*counter 初期化*/
freq = 0;
/*割り込み許可*/
PEIE = 1;
GIE = 1;
//count start
TMR2ON = 1;
/*gate time調整 NOP 25*/
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
/*ゲートを開ける*/
TMR1ON = 1;
while(TMR2ON){
if(TMR1IF == 1){
TMR1IF = 0;
freq ++;
}
}
if(TMR1IF == 1){
TMR1IF = 0;
freq ++;
}
/*換算*/
freq = freq * 65536;
freq = freq + ((unsigned)TMR1H * 256) + (unsigned)TMR1L;
return(freq);
}
void main() {
ANSELA = 0b00000000 ; // AN0-AN4は使用しない
ANSELB = 0b00000000 ; // AN5-AN11は使用しない
TRISA = 0b10000000 ; // RA7は入力 他は出力(RA5は入力)
TRISB = 0b01010010 ; // RB1(SDA),RB4(SDA),RB6は入力他は出力
PORTA = 0b00000000 ; // PORTA初期化
PORTB = 0b00000000 ; // PORTB初期化
I2C_init();
LCD_init();
LCD_cursor(12,0);
printf("Hz");
/*TMR2の設定*/
TMR2IE = 1; //TMR2割り込み許可
TMR2IF = 0; //TMR2割り込みフラッグクリア
T2OUTPS0 = 0; //TMR2 output poststscaler 1:1
T2OUTPS1 = 0;
T2OUTPS2 = 0;
T2OUTPS3 = 0;
TMR2ON = 0; //TMR2 off
T2CKPS0 = 0; //TMR2 prescaler 1:16
T2CKPS1 = 1;
TMR2 = 0; //TMR2 clear
/*TMR1の設定*/
TMR1IE = 0; //TMR1割り込み禁止
TMR1IF = 0; //TMR1割り込みフラッグクリア
T1CKPS0 = 0; //TMR1 prescaler 1:1
T1CKPS1 = 0;
T1OSCEN = 0; //TMR1 Clock source = T1CKI
TMR1CS1 = 1;
TMR1CS0 = 0;
nT1SYNC = 1;
TMR1ON = 0; //TMR1 stop
TMR1L = 0; //TMR1 clear
TMR1H = 0;
while(1){
/*周波数の測定*/
LED = 1;//RA4 LED ON
unsigned long frequency = FreqMeassurement();
LED = 0;//RA4 LED OFF
LCD_cursor(2,0);
printf("%9ld",frequency);
__delay_ms(500);
}
unsigned long FreqMeassurement(){
static unsigned long freq;
/*TIMERの初期化*/
TMR1IF = 0; //TMR1割り込みフラッグクリア
TMR1L = 0; //TMR1クリア
TMR1H = 0;
/*TMR2の初期化*/
TMR2IF = 0; //TMR2割り込みフラッグクリア
MeassuremmentCnt = 1221;
TMR2 = 0x4C;
/*counter 初期化*/
freq = 0;
/*割り込み許可*/
PEIE = 1;
GIE = 1;
//count start
TMR2ON = 1;
/*gate time調整 NOP 25*/
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
/*ゲートを開ける*/
TMR1ON = 1;
while(TMR2ON){
if(TMR1IF == 1){
TMR1IF = 0;
freq ++;
}
}
if(TMR1IF == 1){
TMR1IF = 0;
freq ++;
}
/*換算*/
freq = freq * 65536;
freq = freq + ((unsigned)TMR1H * 256) + (unsigned)TMR1L;
return(freq);
}
void main() {
ANSELA = 0b00000000 ; // AN0-AN4は使用しない
ANSELB = 0b00000000 ; // AN5-AN11は使用しない
TRISA = 0b10000000 ; // RA7は入力 他は出力(RA5は入力)
TRISB = 0b01010010 ; // RB1(SDA),RB4(SDA),RB6は入力他は出力
PORTA = 0b00000000 ; // PORTA初期化
PORTB = 0b00000000 ; // PORTB初期化
I2C_init();
LCD_init();
LCD_cursor(12,0);
printf("Hz");
/*TMR2の設定*/
TMR2IE = 1; //TMR2割り込み許可
TMR2IF = 0; //TMR2割り込みフラッグクリア
T2OUTPS0 = 0; //TMR2 output poststscaler 1:1
T2OUTPS1 = 0;
T2OUTPS2 = 0;
T2OUTPS3 = 0;
TMR2ON = 0; //TMR2 off
T2CKPS0 = 0; //TMR2 prescaler 1:16
T2CKPS1 = 1;
TMR2 = 0; //TMR2 clear
/*TMR1の設定*/
TMR1IE = 0; //TMR1割り込み禁止
TMR1IF = 0; //TMR1割り込みフラッグクリア
T1CKPS0 = 0; //TMR1 prescaler 1:1
T1CKPS1 = 0;
T1OSCEN = 0; //TMR1 Clock source = T1CKI
TMR1CS1 = 1;
TMR1CS0 = 0;
nT1SYNC = 1;
TMR1ON = 0; //TMR1 stop
TMR1L = 0; //TMR1 clear
TMR1H = 0;
while(1){
/*周波数の測定*/
LED = 1;//RA4 LED ON
unsigned long frequency = FreqMeassurement();
LED = 0;//RA4 LED OFF
LCD_cursor(2,0);
printf("%9ld",frequency);
__delay_ms(500);
}
}