UMEHOSHI ITA TOP PAGE    COMPUTER SHIEN LAB

[UMEHOSHI ITA]の制御で使っているIC「PIC32MX270F256B-I/SO」のフラッシュメモリには、テスト用プログラムが書き込まれいています。
以降では、このプログラムを「テスト・ウメ・フラッシュ」と呼ぶことにして解説します。
また、「テスト・ウメ・フラッシュ」を利用したユーザー用のプログラムを 「ウメ・エディットプログラム」と呼ぶことにします。
「ウメ・エディットプログラム」の開発では「umehoshiEditツール」が必要で、 その取得や最初の操作情報は、こちらを参照してください。
(「PICKit3などの書き込みツール」をお持ちの方で、「テスト・ウメ・フラッシュ」を利用しないで、 「MPLAB X IDE」の開発環境ですべてをプログラミングする場合の情報ではありません。)
「テスト・ウメ・フラッシュ」をどのように利用して、「ウメ・エディットプログラム」を 作るかの解説で、「umehoshiEditツール」の使用例を示しています。
各サンプルは、このWebページ上でドラック&コピーして、貼り付けしてご利用ください。

各種確認プログラム左記に必要な部品の追加例

USBで接続されるホストにメッセージ(文字列)を送信する。

とくに必要ありません。
(D1のLEDは、あるとよい)

モータ制御、PWM (Timer2利用)

PWM対応の部品追加例

ADC(A/Dコンバータ)を使う(Timer3利用)

ADC 対応の部品追加例

BEEP(ブザー音で、デフォルトのCORE Timer、Timer1を利用)

BEEP SWITCH 対応の部品追加例

[UMEHOSHI ITA]単体で動作させる

Reset SW, Type-A, CN2 部品追加例

赤外線制御

U20,D4,D5,NPN, D3 部品追加例

UART を介して、Bluetoothで制御

CN11,CN-12 部品追加例

UART を介して、ESP32-WROOM-32DをBASIC制御

U19 部品追加例

UART を介して、RN4020(BLE)で制御

U17にRN4020の部品を追加する例

[UMEHOSHI ITA]で、バイナリデータをEEPROMに配置する(PORTB5のパルス変調データをROMに配置する)例

[UMEHOSHI ITA]このプロジェクトに必要な部品の追加例です。


[UMEHOSHI ITA]のPORTB5の端子をON・OFFすることで、音を鳴らすことができます。
(PORTB5の端子の端子には、別途にLED D2のON・OFFも兼用しています。)

このページは、こちらのリンク先で行ったpb5_pwd関数で使う 複数のサウンドデータをEEPROM内に設定する方法を紹介しています。
これにより、よく使う音を、ウメ・エディットプログラム」ですぐ再生できるようになります。


「ウメ・エディットプログラム」で利用できるEEPROM配置用の関数定義

以下では、「え、何?」、「はいわかりました」の音声と、「ドレミファソラシド」の音程がある音を、隙間なくEEPROMに書き込む目標です。
後から別の「ウメ・エディットプログラム」のROM化で利用しやすいように、 アドレス値の大きい領域から使え、なるべく隙間なく、複数のデータを共存させる考慮して、 データの最後を特定のアドレスに合わせた「UME専用Hexコマンド」ファイルを生成するPythonコード「wav_to_UME2.py」を以下に示します。
これは、このページで作った「wav_to_UME.py」のファイルを変更して作成しています。
(前のソースと違って、別モジュールを使わずに1つのソースにまとめました。)
# wav_to_UME2.py 『wavファイルからパルス変調用ファイルを作成する』
import wave
import numpy as np
import matplotlib.pyplot as plt
from typing import Callable

WAV_FILE_PATH="え_何.wav"# 【1】変換対象のこの音声が記憶されるサウンドファイル
last_address=0x9D03E000-1 # 【1】最後のデータをこのアドレスにする。

