Wszystko o wyjściu dźwięku przy użyciu Pico W

Dźwięk na obrazie tytułowym Pico W

Raspberry Pi Pico W to potężna płytka mikrokontrolera zbudowana w oparciu o własny układ mikrokontrolera Raspberry Pi, RP2040. Możesz sprawdzić wybór płytek z mikrokontrolerami opartymi na RP2040 tutaj Lub czytaj dalej, aby dowiedzieć się wszystkiego, co musisz wiedzieć o odtwarzaniu dźwięku za pomocą Pico W.

Czy kiedykolwiek zastanawiałeś się nad stworzeniem odtwarzacza muzyki, analizatora widma audio lub beatboxu, ale napotkałeś problemy z odtwarzaniem dźwięku za pomocą mikrokontrolera? Nie szukaj dalej, to jest artykuł dla Ciebie!

Tutaj wprowadzę Cię we wszechświat modulacji szerokości impulsu (PWM), protokołów komunikacyjnych dla Pico W i sposobu korzystania z bezprzewodowej łączności Pico W.

Pico W Image

Modulacja szerokości impulsów

Jednym ze sposobów odtwarzania dźwięku na Pico W (lub jakimkolwiek innym mikrokontrolerze) jest modulacja szerokości impulsu (Pulse-Width Modulation, w skrócie PWM).

PWM to technika używana do sterowania urządzeniami analogowymi za pomocą sygnału cyfrowego. Chociaż wyjście cyfrowe mikrokontrolera może być tylko włączone lub wyłączone (0 lub 1), dzięki PWM możemy naśladować sygnał analogowy. (Warto wspomnieć, że PWM nie jest prawdziwym sygnałem analogowym). PWM osiąga to poprzez szybkie przełączanie stanu zasilania między włączonym i wyłączonym, a tym samym kontrolowanie prądu.

Możesz dowiedzieć się więcej o PWM tutaj!

Obejrzyj nasz materiał wideo:

Ponieważ PWM jest bardzo wydajny, traci bardzo mało mocy i jest niezwykle precyzyjny, jest to technika używana w wielu aplikacjach do tworzenia różnych efektów dźwiękowych.

Każdy pin na Pico W jest zdolny do PWM. To tylko kwestia kodowania!

Pozwól, że omówię przykład, abyś mógł zobaczyć PWM w akcji.

Projekt

Aby zademonstrować generowanie dźwięku za pomocą PWM z Pico W, użyłem 2 kabli aligatora z męskimi końcówkami, płytki prototypowej i słuchawek.

Alternatywnie, jeśli chcesz uniknąć lutowania, możesz spróbować przypiąć drugi koniec kabla aligatora bezpośrednio do słuchawek lub wtyczki stereo (Zdecydowanie odradzam używanie słuchawek, aby chronić swoje bębenki!).

Pico W podłączony do słuchawek

Podłączyłem czerwony kabel aligatora do pinu 23, który jest pinem uziemienia (wystarczy dowolny pin uziemienia). PWM może być generowany przez dowolny z pinów GP. Jak widać na powyższym zdjęciu, używam GP0.

Każdy nowoczesny zestaw słuchawkowy lub gniazdo stereo ma trzy lub cztery sekcje - dwie zewnętrzne to lewy i prawy dźwięk. Dlatego też przypiąłem czerwony kabel aligatora do lewego i czarny kabel aligatora do prawego.

Jeśli zastosujesz tę konfigurację, uważaj, aby kable aligatora nie stykały się ze sobą.

Kolejne zdjęcie Pico W podłączonego do słuchawek

Po zakończeniu fizycznej konfiguracji, następnym krokiem jest pobranie plików audio.

Jeśli używasz plików .wav, upewnij się, że są one zgodne z poniższymi specyfikacjami:

1. kanał: mono (nie stereo)

2. szybkość transmisji: 22 kHz lub niższa

3. próbkowanie: 16-bitowe

Oto przykład kodu do generowania dźwięku w formacie .wav przy użyciu CircuitPython:

# PiCockpit.com
# audio output via digital PWM

import board
# loads a wav file for audio playback
from audiocore import WaveFile
# the audiopwmio module contains classes to provide access to audio ID
from audiopwmio import PWMAudioOut

# outputs an analog audio signal by varying the PWM duty cycle under the hood
audio_out = PWMAudioOut(board.GP0)

def play_wave(filename):
    with open(filename, "rb") as wave_file:
        sample = WaveFile(wave_file)
        audio_out.play(sample, loop=True)
# outputs an analog audio signal by varying the PWM duty cycle under the hood
audio_out = PWMAudioOut(board.GP0)

