/******************************************************* * LCメータ *  セグメントLCDで表示 PIC16F1936 * ファイル名:PIC16F_LC_METER.c *      (オリジナルは、LCMeter2.c) * * オリジナル作成者:後閑氏 *========================================-================ * 220127:N.Ishii * (1) オリジナルは、Hi-tech-PLCCコンパイラ使用 *   これを、XC8コンパイラ使用の記述に変更してみた。 * (2) コメント追加等、整理 **********************************************************/ #include #include "lcd_def2.h" /// コンフィギュレーション1の設定 #pragma config FOSC = XT // 外部水晶を使用する #pragma config WDTE = OFF // ウオッチドッグタイマ無し(OFF) #pragma config PWRTE = ON // 電源ONから64ms後にプログラムを開始する(ON) #pragma config MCLRE = ON // 外部リセット信号使用 #pragma config CP = OFF // プログラムメモリを保護しない(OFF) #pragma config CPD = OFF // データメモリを保護しない(OFF) #pragma config BOREN = ON // 電源電圧降下常時監視機能ON(ON) #pragma config CLKOUTEN = OFF // CLKOUTピンをRA6ピンで使用しない(OFF) #pragma config IESO = OFF // 外部・内部クロックの切替えでの起動はなし(OFF) #pragma config FCMEN = OFF // 外部クロックク監視しない(OFF) /// コンフィギュレーション2の設定 #pragma config WRT = OFF // Flashメモリを保護しない(OFF) #pragma config VCAPEN = OFF // 低電圧レギュレータ用のキャパシタは使用しない(OFF) #pragma config PLLEN = OFF // 動作クロックを32MHz(4xPLL)では動作させない(OFF) #pragma config STVREN = OFF // スタックがオーバフローやアンダーフローしたらリセットしない(OFF) #pragma config LVP = OFF // 低電圧プログラミング機能使用しない(OFF) /// グローバル定数変数定義 #define _XTAL_FREQ 4000000 #define PI 3.141516 unsigned int i, Interval; unsigned char number, Digit[5], WaitFlag; float Freq1, Freq2, Freq3, Freq4, temp1, temp2; float C1, CX, L1, LX; /// 関数のプロトタイピング void Calibrate(void); void Display(float value); void delay_100ms(unsigned int time); void LCDTest(unsigned int data); void SetDigit(void); float GetFreq(void); void ftostring(int seisu, int shousu, float data, char *buffer); ///// メイン関数 void main(void) { /// 入出力ポートの設定 TRISA = 0x08; // RA3のみ入力設定 TRISB = 0x0E; // RB1,2,3のみ入力 TRISC = 0x01; // RC0,T1CKI以外出力 TRISE = 0xFF; // RE3のみ使用 ANSELA = 0x08; // RA3以外デジタル ANSELB = 0x02; // RB1以外デジタル OPTION_REGbits.nWPUEN = 0; // プルアップ有効化 WPUB = 0x0C; // RB2,3 pullup /// コンパレータの設定 発振回路を構成 CM1CON0 = 0xA4; // C1OUT On. Hi Speed // b7: C1ON: 1:コンパレータが有効 // b6: C1OUT: 0: b4:CxPOL=0(極性非反転)の場合、CxVP < CxVN // b5: C1OE: 1: C1OUT が C1OUT ピンに現れる。 // b4: C1POL:0: コンパレータ出力は非反転 // b3: 0:未実装 // b2: C1SP: 1: 標準電力の高速モードで動作する // b1: C1HYS:0: ヒステリシス機能は無効 // b0: C1SYNC:0: Timer1 および I/O ピンに対してコンパレータ出力は非同期 CM1CON0 = 0xA4; CM1CON1 = 0x03; // C1IN+, C12IN3- CM2CON0 = 0; // CM2無効化 /// LCDドライバ初期設定 LCDCON = 0x02; // 1/3 MPLX, Fosc/256 LCDPS = 0x00; // Type A, PS=1/1, 3BIAS LCDREF = 0x80; // 内部コントラスト LCDCST = 0x07; // 最小コントラスト指定 LCDSE0 = 0xE9; // SEG0,3, SEG5,6,7を使用 LCDSE1 = 0x7F; // SEG8 to SEG14を使用 LCDRL = 0x50; // Low Power, 常時 B mode LCDEN = 1; // Start LCD Driver /// 全セグメント消去 LCDDATA0 = 0; LCDDATA1 = 0; LCDDATA3 = 0; LCDDATA4 = 0; LCDDATA6 = 0; LCDDATA7 = 0; /// タイマ1:16bitカウンタ(TMR1H+TMR1L)の初期設定 外部カウンタ T1CON = 0xA0; // T1CKI, Sync, 1/4, TMR1OFF // b7: クロックソース選択ビット: 1: T1OSCEN = 0 の場合T1CKI ピンからの外部クロック ( 立ち上がりエッジ) // b6: クロックソース選択ビット: 0: // b5: Timer1 入力クロック プリスケール選択ビット: 1: 1/4 // b4: Timer1 入力クロック プリスケール選択ビット: 0: // b3: LP オシレータ イネーブル制御ビット: 0: 無効 // b0: Timer1 オン ビット: 1: Timer1 を停止 ////// TIMER1 ゲート制御レジスタ T1GCON = 0; // GE Off /// タイマ2初期設定:20mS周期設定: Fosc=4MHz 1MHz/(16*5)=12500Hz/250=50Hz /// タイマ2は、周波数カウンタとしての、タイマ1の正確な、0.5秒ゲートとして使う。 /// 0.5秒間入力クロックを取込んでタイマ1でカウントし、0.5秒経過後タイマ1カウントを停止する。 /// つまり、1秒毎の計測となる。 TMR2 = 0; // カウンタリセット T2CON = 0x22; // プリスケール1/16, ポスト1/5 PR2 = 249; // 250カウントで50Hz周期設定 TMR2IF = 0; // 割り込みフラグクリア TMR2IE = 1; // タイマ2割り込み許可 PEIE = 1; // 周辺割り込み許可 GIE = 1; // グローバル割り込み許可 /// 変数初期化 RC1 = 0; // リレーオフ number = 0; // LCDテスト用数値初期化 Calibrate(); // ゼロ調整実行 //// メインループ while (1) { //// テストモードチェック if(PORTBbits.RB3 == 1){ // ジャンパチェック -> OPEN時はテストモード if(PORTBbits.RB2 == 0){ // 切り替えスイッチチェック -> L側の場合 /// LCD テストを実行 LCDTest(number++); // 数値表示実行 if(number > 9) // 数値上限チェック number = 0; // ゼロに戻す delay_100ms(10); // 繰り返し遅延 1sec } else{ /// C側の場合 /// 周波数表示テストを実行 Freq1 = GetFreq(); // 周波数取得 Display(Freq1*10); // LCDに表示 0.1kHzまで表示 DP2 = 1; // 小数点表示 delay_100ms(10); // 繰り返し周期遅延 1sec } } else{ //// ジャンパチェック -> CLOSE時は、LCDメータ機能モード if(PORTBbits.RB2==1){ // L,C切り替えチェック /// Cの場合 Freq3 = GetFreq(); // 測定時の周波数取得 CX = ((Freq1/Freq3)*(Freq1/Freq3)-1)*C1; if(CX < 0) // CXが負になったら0にする CX = 0; Display(CX); // 測定値表示 単位pFかuF DP4 = 1; // 小数点表示 DP5 = 0; // 小数点消去 DP2 = 0; // 小数点消去 Minus = 0; // マイナス記号消去 } else{ /// Lの場合 Freq4 = GetFreq(); // 周波数取得 LX = ((Freq1/Freq4)*(Freq1/Freq4)-1)*L1; if(LX > 10000) // 10mH以上なら未接続で0とする LX = 0; Display(LX*10); // 測定値表示 単位uH Minus = 1; // マイナス記号表示→ これはたぶん、C/L切替え確認の為の表示。 DP5 = 0; // 小数点消去 DP4 = 0; // 小数点消去 DP2 = 1; // 小数点表示 } /// 繰り返し目印表示 -> 「CONTINUITY」セグメントを、1秒間カウント毎に、ブリンク表示(目安) if(CON) // 反転表示 CON = 0; else CON = 1; delay_100ms(10); // 繰り返し周期遅延 1sec } } } /*************************************** * データ表示関数 ****************************************/ void Display(float value){ unsigned long Data; Data = (unsigned long)value; // 型変換 float→long if(Data > 19999){ // 表示オーバーか? Data /= 1000; //単位変更 pF→uF } /// LCDへ表示 Digit[0] = Data % 10; // 1桁目セット Data /= 10; Digit[1] = Data % 10; // 2桁目セット Data /= 10; Digit[2] = Data % 10; // 3桁目セット Data /= 10; Digit[3] = Data % 10; // 4桁目セット Data /= 10; Digit[4] = Data; // 5桁目セット SetDigit(); // 表示実行 } /************************************** * 初期較正実行関数 * 何も接続されていない状態とする ***************************************/ void Calibrate(void){ RC1 = 0; // リレーオフ:C= 1000p delay_100ms(5); // 0.5sec Freq1 = GetFreq(); // 周波数1:Freq1取得 単位はkHz RC1 = 1; // リレーオン:C= 1000p//1000p= 2000p delay_100ms(5); // 0.5sec Freq2 = GetFreq(); // 周波数2:Freq2取得取得 単位はkHz RC1 = 0; // リレーオフ delay_100ms(5); // 0.5sec /// C1とL1を求める 基準1000pFは補正し1020pFとしている temp1 = Freq1 * Freq1; // Freq1の二乗 temp2 = Freq2 * Freq2; // Freq2の二乗 C1 = (temp2 * 1020) / (temp1 - temp2); // 単位 pF 基準CをpFにしているので、pFになる。 L1 = 1000000000000.0/(4.0 * PI * PI * temp1 * C1); // 単位 uH } /*************************************** * Timer2 割り込み処理 500msecゲート ****************************************/ void interrupt isr(void) { if(TMR2IF){ // タイマ2の割り込み確認 TMR2IF = 0; // 割り込みフラグクリア Interval++; // 回数カウントアップ if(Interval == 1) // 1回目の場合 TMR1ON = 1; // タイマ1カウント開始 if(Interval > 25){ // 26回目の場合 500msec経過(t2周期=20mS x 26-1) TMR1ON = 0; // タイマ1カウント停止 WaitFlag = 0; // 待ちフラグ解除 Interval = 0; // 回数カウンタリセット } } } /***************************************** * 周波数カウントサブ関数 単位kHz *  500msec間 タイマ1でカウント *****************************************/ float GetFreq(void){ int Upper; float temp; Upper = 0; // 桁上げカウンタクリア TMR1IF = 0; // 割り込みフラグクリア TMR1H = 0; // タイマ1カウンタクリア TMR1L = 0; // タイマ1カウンタクリア Interval = 0; // 0.5秒カウンタクリア WaitFlag = 1; // 0.5秒待ちフラグオン TMR2ON = 1; // ゲートタイマ2スタート /// この間で0.5秒間カウント while(WaitFlag){ // 0.5秒待ち if(TMR1IF){ // タイマ1オーバーフローか? // 16bitなので、65536でオーバーフロー Upper++; // オーバーフロー回数カウント TMR1IF = 0; // フラグクリア } } TMR2ON = 0; // タイマ2停止 /// 周波数を取得し返す 0.5sec, prescaler 1/4 temp = (float)TMR1H * 256.0 + (float)TMR1L; // 0.5秒間カウント終了後の、Upperを含めないカウント値をテンポラリに保存 // TMR1H * 256.0は、左に8bitシフトしているのと同じこと。 // それと、TMR1Lを結合し、16bitのカウント値をfloatに型変換している。 return((Upper * 65536.0 + temp) / 125); // 単位kHz *2*4/1000 // Upper * 65536.0 + tempで、トータルのカウント値になる。 // 0.5秒間のカウント値なので周波数変換するには1秒間必要で、2倍する。 // またカウンター入力を1/4しているので入力のカウント値は4倍する必要がある。 // 単位をkHzにするために、1000で割る。 // *2*4/1000= 1/125ということ。 } /****************************** * 100msec遅延関数 ******************************/ void delay_100ms(unsigned int time) { time *= 4; // 4倍 while(time){ __delay_ms(25); // 25msec time--; // 100msec x time } } /************************************* * LCD数値表示関数 * 変数 Digit[5]で5桁数値指定 **************************************/ void SetDigit(void){ while(!WA){}; // レディー待ち LCDDATA0 = 0; // 数値部いったん消去 LCDDATA1 = 0; LCDDATA3 = 0; LCDDATA4 = 0; /// 各桁の設定 Digit5 = Digit[4]; LCDDATA0 = Digit4[Digit[3]][0] | Digit3[Digit[2]][0] | Digit2[Digit[1]][0] | Digit1[Digit[0]][0]; LCDDATA1 = Digit4[Digit[3]][1] | Digit3[Digit[2]][1] | Digit2[Digit[1]][1] | Digit1[Digit[0]][1]; LCDDATA3 = Digit4[Digit[3]][3] | Digit3[Digit[2]][3] | Digit2[Digit[1]][3] | Digit1[Digit[0]][3]; LCDDATA4 = Digit4[Digit[3]][4] | Digit3[Digit[2]][4] | Digit2[Digit[1]][4] | Digit1[Digit[0]][4]; LCDDATA6 &= 0xA8; // 小数点等固定部は残し数値部変更 LCDDATA6 |= Digit4[Digit[3]][6] | Digit3[Digit[2]][6] | Digit2[Digit[1]][6] | Digit1[Digit[0]][6]; LCDDATA7 &= 0x3D; // 小数点等の固定部は残し数値部変更 LCDDATA7 |= Digit4[Digit[3]][7] | Digit3[Digit[2]][7] | Digit2[Digit[1]][7] | Digit1[Digit[0]][7]; } /*********************************** * LCDテスト関数 * 0から9まで順次表示 他の表示は交互 ***********************************/ void LCDTest(unsigned int data){ while(!WA){}; // レディー待ち Minus = data % 2; // 0,1交互 DP2 = data % 2; DP3 = data % 2; DP4 = data % 2; DP5 = data % 2; CON = data % 2; Low = data % 2; Digit[0] = data; // 数値セット Digit[1] = data; Digit[2] = data; Digit[3] = data; Digit[4] = data % 2; // 0.1交互 SetDigit(); // 表示実行 }