10.マイクで音を感じてみよう


マイクから音が入力されると赤いLEDが光るようしたり、3つのマイクを用意し一番大きな音が入ってきたマイクの方向へ回転させるようなプログラムを実行してみます。


マイク入力とLED(MicLED)
マイク入力とLED 改良版(MicLED2)
3つのマイクで大きな音が入力された方向へ回転させる(MicLED3)

マイクに大きな音が入ると赤のLEDが光る


Vsは推奨で2V最大で10Vとなっています。今作っているロボットの電源は電池だと4.5V、充電池では3.6Vです。ですから電源としては問題ありません。また、常識的にマイクはこのままでは使い物にはならなく、アンプをつなげなければいけません。手を抜いて直接CPUに繋いだとしたら1000ぐらいの数字のうちで10とか20ぐらいの変化を見ることになります。そこで、レベルシフトを行い、電源電圧の22/(100+22)=0.18となり、ADコンバータの値としては、1024*0.18=185となります。これは十分に8bit(255)の中に入り、小さい電圧変化を見ることができるようになります。また、高い周波数成分は要らないので1μFの指定のところは意図的に10μFに変更しています。


CdSのように電圧変化が大きい場合は上位8bitのみで計測しましたが、今度は逆に電圧変化が小さいので下8bitを使うようにプログラムします。
3つのマイクを使う場合には、ADコンバーターはAN7, AN10, AN11を用います。ポートに置き換えると、それぞれ、RC3, RB4, RB5を用いることになります。
ここでは練習なのでAN7(RC3)を用います。
「マイクに大きな音が入ると赤のLEDが光る」プログラム
// ファイル名 MicLED.c
// マイクに大きな音が入ると赤のLEDが光る
#include <htc.h>
__CONFIG(PWRTEN&HS&WDTDIS&UNPROTECT&MCLRDIS&BORDIS&IESODIS&FCMDIS);

#define _XTAL_FREQ 20000000
#define ANS7 0x80

typedef unsigned char byte;
void LED(byte n, byte times);
void Delay50();
void Delay100();

main()
{
  int i;
  int value, th = 16;
  int average=128;
  PORTA = 0;                // PORTAを0にする
  TRISA = 0;                // PORTAを出力に設定する
  ANSEL = ANS7;             // アナログ入力をAN7をON
  ADCON0 = 0x9d;            // ADFM=1(右詰)  AN7 adOn
  OPTION = 0;               // FOSC/4 プリスケーラ1/2

  for(i=0; i<20; i++)       // 電源電圧が安定するまで待つ
    Delay100();
  GODONE = 1;               // A/D 変換開始
  while(GODONE);            // 変換完了
  average = ADRESL;         // 平均の初期値データを得る
  while(1) {
    GODONE = 1;             // A/D 変換開始
    while(GODONE);          // 変換完了
    value = ADRESL;
    average = (average*9+value)/10;
    if(value > average+th) LED(1, 1);
    else if(value < average-th) LED(2, 1);
  }
}

void LED(byte n, byte times)
{
  byte i;
  for(i=0; i<times; i++) {
    PORTA =  n;
    Delay50();          // 50msディレイ
    PORTA =  0;
    Delay50();          // 50msディレイ
  }
}

void Delay50()          // 50m秒ディレイ
{
  int i;
  for(i=0; i<5; i++)        
    __delay_ms(10); 
}

void Delay100()          // 100m秒ディレイ
{
  int i;
  for(i=0; i<10; i++)        
    __delay_ms(10); 
}

まず、averageで平均のマイクからの電圧を求めますが、始めは不安定なのでLEDがピカピカ光るかもしれません。消えてからマイクの近くで手を叩いてみてください。
マイクへのノイズレベルを超えた場合にLEDが点灯するようになっています。
void LED(byte n, byte times)
nで示されたLEDをtimesで指定された回数点滅させる。
n=1赤、n=2青、n=4緑

 

マイクに大きな音が入ると赤のLEDが光る(改良版)

 

以下のプログラムをコピーして、貼り付けて使用してください。

「マイクに大きな音が入ると赤のLEDが光る(改良版)」プログラム
// ファイル名 MicLED2.c
// マイクに大きな音が入ると赤のLEDが光る
// 改良版
#include <htc.h>  
__CONFIG(PWRTEN&HS&WDTDIS&UNPROTECT&MCLRDIS&BORDIS&IESODIS&FCMDIS);

#define _XTAL_FREQ 20000000
#define ANS7 0x80

typedef unsigned char byte;
void LED(byte n, byte times);
void Delay50();
void Delay100();