# WAV_FILE_PATH="はいわかりました.wav"# 【2】変換対象のこの音声が記憶されるサウンドファイル(実行で、0x9D03B990が先頭アドレスになった)
# last_address=0x9D03D285-1 # 【2】最後のデータをこのアドレスにする。

# WAV_FILE_PATH="ドレミ.wav"# 【3】変換対象のこの音声が記憶されるサウンドファイル(実行で、0x9D038E78が先頭アドレスになった)
# last_address=0x9D03B990-1 # 【3】最後のデータをこのアドレスにする。

path=r"C:\Microchip\umehoshiEdit\samples\_20240305\PB5_PWD\data.bin.hex" # [UMEHOSHI ITA]のメモリに設定する生成ファイルパス

def getframedatas_by_wav(wav_file_path:str): # wavファイルからフレームレートと、16bitPCMバイナリーを取得
   print(f"{wav_file_path}サウンドファイルから、再生用のバイト列を取得")
   wavefile = wave.open(wav_file_path, "r")
   framerate = wavefile.getframerate() # フレームレート(1秒間のデータ出力数)
   print(f"フレームレート:{framerate}Hz、サンプリング周期:{1/framerate*1e6:6.2f}u秒") 
   nchannel=wavefile.getnchannels()
   print("チャンネル数:", nchannel)
   sampwidth=wavefile.getsampwidth()
   print("サンプル幅数:", sampwidth)
   if sampwidth != 2:
      print(f"サンプル幅数が2(PCM 16bit)以外に対応していません")
      exit()
   nframes = wavefile.getnframes() # 全フレーム内データ数
   print(f"フレームの数:{nframes}, 時間:{nframes/framerate}秒") 
   buffer = wavefile.readframes(nframes) # 全フレームを読み取る
   wavefile.close() # ファイルを閉じる 
   if nchannel == 2: # 2チャンネルであれば、左チャンネルだけ抽出
      ba=bytearray()
      for i,c in enumerate(list(buffer)):
         if i%4==0: ba.append(c)# 左チャンネル抽出(0,1)、右なら(3,4)
         if i%4==1: ba.append(c)
      buffer = bytes(ba)
   return framerate, buffer

framerate, buffer=getframedatas_by_wav(WAV_FILE_PATH) # wavファイルからフレームレートと、16bitPCMバイナリーを取得

print(f"オリジナルのサウンドの{len(buffer)/2/framerate} 秒を再生する。")
import pyaudio # サウンド再生用
audio = pyaudio.PyAudio()
format=audio.get_format_from_width(width=2) # ストリームを読み書きするときのデータ型
stream = audio.open(format=format,channels=1,rate=framerate,output=True)
stream.write(buffer)  # オリジナルのサウンドの音を確認

print(f"サウンドデータ(16bitPCMバイナリー)からpulse_modulationで使う値(符号なし16bitのnumpy配列)に変換")
int16_np=np.frombuffer(buffer, dtype="int16")
uint16_np=int16_np+(2<<14) # int16をunsigned int 16へ
time_array=[n * (1/framerate) for n in range(len(uint16_np))] # 過度特性の時間プロットデータ

#uint16_np=np.array([getTestSingnal(t*5e-6) for t in range(int(20e-3/5e-6))], np.uint16) # uint16_nにテストデータを使う
#time_array=[t*5e-6 for t in range(int(20e-3/5e-6))] # 上記テストデータの時間プロットデータ

plt.subplot(5, 1, 1)
plt.plot(time_array,uint16_np)  # オリジナルのサウンド(uint16)の視覚化

def getSingnal(elapsed_time: float): # 外部リスト変数のuint16_nを参照し、経過時間から信号源のデータを得る
    period = time_array[1]
    idx=int(elapsed_time/period)
    if idx >= len(uint16_np) : idx = len(uint16_np)-1 # 最後のデータ
    val = uint16_np[idx]
    return val

