自作パチスロに挑む! ジャグラー編 第3回
第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のアドレス指定部分を上記プログラムのコピペして
書き換えて頂ければ動くかもしれません。
ハード面で原因があるとすると、
スピーカーの前にコンデンサを付けると解決するかも。
こちらも”きむ工房様”のサイトが参考になります。
『メロディをスピーカーで鳴らす』
私のような初心者の方にとって、少しでも参考になれば幸いです。
コメントを残す