Nゲージ レイアウト内のスピーカー・・その後

Nゲージ レイアウト内のスピーカー

Nゲージのレイアウト内に置こうとして作ったスピーカー

Nゲージ レイアウト内のスピーカー
反対側は・・
高架駅付近に紛れ込まそうとペーパークラフトのビルの絵を貼り付けた。
 
こちらが正面なのか!?

駅のアナウンス、交通信号のピヨピヨ音・故郷の空などを流すつもり。
音源は市販のMK-144というMP3ボード。
音声データ(MP3ファイル)はパソコンとUSBでつないでメモリ内に入れるのだが、この製品も古いものでサポートするOSはWindowsXPまで。
実際にはWindows7でも問題なく動いていたがWindows10になってからは認識しなくなった。
後継の製品も出ているようだがPICマイコンとMP3デコーダICのVS1011Eを使ったプレーヤー、dsPIC33を使って単体でWAV再生ができるプレーヤーを結構作ったのでもう市販品は買わず、こちらもお役御免間近というところ。
 

自作のものは音源をSDカードに記録するもの。
VS1011Eも終息気味のようだがSDカードにさえ書き込みができればずっと使えるはず。

Nゲージレイアウト用交通信号機音声部
レイアウトのメインとなる部分のポイントマシン類を制御する補助の制御盤。
レイアウトのすぐそばで、Nゲージレイアウト用制御盤のコマンドを受けて動作する。
この中にMP3ボードMK-144を入れ、ここにあるスピーカーとレイアウト内に設置するスピーカーを切り替えて再生できるようにしていたのだが。

 
今のところまだ廃棄前のWindowsXP機があるので、音声データの入れ換えが困難になる前にひとまず取り外してプラモデルなどの展示台に流用することを考えて音声データを入れておくことにした。
 
補助の制御盤
MK-144を取っ払った補助の制御盤。
しかし、ボードは徐々に出来つつある。
ここでもポイントの操作ができるようになる予定。
ポイントの開通方向もLEDで表示。

話を戻して、取っ払ったサウンド部にはPIC24でSDカードのWAVを再生する機能も付けていたが、WAVファイルの対応はビットレート8kHzまで。
同時に2ファイルのモノラルのものが再生できるが、dsPIC33で44.1kHzのものが再生できる今となってはちょっと見劣りがする。
 

Nゲージ レイアウト内のスピーカー
PIC24は取り払ってMK-144の制御用にPIC16F1823を付けた。
黄色いロータリースイッチでフォルダを指定する。
ロータリースイッチのポジションは10個。
なので、10個のフォルダに分けて管理する音を再生できる。
 

耳のように見えるのは基板取り付け用のスピーカー。
 
基板からはみ出して付けてます・・

Nゲージ レイアウト内のスピーカー
1つのスピーカーにミキシングせず、両耳つけてステレオ対応。

Nゲージ レイアウト内のスピーカー
背面はアンプとスピーカー切換のロータリースイッチ。
ここにアンプがあるので、スピーカー側に用意した基板にはアンプは付けなかった。

Nゲージ レイアウト内のスピーカー
・・ということで、スピーカー部の基板はミニプラグのジャックのみという寂しいものに。
 
スピーカーの回りに張った壁紙用のシートに皺ができてた。
なんでじゃ!?

 
 
肝心の音は・・
当初、中は区切らず一つの箱の状態で試してみたが少しこもった感じだった。
そこで、中をダンボールで右のスピーカーの部屋、真ん中の部屋、左のスピーカーの部屋に区切ったところ結構いい音になった。
アンプはLM386。
よく無音時にボリュームを上げるとブーンというハム音がでる場合があるが、今回の回路、配線には問題が無かったようでこれもほとんど気にならないレベル。
アマゾンで実売1,000円程度のスピーカーなら十分太刀打ち出来る。
 
 
駅のアナウンス、雑踏の音などを流すつもりだったがもうちょっとまともなBGMを流してもいいかも。
 
 
あれ!?
これまでレイアウトの高架駅部分は背面が見えない前提でスピーカーを背面方向に向け、こちらに見える側にペーパークラフトの絵を貼り付けていた。
たいした音を出すわけじゃないからとスピーカーは後ろ向き。
でもちゃんとしたBGMを流すなら、スピーカーはこっちを向かないと。
で、反対に向けると左右の音が逆になる。しかも、高架の下にスピーカーの黒い丸が2つこちらを向くことになる。
 
やっぱ、音は背後に向けて出さざるを得ないか。

Nゲージ レイアウト内のスピーカー