def play_wave(filename):
    with open(filename, "rb") as wave_file:
        sample = WaveFile(wave_file)
        audio_out.play(sample, loop=True)

        while audio_out.playing:
            pass

# audio will loop until program interruption
play_wave("meditation-music.wav")

Jeśli używasz plików .mp3, które muszą spełniać te same specyfikacje, co plik .wav, możesz odwołać się do tego przykładu kodu:

# PiCockpit.com
# audio output via digital PWM

import board
# loads an mp3 file for audio playback
from audiomp3 import MP3Decoder
# the audiopwmio module contains classes to provide access to audio IO
from audiopwmio import PWMAudioOut

# outputs an analog audio signal by varying the PWM duty cycle under the hood
audio_out = PWMAudioOut(board.GP0)

mp3 = MP3Decoder("meditation-music.mp3")

audio_out.play(mp3)
while audio_out.playing:
    pass

Dodatkową rzeczą, na którą należy zwrócić uwagę w przypadku plików .mp3, jest stała szybkość transmisji bitów. Jeśli dźwięk jest zniekształcony, spróbuj jeszcze bardziej obniżyć częstotliwość próbkowania i szybkość transmisji. Najwyższa szybkość transmisji, jaką udało mi się uzyskać, wynosiła 192 kb/s.

Jeśli używasz MicroPythona, a nie CircuitPythona, możesz przejść do poniższego przykładu kodu, który generuje 3 różne tony za pomocą PWM, ustawiając cykl pracy i zmieniając częstotliwość.

# PiCockpit.com

from machine import Pin, PWM
from utime import sleep

# lower right corner with USB connector on top
SPEAKER_PIN = 16

# create a PWM object on this pin
speaker = PWM(Pin(SPEAKER_PIN))

# the time each tone will be on
ON_TIME = .25
# the time between tones
OFF_TIME = .1

# low tone
speaker.duty_u16(1000)
speaker.freq(300)
sleep(ON_TIME)
speaker.duty_u16(0)
sleep(OFF_TIME)

# high tone
speaker.duty_u16(1000)
speaker.freq(800)
sleep(ON_TIME)
speaker.duty_u16(0)
sleep(OFF_TIME)

# medium tone
speaker.duty_u16(1000)
speaker.freq(400)
sleep(ON_TIME)

# turn off the PWM
speaker.duty_u16(0)

Protokoły komunikacyjne

Aby Pico W i części zewnętrzne mogły się ze sobą komunikować, dostępnych jest wiele różnych protokołów. Jeśli kiedykolwiek pracowałeś z biblioteką maszynową w MicroPython, wiesz, że standardowym protokołem komunikacyjnym dla pinów GPIO w Pico W jest biblioteka GPIOZero, która zapewnia prosty interfejs do sterowania i monitorowania pinów GPIO. Ponadto Pico W obsługuje również inne protokoły komunikacyjne.

W tym miejscu chciałbym opowiedzieć trochę o UART, SPI, I2C i I2S, ponieważ wszystkie oferują pewne zalety i wady w pracy z dźwiękiem na Pico W.

Po pierwsze, protokół UART (Universal Asynchronous Receiver/Transmitter) jest teoretycznie prosty w implementacji, wymaga tylko 2 przewodów i jest wysoce niezawodny. UART ma jednak ograniczony zasięg, ograniczoną przepustowość i może obsługiwać komunikację tylko z jednym innym urządzeniem.

Co więcej, podczas moich badań odkryłem, że UART nie nadaje się do odtwarzania dźwięku na Pico W. Jeśli używałeś UART na Pico W do wyjścia audio, daj mi znać w komentarzach!

SPI

Podczas gdy UART nie spełnia swojego zadania, szeregowy interfejs peryferyjny (SPI) oferuje wysoką szybkość przesyłania danych i jest niezwykle niezawodny. Jego największe wady dotyczą faktu, że SPI pozwala tylko na jeden kontroler i wykorzystuje 4 przewody. Udało mi się go uruchomić przy użyciu następującego kodu:

# PiCockpit.com
# registers addresses for the PCM5102A module
REG_POWER_CONTROL = 0x02
REG_MODE_CONTROL = 0x03
REG_DAC_CONTROL = 0x0A
REG_DAC_VOLUME = 0x0B

# powers on the PCM5102A module
cs_pin.value(0)
spi.write(bytes([REG_POWER_CONTROL, 0x08]))
cs_pin.value(1)

# sets the mode to I2S
cs_pin.value(0)
spi.write(bytes([REG_MODE_CONTROL, 0x04]))
cs_pin.value(1)

# sets the DAC control bits
cs_pin.value(0)
spi.write(bytes([REG_DAC_CONTROL, 0x00]))
cs_spin.value(1)