main()
{
  int i;
  int value, th = 3;
  int average=128;
  int average2=0;
  PORTA = 0;                // PORTAを0にする
  TRISA = 0;                // PORTAを出力に設定する
  ANSEL = ANS7;             // アナログ入力をAN7をON
  ADCON0 = 0x9d;            // ADFM=1(右詰)  AN7 adOn
  OPTION = 0;               // FOSC/4 プリスケーラ1/2

  for(i=0; i<20; i++)       // 電源電圧が安定するまで待つ
    Delay100();
  GODONE = 1;               // A/D 変換開始
  while(GODONE);            // 変換完了
  average = ADRESL;         // 平均の初期値データを得る
  while(1) {
    GODONE = 1;             // A/D 変換開始
    while(GODONE);          // 変換完了
    value = ADRESL;
    average = (average*9+value)/10;
    if(value>average) average2 = (average2*11+value)/12;
    if(average2 > average+th) {
      LED(1, 1);
      average2 = average;
    }  
  }
}

void LED(byte n, byte times)
{
  byte i;
  for(i=0; i<times; i++) {
    PORTA =  n;
    Delay50();          // 50msディレイ
    PORTA =  0;
    Delay50();          // 50msディレイ
  }
}

void Delay50()          // 50m秒ディレイ
{
  int i;
  for(i=0; i<5; i++)        
    __delay_ms(10); 
}

void Delay100()          // 100m秒ディレイ
{
  int i;
  for(i=0; i<10; i++)        
    __delay_ms(10); 
}

if(value>average) average2 = (average2*11+value)/12;
if(average2 > average+th) {
LED(1, 1);
average2 = average;
}
前のプログラムではマイク電圧が平均からどちらかにずれたらLEDが点灯しましたが、これは+に変化したときのみその平均を求め、そのずれた値の平均が元の平均よりずれた場合にLEDが点灯します。このようにすると、突発的なノイズを軽減することができます。

3つのマイクで大きな音が入力された方向へ回転させる(MicLED3)

マイクの場合ADコンバーターは割り込みを使わないことにします。

こちらも同様に次のプログラムも動かしてみます。

以下のプログラムをコピーして、貼り付けてください。

「3つのマイクで大きな音が入力された方向へ回転させる(MicLED3)」プログラム
// ファイル名 MicLED3.c
// 3つのマイクのうち一番大きな音が入った方向に回転する
// 左:赤LED 右:緑LED 後:青LED

#include <htc.h> 
__CONFIG(PWRTEN&HS&WDTDIS&UNPROTECT&MCLRDIS&BORDIS&IESODIS&FCMDIS);

#define _XTAL_FREQ 20000000
#define PERIOD 100
#define false 0
#define true  1
#define AN4   0x91
#define AN5   0x95
#define AN6   0x99
#define AN7   0x9d
#define AN10  0xa9
#define AN11  0xad
#define ABS(x) ((x>=0) ? (x) : -(x))

typedef unsigned char byte;

void LED(byte n, byte times);
void Delay50();
void Delay100();
void Mic();
void initialize();

// グローバル変数の宣言
byte counter=0;
byte LeftCdS, CenterCdS, RightCdS;
byte ADch=0;
byte ConvEndFlag;
byte ADC[4]={AN4,AN5,AN6,AN11};

// 割り込み処理ルーチン
void interrupt Intr(void)
{
  // 約2ms毎に割り込む
  if(T0IF) {       // 割り込みがtimer0からか?
    counter++;
    TMR0 = PERIOD;
    T0IF = 0;                 // 割り込みフラグクリア
    return;
  }
  if(ADIF) {      // 割り込みがADコンバーターからか?
    if(ADch==0) LeftCdS = ADRESH;
    else if(ADch==1) CenterCdS = ADRESH;
    else if(ADch==2) RightCdS = ADRESH;
    ConvEndFlag=true;         // データの更新あり
    ADIF = 0;                 // 割り込みフラグクリア
  }
}

main()
{

  int i;

  initialize();
  // Timer0 の設定
  T0IE=1;                     // Timer0 の割り込みON
  T0IF=0;                     // 割り込みフラグクリア
  // ADコンバーターの設定
  ADIE = 1;                   // ADコンバータの割り込み許可
  ADIF = 0;                   // 割り込みフラグクリア
  PEIE = 1;                   // 周辺装置の割り込み許可
  GIE  = 1;                   // CPUへの割り込み許可
  GODONE = 1;                 // AD変換スタート

  for(i=0; i<20; i++)         // 電源電圧が安定するまで待つ
    Delay100();
  LED(5, 3);                  // 用意ができたら黄色LED点滅
  Mic();
}

