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

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

車載AudioのAUX入力の増設

2021-04-10 07:30:12 | その他工作
   15年ほど乗っている我が家の車の、FMラジオ(いわゆるカーステレオってやつです。純正ご用達品)の感度がなぜか下がって来たようで、家の近くの街乗りでノイズが混じるようになってきたのが気になっていました。スマホの音を車載Audioに入力できないかなと、以前FMトランスミッタでやってみて、音が悪すぎで断念。カーステレオに増設用のI/Fがあることはわかっていたのですが、なかなか手を出さずに今日まで来ました。
 だんだん聞こえが悪くなってきて、じゃ、やるか、と色々と調べていたら、2012年ころにshiozokuさんが同じようなことをやってたのを発見。いろいろと調べてゆくと、うちの同じカーステレオの拡張I/Fも見つかり、比べてみると同じ系列のカーステレオのようだとわかりました。
 ここまでわかればなんとかなるだろうと、回路を作って、ソフト(shiozokuさんはAVRで作られたようですが、PIC用に修正)を作ってみました。以下それの備忘録です。
 動作についての説明と、ソフトウェア、ハードウェア(制御部/アナログ部)について記述します。

動作概要:
 うちの車のカーステレオは、松下さんのモデルNo.RM-A51SAQというTC24というセレナに乗っているものです。これにはAUX入力用のコネクタがあって、そこへ外部からのAUX_ONというレベル信号(+12V)を与えてやると、AUXモード(LCDにAUXと表示される)に遷移します。また外部へのRADIO_ONというレベル信号が出力されており、カーステレオの本体機能が動作するとレベル信号(+12V)が出力されます。この時、外部の装置は、AUX_ON信号をReset(OFF/0V)する必要があります。AUX_ONをResetしないとAUXモードから抜けられない状態、RADIOが聞けない状態になります。つまり外部からのAUX_ON信号が優先されるようです。
 shiozokuさんのソフトは、上記のRADIO_ON信号を監視していて、カーステレオ機能がONの状態と、AUXモードが電源OFFを挟んで交互に動作するようになっていて、本体の電源ボタンの押下によってそれが切り替わる、つまり外部にボタンが必要ないことになります。例えばカーステレオがOnの状態で、電源ボタンを二度押すと、OFF状態を経由してAUXモードになります。AUXモードで、電源ボタンを二度押すと、OFF状態を経由してカーステレオが動作します。また、おまけで、AUXモードの時に、ラジオのボタンなどを押すとカーステレオ側の機能が優先されて切り替わります。純正っぽい。

ソフトウェア:
 ソフトウェアは、AVR用のものをPIC用に一部追加、修正しました。
 ソフトの内部では、mode_stateで状態を管理していて、外部からのイベントでmode_statusを遷移させます。
  • MAIN_OFF:カーステレオの電源が切れている状態
  • MAIN_RADIO:カーステレオ本体側が動作している状態
  • MAIN_AUXSW:前の状態がカーステレオ本体が動作していて、その後電源がOFFになり、次に電源が入った時にはAUXに切り替える状態
  • MAIN_AUX:外部装置が動作している状態、AUXモード
これをforとswitchで回しています。
shiozokuさんとの差分:
shiozokuさんの方ではモードをEEPROMに記憶していますが、面倒なので、スキップ。カーステレオ本体側で、電源が切れた時のカーステレオの動作モードを覚えている(RADIOがONかOFFかだけ。AUXは忘れる、、)ので、本装置が起動時にRADIO_ONを監視していて、RADIO_ONであればそのモードに強制的に遷移させています。逆に、AUXモードで電源が切れた場合は、MAIN_OFF状態にしてあるので、次に電源ボタンが押されるとカーステレオが起動します。
開発環境:
 MPLAB X IDE/5.35, XC8/2.20, C99

2021/4/12写真入れ替え(状態遷移追加)

ハードウェア(制御部分):
 ハードウェアは、PIC12F675(8pin)を用いました。扱う信号線が少ないのと、単に手元にあったから、という理由です。これも古いPICです。
 RADIO_ONという12Vのレベル信号入力と、AUX_ONという12Vのレベル信号出力、電源12V(ACC:アクセサリからとりました)、が外部とのI/Fになります。
 RADIO_ONは、12Vを抵抗で5V相当に分圧して直接PICのPortに入力しています。車の電源は15V位になることがあるので、2:1にしてあります。12Vになっても4VあればHigh判定してくれます。
 AUX_ONは、PICのPort出力を2SC1815と2SA1015のコンプリメンタリの石を使ってドライブしています。
 電源は、PIC用に5Vのレギュレータを入れてあります。
 ICEのコネクタをつけようかと思って回路図に記載はしていますが、PICが8pinなので、外せばいいやと面倒になったので基板上には実装していません。