# 積分回路のシミュレーション関数
def integral_circuit(getV1:Callable, time_array:list,  R1:float , C1:float, q1=0 , dt=0.5e-6)->list:
    '第1引数:getV1は経過時間を引数にしてその時の入力電圧を返す関数を登録するための引数'
    '第2引数:time_arrayのリスト要素のタイミングで戻り値ので出力電圧のリストが作る'
    '第3,4引数:R1は積分回路の抵抗値(単位はオーム)と、C1はコンデンサ(単位はファラット)'
    '第5,6引数:q1はCに蓄えられた初期電荷(単位はクローン)と、dtはシミュレーションで使う微小時間(単位は秒)'
    Vc1_list=[q1/C1]
    time_idx=1
    time_next=time_array[time_idx]
    time_now=0
    while True:
        v1=getV1(time_now)
        i=(v1-q1/C1)/R1
        q1+=i*dt
        time_now+=dt
        #print('-', end="")
        if time_now > time_next:
            #print(f"{time_idx:5}:{time_now:10.7f}, {q1/C1}v")
            Vc1_list.append(q1/C1)
            time_idx+=1
            if time_idx >= len(time_array): break
            time_next=time_array[time_idx]
    return Vc1_list

# 積分回路を通す
uint16_np=uint16_np.astype(np.float) # getSingnal入力源を変更
R1=1.2e3
C1=1e-6
v0=getSingnal(0)
q1=v0*C1
Vc1_list = integral_circuit(getSingnal, time_array, R1 , C1, q1 , dt=0.5e-6) # 積分回路の出力リスト
plt.subplot(5, 1, 2)
plt.plot(time_array, Vc1_list) # シミュレーション結果のアナログプロット

# 微分回路のシミュレーション関数
def differentiator(getV1:Callable, time_array:list,  R1:float , C1:float, q1=0 , dt=0.5e-6)->list:
    '第1引数:getV1は経過時間を引数にしてその時の入力電圧を返す関数を登録するための引数'
    '第2引数:time_arrayのリスト要素のタイミングで戻り値ので出力電圧のリストが作る'
    '第3,4引数:R1は積分回路の抵抗値(単位はオーム)と、C1はコンデンサ(単位はファラット)'
    '第5,6引数:q1はCに蓄えられた初期電荷(単位はクローン)と、dtはシミュレーションで使う微小時間(単位は秒)'
    time_now=0
    v1=getV1(time_now)
    Vc1_list=[v1-q1/C1]
    time_idx=1
    time_next=time_array[time_idx]
    while True:
        v1=getV1(time_now)
        i=(v1-q1/C1)/R1
        q1+=i*dt
        # print('-', end="")
        if time_now > time_next:
            # print(f"{time_idx:5}:{time_now:10.7f}, {q1/C1}v")
            Vc1_list.append(v1-q1/C1)
            time_idx+=1
            if time_idx >= len(time_array): break
            time_next=time_array[time_idx]
        time_now+=dt
    return Vc1_list

# 微分回路を通す 
uint16_np=Vc1_list # getSingnal入力源を変更
R1=2.2e3
C1=1e-6
v0=getSingnal(0)
q1=v0*C1
Vc1_list = differentiator(getSingnal, time_array, R1 , C1, q1 , dt=0.5e-6) # 微分回路の出力リスト
plt.subplot(5, 1, 3)
plt.plot(time_array, Vc1_list) # シミュレーション結果のアナログプロット

# Vc1_listをuint16 に変換してuint16_npにセット
uint16_np=np.array(Vc1_list)-min(Vc1_list) # 負のデータを無しに調整
uint16_np*=65535/max(uint16_np) # 0から65535に調整
uint16_np=uint16_np.astype(np.uint16) # 符号無し整数に変換
plt.subplot(5, 1, 4)
plt.plot(time_array, uint16_np) 
#plt.show()

minV=min(uint16_np)
value_width=max(uint16_np)-minV
print(f"最小値:{minV}, 値の幅:{value_width}")
print(f"データ数:{uint16_np.shape[0]}")

