強化されたTimer1のゲート機能を使い、周波数カウンターを作成しました。ゲート機能で、Timer1のカウントを制御することができるため、外部から正確な1Secのゲート信号(RA4)を入力しカウントをOn/Offすれば、簡単に周波数カウンターを実現することができます。ゲート信号には、エプソン社製RTC-4543SAからの1Hzを使用します。 プログラムでは、トグルモードとシングルパルスモードを使いこの正確な1Hzパルスで、入力(RA5)をカウントします。
*注意*
PIC16F1455のI2Cピンは、ICSPピンと同一です。 PICにプログラムするためPICkit3を接続したままにすると、このピンがPICkit3内部で4.7kΩの抵抗でプルダウンされるため、I2C機能が動作しません。 正常に動作させるには、PICにプログラムを書き込んだ後で、PICkit3を回路から取り外す必要があります。
カウント入力はロジックレベルです。アナログ入力アンプやプリスケラを追加すると実用的な周波数カウンターが完成します。
<回路図>
<回路図>
PIC内部のゲート機能を利用しているため、ソフトウエアで、TiGGO/_DONE ビットを「1」にしてゲートを開きカウントを開始させます。1Sec間入力パルスをカウントし終えゲートが閉じ、TiGGO/_DONE ビットがハードウエアにより「0」にリセットされるのを待って、Timer1を読み込み、LCD表示します。なお、高い周波数の信号が入力されるとTimer1の16ビットでは、オーバーフローするため、Timer1からのキャリーで割込みを発生させ、その回数をカウントしています。
<プログラム>
// 周波数カウンタ
// RTCからの1Hzを基準信号とし
// 秋月(ACM1602NI)I2C LCDに表示する。
// 1)Define I/O PORT
// 1Hz : PORTA4 RTCからの基準タイムベース
// IN : PORTA5 計測する入力
// LED : PORTC2 計測処理の表示LED
// --PIC16F1455-- --PIC16F1459--
// SDA : PORTC(1) pin 9 PORTB(4) pin13
// SCL : PORTC(0) pin10 PORTB(6) pin11
// 2)OSC
// 4MHz (内部クロック)
//
// Language: MPLABX XC8
// Target: PIC16F1455
// Copyright (c) 2012 iwamoto All Rights Reserved
//*********************************************************
#include <xc.h>
#define _XTAL_FREQ 4000000
//************* Config ***********************************
#pragma config FOSC = INTOSC, WDTE = OFF, PWRTE = OFF, MCLRE = OFF, CP = OFF
#pragma config BOREN = ON, CLKOUTEN = OFF, IESO = OFF, FCMEN = OFF
#pragma config WRT = OFF, CPUDIV = NOCLKDIV, USBLSCLK = 48MHz, PLLMULT = 3x
#pragma config PLLEN = DISABLED, STVREN = ON, BORV = LO, LPBOR = OFF, LVP = OFF
//********************************************************
void i2cByteWrite(char, char, char); // i2c byte送信
void i2cTxData(char); // i2c SSPBUF セット
void LCD_dat(char); // 1文字表示
void LCD_cmd(char); // コマンド出力
void LCD_clr(void); // 全消去
void LCD_int(void); // 初期化
void LCD_str(char *); // 文字列表示
void LCD_ROMstr(const char *); // ROM文字列表示
void LCD_posyx(char,char); // カーソル位置指定
void LCD_hex(char); // 16進文字変換表示
void ito_st(unsigned long, char *, char);
// --------------------- 共通変数 ----------------------
unsigned int T1Carry;
//---------------------- 割込み処理 ------------------------
void interrupt T1_isr(void){
if(TMR1IF){ //Timer1からの割込みを確認
TMR1IF = 0; // 割込みフラッグをクリア
T1Carry++;
}
}
//////////// Main //////////////////////////////////
void main(void){
char MsgUpper[]= "123,567,901 Hz";
unsigned long T1Count;
OSCCON = 0b00110100; // 内部クロック4Mhz
ANSELA = 0; // PortAをデジタルI/O
TRISA4 = 1; // PortA4をデジタルInput (1Hz)
#if defined (_16F1459) // IC型番でI2Cの端子が異なる
ANSELB = 0; // PortBをデジタルI/Oにする
TRISB = 0b11111111; // PortBを入力I/Oにする
#else
ANSELC = 0; // PortCをデジタルI/Oにする
TRISC = 0b11111111; // PortCを入力I/Oにする
#endif
TRISC2 = 0; // PortC2をデジタルOut (LED)
// ---------------- I2C 初期化 --------------------------------------
SSPCON1 = 0x28; // I2Cマスターモード指定
SSPSTAT = 0x00;
SSPADD = 0x09; // I2Cクロック周波数100KHz for 4MHz
LCD_int(); // LCD初期化
/* ---------------- Timer1 初期化 --------------------------------------
* Timer1 外部信号をカウント プリスケラ 1/1 キャリ発生で割込み
* Gate機能は 外部信号によるトグル・シングル・パルスモード
* T1GGO_nDONE に1を書き込むと一度だけ1sec間ゲートが開き
* Timer1が、外部入力信号をカウントする。
* -------------------------------------------------------------- */
T1CON = 0b10000101; // Timer1 設定
T1GCON = 0b11110000; // Timer1 Gate 設定
TMR1IF = 0; // Timer1 割込みフラッグをクリア
TMR1IE = 1; // Timer1 割込みを使用許可
PEIE = 1; // 周辺機能割込みの使用許可
GIE = 1; // 全割込みの使用許可
while(1){
LATC2 = 0; // LED OFF
TMR1H = 0; // 計測に備えリセット
TMR1L = 0;
T1Carry = 0;
T1GGO_nDONE = 1; // 計測を指示
while(T1GGO_nDONE); // 計測終了を待つ
LATC2 = 1; // LED ON (処理開始)
T1Count = T1Carry; // カウント値集計
T1Count = (T1Count<<8) | TMR1H ; //
T1Count = (T1Count<<8) | TMR1L ; //
ito_st(T1Count, MsgUpper, 11); // 文字列に変換
LCD_posyx(0,0); // 表示位置指定
LCD_str(MsgUpper); // 値を表示
}
}
//////////// LED 表示関数 //////////////////////////////////
//-------- i2cで1byteデータを送信する -----------------------
// 以下の引数が必要
// addr : Slaveのアドレス
// cont : Slaveへ制御コード
// data : 送信するデータ
// NACKやBus衝突などの対応は行っていない
// -----------------------------------------------------------
void i2cByteWrite(char addr, char cont, char data){
SEN = 1; // Start condition 開始
while(SEN); // Start condition 確認
i2cTxData(addr); // アドレス送信
i2cTxData(cont); // 制御コード送信
i2cTxData(data); // データ送信
SSP1IF = 0; // 終了フラグクリア
PEN = 1; // Stop condition 開始
while(PEN); // Stop condition 確認
}
//-------- SSPBUFに1文字保存し送信終了を待つ -----------------
void i2cTxData(char data){
SSP1IF = 0; // 終了フラグクリア
SSPBUF = data; // データセット
while(!SSP1IF); // 送信終了待ち
}
//-------- 1文字表示 --------------------------------------
void LCD_dat(char chr){
i2cByteWrite(0xA0, 0x80, chr);
__delay_us(60); // 60μsec
}
//-------- コマンド出力 --------------------------------------
void LCD_cmd(char cmd){
i2cByteWrite(0xA0, 0x00, cmd);
if(cmd & 0xFC) // 上位6ビットに1がある命令
__delay_us(60); // 60usec
else
__delay_ms(3); // 3msec ClearおよびHomeコマンド
}
//-------- 全消去 ------------------------------------------------
void LCD_clr(void){
LCD_cmd(0x01); //Clearコマンド出力
}
//-------- カーソル位置指定 --------------------------------------
void LCD_posyx(char ypos, char xpos){
unsigned char pcode;
switch(ypos & 0x03){ // 縦位置を取得
case 0: pcode=0x80;break; // 1行目
case 1: pcode=0xC0;break; // 2行目
case 2: pcode=0x94;break; // 3行目
case 3: pcode=0xD4;break; // 4行目
}
LCD_cmd(pcode += xpos); // 横位置を加える
}
//-------- 文字列出力 -----------------------------------------
void LCD_str(char *str){
while(*str) //文字列の終わり(00)まで継続
LCD_dat(*str++); //文字出力しポインタ+1
}
//-------- Rom 文字列出力 ------------------------------------
void LCD_ROMstr(const char *str){
while(*str) //文字列の終わり(00)まで継続
LCD_dat(*str++); //文字出力しポインタ+1
}
//-------- 16進文字変換表示 --------------------------------
void LCD_hex(char c){
const char hexch[] ="0123456789ABCDEF";
LCD_dat(hexch[c >> 4]); //上位4bit表示
LCD_dat(hexch[c & 0xF]); //下位4bit表示
}
//-------- 初期化 --------------------------------------
void LCD_int(void){
__delay_ms(100); // 電源安定するまでの時間
LCD_cmd(0x38); // 8bit 2行 表示命令モード
LCD_cmd(0x0C); // Display on Cursor=0 Blink=0
LCD_cmd(0x06); // Entry Inc/Dec=1 Shift=0
LCD_cmd(0x01); // Clear Display
}
//****** 正整数を文字列に変換する関数 ************************
// num 変換する整数 (ゼロサプレスあり)
// strAdd 変換結果の格納される文字列 (コンマ あり)
// digit 変換される文字数
//************************************************************
void ito_st(unsigned long num, char *strAdd, char digit){
char i;
char flag=1; //ゼロサプレス Flag
char coma=0; //コンマ Flag
strAdd += digit; // 文字列の最後
for(i=digit; i>0; i--) { // 下位桁から処理
strAdd--; // 1桁上に
if (flag == 1){
if (coma++ == 3){ //コンマ の位置か
*strAdd = ','; //コンマ を格納
coma=0;
}else{ // 1桁の数値を文字で格納
*strAdd = (num % 10) + 0x30;
num = num / 10; // 次桁の準備
if (num == 0) flag=0; // ゼロなら以降は抑制
}
}else{ // 上位の0は抑制する
*strAdd = 0x20; // 0の代わりにスペース格納
}
}
}