ハードウェア(アナログ部分):
 アナログ部分は、スマホの音声出力を直接カーステレオのAUX入力に入れても音が小さい、ということで、OpeAMPを使って増幅しています。OpeAMPはこれまた古い、手元にあったML307Sという8pinで1chを二個使いました。
 回路はマルツさんの「■オペアンプ 単電源1段アンプの設計手順 」というのを参考にして、手元にあった部品とにらめっこして作成してあります。反転増幅になっていますが、聞いてもわからんと思うのでそのままにしてあります。片電源なので、+端子には12/2Vに分圧して加えてあります。
 ゲインの調整は、現地調整でカーステレオのラジオとスマホの音量を比較して、入力側10kΩを固定として、最終的には120kΩとしました。この抵抗、後(現地?)で変えると思ったので、アクセスしやすいように、OpeAMPの上にしてあります。正解でした。交換するときに抵抗の根本で切って、そこに半田付けしました。大丈夫だろうとは思いますが、OpeAMPが発熱すると抵抗値が変わって増幅度が変わるのも嫌なので、多少物理的に浮かせてあります。右半分が制御部分ですPICとトランジスタが2個、5Vのレギュレータです。左上がLとRの2ch分のアナログ回路です。


結言:
 ということで割と順調に作ることができました。shirozokuさんに感謝します。
 車で動作させた(エンジンOFFで)ところうまくいったので、喜んでいましたが、後で出かけたときにつけてみたら、オールタネータノイズがとてつもなくでかかったので、電源にチョークコイルを入れたり、ヘッドホンプラグへのケーブルを太くしたりしましたがだめ。あとは感でコンデンサを電源周りに入れたりして当たって、最終的に12/2V(12Vを2分圧して、中点をOpeAMPの+に加えるところ)とGNDの間に10μFの電解コンデンサを入れたところ、ぴたっと止まりました。こういうところは難しいところですね。(上下の写真は最後のオルタネータノイズ取りのコンデンサは入っていない版です。最終状態の写真を取り忘れました、、、。)

 あと、ハードウェアの写真で、左下の部分に「空地」がありますが、Bluetoothのレシーバでも載せようかと思って空けてあります。大陸にOrderしたので、到着したらまた発掘作業からやってみたいと思います。

 お決まりですが、本記事は自分の備忘録です。なんの動作保証もしませんし、接続先の装置、システム等への影響、故障などを起こさせる可能性もあります。もし万が一これを見てやってみる方(古い装置なのでまずいないとは思いますが)は、すべて自己責任でお願いいたします。
/* **************************************************
 * ファイル名	main.c for CarRadio_AUX_control Project
 * 作成者   tomoaric
 * 作成日	2021/4/4
 * 更新日
 * バージョン   1.0
 * 開発環境:
 *  MPLAB X IDE/5.35, XC8/2.20, C99
 * 使用PIC:
 *  PIC12F675
 * クロック	4MHz(INTIO)
 * 概要
 *     CarRadioのDIN8pin経由でRadio/AUXを電源ボタンのOn/Off/Onで切り替える
 *     電源ボタンを二度押しで、RadioとAUXを、全Off経由で切り替える
 *     Raio_On時に、電源ボタン押下→Off、電源ボタン押下→AUX_On、
 *     AUX_On時に、電源ボタン押下→Off、電源ボタン押下→Radio_On
 *     となる
 * 割り込み未使用
 * 使用ポート:
 * 1:VSS(+5V) 2ICE
 * 2:GP5 Radio_On In
 * 3:GP4 /AUX_On Out
 * 4:/MCLR 2ICE
 * 5:LED for test purpose...
 * 6:ICSCLK 2ICE
 * 7:ICSDAT 2ICE
 * 8:VDD(GND) 2ICE
 * 
 * Analog部分は、
 * https://www.marutsu.co.jp/contents/shop/marutsu/mame/102.html
 * の辺りを見て作成。
 *
 * 本プログラムは以下の記事をもとにして作成しています。
 * shiozokuさんに感謝します。
 * http://shiozoku.blogspot.com/2012/01/aux.html
 * 
 * コメントは処理を理解するために、追加してあります。
 * 
*/

#include \<xc.h\>

// CONFIG
#pragma config FOSC = INTRCIO   // Oscillator Selection bits @12F675 set to 4MHz
#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      // GP3/MCLR pin function select (GP3/MCLR pin function is digital I/O, MCLR internally tied to VDD)
#pragma config BOREN = OFF      // Brown-out Detect Enable bit (BOD disabled)
#pragma config CP = OFF         // Code Protection bit (Program Memory code protection is disabled)
#pragma config CPD = OFF        // Data Code Protection bit (Data memory code protection is disabled)

#define _XTAL_FREQ  4000000     //for __delay operation...
#define Radio_On    GP5         //Input
#define AUX_On      GP4         //Output
#define LED         GP2         //Output for test purpose
#define TRUE        1
#define FALSE       0
#define ON          1
#define OFF         0
#define MAIN_OFF    0
#define MAIN_RADIO  1
#define MAIN_AUXSW  2
#define MAIN_AUX    3
#define initial_WAIT    3000
#define WAIT        200