NREP=6 # 1つ(4bit)のデータを、約(1/4410)秒周期で繰り返し使う回数(水平分解能指定パラメタ)
count_out=0 # NREPの回数カウント用で、この回数分のデータ
nrep_list =[]
prev_val=0
print(f"サウンドデータ(16bitPCMバイナリーのuint16_np)を4bitデータに変換し、{NREP}個のサンプル数を1つまとめる")
datas_4bit=[] # 4bitに変換したデータの記憶用リスト
for i in range(uint16_np.shape[0]):
    val= uint16_np[i]
    nrep_list.append(val)
    count_out+=1
    if count_out == NREP:
       val=nrep_list[0]
       val=sum(nrep_list)/len(nrep_list) # ここで、データの変化に対応した特殊抽出の制御検討が可能
       if val > prev_val : val = max(nrep_list)
       elif val < prev_val : val = min(nrep_list)
       prev_val=val
       idx_v= int( (val-minV)/value_width*16 )
       idx_v = 15 if idx_v >= 16 else idx_v 
       datas_4bit.append(idx_v)
       count_out=0
       nrep_list =[]

print(f"最小値:{min(datas_4bit)}, 最大値:{max(datas_4bit)}")
print(f"データ数:{uint16_np.shape[0]}から{len(datas_4bit)}個になった。")
plt.subplot(5, 1, 5)
plt.plot(datas_4bit)
plt.show() 

# datas_4bitの4bitデータリストを内容を4096倍してバイナリ化し、その音を再生
buffer2=(np.array( [[x<<12]*NREP for x in datas_4bit] ).flatten()-(2<<14)).astype(np.int16).tobytes()
stream.write(buffer2)  # 変換した音を確認

print("4ビット要素の2つを、1バイトにまとめて、リストにする。")
bin_data_array=[]
for n,bit4 in enumerate(datas_4bit): # (249600バイト以下にしないとリンクエラーの可能性あり)
    if n % 2 == 0:
        v = bit4
    else:
        v |= bit4<<4
        bin_data_array.append(v)
        #print(f" '\\x{v:02X}',", end="")

print(f"生成したデータ:{len(bin_data_array)}byte")

num_maxdata=2950 # UMEHOSHI ITAにおいて3000の配列要素数のデータはビルドで失敗した。
count_byte=0
print(f"C言語用の配列初期化データを、テキストファイルとして出力(最大で{num_maxdata}バイトの出力制限)")
with open("bin_data_array.txt", "w") as f: # C言語配列用パルス変調のデータを書き込み
    for n,v in enumerate(bin_data_array): 
        if n > 0 and n % 10 == 0 : 
            f.write("\n")
        if count_byte >= num_maxdata: break
        s=f" '\\x{v:02X}',"
        #print(s, end="")
        f.write(s)
        count_byte += 1

def data_to_ume_hex_faile(path, datas_list, starting_address, limit_address):
    'datas_listが8bitデータのリストのファイル化で、starting_addressの番地から'
    'UMEHOSHI ITA用の「S」から始まる設定データ群を「UME専用Hexファイル」として出力'
    idx=0
    address16 = starting_address
    with open(path, "w") as f: # UMEHOSHI C言語配列用パルス変調のデータを書き込み
        while idx < len(datas_list) and address16 < limit_address:
            nn = 16
            if idx+nn >= len(datas_list):nn = len(datas_list)-idx
            if address16+nn >= limit_address:nn = limit_address-address16
            s=f"S{nn:02X}{address16:08X}00"
            for n in range(nn):
                s+=f"{datas_list[idx+n]:02X}"
            checksum=sum([ord(c) for c in s])
            s+=f"{((0x100-checksum)) & 0x0ff:02X}"
            #print(s)
            f.write(f"{s}\n")
            idx += nn
            address16+=nn
        return address16

setting_limit_address=last_address+1 # 設定限界アドレス EEPPOM領域であれば(0x9D03E400)
starting_address=setting_limit_address-len(bin_data_array)    # 設定開始アドレス 

