文書の表示以前のリビジョンバックリンクPDF の出力全て展開する/折り畳むODT 出力文書の先頭へ この文書は読取専用です。文書のソースを閲覧することは可能ですが、変更はできません。もし変更したい場合は管理者に連絡してください。 ====== 簡易WavePlayer(PIC18F2620) ====== ===== 概要 ===== 今迄、音声を記録したり、再生したりするものを製作したことがありません。 そこで今回は、音声を再生することだけに注力した、“簡易WavePlayer"なるものを製作してみました。 今回製作した、“簡易WavePlayer"には、当初、SDCに保存した、Waveファイルを再生するだけの機能を搭載する予定でしたが、SDC接続に向かないPIC(例えば、PIC12F683など)では音声が扱えないことになってしまいます。 そこで、Waveファイルの音声データ(PCM)部だけを、EEPROMに記録し、再生することが出来る機能も付加しました。 <仕様> * SDCに記録されたWaveファイルを再生します。\\ ※Wave形式は、PCM(無圧縮)、8kHz、8ビット、モノラルのみとします。 * SDCに記録されたWaveファイルのPCMデータ部をEEPROMに保存します。\\ ※EEPROMの記憶容量は、約131kバイトです。 * EEPROMに記録されたPCMデータを再生します。 * Waveファイルは、パソコン上のツール(サウンドレコーダーなど)で作成します。 {{:imgpaste:202004:admin-20200430-213010.png?500}} ===== 動作原理(ハードウェア) ===== ◎電源電圧 3.3Vの単一電源による駆動とします。 ◎SDC FAT16(最大2Gバイト)に対応します。 Waveファイル名は、“wave.wav"固定とします。 Wave形式は、PCM(無圧縮)、8kHz、8ビット、モノラルのみとします。 ◎EEPROM 約131kバイトの記憶容量を持つ、マイクロチップ社の、“24LC1025"を使用します。 ◎LCD 16文字2行のLCDを使用します。 LCDを動作させるために、PICのPWM信号を利用して、“負電圧"を発生させ、LCDに供給します。 ◎再生信号出力 PICのPWMとローパスフィルタを使用して、アナログ信号に変換します。 微弱な信号なので、アンプを接続し、スピーカを駆動します。※実験ではクリスタルイヤホンを使用しました。 ===== 動作原理(ソフトウェア) ===== ◎SDCに記録されたWaveファイルの再生(SW1) Waveファイルのフォーマットの詳細については、公開サイトが多くありますので、其方を参考にしてください。 Waveファイルの最初の58バイトには、以下に示すように、各種情報が記載されています。 本来は、この58バイトについてもチェックを行い、該当するデータかどうかを判断する必要があります。 今回は、これらのチェックを省略(58バイト読み飛ばす)しています。 59バイト以降のPCMデータを順次読み出しPWMに出力します。 尚、Waveファイルの作成ツールは、フリーソフトも含めいろいろ公開されています。 最も簡単な方法は、Windows標準の“サウンドレコーダー"を使って作成する方法です。 ◎SDCに記録されたWaveファイルのPCMデータ部のEEPROMへの保存(SW3) Waveファイルの59バイト以降のデータを、EEPROMに書き込みます。 EEPROMの容量を超えるデータは無視されます。 ◎EEPROMに記録されたPCMデータの再生(SW2) EEPROMに保存された、PCMデータを順次読み出しPWMに出力します。 ◎停止(SW4) 上記の処理を停止する場合には、SW4を押下します。 <code> <WAVEファイルのフォーマット(例)> 0 : 52 49 46 46 B2 38 01 00 57 41 56 45 66 6D 74 20 1 : 12 00 00 00 01 00 01 00 40 1F 00 00 40 1F 00 00 2 : 01 00 08 00 00 00 66 61 63 74 04 00 00 00 80 38 3 : 01 00 64 61 74 61 80 38 01 00 80 80 7F 80 7F 80 4 : 7F 80 7F 80 7F 84 85 85 85 85 85 85 85 85 85 85 5 : 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 6 : 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 7 : 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 8 : 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 4バイト:52 49 46 46:“RIFF" 4バイト:B2 38 01 00:RIFFデータサイズ 4バイト:57 41 56 45:“WAVE" 4バイト:66 6D 74 20:“fmt" 4バイト:12 00 00 00:fmtサイズ 2バイト:01 00:フォーマットID 2バイト:01 00:チャネル数 4バイト:40 1F 00 00:サンプリングレート 4バイト:40 1F 00 00:データ速度 2バイト:01 00:ブロックサイズ 2バイト:08 00:サンプルあたりのビット数 2バイト:00 00:拡張部分のサイズ 4バイト:66 61 63 74:“fact" 4バイト:04 00 00 00:factサイズ 4バイト:80 38 01 00:factデータ 4バイト:64 61 74 61:“data" 4バイト:80 38 01 00:dataサイズ (計58バイト) ※59バイト以降がPCMデータになります。 </code> ===== 回路図 ===== {{:imgpaste:202004:admin-20200430-220116.png}} ===== ソースコード ===== <code c wave_player.c> //********************************************************************** /* 『wave-player』 */ //********************************************************************** //■マクロの定義 #define BYTE unsigned short #define WORD unsigned int #define WWORD unsigned long #define SW_PLAY_SDC PORTB.B7 #define SW_PLAY_EEPROM PORTB.B6 #define SW_LOAD PORTB.B5 #define SW_STOP PORTB.B4 #define LED PORTB.B3 #define ACK 1 #define NO_ACK 0 #define WRITE_CYCLE_TIME 5 //********************************************************************** //■関数および変数の宣言 extern void main(); extern void init_lcd(); extern void init_sdc(); extern void init_ccp_compare(); extern void init_i2c(); extern void EEPROM_24LC1025_Page_Write(WWORD addr, BYTE *msg, WORD len); extern void EEPROM_24LC1025_Page_Read(WWORD addr, BYTE *msg, WORD len); extern void play_sdc(); extern void play_eeprom(); extern void load_sdc2eeprom(); //********************************************************************** //■グローバル変数の定義 char buf[500]; //********************************************************************** //■メイン関数です。 void main() { //ポートの設定 TRISA = 0b11111111; TRISB = 0b11110000; TRISC = 0b00000000; ADCON1.PCFG3 = 1; ADCON1.PCFG2 = 1; ADCON1.PCFG1 = 1; ADCON1.PCFG0 = 1; //PWMの初期化(1)→WAVE再生用に使用します。 PWM1_Init(100000); //100kHz PR2 = 0xFF; PWM1_Set_Duty(PR2 / 2); PWM1_Start(); //PWMの初期化(2)→LCDの輝度電圧(負電圧発生)に使用します。 PWM2_Init(100000); //100kHz PR2 = 0xFF; PWM2_Set_Duty(PR2 / 2); PWM2_Start(); Delay_ms(100); //LCDの初期化 init_lcd(); //SDCの初期化 init_sdc(); //I2Cの初期化 init_i2c(); // while (1) { Lcd_Out(1, 1, "RUN MODE ?"); // if (SW_PLAY_SDC == 0) { Lcd_Cmd(_LCD_CLEAR); Lcd_Out(1, 1, "PLAY SDC"); play_sdc(); Lcd_Out(1, 1, "STOP "); Delay_ms(500); Lcd_Cmd(_LCD_CLEAR); } if (SW_PLAY_EEPROM == 0) { Lcd_Cmd(_LCD_CLEAR); Lcd_Out(1, 1, "PLAY EEPROM"); play_eeprom(); Lcd_Out(1, 1, "STOP "); Delay_ms(500); Lcd_Cmd(_LCD_CLEAR); } if (SW_LOAD == 0) { Lcd_Cmd(_LCD_CLEAR); Lcd_Out(1, 1, "LOAD SDC->EEPROM"); load_sdc2eeprom(); Lcd_Out(1, 1, "STOP "); Delay_ms(500); Lcd_Cmd(_LCD_CLEAR); } } } //********************************************************************** //■SDCのWAVEファイルの内容を再生する関数です。 void play_sdc() { WWORD fsize, length, cnt, addr; char character; // Mmc_Fat_Assign("wave.wav", 0); Mmc_Fat_Reset(&fsize); length = 0; for (cnt = 0; cnt < 58; cnt++) { Mmc_Fat_Read(&character); } length += 58; addr = 0; while ((length < fsize) && (SW_STOP == 1)) { Mmc_Fat_Read(&character); PWM1_Set_Duty(character); length++; Delay_us(70); } } //********************************************************************** //■EEPROMの内容を再生する関数です。 void play_eeprom() { WWORD addr; // addr = 0; Soft_I2C_Start(); Soft_I2C_Write(0xA0); Soft_I2C_Write((addr >> 8) & 0xFF); Soft_I2C_Write(addr & 0xFF); Soft_I2C_Start(); Soft_I2C_Write(0xA1); while (addr < 0x10000) { PWM1_Set_Duty(Soft_I2C_Read(ACK)); addr++; Delay_us(50); if (SW_STOP == 0) { break; } } PWM1_Set_Duty(Soft_I2C_Read(NO_ACK)); Soft_I2C_Stop(); // addr = 0; Soft_I2C_Start(); Soft_I2C_Write(0xA8); Soft_I2C_Write((addr >> 8) & 0xFF); Soft_I2C_Write(addr & 0xFF); Soft_I2C_Start(); Soft_I2C_Write(0xA9); while (addr < 0x10000) { PWM1_Set_Duty(Soft_I2C_Read(ACK)); addr++; Delay_us(50); if (SW_STOP == 0) { break; } } PWM1_Set_Duty(Soft_I2C_Read(NO_ACK)); Soft_I2C_Stop(); } //********************************************************************** //■SDCのWAVEファイルの内容をEEPROMに書き込む関数です。 void load_sdc2eeprom() { WWORD fsize, length, cnt, addr; char character; // Mmc_Fat_Assign("wave.wav", 0); Mmc_Fat_Reset(&fsize); length = 0; for (cnt = 0; cnt < 58; cnt++) { Mmc_Fat_Read(&character); } length += 58; addr = 0; while ((length < fsize) && (addr < 0x20000) && (SW_STOP == 1)) { for (cnt = 0; cnt < 128; cnt++) { Mmc_Fat_Read(&character); //1個データを飛ばします。 Mmc_Fat_Read(&character); buf[cnt] = character; } EEPROM_24LC1025_Page_Write(addr, buf, 128); addr += 128; fsize += 128; LongWordToStr(addr, buf); Lcd_Out(2, 1, buf); } Lcd_Cmd(_LCD_CLEAR); } //********************************************************************** //■LCDを初期化する関数です。 sbit LCD_RS at RA5_bit; sbit LCD_EN at RA4_bit; sbit LCD_D7 at RA0_bit; sbit LCD_D6 at RA1_bit; sbit LCD_D5 at RA2_bit; sbit LCD_D4 at RA3_bit; sbit LCD_RS_Direction at TRISA5_bit; sbit LCD_EN_Direction at TRISA4_bit; sbit LCD_D7_Direction at TRISA0_bit; sbit LCD_D6_Direction at TRISA1_bit; sbit LCD_D5_Direction at TRISA2_bit; sbit LCD_D4_Direction at TRISA3_bit; void init_lcd() { short cnt; // Lcd_Init(); Lcd_Cmd(_LCD_CLEAR); Lcd_Cmd(_LCD_CURSOR_OFF); Lcd_Out(1, 1, "wave player v1.0"); for (cnt = 0; cnt < 16; cnt++) { Lcd_Chr(2, cnt + 1, 0xFF); Delay_ms(100); } Lcd_Cmd(_LCD_CLEAR); } //********************************************************************** //■SDCを初期化する関数です。 sfr sbit Mmc_Chip_Select at RC6_bit; sfr sbit Mmc_Chip_Select_Direction at TRISC6_bit; void init_sdc() { SPI1_Init_Advanced(_SPI_MASTER_OSC_DIV64, _SPI_DATA_SAMPLE_MIDDLE, _SPI_CLK_IDLE_LOW, _SPI_LOW_2_HIGH); if (Mmc_Fat_Init()) { while (1) { Lcd_Out(2, 1, "SDC error!"); Delay_ms(500); Lcd_Out(2, 1, " "); Delay_ms(500); } } SPI1_Init_Advanced(_SPI_MASTER_OSC_DIV4, _SPI_DATA_SAMPLE_MIDDLE, _SPI_CLK_IDLE_LOW, _SPI_LOW_2_HIGH); Lcd_Out(2, 1, "SDC complete!"); Delay_ms(500); Lcd_Cmd(_LCD_CLEAR); } //********************************************************************** //■I2Cを初期化する関数です。 sbit Soft_I2C_Scl at RC0_bit; sbit Soft_I2C_Sda at RC1_bit; sbit Soft_I2C_Scl_Direction at TRISC0_bit; sbit Soft_I2C_Sda_Direction at TRISC1_bit; void init_i2c() { Soft_I2C_Init(); } //********************************************************************** //■EEPROMにページ単位でデータを書き込む関数です。 void EEPROM_24LC1025_Page_Write(WWORD addr, BYTE *msg, WORD len) { int cnt; // Soft_I2C_Start(); if ((addr & 0x10000) == 0) Soft_I2C_Write(0xA0); else Soft_I2C_Write(0xA8); Soft_I2C_Write((addr >> 8) & 0xFF); Soft_I2C_Write(addr & 0xFF); for (cnt = 0; cnt < len; cnt++) { Soft_I2C_Write(msg[cnt]); } Soft_I2C_Stop(); // Delay_ms(WRITE_CYCLE_TIME); } //********************************************************************** //■EEPROMからページ単位でデータを読み込む関数です。 void EEPROM_24LC1025_Page_Read(WWORD addr, BYTE *msg, WORD len) { int cnt; // Soft_I2C_Start(); if ((addr & 0x10000) == 0) Soft_I2C_Write(0xA0); else Soft_I2C_Write(0xA8); Soft_I2C_Write((addr >> 8) & 0xFF); Soft_I2C_Write(addr & 0xFF); Soft_I2C_Start(); if ((addr & 0x10000) == 0) Soft_I2C_Write(0xA1); else Soft_I2C_Write(0xA9); for (cnt = 0; cnt < (len - 1); cnt++) { msg[cnt] = Soft_I2C_Read(ACK); } msg[cnt] = Soft_I2C_Read(NO_ACK); Soft_I2C_Stop(); // Delay_ms(WRITE_CYCLE_TIME); } //********************************************************************** </code> ===== 動作確認 ===== {{:imgpaste:202004:admin-20200430-220234.png?500}} サウンドレコーダー(Windows標準付属ツール)を使用して、Waveファイルを作成します。 {{:imgpaste:202004:admin-20200430-220259.png}} SDCに保存したWAVEファイル(例)です。 {{:imgpaste:202004:admin-20200430-220310.png}} 左側:動作スイッチ押下待ちの画面です。 右側:SDC内のWaveファイルに記録されているPCM(無圧縮)データを再生しています。 {{:imgpaste:202004:admin-20200430-220321.png}}{{:imgpaste:202004:admin-20200430-220324.png}} 左側:EEPROM内に記録されているPCM(無圧縮)データを再生しています。 右側:SDC内のWaveファイルのPCM(無圧縮)データをeepromに書き込んでいます。 {{:imgpaste:202004:admin-20200430-220328.png}}{{:imgpaste:202004:admin-20200430-220333.png}} 如何ですか? EEPROMに音声データを記録し、再生することが出来るので、今後の製作物に多いに活かせそうです。 例えば、 * 電圧や電流の測定器に置いて、LCDや7セグで表示するのではなく、音声で読み上げてくれる。 * 温度制御では、温度の高低を、音声で教えてくれる。 * モーター制御では、回転の異常を、音声で警告してくれる。 * 時計の目覚ましでは、音声で時間を教えてくれる。 <callout type="warning" title="著作権表示 copyright notice"> このページは稲崎様の閉鎖したHPのコピーで、著作権は稲崎様にあります。[[elechobby:picdic:picdic|詳細]] This page is a copy of Mr. Inasaki's closed website, and the copyright is held by him.[[elechobby:picdic:picdic|Details]] </callout> elechobby/picdic/otherpic/189.txt 最終更新: 2025/10/17 14:29by 127.0.0.1