//AUX_On signal control to the RADIO
// "AUX_On = On" -> request changing AUX mode to the RADIO
void AUXON_Set(unsigned int on_off)
{
    if(on_off==ON) {
        AUX_On = ON;
//        LED = ON;
    }
    else{
        AUX_On = OFF;
//        LED = OFF;
    }
}

// Raio_On signal check
int RADIOON_Get()
{
    return Radio_On;
}

// pic initialize
static void pic_init() {
//  OSCCON = 0b01110000;      //内部周波数8MHz
//  OSCCON = 0b01110000;      //111:内部周波数8MHz,101:2M,110:4M
    // ポートの設定 1=input
	TRISIO = 0b00100000;		// GP4:out, GP5:in
    GPIO = 0;
    // アナログの設定
    ANSEL = 0b00000000;        //do not use any Aanalog input
	CMCON = 0x07;               // コンパレータ未使用
}    

// ==================== メイン処理 ===========================
void main()
{
//	unsigned char once_flag=TRUE;   //eepromを使う場合
    unsigned int once_flag=FALSE;  //eepromを使わない場合
    unsigned int mode_state;
        
	//once_flagは、初めて「MAIN_RADIOとMAIN_AUX」になったときにセット
	//次のloop回でEEPROMに覚えさせる
	
    pic_init();
// Radioが立ち上がるのを待つ
    __delay_ms(initial_WAIT);   

// eepromを使わない設定として、電源ON時にRADIO側が前の状態を記憶しているので
// それに従う。この場合、電源OFF時にAUXだった場合(MAIN_AUX)は、MAIN_OFFとする。
// RADIO_On信号をチェックして、On(ラジオ動作中)だったら、そのモードにする
// それ以外はState:MAIN_OFF、AUX_OnはOFFとする
    if (RADIOON_Get()==ON) mode_state=MAIN_RADIO;
    else{
        mode_state=MAIN_OFF;
        AUXON_Set(OFF);
    	__delay_ms(WAIT);
    }
    
	// 前回状態復帰
//	ReadEEPROM(0,&mode_state);
//	if(mode_state==MAIN_AUX) AUXON_Set(ON);	
			
	for(;;){

	switch(mode_state)
	{
		case MAIN_OFF:
		//電源OFFの状態
			if(RADIOON_Get()==ON)
		//RadioがOnになった(2個めの矢印)
			{
				mode_state=MAIN_RADIO;
				once_flag=TRUE;
			}						
			else
		//電源がOFFのままだけど、一度ONになったかの判断
			{
				if(once_flag)
				{
//					WriteEEPROM(0,mode_state);
					once_flag=FALSE;
				}					
			}				
		break;
		
		case MAIN_RADIO:
		//ラジオが付いている状態
			if(RADIOON_Get()==ON)
		//まだラジオのまま
			{
				if(once_flag)
				{
//					WriteEEPROM(0,mode_state);
					once_flag=FALSE;
				}					
			}
			else
		//ラジオがついているから消えた状態に遷移(3個めの矢印)
			{
				mode_state=MAIN_AUXSW;
				once_flag=TRUE;
			}	
		break;

		case MAIN_AUXSW:
		//一回ラジオがOnになって、消された状態
		//AUXへの切り替えを待っている状態
			if(RADIOON_Get()==ON)
		//一旦ラジオが消されて、再度ついた!(4つ目の矢印)
		//AUXへ遷移させる
			{
				mode_state=MAIN_AUX;
				AUXON_Set(ON);
				__delay_ms(WAIT);
			}				
		break;
		
		case MAIN_AUX:
		//AUXになっている状態
			if(RADIOON_Get()==ON)
		//AUX状態で、ラジオがONになった(1個めの矢印)
		//AUXOnをOFFにして、全部消す
			{
				mode_state=MAIN_OFF;
				AUXON_Set(OFF);
				__delay_ms(WAIT);
			}
			else
		//AUX状態が継続
			{
				if(once_flag)
				{
//					WriteEEPROM(0,mode_state);
					once_flag=FALSE;
				}
			}				
		break;
		
		default:
		mode_state = MAIN_OFF;
	}
		__delay_ms(WAIT);
  }

}

PICkit3 MPLAB X/XC8 設定の備忘録 #include <xc.h>でエラーの対応

2021-04-04 06:11:27 | その他工作
これまでPIC16F88でProject開発をしてきて、PIC12F675(これまた古い)で新しいProjectを始めようとしてハマったのでメモ。
(MPLAB X IDE/5.35, XC8/2.20, C99)

#include でエラーが出る時の対処
これは、header file が見つからない場合に出る。
(ちなみに、<>でくくる場合はSystemのheader file、Userが作ったheader fileは、""でくくるんだそうです。)
このエラーが解決しないと、editerが既定の変数がわからないのでediter上でエラーが山程出る。