print(f"「UME専用Hexファイル:{path}」を出力")
print(f"starting_address:0x{starting_address:08X}〜", end="")
data_to_ume_hex_faile(path, bin_data_array, starting_address, setting_limit_address)
print(f"last_address:0x{last_address:08X}, {setting_limit_address-starting_address}byteのデータ")
上記で使った("え_何.wav")44100Hz,16BitPCMの音ファイルです。→
上記ソースの実行結果を、以下に示します。
C:\Users\suzuki\Music>python wav_to_UME2.py
え_何.wavサウンドファイルから、再生用のバイト列を取得
フレームレート:48000Hz、サンプリング周期: 20.83u秒
チャンネル数: 2
サンプル幅数: 2
フレームの数:41414, 時間:0.8627916666666666秒
オリジナルのサウンドの0.8627916666666666 秒を再生する。
サウンドデータ(16bitPCMバイナリー)からpulse_modulationで使う値(符号なし16bitのnumpy配列)に変換
最小値:0, 値の幅:65534
データ数:41414
サウンドデータ(16bitPCMバイナリーのuint16_np)を4bitデータに変換し、6個のサンプル数を1つまとめる
最小値:0, 最大値:15
データ数:41414から6902個になった。
4ビット要素の2つを、1バイトにまとめて、リストにする。
生成したデータ:3451byte
C言語用の配列初期化データを、テキストファイルとして出力(最大で2950バイトの出力制限)
「UME専用Hexファイル:C:\Microchip\umehoshiEdit\samples\_20240305\PB5_PWD\data.bin.hex」を出力
starting_address:0x9D03D285〜last_address:0x9D03DFFF, 3451byteのデータ
この実行で、0x9D03D285〜0x9D03DFFFの3451byteのデータを [UMEHOSHI ITA]へ設定するdata.bin.hexのファイルが生成されます。
この転送は「umehoshiEdit」ツールなどを使います。ツールの使い方は、[UMEHOSHI ITA]にUSBを介して転送のリンクを参照してください。


「はいわかりました」の音声をROMに書き込む。

前述のwav_to_UME2.pyにおいて、【1】の2行をコメントにして、次のように【2】の2行のコメントを外して有効にします。
WAV_FILE_PATH="はいわかりました.wav"# 【2】変換対象のこの音声が記憶されるサウンドファイル(実行で、0x9D03B990が先頭アドレスになった)
last_address=0x9D03D285-1 # 【2】最後のデータをこのアドレスにする。
上記で使った("はいわかりました.wav")44100Hz,16BitPCMの音ファイルです。→
上記ソースの実行結果を、以下に示します。
C:\Users\suzuki\Music>python wav_to_UME2.py
はいわかりました.wavサウンドファイルから、再生用のバイト列を取得
フレームレート:44100Hz、サンプリング周期: 22.68u秒
チャンネル数: 2
サンプル幅数: 2
フレームの数:76635, 時間:1.7377551020408164秒
オリジナルのサウンドの1.7377551020408164 秒を再生する。
サウンドデータ(16bitPCMバイナリー)からpulse_modulationで使う値(符号なし16bitのnumpy配列)に変換
最小値:0, 値の幅:65535
データ数:76635
サウンドデータ(16bitPCMバイナリーのuint16_np)を4bitデータに変換し、6個のサンプル数を1つまとめる
最小値:0, 最大値:15
データ数:76635から12772個になった。
4ビット要素の2つを、1バイトにまとめて、リストにする。
生成したデータ:6386byte
C言語用の配列初期化データを、テキストファイルとして出力(最大で2950バイトの出力制限)
「UME専用Hexファイル:C:\Microchip\umehoshiEdit\samples\_20240305\PB5_PWD\data.bin.hex」を出力
starting_address:0x9D03B990〜last_address:0x9D03D284, 6389byteのデータ
この実行で、0x9D03B990〜0x9D03D284の6389byteのデータを [UMEHOSHI ITA]へ設定するdata.bin.hexのファイルが生成されます。
この転送は「umehoshiEdit」ツールなどを使います。ツールの使い方は、[UMEHOSHI ITA]にUSBを介して転送のリンクを参照してください。


