第3回 音楽再生について

3回目の今回は音楽再生についてです。

オペアンプ内蔵の”PIC16F1705″を使用して、
EEPROMの”24LC256″に保存した音声データを
I2C通信でデータの受け渡しを行っています。

 

#include <xc.h>
#include <stdio.h>
#include <htc.h>
#define _XTAL_FREQ 8000000
#pragma config FOSC = INTOSC
#pragma config WDTE = OFF
#pragma config PWRTE = OFF
#pragma config MCLRE = OFF
#pragma config CP = OFF
#pragma config BOREN = ON
#pragma config CLKOUTEN = OFF
#pragma config IESO = OFF
#pragma config FCMEN = OFF
#pragma config WRT = OFF
#pragma config PLLEN = OFF
#pragma config STVREN = ON
#pragma config BORV = HI
#pragma config LVP = OFF
#pragma config PPS1WAY = OFF
//******
#define ACK 0
#define NOACK 1
//******
void InterI2C(void);
void InitI2C_Master();
int I2C_Start(int adrs);
int I2C_rStart(int adrs);
void I2C_Stop();
int I2C_Send(char dt);
unsigned char I2C_Receive(int ack);
//******
int AckCheck;
unsigned int i;
unsigned int prev;
//******************************************************************************

//******************************************************************************
void __interrupt() isr(void){
    InterI2C();
}
//******************************************************************************

//******************************************************************************
//***メインプログラム***
void main(){    
//***初期設定***
OSCCON = 0b01111010;//8MHz
ANSELA = 0b00000000;
RC0PPS=0x10;
RC1PPS=0x11;
SSPDATPPS=0x11;
ANSELC = 0b00000000;
TRISA = 0b00010000;//RA4は入力
WPUA = 0b00010000;
TRISC = 0b00111011;//RC3~5はボタン入力,0:SCL,1;SDA
WPUC = 0b00111011;
OPTION_REG=0b00000000;
PORTA=0;
PORTC=0;
DAC1CON1=0;
DAC1CON0=0b10000000;
OPA1CON=0b11010010;
//******

//***I2C初期設定***
InitI2C_Master();
//******

prev=0;

while(1){

    DAC1CON1=0;
    //スタート音♪
    if(prev==0 && RC5==1){
        prev==1;
        //***データの読み込み***
        I2C_Start(0xA0);
        I2C_Send(0x00);//high order address byte
        I2C_Send(0x00);//low order address byte
        I2C_rStart(0xA1);
        for(i=0;i<7520;i++){
        DAC1CON1=I2C_Receive(ACK);
        __delay_us(100);
        }
        DAC1CON1=I2C_Receive(NOACK);
        __delay_us(100);
        I2C_Stop();
        prev=0;
    }
    
    //ストップ音♪
    if(prev==0 && RC4==1){   
        prev=1;
        //***データの読み込み***
        I2C_Start(0xA0);
        I2C_Send(0x1D);
        I2C_Send(0x60);
        I2C_rStart(0xA1);
        for(i=0;i<3070;i++){
            DAC1CON1=I2C_Receive(ACK);
            __delay_us(100);
        }
        DAC1CON1=I2C_Receive(NOACK);
        __delay_us(100);
        I2C_Stop();
        prev=0;
    }  
    
    //ガコッ♪
    if(prev==0 && RC3==1){  
        prev=1;
        //***データの読み込み***
        I2C_Start(0xA0);
        I2C_Send(0x29);
        I2C_Send(0x60);
        I2C_rStart(0xA1);
        for(i=0;i<4610;i++){
            DAC1CON1=I2C_Receive(ACK);
            __delay_us(100);
        }
        DAC1CON1=I2C_Receive(NOACK);
        __delay_us(100);
        I2C_Stop();
        prev=0;
    } 
    
        //ファンファーレ♪
    if(prev==0 && RA4==1){  
        prev=1;
        //***データの読み込み***
        I2C_Start(0xA0);
        I2C_Send(0x3B);
        I2C_Send(0x60);
        I2C_rStart(0xA1);
        for(i=0;i<13760;i++){
            DAC1CON1=I2C_Receive(ACK);
            __delay_us(100);
        }
        DAC1CON1=I2C_Receive(NOACK);
        __delay_us(100);
        I2C_Stop();
        prev=0;
    } 
    
}

}
//******