エラーの状況を確認する手立て:
  • Controlを押しながら、エラーの部分にマウスオーバすると、なんちゃら言ってくるので参考にする。以下でxc.hが見つかるようになっても、更にxc.hの中でその先が見つからない場合も同様なエラーになるが、Control+マウスオーバでその内容を見ると見つからないファイルがわかるので、参考にする。
  • View/IDE Log を見ると、エラーが出ているはずなので、参考にする。私の場合は、Logのエラーで、v1.48?のpathを読み込みに行っていたので、その辺りか、と当たりをつけて以下の対応をしました。
Compile環境の再設定
これは、IDE Logから、読み込むPathが古いXC8のpathを読み込んでいたため、適切なPathを再構築する、という手順。
  1. Tools/Options/Embedded/Build Tools で
  2. Toolchain: の該当するコンパイラを選択し、Removeする。
  3. Scan for Build Tools(右下)を押下して、再設定。Apply, OKで抜ける。
header fileの入っているinclude pathの設定
  1. Projectsウィンドウの、該当するProject名を右クリック/一番下のPropatiesを選択する
  2. Categoriesで、Conf:[default]を選択し、Compiler Toolchain:で該当するXC8のversionを選択する
  3. Categoriesで、XC8 Global Options/XC8 Compilerを選択する
  4. Option categories: Prreprocessing and messagesで、Include directoriesを確認する。行尾の「・・・」を押して、「Browse」し、
    1. C:\Progaram Files\Microchip\xc8\v2.20\pic\include (versionのところは合ったものを入れる) 同様に、
    2. C:\Progaram Files\Microchip\xc8\v2.20\pic\include\proc と2つ選択する。
  5. includeの方は、xc.h、procの方はデバイス用のheader fileが入っている
で、Apply/OK後、一旦全部閉じて、MPLAB Xを再起動。
私の場合は、まず、xc.hが見つからない、その後、12F675が見つからない、という状況でした。
個別のProject毎にやることではなくて、"System"で設定する話のような気もしますが、毎回同じ環境、同じChipで開発するわけは無いので、仕方ないのかもしれません。新しいChipで開発をするたびにやることになるのだと思います。
多分これでheader fileが見つからない状態は脱すると思う。


電圧・電流・電力計にシリアル出力をつける BUG-Fix(V2.2)と5Vレギュレータ追加とシャント抵抗が壊れた話

2021-03-14 17:25:51 | その他工作

だめですね。ソフトにBUGがありました。V2.2版です。
前の記事のV2.1のソフトは消します。

・ソフトのBUG取り
 AD変換器からのデータを繰り返し数だけ加算してゆく変数がOverFlowしていましたので、Longにしました。電源電圧を上げてゆくとゼロに戻る、というとても恥ずかしいBUGです。
・繰り返し回数を1000回に
 あと、データの繰り返し数を1000回にしました。繰り返し数が100回だとチラチラするので、思い切って1000回にしたらちょうどよい感じです。(秒5回位のUpdateになりました。)
・5Vレギュレータの追加とノイズ退治
 PICやOpeAMPの電源をPICkit3から取っていたのですが、これを入力電源から取るように、5Vのレギュレータ(LP2950L-5.0V)を入れました。このレギュレータ、秋月で低損失のレギュレータがあったので購入したのですが、結構Noisyです。あちこちにコンデンサを入れましたが完全には取れませんでした。多少数値が暴れますが、まぁmVくらいなので良しとします。これにより、5V以下は測定できなくなりましたが、必要に応じて別電源で動かすことも可能(例えばPICkit3のICEを接続すれば良い)なので、これも良しとします。
・シャント抵抗が逝った話、、ステンレスの針金シャント
 完成! と思ってオートバイから外したバッテリーに接続して、ダミーの抵抗をつないだりして遊んでいたのですが、何の拍子にShortさせてしまったようで、これまでは安定化電源で、電流の上限値を設定していたのでなんともなかったのですが、バッテリーはだめですね、、、、。ぷちっ、ん?でR050のシャント抵抗が逝かれてしまったようです、、、。どれだけ流れたのでしょうね、、、。(2021/3/22追記:バッテリはバイクから外したもので、6.3Ah、なんとCCAが130Aと書いてありました、、、、だめですね、これでは、、、。)
でも、電流の数値がむちゃくちゃでかい(数A)だけで動いていることは動いてい状態です。破断では無いようです。外して抵抗値を測ってみると、1.010kΩとおっしゃる。なんだコレ状態です。
 シャント抵抗として使っていたR050(50mΩ)は、壊れた電源から外して使っていたので交換部品がありません。
 脱力感の中、どうしようかと一晩考えました。購入するにしても1個2個は売っていないし、、、。どうせだから抵抗のアソートでも買うか、とか。結局、ステンレスの針金を使って、50mΩを作りました。ステンレス針金のなかでも抵抗値が高いそうで、手元にあった0.9mmΦの針金の抵抗を計測して、所望の抵抗値を計測して計算、4cm弱で50mΩになることが判明。既知の抵抗を使って電流値がちょうど合うように多少調整をして、ほぼ?所望の数値になるようにしました。アマチュアが使うので、数%の誤差であれば上出来でしょう。
 求め方は、長いステンレス針金に既知の電流を流して、両端の電圧を計測して抵抗値を求め、その長さに比例した必要抵抗を求める、あとは現地合わせ、です。
 一番上の写真は、逝ってしまったR050と、ステンレスの針金シャント50mΩです。
