おもちゃ、家電、もろもろの修理の足跡と備忘録

色々と忘れるので、趣味のメモ

自作の周波数カウンタ ソフトウェア編

2020-08-04 17:27:23 | その他工作
ハードウェア編に続いて、ソフトウェア編です。
 その前にLCDの表示について。
 1段目は、先頭がGateTime表示「*」です。よく見るとパカパカと表示が変わっているので、動いているのがわかります。これは、GateTime表示用のLEDが裏の基板についているので、LCDでわかるようにしました。
 続いて周波数。2段目の「M」「K」「Hz」の位置がその位置です。上記であれば、39M999K990Hzです。
 1段目の右端がGateTimeです。現在は、0.1secです。
 2段目の左端は、Scalerで、現在は1/1です。
 2段めの右端の空いているところは、-455kHzが表示されます(私は多分使わないでしょうけれど、、)

 ソフトウェアはSourceファイルの中に沢山コメントを入れてあるので、見ていただければわかるのではと思います。多分オリジナルとは随分変わっていると思います。私自身がオリジナルを見てわからなかった部分を調べてコメントに加えてあります。
 コンパイルした環境は、MPLAB X v5.35 + XC8 2.20 です。C99に修正してあります。
 オリジナルは、2020/7月時点で既にリンクが切れていて追えませんが、当時コピーしてあった、このカウンタの作成にあたっての考え方?がありましたので、Sourceファイルの中に入れておきました。
 計時カウンタに用いている設定数値ですが、複数のXtalを使っての合わせ込みをしてあります。計算値と多少違いますが、動作用に使っているXtalの精度もわからないので、こんなもんかな、と思っています。実際に設定したときには、40MHzで2ppm位まであっていました。また、実際には、ハードのカウンタ以外にも、割り込みで動くソフトの部分のOverheadがありますが、アセンブラではないので、どのくらいのOverheadがあるのかわかりません。それらを全部含めての合わせ込みですので、まぁ、そんなものかと思います。
 多分、今どきのPICを使うと、カウンタのbit数もでかくなっていると思うので、もっと簡単に作成できるのだろうな、と思いつつ。
 以下、ベタ貼りをすると、インデントが飛んでしまうので、見ずらいですが、ご容赦ください。(うまくプログラムを貼れる方法があったら教えて下さい。)
→2020/8/5追加:htmlで、<pre>...</pre>で囲むと原本のままの表示なります。修正しました。

/* 
 * File:   main.c
 * Author: tomo
 *
 * 2020/7/18 v6
 *  V5でgatetimeが1secと0.1secで、別々にgatetime用カウンタを制御しているので、
 *  0.1->1secに変更すると数値が大きく変わるのが気持ち悪い
 *  0.1secをベースにして、0.1secを10回加算する(平均化する)方法に変更
 *  この方法によると、微調整するにしても一か所で済む
 *  @1sec(10times of gatetime 0.1s
 *              40,000,000	39,999,920	-80     -2.000ppm
 *               6,000,000	 5,999,993	 -7 	-1.167ppm
 */

#define INITIAL_CREDIT1 "Freq Counter v6"
#define INITIAL_CREDIT2 "20200718a"

/*
 * この周波数カウンタのもとは以下のInHisTimeのV6。
 * 当初(2018年頃)Tryしたときには、開発環境が変更->MPLABXされていて、
 * Compilerも->XC8に変更されていたので頑張ってPortingした。
 * しばらく放置していたが、2020/6頃から再開。
 * 再度環境が変わっていた(C90->C99)ので再度調整
 * 
 * 2020/6/26 MPLAB X v5.35 + XC8 2.20 でCompileできた。
 * ・Interrupt の書き方の変更
 * ・asmの書き方の変更
 * ・ultoaのbufのunsignedを削除
 */
 