「ドレミファソラシド」の音をROMに書き込む。

前述のwav_to_UME2.pyにおいて、【2】の2行をコメントにして、次のように【3】の2行のコメントを外して有効にします。
WAV_FILE_PATH="ドレミ.wav"# 【3】変換対象のこの音声が記憶されるサウンドファイル(実行で、0x9D038E78が先頭アドレスになった)
last_address=0x9D03B990-1 # 【3】最後のデータをこのアドレスにする。
上記で使った("ドレミ.wav")44100Hz,16BitPCMの音ファイルです。→
上記ソースの実行結果を、以下に示します。
C:\Users\suzuki\Music>python wav_to_UME2.py
ドレミ.wavサウンドファイルから、再生用のバイト列を取得
フレームレート:44100Hz、サンプリング周期: 22.68u秒
チャンネル数: 2
サンプル幅数: 2
フレームの数:132372, 時間:3.0016326530612245秒
オリジナルのサウンドの3.0016326530612245 秒を再生する。
サウンドデータ(16bitPCMバイナリー)からpulse_modulationで使う値(符号なし16bitのnumpy配列)に変換
最小値:0, 値の幅:65535
データ数:132372
サウンドデータ(16bitPCMバイナリーのuint16_np)を4bitデータに変換し、6個のサンプル数を1つまとめる
最小値:0, 最大値:15
データ数:132372から22062個になった。
4ビット要素の2つを、1バイトにまとめて、リストにする。
生成したデータ:11031byte
C言語用の配列初期化データを、テキストファイルとして出力(最大で2950バイトの出力制限)
「UME専用Hexファイル:C:\Microchip\umehoshiEdit\samples\_20240305\PB5_PWD\data.bin.hex」を出力
starting_address:0x9D038E78〜last_address:0x9D03B98F, 11032byteのデータ

C:\Users\suzuki\Music>
この実行で、0x9D038E78〜0x9D03B98F11032byteのデータを [UMEHOSHI ITA]へ設定するdata.bin.hexのファイルが生成されます。
この転送は「umehoshiEdit」ツールなどを使います。ツールの使い方は、[UMEHOSHI ITA]にUSBを介して転送のリンクを参照してください。


上記3つの音を、それぞれ転送した後の[UMEHOSHI ITA]のメモリ配置状況とその利用例

上記3つの音のEPROM設定用のdata.bin.hex、それぞれ「umehoshiEdit」ツールでUSBを介して転送することで、 EEPROM内に各サウンドデータが常駐できたことになります。
そして、こちらのリンク先で行ったpb5_pwd関数群のEEPROM埋め込みが終わっている[UMEHOSHI ITA]のメモリ内容が次のようになります。
アドレス 0x80005000〜0x80008000 0x80009EC8〜0x80009F00 0x9D038E78〜0x9D03B98F 0x9D03B990〜0x9D03D284 0x9D03D285〜0x9D03DFFF 0x9D03E000〜のROM領域 0x9D03E620〜のROM領域
内容 ウメ・エディットプログラム
のユーザプログラミング域
起動用main関数などを定義する
割り込み用のchange関数と
pb5_pwd関数で使う
グローバルRAM記憶域
「ドレミファソラシド」の音の
11032byteROM領域
「はいわかりました」の音の
6389byteROM領域
「え、何?」の音の
3451byteROM領域
上記pb5_pwd関数定義域
アドレスが示す音データを
鳴らす指定関数
change割り込み関数定義域
PWDの周期を変更する

上記の0x80005000番地から使える「ウメ・エディットプログラム」領域で、 ROM内に存在する「え、何?」、「はいわかりました」、「ドレミファソラシド」の音を順番に再生するプログラム例を下記に示します。
#include <xc.h> //pb5_pwd
#include "common.h"