void initialize()
{
  PORTA = 0;                 // PORTAを0にする
  TRISA = 0;                 // PORTAを出力に設定する
  TRISB = 0x30;              // 4,5input
  PORTB = 0;
  PORTC = 0;                 // PORTCを0にする
  TRISC = 0x0f;                 // PORTC下4bit入力、上4bit出力
  ANSEL = 0xF0;                 // AN4,5,6,7 ON
  ANSELH = 0x0C;                 // AN10,11 ON
  ADCON0 = AN4;                 // ADFM=0(左)  AN4 adOn
                                // 1 = Right justified
  OPTION = 0;
                                // 1:8 prescale
  T1CON = 0x31;                  // Timer1 settings
  TMR1IF = 0;            // clear TMR1IF
  TMR1H = 0xf8;               // Initialize Timer1 register
  TMR1L = 0x00;

  ADC[0]=AN4;
  ADC[1]=AN5;
  ADC[2]=AN6;
  ADC[3]=AN11;
}

void LED(byte n, byte times)
{
  byte i;
  for(i=0; i<times; i++) {
    PORTA =  n;
    Delay50();          // 50msディレイ
    PORTA =  0;
    Delay50();          // 50msディレイ
  }
}

void Delay50()          // 50m秒ディレイ
{
  int i;
  for(i=0; i<5; i++)        
    __delay_ms(10); 
}

void Delay100()          // 100m秒ディレイ
{
  int i;
  for(i=0; i<10; i++)        
    __delay_ms(10); 
}

byte Leftspeed=208;
byte Centerspeed=208;
byte Rightspeed=208;
#define MICTH 5
#define MIDLOW 80
void Mic()
{
  byte i;
  int average[3]={185,185,185};
  int x,diff[3];

  byte a,b,c,max;
  unsigned int cnt=0;
  
  GIE=0;
  ADC[0]=AN7;
  ADC[1]=AN10;
  ADC[2]=AN11;
  for(cnt=0; cnt<1000; cnt++) {
    for(i=0; i<3; i++) {
      ADCON0 = ADC[i];
      GODONE = 1;
      while(GODONE);
      x = ADRESL;
      average[i] = (average[i]*9+x)/10;
    }
  }

  while(RA3){  // スイッチが押されたら抜ける
    for(i=0; i<3; i++) {
      ADCON0 = ADC[i];
      GODONE = 1;
      while(GODONE);
      x = ADRESL;
      diff[i] = ABS(average[i] - x);
      average[i] = (average[i]*9+x)/10;
    }
    if((diff[0] >MICTH) || (diff[1]>MICTH) || (diff[2]>MICTH)) {
      a=diff[0]; b=diff[1]; c=diff[2];
      if(a>b) {
        if(a>c) { max=0; }
        else    { max=2;}
      }
      else if(b>c) { max = 1; }
      else { max=2; }

      GIE=1;
      x=500;       // 回転カウント
      switch(max){
        case 2: // Left
              PORTA = 4;
              Rightspeed = ~MIDLOW;  Leftspeed = MIDLOW;
              RC5=1; RC7=0;
              break;
        case 1: // backward
              PORTA = 2;
              if(diff[1]&1){  // たまに逆回りする
                Rightspeed = ~MIDLOW;  Leftspeed = MIDLOW;
                RC5=1; RC7=0;
              }
              else {
                Rightspeed = MIDLOW;  Leftspeed = ~MIDLOW;
                RC5=0; RC7=1;
              }
              x = 700;
              break;
        case 0: // Right
              PORTA = 1;
              Rightspeed = MIDLOW; Leftspeed = ~MIDLOW;
              RC5=0; RC7=1;
              break;
        default:
              PORTC &= 0x0f;
      }
      counter=0;
      cnt=0;
      while(cnt!=x) {
        if(counter==0) cnt++;
        RC4 = counter<Rightspeed;
        RC6 = counter<Leftspeed;
        if(!RA3) return;
      }
      PORTC &= 0x0f;
      PORTA = 0;
      GIE=0;
      Delay100();
      for(cnt=0; cnt<500; cnt++) {
        for(i=0; i<3; i++) {
          ADCON0 = ADC[i];
          GODONE = 1;
          while(GODONE);
          x = ADRESL;
          average[i] = (average[i]*9+x)/10;
        }
      }
    } //if
  }
  GIE=1;
}

目次へ戻る