/* 
オリジナルは以下のホームページ(InHisTime)。
閉鎖されてしまっているので、もう、追えない。

オリジナルとの変更点:
1)TCXO→Xtalへ
2)Compilerの変更XC8 with MPLABX
3)GateTime 0.1sec 1secでの処理の一本化(1sec=0.1x10回)
4)LCDの表示(M,Kを表示、その他表示場所の変更)
5)LCDへのGate中表示(Gate中はカウンタ値の先頭に「*」を表示)
他
----
http://www8.plala.or.jp/InHisTime/page078.html
【PIC16F88】

 ■周波数カウンタV7
  ★概要
    以前に、周波数カウンタV5(集大成版)を作成し、ほぼ終了と思っていましたが、更に検討を進めるうちに、
    ちょっとした工夫で、思わぬ効果を得ることが出来ました。
     ◎プリスケーラ無(1/1)で、40MHzまで測定可能(実測値)
     ◎プリスケーラ有(1/8)で、80MHzまで測定可能(実測値)

  ★動作原理
    PICを使用した周波数カウンタでは、一般的には、TIMER0とTIMER1を組み合わせて使います。
     ◆信号をカウントするために、TIMER0(8ビット)を使用(外部クロック入力同期ON固定)
     ◆ゲートタイム(1秒、0.1秒)を得るために、TIMER1(16ビット)を使用
    しかし、TIMER0は、TIMERのON/OFF制御が出来ないため、入力ピンを強制的に出力モードにして“0”を出力
    する等の工夫(本来は余分な事)が必要となります。

    そこで、TIMERでON/OFFの出来る、TIMER1とTIMER2を組み合わせて使いました。
     ◆信号をカウントするために、TIMER1(16ビット)を使用(外部クロック入力同期制御ON/OFF可能)
     ◆ゲートタイム(1秒、0.1秒)を得るために、TIMER2(8ビット)を使用→計時カウンタ

    特に、外部クロック入力同期制御をOFFにすることが肝心で、もしもこれをONにすると、20MHzの信号でも
    1/8のプリスケーラに設定しないと測定が出来ません。多分、TIMER0方式でのカウントが高い周波数まで
    伸びないのは、外部クロック入力同期制御がON固定に原因があるのかもしれません。
 
     ■Timer2の計時カウンタ割り込みで行い、Timer1の信号カウンタはソフトウェアでカウント
*/

#include 
#include 
#include 
#include "LCDlib2.h"

//--------------------------------------
// コンフィグレーションビットの指定
//--------------------------------------

// CONFIG1
#pragma config FOSC = HS        // Oscillator Selection bits (INTRC oscillator; port I/O function on both RA6/OSC2/CLKO pin and RA7/OSC1/CLKI pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON       // Power-up Timer Enable bit (PWRT enabled)
#pragma config MCLRE = ON       // RA5/MCLR/VPP Pin Function Select bit (MCLR enabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF        // Low-Voltage Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EE Memory Code Protection bit (Code protection off)
#pragma config WRT = OFF        // Flash Program Memory Write Enable bits (Write protection off)
#pragma config CCPMX = RB3      // CCP1 Pin Selection bit (CCP1 function on RB3)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)

// CONFIG2
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor disabled)
#pragma config IESO = OFF       // Internal External Switchover bit (Internal External Switchover mode disabled)

///////////delay関数を使うために発振周波数を定義 1Mhz////////////
#define _XTAL_FREQ 20000000

//********************************************************************** 
/*
	<周波数カウンター>
 ■機能
  sw1:プリスケーラの切り替え 
    ・sw1=1 1/1 
    ・sw1=0 1/8 
  sw2:ゲートタイムの切り替え
    ・sw2=1 1秒
    ・sw2=0 0.1秒
  sw3:-455kHzの有無切り替え 
    ・sw3=1 -0kHz
    ・sw3=0 -455kHz 
  sw4:表示レンジの切り替え
    ・sw4=1 Hz表示
    ・sw4=0 kHz表示
 ■コンフィグ設定
  LVP_OFF
  MCLR_OFF
  WDT_OFF
  EXTCLK
 ■ピンアサイン (16F88)
  Pin-01 RA2/LCD:D5 
  Pin-02 RA3/LCD:D4 
  Pin-03 LED
  Pin-04 /MCLR to VDD(+5V) via 10kOhm 
  Pin-05 Vss to GND
  Pin-06 RB0/プリスケーラの切替SW
  Pin-07 RB1/ゲートタイムの切替SW
  Pin-08 RB2/455kHzの切替SW
  Pin-09 RB3/表示レンジの切替SW
  Pin-10 RB4/LCD:RS
  Pin-11 RB5/LCD:WR
  Pin-12 RB6/Signal
  Pin-13 RB7/LCD:Enable
  Pin-14 Vdd to +5V 
  Pin-15 Xtal @ 20MHz
  Pin-16 Xtal @ 20MHz 
  Pin-17 RA0/LCD:D7 
  Pin-18 RA1/LCD:D6 
*/
//********************************************************************** 

