〔ESP-WROOM-02〕
〔マイコンのトップに戻る〕
[準備編]
[module編]
[GPIO編]
[通信編]
[WiFi編]
[BLE編1]
[BLE編2]
[BLE編3]
[色々編]
I2S通信の概念図
wavファイルからデータを読み出し、
ESP32で音楽データを取り出しI2S規格のシリアル・データに変換して
それをDACモジュールがアナログ信号に変換し音楽が再生されるといった感じぃ。
ピン名称 |
機能 |
VIN |
3V - 5V の電源を接続、ICは3.3V、ピンは3V - 5Vトレラント |
3VO |
内蔵レギュレータ(MIC5225)から3.3Vを出力(150mA)します |
GND |
グランド |
WSEL |
LRCLK データが左チャンネル、右チャンネルをDACに伝えるピンです |
DIN |
SDATA 実際のデータが入ってくるピンです |
BCLK |
BCLK データピン上のデータを読み取るタイミングを指示するピンです |
Lout |
左ライン出力 出力に47uFブロッキングコンデンサ有り |
AGND |
アナログ用グランド |
Rout |
右ライン出力 アンプで使用する様に3KΩ負荷に対して設計されている |
SCLK |
MCLK システムクロック入力か27MHz「ビデオモード」入力 |
SF1 |
入力データ形式を設定するために使用されます(下記参照) |
MUTE |
HIGH接続でミュート |
SF0 |
入力データ形式を設定するために使用されます(下記参照)(3.3Vのみ) |
PLL |
LOWでオーディオモード、HIGHでビデオモード(3.3Vのみ) |
DEEM |
ディエンファシス フィルターを使用する為のピン、 ビデオモードでは、これがクロック出力ピン |
SF0 |
SF1 |
機能 |
LOW |
LOW |
I2S (デフォルト) |
LOW |
HIGH |
16Bit 左詰めデータ |
HIGH |
LOW |
20Bit 左詰めデータ |
HIGH |
HIGH |
24Bit 左詰めデータ |
電源はUSB接続で、回路電源はボード出力3.3Vです。
GPIO32 - DIN、GPIO33 - BCLK、GPIO25 - WSELを配線しています。
# "play-mono-wav-from-sdcard.py"をベースに作成しています。 # SDカード上のWAVファイルから16ビットオーディオサンプルを読み取る # オーディオサンプルをI2SアンプまたはDACモジュールに書き込む # from machine import Pin, SDCard from machine import I2S, freq import uos freq(240000000) # CPUクロックを240MHzに変更 WAV_FILE = 'Summer-time-magic-16k.wav' SAMPLE_RATE = 16000 BCLK_pin = Pin(33) LRCLK_pin = Pin(25) SDATA_pin = Pin(32) # wavファイルのヘッダ部を読み飛ばしてデータ部を探す処理 def find_data(): wav_f.seek(0) while True: if wav_f.read(1) == b'd': if wav_f.read(1) == b'a': if wav_f.read(1) == b't': if wav_f.read(1) == b'a': wav_f.seek(4) break # チャネルフォーマット設定 i2s = I2S( I2S.NUM0, bck=BCLK_pin, ws=LRCLK_pin, sdout=SDATA_pin, standard=I2S.PHILIPS, mode=I2S.MASTER_TX, dataformat=I2S.B16, channelformat=I2S.RIGHT_LEFT, samplerate=SAMPLE_RATE, dmacount=16, dmalen=512) # SDカードの初期化とマウントを行う # d1/d3のピンは外部でプルアップする事 d0 = Pin(2, Pin.PULL_UP) d2 = Pin(12, Pin.PULL_UP) sd = SDCard(slot=1, width=4, freq=40000000) uos.mount(sd, "/sd") wav_f = open('/sd/' + WAV_FILE, 'rb') find_data() # オーディオデータバッファ配列を割り当てる # memoryviewを使ってwhileループでヒープ割り当てを減らす為に使用される wav_buf = bytearray(8192) wav_buf_mv = memoryview(wav_buf) print('Starting') # WAVファイルからオーディオデータを継続的に読み取り、I2Sに書き込みます while True: try: read_su = wav_f.readinto(wav_buf_mv) written_su = 0 # WAVファイルの終わりか? if read_su == 0: # データ部の最初のバイトまでシークします find_data() else: # 全てのオーディオデータがI2Sに書き込まれるまでループします while written_su < read_su: written_su += i2s.write(wav_buf_mv[written_su:read_su], timeout=0) except (KeyboardInterrupt, Exception) as e: print('caught exception {} {}'.format(type(e).__name__, e)) break # 終了処理 wav_f.close() uos.umount("/sd") sd.deinit() i2s.deinit() import gc gc.collect() print('Done')--------------------------------------------------------------------------------------------------
I2S通信の概念図
"SPH0645LM4H-B"と言うデバイスで、拾った音をI2Sデータとして出力してくれるマイクです。
これを使い(下記図)ESP32に送り、SDカードにwavファイルで保存します。
電源はUSB接続で、回路電源はボード出力3.3Vです。
GPIO32 - DOUT、GPIO33 - BCLK、GPIO25 - LRCLKを配線しています。
図では割愛していますが、SDカードスロットを接続しています、Slot1のSD/MMC配線での接続です。
詳しくはこちらを参照しましょう。
又、"SEL"ピンは、GND接続なので左チャンネルを選択しています。
# The MIT License (MIT) # Copyright (c) 2019 Michael Shi # Copyright (c) 2020 Mike Teachman # https://opensource.org/licenses/MIT # # 目的: # - I2Sマイクの左チャンネルから32ビットオーディオサンプルを読み取る # - 各32ビットマイクサンプルから上位16ビットを切り取ります # - WAV形式を使用して16ビットサンプルをSDカードファイルに書き込む # # 記録されたWAVファイルの名前は次のとおりです: # "mic_left_channel_16bits.wav" # # テスト済みのハードウェア: # - Adafruit I2S MEMS Microphone Breakout module (SPH0645LM4H) from machine import Pin from machine import SDCard from machine import I2S import uos # ======= ユーザー設定 ======= RECORD_TIME_IN_SECONDS = 5 # 録音時間 SAMPLE_RATE_IN_HZ = 22050 # サンプリングレート # ======= ユーザー設定 ======= WAV_SAMPLE_SIZE_IN_BITS = 16 WAV_SAMPLE_SIZE_IN_BYTES = WAV_SAMPLE_SIZE_IN_BITS // 8 MIC_SAMPLE_BUFFER_SIZE_IN_BYTES = 4096 SDCARD_SAMPLE_BUFFER_SIZE_IN_BYTES = MIC_SAMPLE_BUFFER_SIZE_IN_BYTES // 2 # なぜ2で割るのですか? 唯一の32ビットサンプルの16ビットを使用 NUM_SAMPLE_BYTES_TO_WRITE = RECORD_TIME_IN_SECONDS * SAMPLE_RATE_IN_HZ * WAV_SAMPLE_SIZE_IN_BYTES NUM_SAMPLES_IN_DMA_BUFFER = 256 NUM_CHANNELS = 1 # snip_16_mono(): 32ビットのモノラルサンプルストリームから16ビットのサンプルを切り取る処理 # 仮定 : モノラルマイクのI2S構成。 例えば I2S channelformat = ONLY_LEFT or ONLY_RIGHT # example snip: # samples_in[] = [0x44, 0x55, 0xAB, 0x77, 0x99, 0xBB, 0x11, 0x22] # samples_out[] = [0xAB, 0x77, 0x11, 0x22] # notes: # リトルエンディアン形式で配置されたsamples_in []フォーマット: # 0x77は、32ビットサンプルの最上位バイトです。 # 0x44は、32ビットサンプルの最下位バイトです。 # returns: 切り取られたバイト数(samples_out[]) def snip_16_mono(samples_in, samples_out): num_samples = len(samples_in) // 4 for i in range(num_samples): samples_out[2 * i] = samples_in[4 * i + 2] samples_out[2 * i + 1] = samples_in[4 * i + 3] return num_samples * 2 # wavファイルのヘッダ部を作成する処理 def create_wav_header(sampleRate, bitsPerSample, num_channels, num_samples): datasize = num_samples * num_channels * bitsPerSample // 8 o = bytes("RIFF", 'ascii') # (4byte) ファイルをRIFF識別子としてマークします o += (datasize + 36).to_bytes(4, 'little') # (4byte) これとRIFF識別子を除くファイルサイズ(バイト単位) o += bytes("WAVE", 'ascii') # (4byte) File type ("WAVE"or"AVI ") o += bytes("fmt ", 'ascii') # (4byte) fmt識別子 o += (16).to_bytes(4, 'little') # (4byte) fmtチャンクのバイト数 o += (1).to_bytes(2, 'little') # (2byte) 音声フォーマット(1 - PCM) o += (num_channels).to_bytes(2, 'little') # (2byte) チャンネル数(モノラルは1、ステレオは2) o += (sampleRate).to_bytes(4, 'little') # (4byte) サンプリング周波数(Hz) o += (sampleRate * num_channels * bitsPerSample // 8).to_bytes(4, 'little') # (4byte) バイト/秒 1秒間の録音に必要なバイト数 o += (num_channels * bitsPerSample // 8).to_bytes(2, 'little') # (2byte) ブロック境界 ステレオ16bitなら、16bit*2=32bit = 4byte o += (bitsPerSample).to_bytes(2, 'little') # (2byte) ビット/サンプル 1サンプルに必要なビット数 o += bytes("data", 'ascii') # (4byte) data識別子としてマークします o += (datasize).to_bytes(4, 'little') # (4byte) wavデータサイズ(バイト単位) return o BCLK_pin = Pin(33) # BCLK LRCLK_pin = Pin(25) # LRCLK SDATA_pin = Pin(32) # DOUT # チャネルフォーマット設定 i2s = I2S( I2S.NUM0, bck=BCLK_pin, ws=LRCLK_pin, sdin=SDATA_pin, standard=I2S.PHILIPS, mode=I2S.MASTER_RX, dataformat=I2S.B32, channelformat=I2S.ONLY_LEFT, samplerate=SAMPLE_RATE_IN_HZ, dmacount=50, dmalen=NUM_SAMPLES_IN_DMA_BUFFER ) # SDカードの初期化とマウントを行う # d1/d3のピンは外部でプルアップする事 d0 = Pin(2, Pin.PULL_UP) d2 = Pin(12, Pin.PULL_UP) sd = SDCard(slot=1, width=4, freq=40000000) uos.mount(sd, "/sd") wav_f = open('/sd/mic_left_channel_16bits.wav', 'wb') # WAVファイルのヘッダーを作成してSDカードに書き込む wav_header = create_wav_header( SAMPLE_RATE_IN_HZ, WAV_SAMPLE_SIZE_IN_BITS, NUM_CHANNELS, SAMPLE_RATE_IN_HZ * RECORD_TIME_IN_SECONDS ) num_bytes_written = wav_f.write(wav_header) # マイクデータバッファ配列を割り当てる # memoryviewを使ってwhileループでヒープ割り当てを減らす為に使用される mic_buf = bytearray(MIC_SAMPLE_BUFFER_SIZE_IN_BYTES) mic_buf_mv = memoryview(mic_buf) # 32ビットオーディオサンプルのバッファ wav_buf = bytearray(SDCARD_SAMPLE_BUFFER_SIZE_IN_BYTES) wav_buf_mv = memoryview(wav_buf) # 上位16ビットを切り取ったバッファ num_buf_bytes_written_to_wav = 0 print('Starting') # I2Sマイクから32ビットサンプルを読み取り、 # 上位16ビットを切り取り、切り取ったサンプルをWAVファイルに書き込みます while num_buf_bytes_written_to_wav < NUM_SAMPLE_BYTES_TO_WRITE: try: # I2Sマイクからサンプルのブロックを読み取ってみてください # DMAバッファがいっぱいでない場合、readinto() メソッドは0を返します num_bytes_read_from_mic = i2s.readinto(mic_buf_mv, timeout=0) if num_bytes_read_from_mic > 0: # 各32ビットマイクサンプルから上位16ビットを切り取ります num_bytes_snipped = snip_16_mono(mic_buf_mv[:num_bytes_read_from_mic], wav_buf_mv) num_bytes_to_write = min(num_bytes_snipped, NUM_SAMPLE_BYTES_TO_WRITE - num_buf_bytes_written_to_wav) # サンプルをWAVファイルに書き込む num_bytes_written = wav_f.write(wav_buf_mv[:num_bytes_to_write]) num_buf_bytes_written_to_wav += num_bytes_written except (KeyboardInterrupt, Exception) as e: print('caught exception {} {}'.format(type(e).__name__, e)) break # 終了処理 wav_f.close() uos.umount("/sd") sd.deinit() i2s.deinit() print('Done') print('%d sample bytes written to WAV file' % num_buf_bytes_written_to_wav) import gc gc.collect()--------------------------------------------------------------------------------------------------
【きむ茶工房ガレージハウス】
Copyright (C) 2006-2021 Shigehiro Kimura All Rights Reserved.