__attribute__((address( 0x80005000 ))) void main(void);
void main()
{
	PORTBINV = 0x8000;	// LED1を反転
	void (*pb5_pwd)()=(void*)0x9D03E000;// 音再生の関数pb5_pwdを配置したEEPOROMのポインタを管理

	pb5_pwd((char *) 0x9D03D284, 3451);// 指定番地より指定サイズデータ(え?何。)を鳴らす
	while(OC2CONbits.ON == 1){} // 再生中の間待つ

	pb5_pwd((char *) 0x9D03B990, 6389);// 指定番地より指定サイズデータ(はい、わかりました。)を鳴らす
	while(OC2CONbits.ON == 1){} // 再生中の間待つ

	pb5_pwd((char *) 0x9D038E78, 11032);// 指定番地より指定サイズデータ(ドレミファソラシド)を鳴らす
	while(OC2CONbits.ON == 1){} // 再生中の間待つ

	PORTBINV = 0x8000;// LED1を反転
}
上記のコードの音の再生は、EEPROM内の0x9D03E000の絶対アドレスを指定して関数を 呼び出してを行っています。
0x9D03E000のアドレスを直接指定する表現のコードが分かり難いので、 次のように各音のマクロを定義して鳴らすコードも作成してみました。
#include <xc.h>
#include "common.h"

#define PLAY_WHAT()   (((void (*)(uint32_t, uint32_t) ) 0x9D03E000 ) (0x9D03D284, 3451))
#define PLAY_YES_OK()   (((void (*)(uint32_t, uint32_t) ) 0x9D03E000 ) (0x9D03B990, 6389))
#define PLAY_DOREMI()   (((void (*)(uint32_t, uint32_t) ) 0x9D03E000 ) (0x9D038E78, 11032))

__attribute__((address( 0x80005000 ))) void main(void);
void main()
{
	PORTBINV = 0x8000;	// LED1を反転

	PLAY_WHAT();// (え?何。)を鳴らす
	while(OC2CONbits.ON == 1){} // 再生中の間待つ

	PLAY_YES_OK();// (はい、わかりました。)を鳴らす
	while(OC2CONbits.ON == 1){} // 再生中の間待つ

	PLAY_DOREMI();// (ドレミファソラシド)を鳴らす
	while(OC2CONbits.ON == 1){} // 再生中の間待つ

	PORTBINV = 0x8000;// LED1を反転
}

圧電サンダーなどのいくつかを、聞き比べる

[UMEHOSHI ITA]の基板上で、基板上に取り付け可能な、下記の圧電サンダーなどを付けて、聞き比べてた実験結果です。
なおメモリに記憶した音は、7350Hzで振幅が4ビットに情報量を減らしたデータで鳴らしています。
右の回路で実験しています。
(前述のプログラムによる再生の録音による音で、各社の圧電サンダーの品質を比較したのではありません)
手持ちスピーカ(仕様不明 8Ω)
R9の抵抗値:46Ω
UNIVERSAL(CHANGZHOU) ELECTRONICS CO.,LTD 
基板取付用小型ダイナミックスピーカ
UGCM0603APE (Coil Resistance:8Ω)
定格電圧:3V,最低共振周波数(F0):3100Hz
R9の抵抗値:46Ω
DB Products Limited.
基板取付用スピーカー 9mm、Resonant Frequency ( Hz ) 3100.0
UDX01CLFPH
Rated Voltage: 1.5V、Coil Resistance 5.5Ω
R9の抵抗値:46Ω
株式会社村田製作様の圧電スピーカー(圧電サウンダ)
許容入力電圧 ±15.0Vo-p以下
共振周波数:4kHz、静電容量:0.0065μF PKHPS0013E4000-A2
R9の抵抗値:46Ω
株式会社村田製作所様の 圧電スピーカー(圧電サウンダ)(13mm)
30.0 Vp-p 以下、静電容量:0.0055μF PKM13EPYH4000-A0 
R9の抵抗値:46Ω