#define	sw1	RB0
#define sw2	RB1
#define sw3	RB2
#define sw4	RB3
#define LED	RA4


#define	GATETIME_100MSEC	10
#define GATETIME_1SEC		1

//********************************************************************** 

static	unsigned	int	MeasurementCnt; //16 bits

void __interrupt () myint (void)
//TMR2用の計時割り込み処理 一回走行した後に来る割り込み
//MeasurementCntが0になるまで待つ
{
	PIR1bits.TMR2IF = 0;        //計時用割り込みフラグクリア
	//
	MeasurementCnt--;           //計時用カウンタdecrement
	if (MeasurementCnt == 0) {  //計時用カウンタがゼロなら
		T1CONbits.TMR1ON = 0;	// 信号用ゲート停止
		T2CONbits.TMR2ON = 0;	// TIMER2を停止する。 
	}
}

//********************************************************************** 

unsigned	long	FreqMeasurement(unsigned char gateTime)
{
	static	unsigned	long	freq;
	// unsigned long → 32bit = 4,294,967,295(4G)
	// TIMER1の設定	→信号カウンタ
	PIR1bits.TMR1IF = 0;    //信号割り込みフラグクリア→割り込み禁止
	TMR1L = 0;              //下位16bit
	TMR1H = 0;              //上位16bit
	// TIMER2の設定 →計時カウンタ
	PIR1bits.TMR2IF = 0;    //計時カウンタ割り込みフラグクリア→割り込み禁止
	MeasurementCnt = 123;
        TMR2 = 0xED;        	// 2020/7/18調整
//	TMR2 = 0xEE;		// 31250=0.1/((1/20000000) * 4 * 16)
				// 0xEE=256-(31250-(256*122))
	//
	freq = 0;
	// 割り込みを許可する。 
	INTCONbits.PEIE = 1;
	INTCONbits.GIE = 1;
	// 開始
	T2CONbits.TMR2ON = 1;	//計時タイマを開始する。

	T1CONbits.TMR1ON = 1;	//信号カウンタゲートを開ける。
    
	// 測定 
	while (T2CONbits.TMR2ON != 0) {     //計時カウンタが未Over
		if (PIR1bits.TMR1IF == 1) {     //1=信号カウンタがOverflowed
			PIR1bits.TMR1IF = 0;        //Clearしてあげる
			freq++;                     //信号カウンタをカウントアップ
		}
	}
    	//T1 timerを閉じる処理が抜けていたか@2020/7/9追加
    	T1CONbits.TMR1ON = 0;               //信号カウンタゲートを閉じる。
    
    	//計時カウンタがOver(1secないしは100msec経ったら)したらここに来る
    	//再度最後にTMR1のOverflowをチェック
	if (PIR1bits.TMR1IF == 1) {         //信号カウンタ割り込み
		PIR1bits.TMR1IF = 0;            //信号カウンタ割り込みクリア
		freq++;
	}

	//換算 16bits=65536
    	//freqはTMR1のOverflowを数えているので、16bitをかけて、残りの端数を加える
	freq = freq * 65536;
	freq = freq + ((unsigned)TMR1H * 256) + (unsigned)TMR1L;
	//実周波数としてfreqを返す
	return (freq);
}

//********************************************************************** 