# sets the DAC volume
cs_pin.value(0)
spi.write(bytes([REG_DAC_VOLUME, 0x88]))
cs_pin.value(1)

# generates a sine save for testing
sample_rate = 44100
frequency = 440
duration = 5
samples = int(sample_rate * duration)
amplitude = 0x7FFF
sine_wave = bytearray(samplex * 2)
for i in range(samples):
    value = int(amplitude * math.sin(2 * math.pi * frequency * i / sample_rate))
    sine_wave[i*2] = value & 0xFF
    sine_wave[i*2+1] = (value >> 8) & 0xFF

# writes the sine wave to the PCM5102A module
cs_pin.value(0)
spi.write(sine_wave)
cs_pin.value(1)

# Waits for the sound to finish playing
utime.sleep(duration)

# Powers off the PCM5102A module
cs_pin.value(0)
spi.write(bytes([REG_POWER_CONTROL, 0x00]))
cs_pin.value(1)

I2C

Trzecim protokołem, który chcę poruszyć, jest Inter-Integrate Circuit (I2C), który wymaga tylko 2 przewodów, jest prosty w implementacji i wymaga niskiego poboru mocy. Podobnie jak UART, I2C ma ograniczony zasięg i ograniczoną przepustowość, ale w przeciwieństwie do UART, I2C może komunikować się z wieloma urządzeniami. Co jednak najważniejsze, jestem w stanie uruchomić I2C za pomocą następującego kodu:

# PiCockpit.com

import machine
import utime

# I2C bus
i2c = machine.I2C(0, sda=machine.Pin(0), scl=machine.Pin(1), freq=400000)

# I2C address of the PCM5102A module
i2c_address = 0x4C

# register addresses for the PCM5102A module
REG_POWER_CONTROL = 0x02
REG_MODE_CONTROL = 0x03
REG_DAC_CONTROL = 0x0A
REG_DAC_VOLUME = 0x0B

# powers on the PCM5102A module
i2c.writeto_mem(i2c_address, REG_POWER_CONTROL, b'\x08')

# sets the mode to I2S
i2c.writeto_mem(i2c_address, REG_MODE_CONTROL, b'\x04')

# sets the DAC controls
i2c.writeto_mem(i2c_address, REG_DAC_CONTROL, b'\x00')

# sets the DAC volume
i2c.writeto_mem(i2c_address, REG_DAC_VOLUME, b'\x88')

# generates a sine wave to test
sample_rate = 44100
frequency = 440
duration = 5
samples = int(sample_rate * duration)
amplitude = 0x7FFF
sine_wave = bytearray(samples * 2)
for i in range(samples):
    value = int(amplitude * math.sin(2 * math.pi * frequency * i / sample_rate))
    sine_wave[i*2] = value & 0xFF
    sine_wave[i*2+1] = (value >> 8) & 0xFF

# writes the sine wave to the PCM5102A module
i2c.writeto(i2c_address, sine_wave)

# waits for the sound to finish playing
utime.sleep(duration)

# power of the PCM5102A module
i2c.writeto_mem(i2c_address, REG_POWER_CONTROL, b'\x00')

Biorąc pod uwagę fakt, że I2C jest mniej złożony i obsługuje wiele urządzeń na jednej magistrali, jest to prawdopodobnie lepszy wybór dla prostszych aplikacji audio, które wymagają niższych transferów danych. Jednak SPI może być bardziej odpowiedni do odtwarzania dźwięku o wyższej jakości, ponieważ obsługuje szybszy transfer danych.

I2S

Czwartym i ostatnim protokołem, który chcę omówić, jest Inter-Integrated Sound lub I2S, który jest protokołem zoptymalizowanym do przesyłania danych audio. I2S jest zdecydowanie idealnym protokołem do generowania dźwięku z Pico W, ponieważ zapewnia wysoką wierność dźwięku, szybko przesyła dane, jest niezwykle niezawodny i łatwy w użyciu. Co więcej, istnieje szeroki wybór komponentów sprzętowych obsługujących I2S, takich jak Pimoroni Pico Audio Pack.

Pimoroni Pico Audio Pack

Aby usłyszeć, jak brzmi I2S, muszę przełączyć się z MicroPython na CircuitPython, ponieważ I2S nie jest jeszcze obsługiwany w MicroPython. Możesz jednak delikatnie wcisnąć Pico Audio Pack na nagłówki Pico W, upewniając się, że strona "USB" Audio Pack jest skierowana w stronę micro USB Pico W.

Podłącz słuchawki lub inne urządzenie i gotowe!

Jeszcze jedna rzecz: możesz chcieć trochę zmniejszyć głośność dla tego (Podziękujesz mi później!).

A oto przykładowy kod:

# PiCockpit.com