下の写真は、ステンレス針金シャント抵抗を5Vに5Ωをつないだ時の結果です。5V、5Ω、1Aですね。
回路図: 5VレギュレータをLP2950L-5.0Vにしたのと、あちこちにコンデンサを入れました。
環境: MPLAB-X IDE v5.35 XC8/PICkit3 C99 PIC16F88

以下、V2.2です。


/* 
 * File:   main.c
 * Author: tomoharika
 * 2021/1/16 V1.5
 * 2021/1/23 V1.5a Power表示で32kでOverFlowを修正
 * 2021/2/13 V2.0a Serial Outを追加 @9600bps,8N Fosc=8Mに変更
 *  それに伴い、16F88 pin8をSerial Inのために空けて、ピン15(RA6)をLCD D6へ接続変更
 *  needs "LCDlib2a.c"(pin変更に伴う修正)
 * 2021/2/14 V2.1a sprintf 周りのBUG修正とそれに併せた表示制御の整理(17points/sec)
 * 2021/3/13 V2.2 bug対応 変数adをint->long
 * 
 * 設計目標:16V,5A電源を計測
 * 16x02のLCDに測定電圧(mV)、電流(mA)、消費電力(mW)を表示
 * PIC16F88、TL431、LM358、LCD:SC1602コンパチ?を使用
 * 
 * ・TL431シャントレギュレータを用いて、2.906V(2.495Vから作成)を
 *  標準電圧とし、この2.906をPICでVref+として1024分割したもの用いる
 *  (電源電圧、電流、OpeAmpの増幅率などを勘案して決定した)
 * ・電圧計測: 電圧@16V で、47kohm+10kohmで分圧したものをLM358
 *  VoltageFollowerでPICのAN0に入力
 * ・電流計測: 50mOhmの両端電圧を測定電圧として電流を測定、LM358
 *  で11倍(10kOhm/100kOhm)に増幅してPICのAN1に入力する
 *
 * 環境:
 * MPLAB X IDE/5.35, XC8/2.20, C99, LCDlib, C99
 * 
 * 調整、設定方法:→ソフト側で係数を微調整する
 * 1)電源を入れた際に、電圧、電流ともにゼロになるように、offsetを設定する
 * Line1の右側の4桁x2が、Volt,CurrentのAD変換後の数値:0-1023
 * 2)電圧:例えば、外部から外部計測した12.000Vを加えて、その時のAD変換後の数値XXXXを読む
 * →係数Vcoef=(12.000/XXXX)*1000, if XXXX=734 then 12/734*1000=16.3487=Vcoef
 * 3)電流:同様に、抵抗値のわかっているLoadを接続し、電流を計算、
 * →係数Icoef=(Current/YYYY)*1000, で係数を得、#define に両者を設定する
 */

#include \<stdio.h\>
#include \<xc.h\>
#include "LCDlib2a.h"

//--------------------------------------
// このアプリに必要な設定
//--------------------------------------
#define INITIAL_CREDIT1 "Power Mon v2.2"
#define INITIAL_CREDIT2 "JN1RAC 20210313"

#define offset  0               //印加電圧=0V、電流=0の時に表示がゼロになるように設定
#define repeat_num  1000           //繰り返し計測数。電圧、電流ともに効く
#define Vcoef   16.3043         //例えば、12Vを印加した時には、12.0/adconv()*1000で求めた乗算係数
                                //Over Allでの調整
#define Icoef   5.2215          //電圧と同様、Over Allでの調整係数

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

// CONFIG1
#pragma config FOSC = INTOSCIO  // Oscillator Selection bits (INTRC oscillator; port I/O function on both RA6/OSC2/CLKO pin and RA7/OSC1/CLKI pin)
//#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  8000000
#define LED         RA4             //動作確認用のLED。初期ディスプレイ表示、変換動作中に点灯

