UMEHOSHI ITA TOP PAGE COMPUTER SHIEN LAB
[UMEHOSHI ITA]の制御で使っているIC「PIC32MX270F256B-I/SO」のフラッシュメモリには、テスト用プログラムが書き込まれいています。
以降では、このプログラムを「テスト・ウメ・フラッシュ」と呼ぶことにして解説します。
また、「テスト・ウメ・フラッシュ」を利用したユーザー用のプログラムを
「ウメ・エディットプログラム」と呼ぶことにします。
「ウメ・エディットプログラム」の開発では「umehoshiEditツール」が必要で、
その取得や最初の操作情報は、こちらを参照してください。
(「PICKit3などの書き込みツール」をお持ちの方で、「テスト・ウメ・フラッシュ」を利用しないで、
「MPLAB X IDE」の開発環境ですべてをプログラミングする場合の情報ではありません。)
「テスト・ウメ・フラッシュ」をどのように利用して、「ウメ・エディットプログラム」を
作るかの解説で、「umehoshiEditツール」の使用例を示しています。
各サンプルは、このWebページ上でドラック&コピーして、貼り付けしてご利用ください。
各種確認プログラム | 左記に必要な部品の追加例 |
---|---|
とくに必要ありません。 (D1のLEDは、あるとよい) | |
PWM対応の部品追加例 | |
ADC 対応の部品追加例 | |
BEEP SWITCH 対応の部品追加例 | |
Reset SW, Type-A, CN2 部品追加例 | |
U20,D4,D5,NPN, D3 部品追加例 | |
CN11,CN-12 部品追加例 | |
U19 部品追加例 | |
U17にRN4020の部品を追加する例 |
[UMEHOSHI ITA]このプロジェクトに必要な部品の追加例です。
このページは、こちらのリンク先で行ったpb5_pwd関数で使う
複数のサウンドデータをEEPROM内に設定する方法を紹介しています。
これにより、よく使う音を、ウメ・エディットプログラム」ですぐ再生できるようになります。
# 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のファイルが生成されます。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のファイルが生成されます。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のファイルが生成されます。アドレス | 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の周期を変更する |
#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の絶対アドレスを指定して関数を
呼び出してを行っています。#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を反転 }
手持ちスピーカ(仕様不明 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Ω |