HonestQiao 发表于 2024-11-23 15:01

【Follow me第二季第4期】任务三:PDM麦克风小知识+数据读取+波形呈现+wav存储

<p>Arduino&reg; Nano RP2040 Connect提供了一颗PDM麦克风,可以用于声音的录制,这一篇就分享如何在micropython中使用这颗PDM麦克风。</p>

<p>&nbsp;</p>

<p><span style="font-size:18px;"><strong>一、PDM麦克风小知识</strong></span></p>

<p>Arduino&reg; Nano RP2040 Connect上的PDM麦克风的型号是<strong>MP34DT06JTR</strong>,不出意外,它又是ST提供的。</p>

<p>其具体位于板子上如下位置:</p>

<p>&nbsp;</p>

<div style="text-align: center;"></div>

<div style="text-align: center;">&nbsp;</div>

<div style="text-align: center;">&nbsp;</div>

<p>和之前的IMU传感器类似,使用的也是IIC接口,从Pinout可以了解:</p>

<p> &nbsp;</p>

<p>&nbsp;</p>

<p>从ST官方资料可以了解的具体信息:</p>

<ul>
        <li data-immersive-translate-paragraph="1" data-immersive-translate-walked="57bf1b89-f6d8-492f-abe7-67537e1bbea5">MP34DT06J 是一款超紧凑、低功耗、全向、数字 MEMS 麦克风,采用电容式传感元件和 IC 接口构建。</li>
        <li data-immersive-translate-paragraph="1" data-immersive-translate-walked="57bf1b89-f6d8-492f-abe7-67537e1bbea5" >能够检测声波的传感元件采用专用于生产音频传感器的专用硅微加工工艺制造。</li>
        <li data-immersive-translate-paragraph="1" data-immersive-translate-walked="57bf1b89-f6d8-492f-abe7-67537e1bbea5" >IC 接口采用 CMOS 工艺制造,允许设计能够以 PDM 格式从外部提供数字信号的专用电路。</li>
        <li data-immersive-translate-paragraph="1" data-immersive-translate-walked="57bf1b89-f6d8-492f-abe7-67537e1bbea5" >MP34DT06J 是一款低失真数字麦克风,具有 64 dB 信噪比和 &ndash;26 dBFS &plusmn;1 dB 灵敏度。</li>
</ul>

<p>&nbsp;</p>

<p>使用该麦克风,常用的应用逻辑如下:</p>

<p> &nbsp;</p>

<p>而PDM麦克风的内部处理框图如下:</p>

<p> &nbsp;</p>

<p>&nbsp;</p>