//********************************************************************** 
/*
    <電圧・電流計>

 ■コンフィグ設定
  LVP_OFF
  MCLR_OFF
  WDT_OFF
  EXTCLK
 ■ピンアサイン (16F88)
  Pin-01 open
  Pin-02 Vref 
  Pin-03 RA4/LED
  Pin-04 /MCLR to VDD(+5V) via 10kohm 
  Pin-05 Vss to GND
  Pin-06 RB0/LCD D4
  Pin-07 RB1/LCD D5
  Pin-08 RB2/ →SerialIN (SPEN=1するとTX/RXともにEnableになる) open
  Pin-09 RB3/LCD D7
  Pin-10 RB4/LCD:E
  Pin-11 RB5/SerialOUT →未搭載、拡張予定
  Pin-12 PGC/LCD:RS
  Pin-13 PGD
  Pin-14 Vdd to +5V 
  Pin-15 RA6/LCD D6 2021/2/13 modified
  Pin-16 open
  Pin-17 AN0/Vvmon 
  Pin-18 AN1/Vimon
*/
//********************************************************************** 

// ADconv function 
unsigned int adconv()
{
    ADON = 1;           //AD変換
    __delay_us(20);
    GO_nDONE = 1;       //AD変換開始
    while(GO_nDONE);    //完了待ち
//    return ((ADRESH<<8)+ADRESL);        //右詰め
    return (ADRESH*256+ADRESL-offset);
}
//********************************************************************** 

void send_char(unsigned char byte)      //for serial transmission test
{
    while(!TXIF){
        continue;
    }
    TXREG = byte;
}

void send_text(char * text)    //for serial transmission test
{
    int i;
    for (i = 0; text[i] != 0; i++)
        send_char( text[i] );
}
//********************************************************************** 

void main()
{
//    static    unsigned    long    // 32bit 0...4294967295
//    static    unsigned    int     // 16bit 0...64k
    static  unsigned    long    Power, ad ; //mV*mAなのでlongが必要
    static  unsigned    int     Vmon, Imon, count, Vad, Iad;
    static  char        fmtbuf[16];
    
    OSCCON = 0b01110000;    //内部周波数8MHz
//    OSCCON = 0b01110000;  //111:内部周波数8MHz,101:2M,110:4M
    // アナログの設定
    ANSEL  = 0b00000011;    //AN0:Vvmon, AN1:Vimon
    // ポートの設定 1=input
    TRISA  = 0b00001011;    //RA0:AN0,RA1:AN1, RA2:opn, RA3:Vref RA4:LED
    TRISB  = 0b00000000;    //RB0-RB4:LCDout RB6+PGC RB7:PGD

//ADCON0
    //bit7:ADCS1
    //bit6:ADCS0
    //bit5:CHS2
    //bit4:CHS1
    //bit3:CHS0
    //bit2:GO_nDONE
    //bit1:not used
    //bit0:ADON
    
    ADCS2 =0;               //ADCON1のbitです
    ADCS1 =0;
    ADCS0 =0;               //000->ACDのclock:1.6usec

    CHS2 = 0;
    CHS1 = 0;
    CHS0 = 0;               //000->AN0,001->AN1

 //ADCON1
    //bit7:ADFM
    //bit6:ADCS2
    //bit5:Vref+
    //bit4:Vref-
    //bit3-0:not used
    ADFM = 1;               //ADC結果の格納方法、右詰め
    //ADCS2はADCON0で設定
    VCFG0 =0;               //ADCを行うポートの基準電圧をVref+に設定
    VCFG1 =1;               //Vref- ->Vss

// serial initial set   2021/2/11 added
    TXSTA = 0x24;           //Set High Baud rate
    RCSTA = 0x80;           //RX do not use
    SPBRG = 51;             //clock:8MHz, 9600bps, ={8000000/(16*9600)}-1

    LED = 0;
    
// LCD初期化
    lcd_init();
    
// LCD画面クリア
    lcd_cls();
    LED = 1;
// LCDにstart-up messageを出力
    lcd_locate( 0, 0 );
//    lcd_puts( fmtbuf );
    lcd_puts( INITIAL_CREDIT1 );
    lcd_locate( 1, 0 );
    lcd_puts( INITIAL_CREDIT2 );

    __delay_ms(2000);
    LED = 0;
    lcd_cls();
    __delay_ms(2000);

//*** start loop ***
    while (1) {
        LED = 1;                        //動作中表示の点灯

// Voltage ...
    // Vmon計測@AN0
        CHS0 = 0;                       //select AN0
    //  TL431シャントレギュレータを使ってVref+に2.906Vを供給
    //  最小分解能:2.906/1024=2.837mV、これ以上にする場合は複数回で平均を取る
    //  入力電圧を分圧してVref+(2.906Vを設定済)以下にしてAN0に入力する
    //  max入力電圧を16Vとして
    //  10k+47kohm(→16*10/(10+47)=2.807V)を設定 max:16.5V
    //    Vmon = (2.906/1024)*adconv()*(47+10)/10;
    //    Vmon = 0.016176*adconv();      //単位は"V"
    // 計算上は上記だが、Over Allでの誤差があるので、表示とadconv()の数値でVcoef,Icoefを算出する

        ad = 0;
        for (count=repeat_num;count>0;count--)
            ad = ad + adconv();
        Vad = ad / repeat_num ;
        Vmon = Vcoef/repeat_num * (float) ad  ;        
        //表示係数をかけたものの整数部分 単位は"mV"
        if (Vmon <= 1) Vmon = 0;

        LED = 0;
        
// Current ...
        CHS0 = 1;                       //select AN1 for Current...
    //  非反転増幅回路による電流計測
    //  TL431シャントレギュレータを使ってVref+に2.906Vを供給
    //  最小分解能:2.906/1024=2.837mV、これ以上にする場合は複数回で平均を取る
    //    Imon = {R1/(R1+R2)}*adconv()*{(2.906/1024)}*{1000/50ohm};
    //    Imon = {R1/(R1+R2)}*adconv()*0.056757812
    //    @ R1=10kohm, R2=47+47+4.7kohm=108.7kohm(100kohmがなかったので、、)
    //    Imon = 5.2215098896*adconv();  //mA maximum:5.2215*1024=5346mA
    // 最終的には実際の数値を求めて、Icoefの割り戻しする。1Aで234とか。
        ad = 0;
        for (count=repeat_num;count>0;count--)
            ad += adconv();
        Iad = ad / repeat_num;
        Imon = Icoef/repeat_num * (float) ad ;    //単位は"mA"
        if (Imon <= 1) Imon = 0;

// send to RS-232C
        sprintf(fmtbuf, "%6d,%6d\r", Vmon, Imon );
        send_text( fmtbuf );
// LCD一行目        
          sprintf(fmtbuf, "%6dmV%4u%4u", Vmon, Vad, Iad );    //電圧とAD値
        lcd_locate( 0, 0 );
        lcd_puts( fmtbuf );
// Power ...
        Power = (float) Vmon * (float) Imon / 1000 ;  //単位は"mW"
// LCD二行目
        sprintf(fmtbuf, "%6dmA%6ldmW", Imon, Power );  //倍精度long "ld"
        lcd_locate( 1, 0 );
        lcd_puts( fmtbuf );

//  LCD:16x02 display map
//        0123456789012345
// Line1: 123456mVXXXXYYYY XXXX:Voltage ADconved, YYYY:Current ADconved.
// Line2: 123456mA123456mW

    }
}
//********************************************************************** 