//******************************************************************************





//******************************************************************************
void I2C_IdleCheck(char mask){
    while((SSPCON2 & 0x1F)|(SSPSTAT & mask));
}
//******************************************************************************

//******************************************************************************
void InterI2C(void){
    if(SSP1IF == 1){
        if(AckCheck == 1) AckCheck=0;
        SSP1IF = 0;
    }
    if(BCL1IF == 1){
        BCL1IF = 0;
    }
}
//******************************************************************************

//******************************************************************************
void InitI2C_Master(){
    __delay_ms(40);
    SSP1STAT=0b10000000;
    SSP1CON1=0b00101000;
    SSP1ADD=0x13;
    SSP1IE=1;
    BCL1IE=1;
    PEIE=1;
    GIE=1;
    SSP1IF=0;
    BCL1IF=0;
    //

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

//******************************************************************************
int I2C_Start(int adrs){
    I2C_IdleCheck(0x5);
    SSP1CON2bits.SEN =1;
    I2C_IdleCheck(0x5);
    AckCheck=1;
    //SSP1BUF=(char)((adrs<<1)+rw);
    SSP1BUF=(adrs);
    while(AckCheck);
    return SSP1CON2bits.ACKSTAT;
}
//******************************************************************************

//******************************************************************************
int I2C_rStart(int adrs){
    I2C_IdleCheck(0x5);
    SSP1CON2bits.RSEN =1;
    I2C_IdleCheck(0x5);
    AckCheck=1;
    SSP1BUF=(adrs);
    while(AckCheck);
    return SSP1CON2bits.ACKSTAT;
}
//******************************************************************************

//******************************************************************************
void I2C_Stop(){
    I2C_IdleCheck(0x5);
    SSP1CON2bits.PEN=1;
}
//******************************************************************************

//******************************************************************************
int I2C_Send(char dt)
{
    I2C_IdleCheck(0x5);
    AckCheck=1;
    SSP1BUF=dt;
    while(AckCheck);
    return SSP1CON2bits.ACKSTAT;
}
//******************************************************************************

//******************************************************************************
unsigned char I2C_Receive(int ack){
    unsigned char dt;
    I2C_IdleCheck(0x5);
    SSP1CON2bits.RCEN=1;
    I2C_IdleCheck(0x4);
    dt=SSP1BUF;
    I2C_IdleCheck(0x5);
    SSP1CON2bits.ACKDT=ack;
    SSP1CON2bits.ACKEN=1;
    return dt;
}
//******************************************************************************

メイン制御のPIC16F1829からスイッチの信号を受け取って、
それぞれの入力ピンに対応した音を鳴らしています。
今回は4種類の音を使用しているので、
使い分けるためにピンも4つ使用しています。

24LC256に保存する音声データに関しては、
・まずwav形式の音声データを用意
・”Audacity”というソフトで編集
・”WavCnvPlus”というソフトで8bit 8kHz mono音源に変換
・”Stiling”というソフトで先頭にある余分なデータを削除
・”EZP2010″を使って書き込み

という手順で行っています。

上記手順の詳細は、こちらの参考文献が非常に分かりやすいです。
『PSYENCE:MEDIA』
『きむ工房ガレージハウス』

この工程で非常に苦労したのが、
PIC16F1705と24LC256とのI2C通信です。
なかなか上手く音声を再生することが出来ず、
いろいろと試行錯誤した結果、
アドレス指定のプログラムが原因でした。

いまだに理解出来ていないのですが、
ネットで調べて出てきたプログラムのコピペでは
同じ部品構成のはずなのに、なぜか上手くいきませんでした。

私と同じ現象で悩まれている方がもしこの記事を読まれていたら、
I2Cのアドレス指定部分を上記プログラムのコピペして
書き換えて頂ければ動くかもしれません。

ハード面で原因があるとすると、
スピーカーの前にコンデンサを付けると解決するかも。
こちらも”きむ工房様”のサイトが参考になります。
『メロディをスピーカーで鳴らす』

私のような初心者の方にとって、少しでも参考になれば幸いです。

<関連記事>
・第1回 準備するもの
・第2回 スロットマシン メインプログラムについて
・第3回 音楽再生について