SIN@SAPPOROWORKSの覚書

C#を中心に、夜な夜な試行錯誤したコードの記録です。

擬似交換機の作成(その2)

発信やループ検出など、いくつかのデータが揃ったの、いよいよ2回線擬似交換機の作成です。
電源は、とりあえず、実験用の5V/12Vスイッチング電源を使用しました。

1 状態(ステータス)

擬似交換機は、次の5つの状態を遷移する仕様としました。

0 アイドル なにも使用されていない状態
1 発信 片方がフックを上げて「ツー」という発信音が聞こえる状態
2 呼出 呼出信号が流れている状態
3 通話 両方がオフフックで話している状態
4 話中 どちらかがフックを置いた状態


フォトカプラにより、フックの上げ下げを状態を検出し、その割り込みで、それぞれの状態が遷移させます。 また、下記の場合は、特定の時間が経過すると、自動的に状態が遷移させます。

「発信」から「呼出」:受話器を上げてツーという音が聞こえたら、数秒で自動的に呼び出し状態に移行
「発信」から「話中」:特定時間呼出を続けて相手が出ない場合、安全のため自動的に「呼出」を止める
「話中」から「アイドル」:「話中」でそのままフックが上がったままの時、自動的に「アイドル」に戻る

2 回路図

電話回線とPIC制御の系統は、フォトカプラ及びリレーで絶縁されています。

ステータスを表示するためのLEDは、通常は、PICに専用ポートを設けて出力するのでしょうが、使用するポート数を可能な限り節約したくて、それぞれの制御ライン上で点灯させてみました。
特に16Hz呼出信号の発信時は、大きな電流が流れるため、警告のためにも電流のライン上で赤色LEDが点灯するように配置しました。

リレー駆動のラインには、当初100〜470Ω程度の抵抗を入れていたのですが、どうもリレーが元気よく動かないので、制限抵抗をなしにしました。この状態で流れる電流値は、実測で22.3mAでした。これぐらいの電流を流さないとリレーは確実に動作しないようです。

16Hz呼出信号発生には、先日、「オーディオタイマ」を分解した中に入っていたトランスを使用しました。制限抵抗は6Ωになりましたが上手く呼出ベルが鳴っているようです。

回線に流する電流を高くしすぎるとハウリングを起こし、低すぎるとフォトカプラが誤動作(雑音で回線断と判断)します。
しかし、この今回の回路は電話機のループ抵抗に依存しているため電話機の種類ごとに変化してしまいます。
本来は根本的に見直す事項でしょうか(今回は可変抵抗を挿入することで一応OKとしました)
電話機や回線の状況に合わせて調整が必要になるかも知れません。

3 ブレードボードでの試験

右の絵は、ブレッドボードで実験しているようすです。
大まかに見ると、上の方に見えているのが、呼出信号発生回路、ブレッドボードは、左がPIC及びリレー制御トランジスタ、中が回線への電力供給とフォトカプラ、右がリレーです。
真中のボードに伸びている青と紫の線は、400Hzと16Hzのトーンです。

4 AD変換でフォトカプラの状態検出

当初、フォトカプラのオン・オフは、下記のコードのように#INT_RB(ポートBの状態変化割り込み)で処理していたのですが、接続する電話機の種類によっては、電圧が3.3V−1.1Vというような微妙な値になってしまうことや、呼出信号や、通話の電圧変位でもこれが触れるため、後動作が多発してしまいました。


#define UP 0 //オフフック
#define DOWN 1 //オンフック
#int_rb //割込み処理の開始指定
void rb(){ //RCIF割り込み
	//フォトカプラの状態読み込み
	pc[0]=input(PIN_PC0);;
	pc[1]=input(PIN_PC1);;
	//状態に変化が無い場合は、処理しない
	if(keep[0]==pc[0] && keep[1]==pc[1])
		return;
	//状態を保存する
	for(i=0;i<2;i++)
		keep[i]=pc[i];

	//状態変化時の処理
	//・・・・・・・・・

}
void main(){
	enable_interrupts(INT_RB);	//RBIF割り込みを許可する
	enable_interrupts(GLOBAL);	//GLOBAL割り込を許可する
	while(1){
		;
	}
}

そこで、フォトカプラの端子の電圧値をAD変換で取得し、特定の値より大きいか小さいかでオンフック・オフフックを検出するようにしました。 これでも、まだ電話機のループ抵抗には影響を受けますが、制限抵抗(可変)を調整することで使用可能なレベルまで来ました。

やはり、電話機に依存せず、回線ごとに電流値が一定になるようにする回路は必要不可欠のようです。
現在、この対処で思いつくのは・・・・
1.回線ごとに別回路にし、それぞれ独立して電力を加え、トランスで音声を受け渡し
2.回線ごとに定電流(定電流ダイオードオペアンプによる回路)を設ける
ぐらいでしょうか・・・

5 制御PICのコード

メインの処理は次のとおりです。
メインループでフォトカプラ(オンフック・オフフック)の検出と、状態遷移に関する処理を行っています。


