チーム1469

課題名

DTMF Dialer

研究者名

Masahiro Watanabe,Seikai You

使用器具

PSoC *1
PSoC MiniProg *1
4*4キーパッド *1
スピーカー *1
ジャンパ線 数本
ケーブル 数本

概要

キーパッドから入力されたボタンに対応した数字または*、#、A、B、C、Dの記号のDTMF音を発生させる設計である。

詳しい説明

DTMF(英: Dual-Tone Multi-Frequency)とは、0から9までの数字と、*、#、A、B、C、Dの記号の計16種類の符号を、低群・高群の2つの音声周波数帯域の合成信号音で送信する方法である。
国によって、標準のDTMFの周波数も異なるのだが、ここで使用するDTMFの音の周波数は日本総務省令「端末設備等規則」第12条第2号に基づく別表第2号に載せる周波数である。

表1 ここで使用するDTMFの周波数表
    高群 (Hz) 高群 (Hz) 高群 (Hz) 高群 (Hz)
    1209 1336 1477 1633
低群(Hz) 697 1 2 3 A
低群(Hz) 770 4 5 6 B
低群(Hz) 852 7 8 9 C
低群(Hz) 341 * 0 # D

たとえば、「1」の場合、高群の1209Hzの音と、低群の697Hzの音が同時に出るという設計である。