電圧・電流・電力計にシリアル出力をつける BUG-Fix(V2.1)

2021-02-16 17:34:54 | その他工作
ソフトにBUGがあったので備忘録です。VersionはV2.1となりました。

(1)sprintfの書式設定と変数の型の不一致:
 以前の記事「電圧・電流・電力計」で、

 sprintfで入れているのですが、表示できていたのですが、なにかの拍子に突然、2つ目の変数が表示されなくなってしまいました。よくわからないのですが、とりあえず動くように、分割して表示していますので、処理量は多くなってしまっています。

と書いたのですが、原因がわかりました。「何かの拍子に」は、変数をLong(32bit)に変更したこと、でした。
 Powerを計算するのに、電圧(mV)×電流(mA)なので、integer16bitをOverflowしてしまうことが分かったので、すべての変数をinteger(16bit)からLong(32bit)に変更しました。
 sprintfの書式で、本来32bitの場合は%ldとしなければいけないところを変更せず%dとしてしまったために、変数領域は32bit(long)、表示書式は16bit(int)となってしまい、二つ目の変数が、long32bit中の上位16bitを読み込んで、常に「0」ゼロになってしまっていた、ということのようです。変数を3個表示しようとして気が付きました。
 Powerだけをだけを32bitにすればよかったのです。ちなみに、Powerのところは%ldにしてある、というおバカな状況でした。Powerだけを32bitにして、残りは16bitのままとしました。
 これが分かったので、LCD2行に、2回のWrite命令で書き込むように修正したところ、当初10回/秒だったものが、17回/秒へ高速化されました。

(2)XC8の最適化:
 これは、BUG対応ではないのですが、同じ記事で、

あとは、sprintfなどを使うとプログラムエリアを食うようで、V1.5aで、使用率が90%になっています。RS-232CのようなSerial通信を入れようとすると多分溢れます、、、。

と書いたのですが、Project Properties から、XC8 Compiler のPropertyを覗いてゆくと、Options for xc8-cc、に、Optimizationというカテゴリがあって、そこに、Optimization levelという設定項目がありました。試しに設定してみましたが、Optimization lever = 2 まではFreeで使えるようで、90%だったものが80%にOptimizeされました。
 最終的には、(1)の対応をしたり、いらないライブラリを外したりしたのもあり、現在Programは、77%となっています。
 多分、Optimizeしなくても大した量の追加ではなかったので入ったとは思います。

 とりあえず、現時点での最終版です。以下、長くなりますが、Sourceです。
回路図: V2.0と変更ありません。
環境: MPLAB-X IDE v5.35 XC8/PICkit3 C99 PIC16F88 VC2.1.x
#### V2.1 はBUGがあったので、次の記事でV2.2として載せてあります。そちらを見てください ####

電圧・電流・電力計にシリアル出力をつける(V2.0)