Nゲージのレイアウトに組み込むMP3、WAVプレーヤーを増産中。
すでに駅のアナウンスなどを再生するMP3プレーヤーなども幾つか作っている。
4つのMP3プレーヤーの出力はアンプの前でミキシングして駅付近に置く2組のステレオスピーカー計4個から出力するようにした。
980円のパソコン用スピーカーの安いやつ。
 
増産中のプレーヤーの音もそれにミキシングして同じスピーカーから出しても良いのだが、やっぱ音に広がりがなくなりそう。
 
と、いうことで1個100円のスピーカーをいくつか台枠の下に組み込んだりする予定で工作中。
 
まぁ、安いスピーカーなので音質がどうのこうのといえるものではない。
それに、再生しようとしているのは音楽ではなく駅やら機関区の「音」なので。
 
要はその音がそれなりの場所から出てればいいかな、と。
 
高架駅も作る予定なのだが、この付近ではガード下の騒々しい音が出ていれば・・
 
高架駅の裏側に配置するように箱型のスピーカーボックスを作った。
レイアウト正面からは見えない部分に配置する予定。
 
容量の計算?
そんなものしてません。(^^;
エンクロージャーという立派なものじゃなく、ただの箱ですから。
 

Nゲージ レイアウト内のスピーカー

見えなくなる予定とはいえ、実際には視点をずらすと見えるでしょうなぁ。
ということで、表面にはペーパークラフトのビルの絵を貼った。

Nゲージ レイアウト内のスピーカー
一応側面も。

Nゲージ レイアウト内のスピーカー
裏側のスピーカーはφ50のものを2つ。
ステレオです・・
真ん中にアンプも組み込めるように基板を置いたが、電源ケーブルやスイッチ、ボリュームの配線等をどうするかという問題もある。

実際にレイアウトに組み込むときにこれにアンプも作るか、アンプは別の場所に置いてこれは単純にスピーカーだけという形をとるか。
いきあたりばったりです。

 
 
ちなみに、スピーカーを裸で鳴らすと高音でシャカシャカが強調されたかのような音。
雑踏の音、ガード下の音を長いこと流すと本当に不快な音に聞こえてくる。
箱に入れると強調された高音はなくなる。
ただし、低音というより少しこもった音の感じが強くなった。

・・まぁ、いい音というにはほど遠い。
市販の980円のスピーカーの方が少しマシかも。
 
ただ、自作のものはもうちょっと小さい箱にもできるだろうから、場所をとらないものができるというメリットもある。
 
 

その後はこうなりました。

鉄道模型レイアウト・プラモデル展示台用の音(PICでWAV再生) 2

先日、鉄道模型レイアウト・プラモデル展示台用の音源を作ったが、手元にあった市販のMP3ボードの制御用のボードとくっつけたため、ちょっと大きくなったので、小さめのものを作ってみた。
ただ手元にもう一つMP3ボードがあったので、これを載せておくことくしたため、やはり、ちょっと大きめか。

PIC24FJ64GA002 WAV再生ボード
前回のものは単純に市販のボードと連結しただけなので、大きめ。
Nゲージのレイアウトの下に隠すぶんには問題ないけど、単体のプラモデルを飾る台には大きすぎる。

今回のものは電源、スピーカを除いて、95mm×72mmのボード上に収まった。

PIC24FJ64GA002 WAV再生ボード

MP3ボードが7つの押しボタンに応じてダイレクトに選曲できる機能を持っているが、このスイッチは無くしたが、「やっぱ使いたい」と気が変わった時のためにコネクタで接続できるようにはしておいた。
・・こんなことやってるからなかなかコンパクトにならない・・

PIC24FJ64GA002 WAV再生ボード
一応スイッチ類も小さめのものを使った。

[youtube]http://youtu.be/_IyeWwwKgXs[/youtube]

 

これで、鉄道模型レイアウト・プラモデル展示台用に使える手持ちのMP3ボードはなくなったが、SDカードのスロットと、PICが残っているので、もう少しこんなのを作っておこうかな・・
おそらく、この2/3くらいの大きさにはできそう。

microSDカードや圧電スピーカを使って、電池駆動にすれば1/24スケールの自動車の模型に乗っけられるものもできそうであるが・・

 

鉄道模型レイアウト・プラモデル展示台用の音(PICでWAV再生)

 

で、プラモデルの飾り台として作った「箱」はこちら

鉄道模型レイアウト・プラモデル展示台用の音(PICでWAV再生)

Nゲージ 踏切の製作」は、遮断機の部分を作っている途中であるが、踏切の音にも、昔ながらの電鈴式の音、JRの音、私鉄の音、構内踏切の音などいろいろあるということで、いろんな音にしてみたり、遮断機の有無などでバリエーションのある踏切を5個量産中。

なのであるが、踏切ばかりでも能がない。

ちょっと脱線してPICマイコンで、鉄道模型のレイアウトやらプラモデルの展示台に内臓して音を出すためのモジュールを作ってみた。

ディーゼルエンジンのアイドリング音、交通信号のカッコー、ピヨピヨやら、単調な音の繰り返しはSDカードに記録した2~3秒くらいの音を繰り返して再生し、BGMの音楽は市販のMP3再生ボード(MK-144)と連動させて。
SDカードにデータを記録することで、交通信号機も昔ながらの「故郷の空」と「通りゃんせ」などが再生できる。

また、鉄道模型のレイアウト用以外にもプラモデルの展示台にも内蔵させることができ、プラモデルにあった音楽が流せる。

使ったPICマイコンは、PIC24FJ64GA002。

4つのスイッチにBCDコードがだせるロータリーコードスイッチを使って音楽を切換え、同時に2つのWAVファイルが再生できる。

音声はそれぞれ別の出力ポートに。
汽笛など単発的で繰り返しのない音は3つ目の音声として、一時的に1つの音を止めて出力。

また、このロータリーコードスイッチに応じてMK-144に登録されたMP3ファイルも再生。

等々。

もうちょっと詳しいことは下のソースのヘッダ部分に書いてます。

PIC24FJ64GA002でWAV再生
左の緑のボードは市販のMP3再生ボード(MK-144)を制御するボードMK-148。

MK-148上にはMP3ファイルの再生用タクトスイッチが並んでいる。

右のが今回作ったボード。

不要になったMK-148を再利用するために使ったのでアンプはMK-148上の物にミキシング。

2つのボードを並べるとちょっと大きいが、MK-148のタクトスイッチ部分を取り除いたり、MP3の再生機能自体をなくしてWAVファイルの再生だけに特化するともっとコンパクトにできる。

実際にレイアウトにつける時は、このボードは手元において、スピーカー部だけをレイアウト上に置くことになろうかと。
そのときはスイッチは操作しやすいようにつけかえますが。
PIC24FJ64GA002でWAV再生
やはり、ちぃっちゃいスピーカでは音が・・

プラモデルの展示台等ではコンパクトにしたいのでこれでも大きい方ではあるけど。

ためしに980円のスピーカーを繋いでみると、まあ使えるかも。・・場所とりますが。


追記.
ちょっと小さめのものを作ってみました。
鉄道模型レイアウト・プラモデル展示台用の音(PICでWAV再生) 2

[youtube]http://youtu.be/_IyeWwwKgXs[/youtube]


ソース

[c]

// ***********************************************
//
// PIC24FJ64GA002
//	8kHz sampling , 16bit モノラル Wav再生
//
//
// ・SDカードからwavファイルを読み込んで再生する
// ・音声はch1とch2の2つに出力する
// ・wavファイルは#0から#2までの最大3つの音声ファイルで1グループを構成する
// ・#0~#2の3つの再生スイッチで、それぞれのファイルを再生する
// ・スイッチに対応するファイルが存在しないときはそのスイッチに対応する音声は再生されない
// ・#0と#1スイッチが押されている間は、それに対応したファイルは繰り返して再生される
// ・#0と#1スイッチはトグルスイッチ、押しボタンスイッチでもよいがトグルスイッチを想定している
// ・#2スイッチが押されたときは、そのファイルが終了するまで再生し、繰り返さない。
//	  ただし、再生終了時に#2スイッチが押されていると再び再生される。
//	  #2の再生を途中で終了させるボタンはない。
//	  途中で終了させたい場合はロータリーコードスイッチを切り替える
// ・#0と#1スイッチはトグルスイッチ、押しボタンスイッチでもよいが押しボタンスイッチを想定している
// ・#0のファイルの音声はch1に出力する
// ・#1と#2のファイルの音声はch2に出力する
//	  #2の方が優先順位は高い。
//	  優先順位が高いものが再生されているときは、優先順位が低いものは再生されない
//	  #1と#2も再生するファイルがない、または再生スイッチが押されていないときは
//	  ch1と同じ音声(#0ファイルの音声)が出力される
// ・wavファイルのグループは最大10グループもてる
// ・グループはBCDコードが出力できるロータリーコードスイッチを使用し、その4bitをPICに入力する
// ・エラー時は内部に記録したチャイム音を再生する
// ・押しボタンで市販のMP3プレーヤーボードMK144の再生、停止を制御する(UART)
//
// ピンアサイン
// #1  MCLR
// #2  RA0/CN2 (MK-144制御用 input スタート/ストップ) 内部プルアップ
// #3  RA1/CN3 (MK-144制御用 input BUSYチェック) 内部プルアップ Low:busy Hi:not busy 
// #4  RB0/RP0/OC1 ch1音声
// #5  RB1/RP1/OC2 ch2音声
// #6  RP2 TX (MK-144制御用 output USART)
// #7  RP3 RX (MK-144制御用 input USART)
// #8  Vss
// #9  OCSI
// #10 OCSO
// #11 SD-CARD DAT3
// #12 RA4/CN0 #0音声再生(input) (active low--lowの間再生) 内部プルアップ
// #13 VDD
// #14 SD-CARD CMD/DI
// #15 SD-CARD CLK
// #16 SD-CARD DAT0
// #17 SD-CARD CD
// #18 SD-CARD WP
// #19 DISVREG (GND)
// #20 VCSP
// #21 RB10/CN16 #1音声再生(input) (active low--lowの間再生) 内部プルアップ
// #22 RB11/CN15 #2音声再生(input) (active low--lowの間再生) 内部プルアップ
// #23 RB12/CN14 (in ロータリーコードスイッチ BCD Bit0) 内部プルアップ
// #24 RB13/CN13 (in ロータリーコードスイッチ BCD Bit1) 内部プルアップ
// #25 RB14/CN12 (in ロータリーコードスイッチ BCD Bit2) 内部プルアップ
// #26 RB15/CN11 (in ロータリーコードスイッチ BCD Bit3) 内部プルアップ
// #27 VDD
// #28 VSS
//
//
// ***********************************************

//	コンパイラ XC16
//	SDカードのI/OはMicrochip C30のMicrochip Memory Disk Drive File Systemを使用
//	(FSIO.hからインクルードされるモジュールも環境に準備しておく)
//

#include <xc.h>
#include <uart.h>

// CONFIG2
#pragma config POSCMOD = XT 		// Primary Oscillator Select (XT Oscillator mode selected)
#pragma config I2C1SEL = PRI		// I2C1 Pin Location Select (Use default SCL1/SDA1 pins)
#pragma config IOL1WAY = OFF		// IOLOCK Protection (IOLOCK may be changed via unlocking seq)
#pragma config OSCIOFNC = OFF		// Primary Oscillator Output Function (OSC2/CLKO/RC15 functions as CLKO (FOSC/2))
#pragma config FCKSM = CSDCMD		// Clock Switching and Monitor (Clock switching and Fail-Safe Clock Monitor are disabled)
#pragma config FNOSC = PRIPLL		// Oscillator Select (Primary Oscillator with PLL module (HSPLL, ECPLL))
#pragma config SOSCSEL = SOSC		// Sec Oscillator Select (Default Secondary Oscillator (SOSC))
#pragma config WUTSEL = LEG 		// Wake-up timer Select (Legacy Wake-up Timer)
#pragma config IESO = OFF			// Internal External Switch Over Mode (IESO mode (Two-Speed Start-up) disabled)

// CONFIG1
#pragma config WDTPS = PS32768		// Watchdog Timer Postscaler (1:32,768)
#pragma config FWPSA = PR128		// WDT Prescaler (Prescaler ratio of 1:128)
#pragma config WINDIS = ON			// Watchdog Timer Window (Standard Watchdog Timer enabled,(Windowed-mode is disabled))
#pragma config FWDTEN = OFF 		// Watchdog Timer Enable (Watchdog Timer is disabled)
#pragma config ICS = PGx1			// Comm Channel Select (Emulator EMUC1/EMUD1 pins are shared with PGC1/PGD1)
#pragma config GWRP = OFF			// General Code Segment Write Protect (Writes to program memory are allowed)
#pragma config GCP = OFF			// General Code Segment Code Protect (Code protection is disabled)
#pragma config JTAGEN = OFF 		// JTAG Port Enable (JTAG port is disabled)

#include <stdio.h>
#include <string.h>

#include "FSIO.h"					// SDカード i/o用

// クロック周波数指定(__delay_msとボーレート計算用)
//#define FCY 8000000L				// クロック 8MHz
#define FCY 16000000L				// クロック 16MHz
//#define FCY 32000000L 			// クロック 32MHz

#include <libpic30.h>				//delayマクロ用

#include "chime.h"					// エラーチャイムWAVファイル波形データ

#define FHANDLE 3
#define BUFSIZE 256
#define FILENAMEMAX 12


BYTE sdReadyFlag = FALSE;			// SD i/o 初期化 TRUE(1):OK / FALSE(0); NG
BYTE errorFlag = FALSE; 			// TRUE(1):チャイム鳴動 / FALSE(0)
unsigned int chimeidx = 0;			// エラーチャイム配列参照添字


// i/oバッファ

struct buffer {
	int status; 					// 0:無効 1:データ読み込み済 2:データ出力済
	char data[BUFSIZE]; 			// 読み込みバッファ
	int datasize;					// 有効レングス
	unsigned int outputptr; 		// 出力用添字
};

// WAVファイル 先頭部分
// WAVファイルのフォーマットに合わせて修正

struct wavheader {
	char nu[36];					// (読み飛ばし部分)
	char datachid[4];				// dataチャンクID
	unsigned long chunkleng;		// 波形データレングス
};

// ハンドラ

struct handler {
	char fname[FILENAMEMAX];		// ファイル名
	int playFlag;					// 再生フラグ 0:停止 1:再生
	int repeatFlag; 				// 繰り返しフラグ
									// 0:繰り返しあり
									// 1:繰り返しなし(データ未読み込み)
									// 2:繰り返しなし(初期値/データ読み込み完了)
	FSFILE *fptr;					// ファイルポインタ
	unsigned long remaining;		// 波形データ残りバイト数
	int nextRead;					// i/oバッファ参照用添字(読み込み用)
	int currOut;					// i/oバッファ参照用添字(出力用)
	struct buffer buf[2];			// i/oバッファ
	struct wavheader whdr;			// WAVファイル 先頭部分
} fhandle[FHANDLE];

// ロータリーコードスイッチチェック用
// (0~9までのBCDコードをポートから入力して、再生するファイルグループを指定)
typedef union {
	unsigned char selector;

	struct {
		unsigned selB0 : 1; 		//bit0
		unsigned selB1 : 1; 		//bit1
		unsigned selB2 : 1; 		//bit2
		unsigned selB3 : 1; 		//bit3
		unsigned fu : 4;
	} selectorStatusBits;
} selectorStatus;
selectorStatus sel;

BYTE Mk144Switch = 0;				// Mk144制御スイッチ 0:停止 1:再生
BYTE Mk144SwitchSave = 0;			// Mk144制御スイッチ 0:停止 1:再生

// ファイルグループID(ファイル名の先頭)
char *groupid[] = {"00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "xx"};

// UART
//	MK-144の規格 4800bps パリティなし データ8bit ストップビット1 フロー制御なし

const unsigned int UARTMode
		= UART_EN					// UARTモジュール - 有効
		& UART_NO_PAR_8BIT			// パリティなし/データ 8ビット
		& UART_1STOPBIT 			// ストップビット - 1ビット
		& UART_IDLE_CON 			// アイドルモード - Work in IDLE mode
		& UART_MODE_SIMPLEX 		//UxRTSピンモード
		& UART_DIS_WAKE 			// ウェイクアップ - 無効
		& UART_UEN_00				//Port latch control
		& UART_IrDA_DISABLE 		// IrDAエンコーダ 有効/無効
		& UART_DIS_LOOPBACK 		// ループバック - 無効
		& UART_DIS_ABAUD			// 自動ボーレート - 無効
		& UART_UXRX_IDLE_ONE		//Idle state UxRX Idle state is one
		& UART_BRGH_SIXTEEN 		//BRG BRG generates 16 clocks per bit period
		;

const unsigned int UARTSta
		= UART_TX_ENABLE			// 送信 - 有効
		& UART_INT_TX_BUF_EMPTY 	//送信の割り込み条件 送信バッファが空になったとき
		& UART_IrDA_POL_INV_ZERO	//IrDAエンコード
		& UART_SYNC_BREAK_ENABLED
		& UART_TX_ENABLE
		& UART_INT_RX_CHAR			// 受信の割り込み条件 - 受信するたび
		& UART_ADR_DETECT_DIS		// アドレス検出 - 無効
		& UART_RX_OVERRUN_CLEAR 	// 受信バッファオーバーランエラー - クリア
		;

//
// ボーレートクロック
// UARTBaud 8MHz :103  16MHz:207  32MHz: 416

#define BAUDRATE  4800L 						 // ボーレート [bps]
const double UARTBaud = (double) FCY / (16 * BAUDRATE) - 1;

//
// 関数定義
//
void fhandleinit(int);				// ハンドラ初期化
void bhandleinit(int, int); 		// i/oバッファ初期化
void makeFileName(void);			// ファイル名生成
int waveform(int);					// 波形作成
void wavopen(int);					// Wavファイルオープン
void wavread(int);					// Wavファイル波形部読み込み
void mk144ctrl(void);				//MK-144制御
void mk144putcmd(unsigned char);	//MK-144向けコマンド送信

/**
 * メイン関数
 */

int main(void) {
	memset(fhandle, 0x00, sizeof (fhandle));
	selectorStatus check;
	check.selector = 0x00;
	sel.selector = 0x0a;

	CLKDIV = 0;

	AD1PCFG = 0xFFFF;				// I/Oはすべてデジタル
	TRISB = 0xfffc; 				// RB0、RB1を音声出力
	// 内部プルアップ1(CN16 CN15 CN14 CN13 CN12 CN11 CN3 CN2 CN0をON)
	_CN16PUE = _CN15PUE = _CN14PUE = _CN13PUE = _CN12PUE = _CN11PUE = _CN3PUE = _CN2PUE = _CN0PUE = 1;

	CMCON = 0; // コンパレータオフ

	// SDカード用設定
	// SPIのピン割り付け
	RPINR20bits.SDI1R = 7;			// SDI1をRP7に
	RPOR3bits.RP6R = 8; 			// SCK1をRP6に
	RPOR2bits.RP5R = 7; 			// SDO1をRP5に

	// ch1音声用設定
	// タイマ2設定
	T2CON = 0x8000;
	PR2 = 159; //100kHz
	// OC1設定
	RPOR0bits.RP0R = 18;			// OC1をRP0に割付け
	OC1CON = 0x0006;				// タイマ2使用 PWM FAULTなし

	// ch2音声用設定
	// タイマ3設定
	T3CON = 0x8000;
	PR3 = 159; //100kHz
	// OC2設定
	RPOR0bits.RP1R = 19;			// OC2をRP1に割付け
	OC2CON = 0x000e;				// タイマ3使用 PWM FAULTなし

	// タイマ4(音声出力用)モード設定
	T4CON = 0x0000;
	T4CONbits.TCKPS1 = 0;
	T4CONbits.TCKPS0 = 1;
	T4CONbits.TON = 1;

	PR4 = 249;						// 125usec	8kHz
	_T4IP = 5;						// 割り込みレベル
	_T4IF = 0;						// 割り込みフラグクリア
	_T4IE = 1;						// タイマ4割り込み許可

	// MK-144制御用
	// UART用設定
	RPINR18bits.U1RXR = 3;			// UART1 RX を RP3に
	RPOR1bits.RP2R = 3; 			// UART1 TX を RP2に
	U1BRG = UARTBaud;
	U1MODE = UARTMode;
	U1STA = UARTSta;

	// RA0/CN2(MK-144制御ボタン)用CN割り込み設定
	_CN2IE = 1; 					//CN2の割り込みを許可
	_CNIP = 5;						// 割り込みレベル
	_CNIF = 0;						// CN割り込みフラグクリア
	_CNIE = 1;						// CNピン割り込み許可

	int i;

	while (1) {
		// ファイルグループ取得(ロータリーコードスイッチ(Active Low)チェック)
		check.selectorStatusBits.selB0 = !PORTBbits.RB12;
		check.selectorStatusBits.selB1 = !PORTBbits.RB13;
		check.selectorStatusBits.selB2 = !PORTBbits.RB14;
		check.selectorStatusBits.selB3 = !PORTBbits.RB15;

		if (sel.selector != check.selector) {
			// ロータリーコードスイッチが変わった
			sel = check;
			sdReadyFlag = errorFlag = FALSE;
			mk144putcmd(0xEF); //MK-144停止
			Mk144SwitchSave = Mk144Switch = 0;

		}
		if (!sdReadyFlag) {
			// 初期化時、ロータリーコードスイッチ変更時、
			// SDカード抜き差し等でi/oエラー(現仕様はseek時のエラー)となった時のリカバリ
			for (i = 0; i < FHANDLE; i++)
				fhandleinit(i);
			makeFileName();
			while (!sdReadyFlag) {
				// SDi/o 初期化完了まで無限ループ
				sdReadyFlag = FSInit(); // SDi/o 初期化
				errorFlag = !sdReadyFlag;
			}
			for (i = 0; i < FHANDLE; i++)
				wavopen(i);
		}

		// 再生スイッチ(Active Low)チェック
		fhandle[0].playFlag = !PORTAbits.RA4;
		fhandle[1].playFlag = !PORTBbits.RB10;
		fhandle[2].playFlag = !PORTBbits.RB11;
		for (i = 0; i < FHANDLE; i++) {
			if (2 == fhandle[i].repeatFlag && fhandle[i].playFlag)
				fhandle[i].repeatFlag = 1;
			wavread(i);
		}
		mk144ctrl(); //MK-144制御
	}
}

/**
 * ハンドラ初期化
 * @param int i 初期化するハンドラ(fhandle)の番号
 */
void fhandleinit(int i) {
	fhandle[i].fname[0] = 0x00;
	fhandle[i].playFlag = 0;
	// ファイル#0、#1は繰り返し再生 #2は繰り返さない
	fhandle[i].repeatFlag = (0 == i || 1 == i) ? 0 : 2;

	if (NULL != fhandle[i].fptr)
		FSfclose(fhandle[i].fptr);
	fhandle[i].fptr = NULL;
	fhandle[i].remaining = 0L;
	fhandle[i].nextRead = 0;
	fhandle[i].currOut = 0;
	bhandleinit(i, 0);
	bhandleinit(i, 1);
}

/**
 * i/oバッファ初期化
 * @param int  i 初期化するハンドラ(fhandle)の番号
 * @param int  j 初期化するi/oバッファ(buf)の番号
 */
void bhandleinit(int i, int j) {
	fhandle[i].buf[j].status = 0;
	fhandle[i].buf[j].datasize = 0;
	fhandle[i].buf[j].outputptr = 0;
}

/**
 * ファイル名生成
 *
 * ファイル名は、
 * groupid[ロータリーコードスイッチの値で修飾]
 * +
 * 音声再生スイッチの番号(1~3)
 * +
 * '.wav'
 *
 * ロータリーコードスイッチが1の時 :
 *#1音声再生スイッチに対応するファイル名は '01' + '1' + '.wav'
 * → '011.wav'
 *#2音声再生スイッチに対応するファイル名は '01' + '2' + '.wav'
 * → '012.wav'
 *#3音声再生スイッチに対応するファイル名は '01' + '3' + '.wav'
 * → '013.wav'
 *
 */
void makeFileName(void) {
	int i;
	char wk[] = "0.wav";
	for (i = 0; i < FHANDLE; i++) {
		strcpy(fhandle[i].fname, groupid[sel.selector]);
		wk[0] = '0' + (char) (i + 1);
		strcat(fhandle[i].fname, wk);
	}
}

/**
 * タイマ4割り込み処理関数
 */
void __attribute__((interrupt, no_auto_psv)) _T4Interrupt(void) {
	unsigned int e, ch1, ch2;

	if (errorFlag) {
		// エラー時のチャイム
		// chime配列は偶数バイトで作成しておくこと
		e = chime[chimeidx] | (chime[chimeidx + 1] << 8);
		e = e + 32768;
		e = e >> 8;
		e = e & 0xff;
		chimeidx += 2;
		if (chimeidx > (sizeof chime))
			chimeidx = 0;
		OC1RS = (PR2 + 1) * e / 256; // ch1:RP0出力
		OC2RS = (PR3 + 1) * e / 256; // ch2:RP1出力
	} else {
		// ch1は常にファイル#0を再生
		ch1 = waveform(0);
		ch1 = (0 == ch1) ? 128 : ch1; // ch1が0なら無音
		OC1RS = (PR2 + 1) * ch1 / 256; // ch1:RP0出力

		// ch2は、再生指示のあるものをファイル#2、#1、#0の順に判定していずれかを出力
		ch2 = waveform(2);
		if (0 != ch2) {
			// ファイル#2をRP1に出力
			OC2RS = (PR3 + 1) * ch2 / 256;
		} else {
			// ファイル#1か#0をRP1に出力
			ch2 = waveform(1);
			OC2RS = (0 != ch2) ? ((PR3 + 1) * ch2 / 256) : ((PR3 + 1) * ch1 / 256);
		}
	}
	_T4IF = 0; // 割り込みフラグクリア
}

/**
 * 波形作成
 * @param int  i: ハンドラ(fhandle)参照用添字
 * @return int 0:出力データなし その他:波形データ
 */
int waveform(int i) {
	unsigned int d;
	unsigned char wk1, wk2;

	if (1 != fhandle[i].buf[fhandle[i].currOut].status ||
			(!fhandle[i].playFlag && 0 == fhandle[i].repeatFlag)) {
		fhandle[i].buf[fhandle[i].currOut].status = 2;
		fhandle[i].currOut = (0 == fhandle[i].currOut) ? 1 : 0;
		return 0;
	}
	if (fhandle[i].buf[fhandle[i].currOut].outputptr >= (fhandle[i].buf[fhandle[i].currOut].datasize))
		wk1 = wk2 = 0x00;
	else {
		wk1 = fhandle[i].buf[fhandle[i].currOut].data[ fhandle[i].buf[fhandle[i].currOut].outputptr++];
		wk2 = (fhandle[i].buf[fhandle[i].currOut].outputptr >= (fhandle[i].buf[fhandle[i].currOut].datasize)) ?
				0x00 : fhandle[i].buf[fhandle[i].currOut].data[ fhandle[i].buf[fhandle[i].currOut].outputptr++];
	}

	if (fhandle[i].buf[fhandle[i].currOut].outputptr >= (fhandle[i].buf[fhandle[i].currOut].datasize)) {
		fhandle[i].buf[fhandle[i].currOut].status = 2;
		fhandle[i].currOut = (0 == fhandle[i].currOut) ? 1 : 0;
	}
	d = wk1 | (wk2 << 8);
	d = d + 32768;
	d = d >> 8;
	d = d & 0xff;
	return d;

}

/**
 * Wavファイルオープン
 * @param int  i ハンドラ(fhandle)参照用添字
 */
void wavopen(int i) {
	if (NULL == fhandle[i].fptr && (0x00 != fhandle[i].fname[0])) {
		fhandle[i].fptr = FSfopen(fhandle[i].fname, "r"); // ファイルオープン
		if (NULL == fhandle[i].fptr) {
			fhandle[i].fname[0] = 0x00; // ファイル名無効化(次からオープンしない)
			return;
		}

		// ヘッダ部分読み込み
		if (FSfread(&fhandle[i].whdr, 1, (size_t)sizeof (struct wavheader), fhandle[i].fptr)
				== (size_t)sizeof (struct wavheader)) {
			if (0 == strncmp(fhandle[i].whdr.datachid, "data", 4)) {
				if (0L != fhandle[i].whdr.chunkleng) {
					fhandle[i].remaining = fhandle[i].whdr.chunkleng;
					errorFlag = FALSE;
					return;
				}
			}
		}
		fhandleinit(i);
		errorFlag = TRUE;
	}
}

/**
 * Wavファイル波形部読み込み
 * @param int  i: ハンドラ(fhandle)参照用添字
 */
void wavread(int i) {
	unsigned long readsize;

	if (sdReadyFlag &&
			(!errorFlag) &&
			(NULL != fhandle[i].fptr) &&
			(1 != fhandle[i].buf[fhandle[i].nextRead].status)) {

		if (!fhandle[i].playFlag && 1 != fhandle[i].repeatFlag) {
			// 再生停止中
			fhandle[i].remaining = 0L; // 再スタートは最初から
			return;
		}
		if (0L == fhandle[i].remaining) {
			// 波形部を全て読み込んでいたら、波形部先頭に移動
			if (0 != FSfseek(fhandle[i].fptr, (long) sizeof (struct wavheader), SEEK_SET)) {
				errorFlag = TRUE;
				sdReadyFlag = FALSE; // 要リカバリ
				return;
			}
			fhandle[i].remaining = fhandle[i].whdr.chunkleng;
		}
		bhandleinit(i, fhandle[i].nextRead);
		readsize = (fhandle[i].remaining >= BUFSIZE) ? BUFSIZE : fhandle[i].remaining;

		// BUFSIZEバイトファイルリード
		fhandle[i].buf[fhandle[i].nextRead].datasize =
				(int) FSfread(fhandle[i].buf[fhandle[i].nextRead].data, 1, (size_t) readsize, fhandle[i].fptr);
		fhandle[i].remaining -= (unsigned long) fhandle[i].buf[fhandle[i].nextRead].datasize;
		fhandle[i].buf[fhandle[i].nextRead].status = 1;
		// 次回読み込みバッファ切り替え
		fhandle[i].nextRead = (0 == fhandle[i].nextRead) ? 1 : 0;
		if (1 == fhandle[i].repeatFlag && 0 == fhandle[i].remaining)
			fhandle[i].repeatFlag = 2;
	}
}

/**
 * CN入力変化割り込み(Mk144制御用)
 */
void __attribute__((interrupt, no_auto_psv)) _CNInterrupt(void) {
	__delay_ms(20);//チャタリング防止
	if (!PORTAbits.RA0) {
		// onになった
		Mk144Switch = !Mk144Switch;
	}
	_CNIF = 0; // 割り込みフラグクリア
}

/**
 * MK-144制御
 */
void mk144ctrl(void) {
	unsigned char folder;

	if (Mk144Switch != Mk144SwitchSave) {
		Mk144SwitchSave = Mk144Switch;
		if (Mk144SwitchSave) {
			mk144putcmd(0xEF); //停止
	 // mk-144のルートディレクトリはフォルダ1
		   //ロータリーコードスイッチが0、1の時はルートディレクトリ参照
			folder = (0 == sel.selector) ? 1 : sel.selector;
			folder += 240;
			mk144putcmd(folder); //フォルダ指定
			mk144putcmd(0x01); //ファイル1再生
			mk144putcmd(0xEC); //再生
		} else {
			mk144putcmd(0xEF); //停止
		}
	}
}

/**
 * MK-144向けコマンド送信
 * @param unsigned char cmd 送信文字
 */
void mk144putcmd(unsigned char cmd) {
	while (BusyUART1());
	U1TXREG = (unsigned int) cmd;
	__delay_ms(20);
}


[/c]

chime.h
エラー時にならすチャイムの音のWAVファイルの波形データ部を記述したファイルです。

PICでWav再生にある手順で作成しています。


回路等

  • ソース中のピンアサインに従って入出力を接続してください(^^;
  • 音声の出力(#4、#5)の出力はLM386などのオーディオ・パワーアンプに繋いでください。