void main()
{
	static	char*	msg;
	static	unsigned	long	freq, temp;// 32bit 0...4294967295
	static	char	buf[11], fmtbuf[11], prescaler, gateTime;
    	int i;
// アナログの設定
	ANSEL  = 0b00000000;		// 使用しない。
// ポートの設定 1=input
	TRISA  = 0b11100000;    	//p16=RA7=OSC1=in, p15=RA6=OSC2=in, p4=RA5=unuse=out
	TRISB  = 0b01001111;
        
    	OPTION_REGbits.nRBPU = 0;   	//Enable pullup resistors on portB
// TIMER2の設定 → 計時ゲート
	PIE1bits.TMR2IE = 1;
	PIR1bits.TMR2IF = 0;
	T2CONbits.TOUTPS0 = 0;
	T2CONbits.TOUTPS1 = 0;
	T2CONbits.TOUTPS2 = 0;
	T2CONbits.TOUTPS3 = 0;
	T2CONbits.TMR2ON = 0;
	T2CONbits.T2CKPS0 = 1;
	T2CONbits.T2CKPS1 = 1;  	//Timer 2 pre-scaler = *16
	TMR2 = 0;
// TIMER1の設定 → 信号カウンタ
	PIE1bits.TMR1IE = 0;
	PIR1bits.TMR1IF = 0;
	T1CONbits.T1RUN = 0;
	T1CONbits.T1CKPS0 = 0;
	T1CONbits.T1CKPS1 = 0;
	T1CONbits.T1OSCEN = 0;
	T1CONbits.nT1SYNC = 1;
	T1CONbits.TMR1CS = 1;
	T1CONbits.TMR1ON = 0;
	TMR1L = 0;
	TMR1H = 0;
// 変数の初期化 
	prescaler = 1;
	gateTime = GATETIME_1SEC;

// LCD初期化
	lcd_init();
	
// LCD画面クリア
	lcd_cls();
	
	// LCDに文字列出力
	lcd_locate( 0, 0 );
	lcd_puts( INITIAL_CREDIT1 );
	lcd_locate( 1, 0 );
    	lcd_puts( INITIAL_CREDIT2 );
    
    	__delay_ms(2000);
    
    	lcd_cls();
    
	while (1) {				//loop forever
// 周波数の測定 
        msg =  "*";
		lcd_locate( 0, 0 );
        lcd_puts( msg );
		LED = 1;			//count中表示の点灯
        if (gateTime == GATETIME_1SEC){     	//gateTime 1sec = 10 times of 0.1s
            freq = 0;
            for (i = 1; i < 11; ++i)
                {
                    temp = FreqMeasurement(GATETIME_100MSEC); 
                    freq = freq + temp;
            }
        }else{
            freq = FreqMeasurement(GATETIME_100MSEC);   //count指示
        }
		LED = 0;			//count後表示の消灯
        msg =  " ";
		lcd_locate( 0, 0 );
        lcd_puts( msg );
	//換算 
	freq = freq * prescaler * gateTime;

	//LCD1602の表示
	//0123456789012345(16桁)
	//----------------
	//*123456789 0.1s (0 line)
	//1/8M  K  Hz-455K(1 line)
        
	// プリスケーラの切り替え
	if (sw1 == 1) {
		T1CONbits.T1CKPS0 = 0;
		T1CONbits.T1CKPS1 = 0;
		prescaler = 1;
		msg = "1/1M  K  Hz";
	} else {
		T1CONbits.T1CKPS0 = 1;
		T1CONbits.T1CKPS1 = 1;
		prescaler = 8;
		msg = "1/8M  K  Hz";
	}
	lcd_locate( 1, 0 );
	lcd_puts( msg );

	// ゲートタイムの切り替え
	if (sw2 == 1) {
		gateTime = GATETIME_1SEC;
		msg = "1sec ";
	} else {
		gateTime = GATETIME_100MSEC;
		msg = "0.1s ";
	}
        lcd_locate( 0, 11 );         	// 1,4 -> 0,11 2020/7/9
        lcd_puts( msg );
            
	// ?455kHzの有無 
	if (sw3 == 0) {
		freq -= 455000;
		msg = "-455k";
	} else {
		msg = "     ";
	}
	lcd_locate( 1, 11 );
	lcd_puts( msg );

	// 表示レンジの切り替え 
	if (sw4 == 1) {
        	ultoa( buf, freq, 10);
		//	msg = "Hz ";
	} else {
		temp = freq / 1000;
		if ((freq - (temp * 1000)) > 500) {
			temp++;
		}
        freq = temp * 1000;             // M/Kを2列目に表示することにしたので
                                        // kHzのときも、000を加えて10桁で表示
                                        // 2020/7/9
        ultoa( buf, freq, 10 );
        }

        // 周波数の表示 
        lcd_locate( 0, 0 );
	ultoa(buf, freq, 10);
	sprintf(fmtbuf, "%10s", buf );
	lcd_puts( fmtbuf );

	__delay_ms(10);
        }
}
//********************************************************************** 