void main(){
	int i;
	int pc[2]={DOWN,DOWN};//認識したフォトカプラの状態
	setup_oscillator(OSC_4MHZ);
	
	setup_adc(ALL_ANALOG);
	setup_adc(ADC_CLOCK_INTERNAL);
	setup_adc(ADC_CLOCK_DIV_32);
	set_tris_b(0x00);
	#use fast_io(b)

	set_state(0);//「アイドル」へ移行

	setup_timer_0(RTCC_INTERNAL | RTCC_DIV_4);
	enable_interrupts(INT_TIMER0);	//タイマ1の割り込みを許可する
	enable_interrupts(GLOBAL);	//GLOBAL割り込を許可する

	while(true){
		delay_ms(600);
		t++;//トーンの間隔を調整するカウンタ
		count++;//「アイドル」及び「通話」ではカウントは関係無し

		//フォトカプラの状態検出
		for(i=0;i<2;i++){
			set_adc_channel(2+i);//RA2及びRA3をAD変換で読み込む
			delay_us(50);
			//AD変換で取得した値が0x50以上だったら「オンフック」と判断する
			if(read_adc()>0x50){//DOWN
				if(pc[i]==UP){
					pc[i]=DOWN;
					printf("nPC[%d]=DOWN",i);
				}
			}else{//UP
				if(pc[i]==DOWN){
					pc[i]=UP;
					printf("nPC[%d]=UP",i);
				}
			}
		}
		
		if(state==0){//アイドル
			//どちらかがオフフックの時、「着信側」を決定し「発信」に移行する
			if(pc[0]==UP || pc[1]==UP){
				if(pc[0]==UP)
					target=1;//0側がオフフックのとき1側が「着信側」となる
				else
      					target=0;
				set_state(1);//「発信」
			}
		}else if(state==1 || state==2){//発信・呼出
			//ターゲット側がオフフックの時は、「通話」に移行する
			if((target==0 && pc[0]==UP) || (target==1 && pc[1]==UP)){
				state=3;//「通話」
			//発信側のオンフックになった時は、アイドルに戻る
			}else if((target==0 && pc[1]==DOWN) || (target==1 && pc[0]==DOWN)){
				set_state(0);//「アイドル」
			}else{
				//「発信」は、指定秒経過後に自動的に「呼出」に移行する
				if(state==1 && count>2){
					set_state(2);//「呼出」へ移行
				//「呼出」は、指定秒経過後に自動的に「話中」に移行する
				}else if(state==2 && count>10){
					set_state(4);//「話中」へ移行
				}
			}
		}else if(state==3){//通話
			//どちらがオンフックになった時、「話中」に移行する
			if(pc[0]==DOWN || pc[1]==DOWN){
				set_state(4);//「話中」
			}
		}else if(state==4){//話中
			//両方がオンフックになった時、「アイドル」に移行する
			if(pc[0]==DOWN && pc[1]==DOWN){
				set_state(0);//「アイドル」
			}else{
				if(count>10){
					set_state(0);//「アイドル」へ移行
				}
			}
		}
	}
}

タイマ0の割り込みでは、16Hzと400Hzの発振を行い、状態(status)に応じてオン・オフしています。


//この数値を大きくするとトーンの周波数が上がる
#define WAIT 0x0060

//弛張型波形生成
#define TONE(A) (n)?output_low(A):output_high(A)
void wave16(){
	static bool n=false;//毎回ON/OFFを交互に行う
	bool r=0;//呼出信号の有無
	if(state==2){//「呼出」
		if(t%3==0){
			TONE(PIN_T1);
			r=1;//呼出信号あり
		}
	}
	//呼出信号
	if(r){
		//ターゲット側のリレーをONにする
		if(target==0){
			output_high(PIN_RL0);
		}else{
			output_high(PIN_RL1);
		}
		
		TONE(PIN_T0);
	}else{
		output_low(PIN_T0);//信号停止時は強制的にLowになるようにする
		//リレーをOFFにする
		output_low(PIN_RL0);
		output_low(PIN_RL1);
	}
	n^=1;
}
void wave400(){
	static bool n=false;//毎回ON/OFFを交互に行う
	if(state==1){//「発信」
		TONE(PIN_T2);
	}else if(state==2){//呼出音
		if(t%3==0)
			TONE(PIN_T2);
	}else if(state==4){//話中音
		if(t%2==0)
			TONE(PIN_T2);
	}
	n^=1;
}
#INT_TIMER0   //割込み処理の開始指定
void timer0(){
	//WAITの調整により、400Hzの周期でこの割り込みがかかる
	static int cnt=0;//25回に1回処理すると、約16Hzになる
	cnt++;
	if(cnt>25){
		cnt=0;
		wave16();//16Hz発振関数
	}
	wave400();//400Hz発振関数
	set_timer0(WAIT);//発振周波数の調整

6 結合

制御(PIC周辺)部と電源部以外を実装して試験しているようすです。
一応、問題なく動作しているようです。
制御(PIC周辺)部分と電源部分の実装ですが・・・・
制御部分は、今後のデバッグと拡張を考慮して、USARTによるモニタとオンボードでの書き込みが出来るように実装を考えたいと思っています。 また、電源は、それほど安定度した12Vでもよさそうなので、トランス電源そのままでやってみようかと考えています。