import audiobusio
import audiocore
import board
import array
import time
import math

# sets up I2S, pointing at the correct pins
i2s = audiobusio.I2SOut(board.GP10, board.GP11, board.GP9)

# generates one period of sine wave
# calculates sine wave's length with floor division based on the sample rate and frequency
length = 8000 // 400

# creates an array of signed 16-bit integers based on the length
sine_wave =array.array("H", [0] * length)

for i in range(length):
# calculates sample value with the sin function
    sine_wave[i] = int(math.sin(math.pi * 2 * i / length) * (2 ** 14) + 2 ** 14)

# creates a raw audio sample buffer in memory using the audiocore library
sine_wave = audiocore.RawSample(sine_wave, sample_rate=8000)

i2s.play(sine_wave, loop=True)
time.sleep(1)
i2s.stop()

Wi-Fi

Ponieważ Pico W prezentuje możliwości bezprzewodowe, pozwólcie, że pokażę wam coś, co wykorzystuje łączność bezprzewodową.

Ten kod pobiera plik .mp3 z adresu URL, a następnie odtwarza go z pliku po zakończeniu pobierania:

# PiCockpit.com

import adafruit_requests
import wifi
import socketpool
import ssl
import board
import audiomp3
import audiobusio
import os

# sets up I2S for sound output
audio = audiobusio.I2SOut(board.GP10, board.GP11, board.GP9)

# url for the mp3 file to download, 1 minute mp3
mp3_url = 'https://codeskulptor-demos.commondatastorage.googleapis.com/descent/background%20music.mp3'

# chunk size for downloading the mp3 file in parts
chunk_size = 1024

# connects to SSID
wifi.radio.connect(os.getenv('WIFI_SSID'), os.getenv('WIFI_PASSWORD'))

pool = socketpool.SocketPool(wifi.radio)
requests = adafruit_requests.Session(pool, ssl.create_default_context())

# requests the url and writes the mp3 to a file
try:
    doc = requests.get(mp3_url)
    # opens the file in binary write mode
    with open('sound.mp3', 'wb') as f:
    # writes the file in chunks
    for chunk in doc.iter_content(chunk_size=chunk_size):
        f.write(chunk)
except Exception as e:
    print("Connection error:\n", str(e))

# plays the mp3 file from flash memory
try:
    # creates an mp3 object
    mp3 = audiomp3.MP3Decoder(open("sound.mp3", "rb"))
    # optimizes the sample rate for your setup before playing
    mp3.sample_rate=24000
    # mp3.bits_per_sample=50
    audio.play(mp3)

    while audio.playing:
        pass

    print("Done playing!")
except Exception as e:
    print("Error in creating mp3 or playing audio:\n", str(e))

settings.toml:

# PiCockpit.com
# SSID is your network name
# replace myssid with your wi-fi name
WIFI_SSID = "myssid"

# replace mypassword with your wifi password
WIFI_PASSWORD - "mypassword"

Należy pamiętać, że pobranie pliku .mp3 może potrwać dłużej niż rzeczywista długość dźwięku. Aby to zadziałało, musiałem również włączyć uprawnienia do zapisu i zwiększyć pamięć do pobierania przez odśmiecanie w boot.py:

# PiCockpit.com

import gc
import storage

gc.collect(150000)
storage.remount("/", False)

Dodaj zewnętrzną pamięć masową, użyj kilku przycisków i spróbuj przesyłać strumieniowo muzykę z niektórych internetowych stacji radiowych. Nic nie powstrzyma Cię przed słuchaniem ulubionej muzyki z Raspberry Pi Pico W.

Wniosek

Pico W oferuje wiele różnych opcji generowania dźwięku, ale należy pamiętać o ograniczonej przestrzeni, jaką oferuje Pico W.

Ten post jest oparty na artykule napisanym pierwotnie przez Zitę B.

Komentarzy: 3

  1. Rob Frohne grudzień 19, 2023 o 9:23 pm

    Udało mi się wykorzystać UART do wysyłania dźwięków przez USB. Z Pico USB można uzyskać prawie 6Mbps. https://github.com/earlephilhower/arduino-pico/discussions/1765

  2. Prof Patrick Palmer luty 23, 2024 o 8:07 pm

    To bardzo pomocne! Obejmuje wszystkie metody, a nie tylko "ulubione". Właśnie tego potrzebowałem do prowadzenia zajęć z zaawansowanej elektroniki analogowej, gdzie używam PICO do większości eksperymentów. Podstawowe tony w PWM aż do wysokiej jakości funkcji I2S! Mam nadzieję, że inni zobaczą mój komentarz!

  3. HarloTek marzec 9, 2024 o 11:52 am

    A co z Bluetooth?

Pozostaw komentarz