参考資料:2014/12/12バージョン ウェキペディア「DTMF」(http://ja.wikipedia.org/wiki/DTMF)


ソースコード

#include <m8c.h>
#include "PSoCAPI.h"


/*****************使用する変数***************************/

static const keypad_LUT[256] =
{/* 0 1 2 3 4 5 6 7
    8     9     A     B     C     D     E     F  */
/*0*/0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
/*1*/0x20, '1', '4', 0x20, '7', 0x20, 0x20, 0x20,
   '*', 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
/*2*/0x20, '2', '5', 0x20, '8', 0x20, 0x20, 0x20,
   '0', 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 
/*3*/0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 
/*4*/0x20, '3', '6', 0x20, '9', 0x20, 0x20, 0x20,
   '#', 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 
/*5*/0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 
/*6*/0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 
/*7*/0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 
/*8*/0x20, 'A', 'B', 0x20, 'C', 0x20, 0x20, 0x20,
   'D', 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 
/*9*/0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 
/*A*/0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 
/*B*/0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 
/*C*/0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 
/*D*/0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 
/*E*/0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 
/*F*/0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20
};

/* キーパッドの押されたボタンの列番号と行番号を格納 */
BYTE rows,cols;

/* ボタンが押された回数を格納 */
BYTE InterruptCounter=0;

/* 押されたボタンの文字とLUTテーブルの番号を格納 */
unsigned char key[2] = {0x20,0};

int PW=110; //PulseWidthは使う最低音階の数値の半分以下に設定する
int SILENT=0;
/* 各音に対応したPeriod */
int L1=538,L2=487,L3=440,L4=399;
int H1=310,H2=281,H3=254,H4=230;
/*****************************************************/


/****************関数****************************/

void KeyPadHandler(void);

/* キーパッドをスキャンする */
unsigned char keypad_scan(void);

/* 各モジュールの初期化 */
void InitModules(void);

/* GPIO割り込み */
void GPIOInterrupt(void);

void DTMFsound(char key);
/****************************************************/


void main(void)
{
  InitModules();
   M8C_EnableGInt;    
   
   while(1)
   {    
       /* キーパッドをスキャンして、押されたらLCDに表示して音を発生 */
                   
       if(key[0] != 0x20)
       {                
           /* 押された文字を表示 */
           LCD_1_Position(0, 7);
           LCD_1_PrString((char *)key);
           
           /* 音を発生 */
           DTMFsound(key[0]);
           
           /* 押された回数を表示 */
           LCD_1_Position(1, 12);
           LCD_1_PrHexByte(InterruptCounter);
       
           key[0]=0x20;
       }
   }
}



/* 使われているモジュールを初期化します */
void InitModules(void)
{
  PWM16_1_Start();
   PWM16_2_Start();
   
   /* Initialize LCD */
   LCD_1_Start();
   LCD_1_Position(0,0);
   LCD_1_PrCString("Switch:");
   LCD_1_Position(1,0);
   LCD_1_PrCString("Interrupts:");
       
   /* Initilize timer- 10ms delay */
   Timer16_1_WritePeriod(320);
   Timer16_1_EnableInt();
       
   /* Enable GPIO interrupt */
   INT_MSK0|=0x20;
       
   /* Write 1's at column lines */
   PRT0DR|=0xF0;
}

/*
This is the port mapping for the keypad ports.

    p0.4 p0.5 p0.6 p0.7
       |    |    |    |
p0.0 ---1----2----3----A
      |    |    |    |
p0.1 ---4----5----6----B
      |    |    |    |
p0.2 ---7----8----9----C
      |    |    |    |
p0.3 ---*----0----#----D   */

/* キーパッドをスキャンして押されたキーを特定します */
unsigned char keypad_scan(void)
{
  BYTE key_result;
       
   /* Drive rows */
   PRT0DR = 0x0F;
   
   /* Read columns */
   rows = PRT0DR;
   
   /* Drive columns */
   PRT0DR = 0xF0;
   
   /* Read rows */
   cols = PRT0DR;
   
   /* Combine results */
   key_result = rows & cols;
       
   /* Get the key number from LUT */
   return(keypad_LUT[key_result]);
}


/* GPIO割り込み */
#pragma interrupt_handler GPIOInterrupt
void GPIOInterrupt(void)
{
  /* Disable GPIO interrupt */
   INT_MSK0&=~0x20;    
       
   /* Start Timer */
   Timer16_1_Start();
}


/* キースキャンのタイマー割り込み */
void TimerInterrupt(void)
{
  /* Stop the timer and update the flag */
   Timer16_1_Stop();
       
   /* Get the key */
   key[0] = keypad_scan();
       
       
   /* Check if no key or multiple keys are pressed */
   if(key[0] != 0x20)
   {
   /* Valid key. Take action if required */
           
       /* Increment interrupt counter */
       InterruptCounter++;        
   }
   
   /* Clear GPIO posted interrupt */
   INT_CLR0&=~0x20;
       
   /* Enable GPIO interrupt */
   INT_MSK0|=0x20; 
}

/* 引数で渡された文字に対応する音を発生させます */
void DTMFsound(char key){

switch (key){
  case '1':    PWM16_1_WritePeriod(L1);
               PWM16_2_WritePeriod(H1);
               jikan(2);
               break;
   case '2':    PWM16_1_WritePeriod(L1);
               PWM16_2_WritePeriod(H2);
               jikan(2);
               break;
   case '3':    PWM16_1_WritePeriod(L1);
               PWM16_2_WritePeriod(H3);
               jikan(2);
               break;
   case '4':    PWM16_1_WritePeriod(L2);
               PWM16_2_WritePeriod(H1);
               jikan(2);
               break;
   case '5':    PWM16_1_WritePeriod(L2);
               PWM16_2_WritePeriod(H2);
               jikan(2);
               break;
   case '6':    PWM16_1_WritePeriod(L2);
               PWM16_2_WritePeriod(H3);
               jikan(2);
               break;
   case '7':    PWM16_1_WritePeriod(L3);
               PWM16_2_WritePeriod(H1);
               jikan(2);
               break;
   case '8':    PWM16_1_WritePeriod(L3);
               PWM16_2_WritePeriod(H2);
               jikan(2);
               break;
   case '9':    PWM16_1_WritePeriod(L3);
               PWM16_2_WritePeriod(H3);
               jikan(2);
               break;    
   case '*':    PWM16_1_WritePeriod(L4);
               PWM16_2_WritePeriod(H1);
               jikan(2);
               break;
   case '0':    PWM16_1_WritePeriod(L4);
               PWM16_2_WritePeriod(H2);
               jikan(2);
               break;    
   case '#':    PWM16_1_WritePeriod(L4);
               PWM16_2_WritePeriod(H3);
               jikan(2);
               break;    
   case 'a':    PWM16_1_WritePeriod(L1);
               PWM16_2_WritePeriod(H4);
               jikan(2);
               break;    
   case 'b':    PWM16_1_WritePeriod(L2);
               PWM16_2_WritePeriod(H4);
               jikan(2);
               break;    
   case 'c':    PWM16_1_WritePeriod(L3);
               PWM16_2_WritePeriod(H4);
               jikan(2);
               break;
   case 'd':    PWM16_1_WritePeriod(L4);
               PWM16_2_WritePeriod(H4);
               jikan(2);
               break;
   default:    PWM16_1_WritePeriod(SILENT);
               PWM16_2_WritePeriod(SILENT);
               jikan(2);
               break;
   }
       PWM16_1_WritePeriod(SILENT);
       PWM16_2_WritePeriod(SILENT);
}

/* 時間稼ぎ用の関数 */
void jikan(int q){
  
   while(q){
       int p;
       int t;
       //計算された回数で 回す
       for(t=0;t<3;t++){
           for(p=0;p<50;p++){
               int i;
               for(i=0;i<50;i++);
           }
       }
       q--;
   }
}
      
       
           

ブロック図、写真

キーパッドを一定間隔でスキャンするためにTimerモジュールと、2つの音を同時に発生させるので、PWM16モジュールを2つ用いて、以下のように設置する。図1に表わす。
(クリックすると、拡大できる。)
ブロック図1469.png
図1 ブロック図


また、使用する装置の外観と、基板写真を載せる。図2と図3に表わす。
DSC_0960-min.jpg
図2 全体図


DSC_0961-min.jpg
図3 基板


考察

この実習では12個のボタンを使用して、ボタンに対応した音を発生させるので4*4のキーパッドを用いた。
そのキーパッドを使用するため、マスタープロジェクトのMatrixKeypadプロジェクトを参考にした。
また、音を発生させるためtimer_pwm2も参考にした。

参考にしたプロジェクトより、Timerモジュールを用いて一定間隔でキーの状態をスキャンしている。
何かしらのキーが押されたらどのキーが押されたのかを判定して、12種類のキー対応した音を出力する。
2種類の音を発生させるので2つのPWMモジュールを使って、ちがうPulseに設定することで可能にしている。
また、押された回数と押されたボタンをLCDに表示している。

DTMF音は上にも示したように2つの周波数成分の音を合成して出力している。この実習でもPSoC上で合成を試みたが音を出力することができなかったので、ステレオスピーカを用いて左右で違う音を出力し、人間の耳には合成された音が聞こえるような仕様にした。

また実際のDTMF音は正弦波を合成しているが、今回はバンドパスフィルタの設計が間に合わなかったので矩形波のまま出力している。適切なBPFを設計して合成することで、実際のDTMF音に近づくと考えられる。

  • 最終更新:2014-12-16 17:38:29

このWIKIを編集するにはパスワード入力が必要です

認証パスワード