2021-02-13 13:37:42 | その他工作

前の備忘録に、電圧・電流計の数値をシリアル出力したくなる、と書きました。
で、やってみました。

・PICは16F88、シリアル通信用の機能がある
・一応、回路図には将来用として、Serial-OUTのピンを取ってある
で、あとはソフトを作成すればよいか、と思っていました。

 今回、出力だけなので、TXだけ活かせると思って、Serial-OUT(pin11)を確保していましたが、動かすとLCDに何も表示されない。色々調べてゆくと、16F88のシリアルの機能を活かす(SPEN: SerialPortENableをON)とSerial-IN(pin8:RX)まで生きちゃうんですね。悪いことにpin8:RXはLCDのDataBusの一本になっていて、LCDに適切なデータが設定できず、結果表示ができなかったようです。
 じゃぁ、信号線の場所を変えるか、と思ったのですが、LCDのライブラリは、RA、RBの連続上か下の4bitを使うようになっていて、RAはアナログ入力系や、Vrefなどを入れるPinがあって、空けられないことがわかって呆然。じゃ、できないじゃん。
 使っているLCDライブラリの制約にPICのハードは適合しないけれど何か手は無いか、と考えて、LCDのライブラリを覗いたら、設定をしてやれば他の信号線に信号を出せそうな感じなので、RB2としてLCD用に使っていたpin8をSerial-INに明け渡して(使わないのでopen)、代わりに空いているpin(今回はRA6:pin15)に変更しました。(以下の回路図参照)


 あとはソフト。こちらは、LCDlib2.cでは、LCDを4bitモードで動かしているので、下から3bit目のデータをRA6に反映させる処理を追加しました。このライブラリは、下位4bitにデータが入っている前提で引き継いで来て、このライブラリの中で、上位4bit/下位4bitのbit shiftをさせる記述があります(#ifdef)ので、その判断の前で、強制的に下から3bit目のデータをRA6に反映させる処理をいれました。その後の、従来からあった連続4bitの書き込み処理はそのままにしてあります。多分16F88の最終段でInとOutの切り替えをしているだろうという仮説?で。やってみたところ、思惑通りの動作をしてくれました。

 そもそもの、PICのSerialの設定は、送信側の設定とStatusのTXSTAと受信側の設定とStatusのRCSTA、ボーレートの係数を設定するSPBRGを設定すれば良いことがわかりましたので、適切なデータを設定します(SourceCode参照)。
// serial initial set   2021/2/11 added
    TXSTA = 0x24;           //Set High Baud rate
    RCSTA = 0x80;           //RX do not use
    SPBRG = 51;             //clock:8MHz, 9600bps, ={8000000/(16*9600)}-1
 今回問題になった、SPENは、受信側のRCSTAの7bit目がそれで、受信はしないから、と適当に設定したのですが、それがいけなかったようです。SPENはSerial通信のENablerなので、これを設定しないと動かないし、動かすと、TXもRXも動いてしまう、ということです。お気をつけください。(通常ペアで使うのでしょうね。片側だけ使う、という使い方のほうが普通じゃない、のだと思います、、、)
 後の変更点は、PICの内蔵クロックを当初1MHzで動かしていましたが、このクロックがシリアルにも使われて、SPBRGの値で設定するのですが、1MHzで計算をすると非常に小さい値になるので誤差が広がるのではと思い、内蔵クロックを8MHzに上げました。(上記のSPBRGは8MHz、HighBaudRateとして設定してあります)

 結果ですが、mVとmAで、そのままカンマで区切って、9600bpsで送信すると、秒10回ほどのVとAのペアのデータが出てきました。(写真参照)
まぁ、私の用途には十分なデータなので、とりあえず、これで良いかなと思います。デリミタはLineFeedが良いのか、CaridgeReternが良いのかよくわかりませんが、CRだと同じ場所で書いてくれるので、そうしてあります。

 PICのSerialからは、TTL?レベルの信号が出てきますので、このままPCのRS-232Cに食わせても動きません。私は、TTL/RS-232Cというか、TTL/USBの変換ケーブル(大陸からで数百円位)を持っていたので、それで接続、動作確認をしました。(以前購入した簡易オシロのデータダウンロードとかにも使っています)

 写真は、PICkit3を接続してSoftwareをダウンロード、計測する電源からこの装置を経由して、以前作った電子負荷装置へ接続、Serialは、作成した基板からTTL/USB変換ケーブルを経由して、PCのSerial通信用モニタソフトで実際のデータをチェック、という系になっています。

 これで、自動計測ができるようになったので、ようやくバッテリーの検査?ができるようになりました。
 ソフトウェアの詳細は、長くなるので記載しません。もし、ご所望の方がいらっしゃいましたら、ご連絡ください。
→BUGがあったので、こちらで修正しています。(2021/2/16追記)

環境は、MPLAB-X IDE v5.35 XC8/PICkit3 C99 PIC16F88 です。VC2.0.x