ロータリーエンコーダの清掃 FT-100修理(その2)

2020-08-02 18:15:08 | 無線
2018の書き込みで、FT-100のロータリーエンコーダの清掃、というのを書いたのですが、2年もするとやはり同じ症状(飛ぶ)になりました。
 懲りもせず、また分解して清掃しました。今度は、サンハヤトの接点ブライト、で清掃しました。接点ブライト、これ、強力です。おもちゃの病院で使っていますが、スライドスイッチなどのちょっとした接点不良だったら、スイッチを分解しなくても直ってしまいます。ホームページを見ると、使用後は水等で洗うこと、との記載があります。その手の液剤ですので、使用にはある程度注意が必要です。
 ということで今回はどのくらい持つか、また「その3」を書くことになりそうです。本機、新スプリアス規制対応ができないようなので、マイクも外して受信専用で使っています。
---
FT-100ですが、局面の更新もあるので、その前に変更申請をしようと、ダメもとで八重洲のサポートに、出力下げたり、フィルタ入れたりして新スプリアス規制対応できないの?と問い合わせをしたら、八重洲側で部品の追加や改造によって新スプリアス規制に適合させることはできかねる内容である、との回答が来ました。
ということで、受信専用機決定です、、、。
2020/11/13追記」

空気清浄機の修理

2020-08-02 17:38:11 | 修理

 空気清浄機の光クリエール(ダイキン製)の赤外線リモコンがおかしい、というので、修理。
 まず、発光側は、自作の赤外線受光器でコードがちゃんと出ているかどうかは不明だが、とりあえず発光しているようなので、まぁ、大丈夫だろうということで、装置本体側、受光部側を点検。
 製造年を見ると99年、つまり1999とある。20年選手ですね。一度サポートに連絡して、消耗品(イオン化線とか)を交換した覚えがあります。
 今回もサクッと分解してホコリを清掃し、制御部分を引っ張り出します。分解そのものは裏の4本のネジと、前蓋を開けて、フィルタを取り外した下側に2本、正面下のコントロールパネルの取り外しに2本で全バラできます。内部には高圧発生部分があるので、必ず電源ケーブルは抜いてから。
 正面下のコントロールパネルは、ネジを2本外したら、底面側で引っ掛けてあるので、それを外すと簡単に外れます。
 
 この基板です。中央上の黒い四角いのが赤外線受光部です。回路を追うと、基板上のシルクでいうと、GはGND、Oは信号OUT、Iは電源(5V)です。念の為に信号OUTを以前作った簡易オシロで確認。写真を取り忘れましたが、正常に動くこともありますが、赤外線の入力がなくてもバタバタ信号が0V、5V暴れる、5V(信号なし)でノイズが乗る、というように明らかに不良です。これを手持ちの赤外線受光モジュール(トイレのリモコン受光部の交換の記事を参照)と交換。
 足がGNDと信号OUTが逆(トイレの修理も同様、逆でした)なので、足を入れ替えてはんだ付け、動作確認して完了です。
 赤外線受光モジュール、そんなに壊れるものとは思っていませんでしたが、こうやって見ると結構壊れるものなのですね。
 光触媒フィルタもネットで探してみたらまだ入手できるようです。ダイキンさんはこういう保守品もきちんと流通させてくれているので嬉しいです。我が家ではダイキン製品は、エアコン、エコキュートなども使っていてダイキンファンです。
 最後に、お決まりですが、本記事は自身の備忘録です。家電製品の分解、修理等をすすめるものではありません。特に本装置は内部に高圧(3kV)部分があります。もし、本稿を参照して分解、修理する場合はご自分の責任、判断でお願いします。