MPLABX XC8環境で、PIC16F628Aで周波数カウンタを作ってみます。
PIC16F88/PIC16F648A周波数カウンタは、既にアセンブラで製作し、私のホームページ(JH7UBCホームページ)に記事を掲載しています。
また、PIC16F628AとPIC15F883を使った7セグメントLED表示の周波数カウンタについてもホームページに記事を掲載しています。
これらの周波数カウンタは、「電子工作etc」というサイトの「周波数カウンタV7」をアセンブラに移植したものです。
今回は、周波数カウンタV7のCソースリストをXC8コンパイラに移植しましたので、簡単だったのですが、一部でひっかかり、思ったより時間がかかりました。
PIC16F628A周波数カウンタの原理を簡単に説明します。
周波数カウンタ本体は、TMR1(16bit)を使います。これだけでは、65,535Hzまでしかカウントできません。
そこで、TMR1がオーバーフローしたらカウントアップするのカウンタをソフトでつければ、測定周波数を大きくすることができます。
周波数測定時間1秒を発生するタイムベースは、TMR2(8bit)を使います。クロックは、20MHzのクリスタルオシレータを外部クロックとして使い、プリスケーラを1:16にすれば、(1/20000000)*4*16*256=819.2usごとに割り込みがTMR2発生します。(1秒間に1220.703回の割り込み) そこで、カウンタに12270をセットし、TMR2に残りの時間に対応する数をセットしてスタートさせることにより、1秒を実現しています。
TMR1.TMR2ともプログラムでON/OFFできますので、この2つのタイマを利用して周波数カウンタを作ることができるわけです。
今回は、XC8の演習として製作しましたので、LCD表示ではなく、シリアル通信で周波数データをパソコンに送り、TeraTermで受信して表示します。
回路図です。
ブレッドボードと自作SGから10MHzの信号を与えているところです。
TeraTermの表示です。1秒ごとに周波数が表示されます。
プログラムです。(ちょっと長いです)
/*
* File: PIC16F628A_counter.c
* Author: JH7UBC Keiji Hata
* Frequency Counter V7
* Created on 2018/11/17
*/
* File: PIC16F628A_counter.c
* Author: JH7UBC Keiji Hata
* Frequency Counter V7
* Created on 2018/11/17
*/
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
// CONFIG
#pragma config FOSC = ECIO // Oscillator Selection bits (EC: I/O function on RA6/OSC2/CLKOUT pin, CLKIN on RA7/OSC1/CLKIN)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON // Power-up Timer Enable bit (PWRT enabled)
#pragma config MCLRE = OFF // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is digital input, MCLR internally tied to VDD)
#pragma config BOREN = OFF // Brown-out Detect Enable bit (BOD disabled)
#pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB4/PGM pin has digital I/O function, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EE Memory Code Protection bit (Data memory code protection off)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
#pragma config FOSC = ECIO // Oscillator Selection bits (EC: I/O function on RA6/OSC2/CLKOUT pin, CLKIN on RA7/OSC1/CLKIN)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON // Power-up Timer Enable bit (PWRT enabled)
#pragma config MCLRE = OFF // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is digital input, MCLR internally tied to VDD)
#pragma config BOREN = OFF // Brown-out Detect Enable bit (BOD disabled)
#pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB4/PGM pin has digital I/O function, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EE Memory Code Protection bit (Data memory code protection off)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
/* PIC16F628A pin assign
* pin-01(RA2)
* pin-02(RA3)
* pin-03(RA4) LED
* pin-04(RA5)
* pin-05(VSS)
* pin-06(RB0)
* pin-07(RB1/RX)
* pin-08(RB2/TX)
* pin-09(RB3)
* pin-10(RB4)
* pin-11(RB5)
* pin-12(RB6/T1CLK) Signal input
* pin-13(RB7)
* pin-14(VDD)
* pin-15(RA6)
* pin-16(RA7/CLKIN) Clock input(20MHz)
* pin-17(RA0)
* pin-18(RA1)
*
* 信号をカウントするためにTMR1(16bit)を使用する。TMR1=TMR1H(8bit)+TMR1L(8bit)
* ゲートタイム(1sec)を得るためにTMR2(8bit)を使用する。
*/
* pin-01(RA2)
* pin-02(RA3)
* pin-03(RA4) LED
* pin-04(RA5)
* pin-05(VSS)
* pin-06(RB0)
* pin-07(RB1/RX)
* pin-08(RB2/TX)
* pin-09(RB3)
* pin-10(RB4)
* pin-11(RB5)
* pin-12(RB6/T1CLK) Signal input
* pin-13(RB7)
* pin-14(VDD)
* pin-15(RA6)
* pin-16(RA7/CLKIN) Clock input(20MHz)
* pin-17(RA0)
* pin-18(RA1)
*
* 信号をカウントするためにTMR1(16bit)を使用する。TMR1=TMR1H(8bit)+TMR1L(8bit)
* ゲートタイム(1sec)を得るためにTMR2(8bit)を使用する。
*/
#define LED RA4
#define _XTAL_FREQ 20000000
#define _XTAL_FREQ 20000000
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;
TMR1L = 0;
TMR1H = 0;
/*TMR2の初期化*/
TMR2IF = 0;
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);
}
unsigned long FreqMeassurement(){
static unsigned long freq;
/*TIMERの初期化*/
TMR1IF = 0;
TMR1L = 0;
TMR1H = 0;
/*TMR2の初期化*/
TMR2IF = 0;
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);
}
/*1文字送信 */
void putch(unsigned char byte){
while(!TXIF);
TXREG = byte;
}
void main() {
CMCON = 0x07;//comparator OFF
TRISA = 0b11100000;//RA5-7 input 他はoutput
TRISB = 0b01000110;//RB1,RB2,RB6 input 他はoutput
/*TMR2の設定*/
TMR2IE = 1;
TMR2IF = 0;
TOUTPS0 = 0;
TOUTPS1 = 0;
TOUTPS2 = 0;
TOUTPS3 = 0;
TMR2ON = 0;
T2CKPS0 = 1;
T2CKPS1 = 1;
TMR2 = 0;
/*TMR1の設定*/
TMR1IE = 0;
TMR1IF = 0;
T1CKPS0 = 0;
T1CKPS1 = 0;
T1OSCEN = 0;
TMR1CS = 1;
nT1SYNC = 1;
TMR1ON = 0;
TMR1L = 0;
TMR1H = 0;
/*USART設定*/
TXSTA = 0x24; //送信設定 非同期 高速
RCSTA = 0x90; //受信設定 非同期 継続受信
SPBRG = 129; //クロック20MHz レート9600bps
while(1){
/*周波数の測定*/
LED = 0;//RA4 LED ON
unsigned long frequency = FreqMeassurement();
LED = 1;//RA4 LED OFF
printf("%ld\r\n",frequency);
__delay_ms(500);
}
}
CMCON = 0x07;//comparator OFF
TRISA = 0b11100000;//RA5-7 input 他はoutput
TRISB = 0b01000110;//RB1,RB2,RB6 input 他はoutput
/*TMR2の設定*/
TMR2IE = 1;
TMR2IF = 0;
TOUTPS0 = 0;
TOUTPS1 = 0;
TOUTPS2 = 0;
TOUTPS3 = 0;
TMR2ON = 0;
T2CKPS0 = 1;
T2CKPS1 = 1;
TMR2 = 0;
/*TMR1の設定*/
TMR1IE = 0;
TMR1IF = 0;
T1CKPS0 = 0;
T1CKPS1 = 0;
T1OSCEN = 0;
TMR1CS = 1;
nT1SYNC = 1;
TMR1ON = 0;
TMR1L = 0;
TMR1H = 0;
/*USART設定*/
TXSTA = 0x24; //送信設定 非同期 高速
RCSTA = 0x90; //受信設定 非同期 継続受信
SPBRG = 129; //クロック20MHz レート9600bps
while(1){
/*周波数の測定*/
LED = 0;//RA4 LED ON
unsigned long frequency = FreqMeassurement();
LED = 1;//RA4 LED OFF
printf("%ld\r\n",frequency);
__delay_ms(500);
}
}
今回のトラブルシューティング
T1CONのT1SYNCの書き方が分からず、このままだとエラーになります。
Web上を探しましたが、書き方が分からず。
しかし、T1CONbits.まで入力したら、小さなウインドウが開き、書き方がわかりました。
負論理なのでnT1SYNCと書けばよいのでした。
プログラムがうまく動き、周波数を表示したのですが、32768Hzまでしかう表示できず、それを超えると負表示になります。表示printf("%d\r\n",frequency); では、int(16bit)までしか変換できないのです。
そこで、%dを%ldとしました。これで倍長整数(long)まで変換できるようになりました。