フッキング操作の検出と、ダイアルトーンの送出はできたので、次はいよいよダイアル番号の検出をせねばなりません。ダイアルされた番号は電話機の設定に応じてDTMF(トーン)信号またはパルス信号で送出されてきますので、このどちらか(あるいは両方)を検出してやる必要があります。先の記事で書いたように、今更パルス信号でもないので、おのずとDTMF信号を検出することになります。ところが、Si3215ではDTMF検出の機能はサポートされていません。Si3210やSi3216を使えば、DTMFの検出ができるのですが。。
そういうわけで、DTMFの検出はマイコン側でやってやらねばなりません。電話機が生成したDTMF音はu-LawにエンコードされたPCM信号としてAT91SAM7XのSSCで受信できるので、これを解析してDTMF検出することになります。Googleさんに助けを求めてARM用のコードを探すと、簡単にonARMにあるSTM32用のコードが見つかりました。ターゲットはSTM32用となっていますが、DTMF検出の部分は全てCで書いてあるのでAT91SAM7Xで使うことには何の問題も無さそうです。ただし、Keil/ARMの息がかかっているので、KeilのuVisionで使うことという条件付きになってしまっていました。
しかたなくもう少し探したところ、NXPのサイトでLPC2138を使った電話の自動応答装置のプロジェクトを見つけました。しっかりとした説明とソースも含まれており、かなり参考になる記事です。ちょっと調べてみたら、NXPの前身であるPhilipsが2005年に開催したARM Design Contestで最優秀賞をとったTAM-TAMという作品のようです。ENC28J60を使ってethernetをつなぎ、TCP/IPにはuIPを使うという定番がキッチリおさえられています。基板もきれいにこさえて、開発にはuVision/Keil使っているみたいなので、やはり玄人さんが趣味で作った作品という感じでしょうか。こちらのコードもすべてCで書いてあり、Goertzelのアルゴリズムに基づいてDTMFが使う8つの周波数成分毎のエネルギーを計算しています。Keilのコードもそうでしたが、整数演算で済むようにあらかじめ入力されるサンプルの値はオーバフローが発生しないようにレベルを調整しておく必要があります。このTAM-TAMの場合にはサンプルの値は-256~256の範囲にあることを想定しているようです。
SLICからのPCM信号はu-Lawになっているので、これを展開して-256~255の範囲におさままるようにテーブルで変換してからTAMA-TAMのDTMFprocess()関数を呼び出してやると、いとも簡単にDTMF検出できちゃいました。うーん、デジタル信号処理って素晴らしいですねぇ。実際のところ、変換しないでu-Lawのままで直接 DTMFprocess()関数を呼び出しても DTMF検出できてしまったのですが、誤検出する可能性が高くなるかもしれないので、一応変換してから処理することにしました。
以下、動作の様子です。
"dtmf start"でDTMF検出を呼び出すPCM受信タスクを起動しています。
受話器を取り上げて、ボタンを順番に押していくと、きれいに認識できています。
いったんオンフックして、再度オフフック。今度は、リダイアル・ボタンを押してみました。さきぼどの順番で欠けることなくきれいに認識できています。思わず、
そういうわけで、DTMFの検出はマイコン側でやってやらねばなりません。電話機が生成したDTMF音はu-LawにエンコードされたPCM信号としてAT91SAM7XのSSCで受信できるので、これを解析してDTMF検出することになります。Googleさんに助けを求めてARM用のコードを探すと、簡単にonARMにあるSTM32用のコードが見つかりました。ターゲットはSTM32用となっていますが、DTMF検出の部分は全てCで書いてあるのでAT91SAM7Xで使うことには何の問題も無さそうです。ただし、Keil/ARMの息がかかっているので、KeilのuVisionで使うことという条件付きになってしまっていました。
しかたなくもう少し探したところ、NXPのサイトでLPC2138を使った電話の自動応答装置のプロジェクトを見つけました。しっかりとした説明とソースも含まれており、かなり参考になる記事です。ちょっと調べてみたら、NXPの前身であるPhilipsが2005年に開催したARM Design Contestで最優秀賞をとったTAM-TAMという作品のようです。ENC28J60を使ってethernetをつなぎ、TCP/IPにはuIPを使うという定番がキッチリおさえられています。基板もきれいにこさえて、開発にはuVision/Keil使っているみたいなので、やはり玄人さんが趣味で作った作品という感じでしょうか。こちらのコードもすべてCで書いてあり、Goertzelのアルゴリズムに基づいてDTMFが使う8つの周波数成分毎のエネルギーを計算しています。Keilのコードもそうでしたが、整数演算で済むようにあらかじめ入力されるサンプルの値はオーバフローが発生しないようにレベルを調整しておく必要があります。このTAM-TAMの場合にはサンプルの値は-256~256の範囲にあることを想定しているようです。
SLICからのPCM信号はu-Lawになっているので、これを展開して-256~255の範囲におさままるようにテーブルで変換してからTAMA-TAMのDTMFprocess()関数を呼び出してやると、いとも簡単にDTMF検出できちゃいました。うーん、デジタル信号処理って素晴らしいですねぇ。実際のところ、変換しないでu-Lawのままで直接 DTMFprocess()関数を呼び出しても DTMF検出できてしまったのですが、誤検出する可能性が高くなるかもしれないので、一応変換してから処理することにしました。
以下、動作の様子です。
]]] MMnetSAM7X Console Monitor [[[ > slic init ProSLIC detected. 1652ms > dtmf start
"dtmf start"でDTMF検出を呼び出すPCM受信タスクを起動しています。
> Off-Hook. DTMF: 0 DTMF: 1 DTMF: 2 DTMF: 3 DTMF: 4 DTMF: 5 DTMF: 6 DTMF: 7 DTMF: 8 DTMF: 9 DTMF: * DTMF: 0 DTMF: #
受話器を取り上げて、ボタンを順番に押していくと、きれいに認識できています。
On-Hook. Off-Hook. DTMF: 0 DTMF: 1 DTMF: 2 DTMF: 3 DTMF: 4 DTMF: 5 DTMF: 6 DTMF: 7 DTMF: 8 DTMF: 9 DTMF: * DTMF: 0 DTMF: # > On-Hook.
いったんオンフックして、再度オフフック。今度は、リダイアル・ボタンを押してみました。さきぼどの順番で欠けることなくきれいに認識できています。思わず、
ヤッター!と声が出てしまいました。DTMFの検出なんてコードの実装に時間かかるんじゃないかと心配していたのですが、あっけなくできちゃってビックリです。