<p>当通过PDM麦克风获取音频信号时,是对音频模拟信号,进行了脉冲密度调制:(PDM是一种调制形式,用于表示数字域中的模拟信号。它是1位数字采样的高频数据流。在 PDM信号中,脉冲的相对密度对应于模拟信号的幅度。大量的&#39;1&#39;对应于高(正)幅度值, 而大量的&#39;0&#39;对应于低(负)幅度值,交替的&#39;1&#39;和&#39;0&#39;对应于幅度值0)</p>

<p></p>

<p>&nbsp;</p>

<p>实际上,我们要应用它,是通过IIC接口,从其获得经过处理后的数据即可。</p>

<p>&nbsp;</p>

<p><span style="font-size:18px;"><strong>&nbsp;二、使用板载PDM麦克风获取声音信号</strong></span></p>

<p>在Arduino开发环境中,Arduino&reg; Nano RP2040 Connect的定制环境,提供了MP34DT06JTR的PDM驱动接口。</p>

<p>但是在定制的micropython环境中,没有提供对应的PDM驱动接口。</p>

<p>经过了解,已经有Arduino&reg; Nano RP2040 Connect的使用者,制作了专门的驱动,可以从这个地址下载:&nbsp;<a href="https://github.com/PinkInk/mp34dt05-a-micropython-driver/tree/main">PinkInk/mp34dt05-a-micropython-driver: PDM Microphone on Arduino Nano&nbsp;</a></p>

<p> &nbsp;</p>

<p>&nbsp;</p>

<p>下载后,将&nbsp;st34dt05a.py 放置到&nbsp;Arduino&reg; Nano RP2040 Connect 的micropython 环境对应的U盘根目录下的/lib目录中:</p>

<p> &nbsp;</p>

<p>&nbsp;</p>

<p>在 st34dt05a.py 中,使用的RP2040的PIO状态机编程来进行数据的读取,这样效率更高:</p>

<p> &nbsp;</p>

<p>做好以上准备后,再编写下面的测试程序:</p>

<pre>
<code class="language-python">from time import sleep
from machine import Pin
import st34dt05a as pdm

pcm_rate = 8_000 # Hz - default is 12kHz i.e. 3.072MHz bit-sample rate
pdm.bit_sample_freq = pcm_rate * 256

pdm_clk = Pin(23)
pdm_data = Pin(22)
record_flag = False

index=0
def buffer_handler(inactive_buf):
    global index, record_flag
   
    if record_flag:
      buff = pdm.get_buffer(inactive_buf)

      for i,n inenumerate(buff):
            if index % 100 == 0:
                #print(index, len(buff), i, n)
                print(n, end=" ")
            
            if index % 2000 == 0:
                print("")

            index += 1

pdm.init(pdm_clk, pdm_data, handler=buffer_handler)
pdm.start()

sleep(1) # wait whilst StateMachine inits

print("Start record:")
record_flag = True

while True:
    sleep(1)

record_flag = False
pdm.stop()
print("End record.")
</code></pre>

<p>上面的代码中,先设置采样率,然后启动pdm数据采样,并在中断回调handler中,输出获取到的数据。</p>

<p>对中断回调中,应该快速处理,所以才用使用了计数器,每100次才输出一次采集到的数据,每2000次输出换行。</p>

<p>&nbsp;</p>

<p>为了放置输出的数据过多,设置了每计数100次输出打印一次。</p>

<p>&nbsp;</p>

<p>运行后,输出结果如下:</p>

<p> &nbsp;</p>

<p>靠近麦克风说话,就能看到数据发生变化了。</p>

<p>&nbsp;</p>

<p><span style="font-size:18px;"><strong>三、音频波形数据呈现</strong></span></p>

<p>上面只是将数据给直接输出了,还可以再进一步,将输出的数据,用波形图的方式呈现出来。</p>

<p>如果使用Thonny开发工具,可以很方便的将输出的数据呈现为音频波形。</p>

<p>&nbsp;</p>

<p>先将上面的程序,做一点小的修改,修改buffer_handler:</p>

<pre>
<code class="language-python">def buffer_handler(inactive_buf):
    global index, record_flag
   
    if record_flag:
      buff = pdm.get_buffer(inactive_buf)

      for i,n inenumerate(buff):
            if index % 100 == 0:
                #print(index, len(buff), i, n)
                #print(n, end=" ")
                print(n * 2)
            
            #if index % 2000 == 0:
            #    print("")

            index += 1</code></pre>

<p>&nbsp;</p>

<p>然后把Thonny的绘图器打开:</p>

<p> &nbsp;</p>

<p>&nbsp;</p>

<p>再运行代码,就可以看到音频波形图了:</p>

<p>86e2d4167e160c42c0d8468d00c58b77<br />
再呈现必行的过程中,大声说话,就可以看到变化了。</p>

<p>&nbsp;</p>

<p><span style="font-size:18px;"><strong>四、wav文件存储</strong></span></p>

<p>录制到了音频信号数据,一般还需要转换为wav格式,方便存储和播放,或者进一步处理。</p>

<p>&nbsp;</p>

<p>在&nbsp;<a href="https://github.com/PinkInk/mp34dt05-a-micropython-driver/tree/main">PinkInk/mp34dt05-a-micropython-driver: PDM Microphone on Arduino Nano&nbsp;</a>&nbsp;中,还提供了一个简单的wav处理模块:</p>

<p>&nbsp; 可以用于将采集到的音频信号数据,存储为wav文件。</p>

<p>&nbsp;</p>

<p>在上面程序的基础上,做一些修改,引入该模块,并进行调用:</p>

<pre>
<code>from time import sleep
from wavsimple import wav
from machine import Pin
import st34dt05a as pdm

pcm_rate = 8_000 # Hz - default is 12kHz i.e. 3.072MHz bit-sample rate
pdm.bit_sample_freq = pcm_rate * 256

pdm_clk = Pin(23)
pdm_data = Pin(22)

w = wav('output.wav', SampleRate=pcm_rate)
record_flag = False

index = 0
def buffer_handler(inactive_buf):
    global index, record_flag
    if record_flag:
      buff = pdm.get_buffer(inactive_buf)
      
      w.write(buff)

pdm.init(pdm_clk, pdm_data, handler=buffer_handler)
pdm.start()

sleep(1) # wait whilst StateMachine inits

print("Start record:")

record_flag = True
sleep(5)
record_flag = False

pdm.stop()
w.close()

print("End record.")</code></pre>

<p>&nbsp;</p>

<p>上面的代码中,调用了wavsimple.wav来进行数据的保存。</p>

<p>因为在中断调用中,要快速处理,所以把原有的打印输出给去掉了,只保存数据到wav文件,确保快速处理。</p>

<p>运行后,输出结果如下:</p>

<p> &nbsp;</p>

<p>&nbsp;</p>

<p>在&nbsp;Arduino&reg; Nano RP2040 Connect 的U盘设备中,会出现一个新的文件:</p>

<p> &nbsp;</p>

<p>打开这个文件,就可以听到录制的音频数据了。</p>

<p>不过,直接录制的音频数据,噪点较多,可以考虑用一些消除噪音的算法,来降噪处理。</p>

<p>例如使用下面的简单均值滤波算法:</p>

<pre>
<code class="language-python">def mean_filter(data, window_size=3):
    filtered_data = []
    half_window = window_size // 2
   
    for i in range(len(data)):
      start = max(0, i - half_window)
      end = min(len(data), i + half_window + 1)
      window = data
      filtered_value = sum(window) // len(window)
      filtered_data.append(filtered_value)
   
    return bytes(filtered_data)</code></pre>

Jacktang 发表于 2024-11-24 09:34

<p>已经有Arduino&reg; Nano RP2040 Connect的使用者,制作了专门的驱动,看来前期的使用者是比较用心的</p>

HonestQiao 发表于 2024-11-24 23:05

Jacktang 发表于 2024-11-24 09:34
已经有Arduino&reg; Nano RP2040 Connect的使用者,制作了专门的驱动,看来前期的使用者是比较用心的

<p>是的,micropython就是这么爽,玩的人多</p>
页: [1]
查看完整版本: 【Follow me第二季第4期】任务三:PDM麦克风小知识+数据读取+波形呈现+wav存储