PIC のプログラム
main()の先頭での、output_low(PIN_C0); は動作チェック用のLEDの初期設定です。このLEDはコマンドを受信したとき、再生中のメモリアクセスで点灯します。
while() で繰り返しに入ります。kbhit()関数は、シリアルからデータが到着したとき1になります。これをチェックしないで、getc()を行うと、シリアルからデータが到着するまでこの関数の実行が終了しません。main()
では、スイッチボタンでも「再生」処理を実行したいのですが、getc()で「はまって」しまうとスイッチのチェックまで制御ができません(割り込みを利用するとこの問題はありませんが、あいにく、外部割込み端子RB0をDAコンバータのポートで使ってしまいました)。
受信していた場合、最初と次のバイトをcmndとnumに読みます。cmndが 'h'や'l'の場合 adres をセットします。また、受信の応答としてコマンドを PC側に返します。'w'の場合、とりあえず受信データを
buf[] に保存します。受信後、writemem()でI2C経由で、メモリにページ書きをします。一度バッファに保存しないと、受信が遅れる心配があります。
'r'の場合、readMem() で連続するnumバイトのメモリを読み、PCに値をおくります。送信の場合は、少々遅くなってもかまいませんから、バッファは不要です。
//Serial File Transmission
//PC>SerialCommand
//PIC > I2C > FlashMem
//PIC RS232 xmit:C6 ,rcv:C7
//I2C CL:C3 ,DA:C4
//PC command
//h num :アドレスの上位バイトを設定する
//l num :アドレスの下位バイトを設定する
//w num :PCから続くnumバイトのデータをROMに書く
//r num :現在のアドレスからnumバイトのデータをPCに送る
#include <16f873a.h>
#fuses HS,NOWDT,NOLVP //内部クロック、WDT,LVPなし
#use delay(CLOCK=20000000)
#use RS232(BAUD=9600,xmit=PIN_C6,rcv=PIN_C7)//use delayの後に配置する
#use i2c(MASTER, SDA=PIN_C4, SCL=PIN_C3, FORCE_HW) // I2C使用宣言
void writeMem(long address);
void readMem(long address);
void playwave();
unsigned long adrs;
char buf[64];
char num,cmnd,dat;
long wsize,wcnt;
main()
{
int i;
//set_tris_b(0b11110010);
adrs=0;
output_low(PIN_C0);
while(1){
cmnd=' ';
if(kbhit()) {
cmnd = getc();//get command & num
output_high(PIN_C0);//check light
num = getc();
output_low(PIN_C0);
}
switch (cmnd){
case 'w'://write command
for(i=0;i<num;i++){
buf[i]=getc();//get data
}
writeMem(adrs);//write memory vir I2c
adrs += num;
putc('1');//put response
break;
case 'h'://high Address
adrs = num<<8 ;
putc('h');
break;
case 'l' :
adrs = (adrs & 0xF0) + num;//low address
putc('l');
break;
case 'r'://read command
readMem(adrs);//read memory and send
adrs += num;
break;
case 'p':
playwave();
//putc('p');
}
if(! input_state(PIN_A0))
playwave();
//putc(cmnd); //printf("1")ではバグル
//write ROM
}
return 0;
}
checkWrite() はi2c_isr_state()で書き込みの完了を確認してから、i2c_write(data); で次の書き込みを行います。writeMem()は指定addressからnumバイトとI2Cを通して連続書き込みをします。i2c_start();
で開始準備、i2c_write(0xA0);でメモリチップのデバイスアドレスを指定します。次に、addressの下位と上位のバイトを送ります。続けて、buf[]の値を書き込みます。ここで、連続するアドレスがページアドレスを跨がないよう注意する必要があります。この場合、0番地から64バイトづつ書き込みますから問題ありません。
最後にi2c_stop();でI2Cの利用を終了します。
//状態をチェックしてから書き込む
void checkWrite(int data){
int state;
while(1){
state=i2c_isr_state();
if(state>=0x80) {
i2c_write(data);
//delay_ms(2);
return ;
}
}
}
//addressからROMに書き込む
//chipアドレスは0に固定
void writeMem(long address)
{
//chipのメモリのaddress番地にdataを書く
int i;
i2c_start();
i2c_write(0xA0);
i2c_write(address>>8 );
i2c_write(address);
for(i=0;i<num;i++){
checkWrite(buf[i]);
}
i2c_stop();
delay_ms(5);
//enable_interrupts(INT_RDA);
//printf("wadrs:%lx\r\n",address);
}
以下は、メモリの読み出しです。readMem() はPCからの確認用の読み出しです。最初にaddressを送り、次に、i2c_write(0xA0
| 0x01); で、読み出しを指示します。i2c_read(1) で1を指示して読むと、続けて読み出すことを意味します。num-1 バイトを読み出した後、i2c_read(0)
で読み出し完了を指示します。putc() では読み出した内容を PC 側に送ることを指示します。
readRandomByte()は指定アドレスから1バイトを読み出します。アドレスを送り、読み出した1バイトを戻り値とします。playWave()
は、メモリから波形データを読み出し、再生します。最初の putc(readRandomByte(0x0)); はこのplayコマンドに対する応答です。波形メモリの先頭には'R'が記録されていますので、確認に利用できます。メモリの28,29
番地に波形データのバイト数が記録されています。この2つのバイトを合成して、2バイト整数にします。
(((long)readRandomByte(0x29))<<8) + wsize
の (long)のキャストに注意してください。これがないと、readRandomByte(0x29) を1バイトのデータとして8ビット右シフトしますから(<<8)、値が消えてしまいます。(long)で2バイトのデータにしてからシフトして上位バイトに送り込みます(うっかりこれを忘れて、暫く悩みました)。
波形データは、先頭から 0x2c 番地以後に記録されています。読み出しはページの制限がありませんから、無制限に連続読み出しができます。読み出す間隔を、サンプルした時間間隔にあわす必要があります。ここでは、6KHzサンプルした波形データを利用していますから、約170μS間隔でDAコンバータに送り出す必要があります。問題は、i2c_read(1)
による読み出し時間です。この前後で RC0 ピンに1,0をセットし、この間隔をオシロで測定すると、ほぼ100μSでした(この時間は PIC のクロックや
I2Cのクロックでも変化します)。そこで、70μSを追加して時間を調整します。i2c_read(1)の時間は80μS以下と推測していましたが、やや長めの値です。したがって、この方式では10KHz以上でサンプルした音声ファイルは再生できません。I2Cの関数をアセンブラで書けばもう少し早くなると思います。
//addressからデータを読みPCに送る
void readMem(long address)
{
//chipのメモリのaddressからnumバイトをよみシリアルに送る
int i;
i2c_start();
i2c_write(0xA0);
i2c_write(address>>8);
i2c_write(address);
i2c_start();
i2c_write(0xA0 | 0x01);
for(i=0;i<num-1;i++){
putc(i2c_read(1));//read & put
}
putc(i2c_read(0));//last data
i2c_stop();
}
int readRandomByte(long address){
//sddress1バイトを読む
i2c_start();
i2c_write(0xA0);
i2c_write(address>>8);
i2c_write(address);
//printf("radrs:%lx:",address);
i2c_start();
i2c_write(0xA0 | 0x01);
dat= i2c_read(0);
i2c_stop();
return dat;
}
void playwave(){
putc(readRandomByte(0x0));
wsize = readRandomByte(0x28);
wsize = (((long)readRandomByte(0x29))<<8) + wsize;
adrs = 0x2c;
output_b(readRandomByte(adrs));
i2c_start();//sequential read
i2c_write(0xA0 | 0x01);
for(wcnt=0;wcnt<wsize;wcnt++){
//この間ほぼ100μ秒
output_high(PIN_C0);//check light
output_b(i2c_read(1));
output_low(PIN_C0);//check light
delay_us(70);
adrs++;
}
output_b(i2c_read(0));
i2c_stop();
}