Τα πάντα για την έξοδο ήχου με το Pico W

Ήχος στο Pico W Εικόνα τίτλου

Το Raspberry Pi Pico W είναι μια πανίσχυρη πλακέτα μικροελεγκτή που βασίζεται στο δικό του τσιπ μικροελεγκτή του Raspberry Pi, το RP2040. Μπορείτε να δείτε μια επιλογή από πλακέτες μικροελεγκτών με βάση το RP2040 εδώ ή συνεχίστε να διαβάζετε για να μάθετε όλα όσα πρέπει να ξέρετε για την παραγωγή ήχου με το Pico W.

Σκεφτήκατε ποτέ να φτιάξετε ένα music player, να δημιουργήσετε έναν αναλυτή φάσματος ήχου ή να φτιάξετε ένα beatbox, αλλά αντιμετωπίσατε προβλήματα με την αναπαραγωγή ήχου χρησιμοποιώντας τον μικροελεγκτή σας; Λοιπόν, μην ψάχνετε άλλο, αυτό είναι το άρθρο για εσάς!

Εδώ, θα σας παρουσιάσω το σύμπαν της διαμόρφωσης εύρους παλμών (PWM), τα πρωτόκολλα επικοινωνίας για το Pico W και τον τρόπο αξιοποίησης της ασύρματης συνδεσιμότητας του Pico W.

Pico W Εικόνα

Διαμόρφωση εύρους παλμών

Ένας τρόπος για την αναπαραγωγή ήχου στο Pico W (ή σε οποιονδήποτε άλλο μικροελεγκτή) είναι η διαμόρφωση εύρους παλμών (Pulse-Width Modulation, ή PWM, για συντομία).

Η PWM είναι μια τεχνική που χρησιμοποιείται για τον έλεγχο αναλογικών συσκευών με τη χρήση ψηφιακού σήματος. Αν και η ψηφιακή έξοδος ενός μικροελεγκτή μπορεί να είναι πάντα μόνο ενεργοποιημένη ή απενεργοποιημένη (0 ή 1), με τη PWM μπορούμε να μιμηθούμε ένα αναλογικό σήμα. (Αξίζει να αναφέρουμε ότι η PWM δεν είναι ένα πραγματικό αναλογικό σήμα.) Η PWM το επιτυγχάνει αυτό με την ταχεία εναλλαγή της κατάστασης ισχύος μεταξύ ενεργοποίησης και απενεργοποίησης και έτσι τον έλεγχο του ρεύματος.

Μπορείτε να μάθετε περισσότερα για το PWM εδώ!

Και δείτε το βίντεό μας:

Επειδή η PWM είναι εξαιρετικά αποδοτική, χάνει πολύ λίγη ενέργεια και είναι εξαιρετικά ακριβής, αυτή είναι η τεχνική που χρησιμοποιείται σε πολλές εφαρμογές για τη δημιουργία διαφορετικών ηχητικών εφέ.

Κάθε ακροδέκτης του Pico W είναι ικανός για PWM. Είναι απλά θέμα κωδικοποίησης!

Επιτρέψτε μου να σας δώσω ένα παράδειγμα ώστε να δείτε το PWM σε δράση.

Έργο

Για να επιδείξω την παραγωγή ήχου με PWM από το Pico W, χρησιμοποίησα 2 καλώδια αλιγάτορα με αρσενικές απολήξεις, ένα breadboard και ακουστικά.

Εναλλακτικά, αν θέλετε να αποφύγετε τη συγκόλληση, μπορείτε να δοκιμάσετε να κουμπώσετε το άλλο άκρο των καλωδίων αλιγάτορα απευθείας στα ακουστικά σας ή στο στερεοφωνικό βύσμα (Συνιστώ ανεπιφύλακτα να μην χρησιμοποιείτε ακουστικά, μόνο και μόνο για να προστατεύσετε τα τύμπανά σας!).

Ένα Pico W συνδεδεμένο με ακουστικά

Συνδέω το κόκκινο καλώδιο αλιγάτορα στην 23η ακίδα, η οποία είναι ακίδα γείωσης (οποιαδήποτε ακίδα γείωσης κάνει). Και η PWM μπορεί να παραχθεί μέσω οποιουδήποτε από τους ακροδέκτες GP. Όπως μπορείτε να δείτε στην παραπάνω φωτογραφία, χρησιμοποιώ το GP0.

Κάθε σύγχρονο σετ ακουστικών ή στερεοφωνική υποδοχή έχει τρία ή τέσσερα τμήματα - με τα δύο εξωτερικά να είναι ο αριστερός και ο δεξιός ήχος. Ως εκ τούτου, έκοψα το κόκκινο καλώδιο αλιγάτορα στα αριστερά και το μαύρο καλώδιο αλιγάτορα στα δεξιά.

Αν ακολουθήσετε αυτή τη ρύθμιση, προσέξτε να μην ακουμπήσουν τα καλώδια αλιγάτορα.

Μια άλλη εικόνα του Pico W συνδεδεμένου με ακουστικά

Μόλις ολοκληρώσετε τη φυσική εγκατάσταση, το επόμενο βήμα είναι η λήψη αρχείων ήχου.

Αν χρησιμοποιείτε αρχεία .wav, βεβαιωθείτε ότι πληρούν τις ακόλουθες προδιαγραφές:

1. κανάλι: μονοφωνικό (όχι στερεοφωνικό)

2. ρυθμός bit: 22 kHz ή χαμηλότερος

3. δειγματοληψία: Δειγματοληψία: 16 bit

Ακολουθεί ένα παράδειγμα κώδικα για τη δημιουργία ήχου με .wav χρησιμοποιώντας το 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")

Αν χρησιμοποιείτε αρχεία .mp3, τα οποία πρέπει να πληρούν τις ίδιες προδιαγραφές με το αρχείο .wav, μπορείτε να ανατρέξετε σε αυτό το παράδειγμα κώδικα:

# 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

Ένα επιπλέον πράγμα που πρέπει να προσέξετε με τα αρχεία .mp3 είναι ο σταθερός ρυθμός μετάδοσης. Εάν ο ήχος ακούγεται παραμορφωμένος, δοκιμάστε να μειώσετε περαιτέρω το ρυθμό δειγματοληψίας και το ρυθμό bit. Ο υψηλότερος ρυθμός bit που μπόρεσα να πετύχω ήταν 192 kbps.

Αν χρησιμοποιείτε MicroPython και όχι CircuitPython, μπορείτε να ανατρέξετε στο ακόλουθο παράδειγμα κώδικα, το οποίο παράγει 3 διαφορετικούς τόνους με PWM ρυθμίζοντας τον κύκλο λειτουργίας και μεταβάλλοντας τη συχνότητα.

# 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)

Πρωτόκολλα επικοινωνίας

Προκειμένου το Pico W και τα εξωτερικά εξαρτήματα να επικοινωνούν μεταξύ τους, υπάρχουν πολλά διαφορετικά πρωτόκολλα. Αν έχετε δουλέψει ποτέ με τη βιβλιοθήκη machine στο MicroPython, γνωρίζετε ότι το τυπικό πρωτόκολλο επικοινωνίας για τις ακίδες GPIO του Pico W είναι η βιβλιοθήκη GPIOZero, η οποία παρέχει μια απλή διεπαφή για τον έλεγχο και την παρακολούθηση των ακίδων GPIO. Επιπλέον, το Pico W υποστηρίζει και άλλα πρωτόκολλα επικοινωνίας.

Εδώ, θέλω να μιλήσω λίγο για τα UART, SPI, I2C και I2S, καθώς όλα προσφέρουν ορισμένα πλεονεκτήματα και μειονεκτήματα στην εργασία με τον ήχο στο Pico W.

Πρώτον, το πρωτόκολλο Universal Asynchronous Receiver/Transmitter (UART) είναι θεωρητικά απλό στην υλοποίηση, απαιτεί μόνο 2 καλώδια και είναι εξαιρετικά αξιόπιστο. Ωστόσο, το UART έχει περιορισμένη εμβέλεια, περιορισμένο εύρος ζώνης και μπορεί να υποστηρίξει επικοινωνία μόνο με μία άλλη συσκευή.

Επιπλέον, κατά τη διάρκεια της έρευνάς μου, διαπίστωσα ότι το UART δεν είναι κατάλληλο για την αναπαραγωγή ήχου στο Pico W. Αν έχετε χρησιμοποιήσει το UART στο Pico W για έξοδο ήχου, ενημερώστε με στα σχόλια!

SPI

Ενώ η UART δεν κάνει τη δουλειά της, η σειριακή περιφερειακή διεπαφή ή SPI προσφέρει υψηλούς ρυθμούς μεταφοράς δεδομένων και είναι εξαιρετικά αξιόπιστη. Τα μεγαλύτερα μειονεκτήματά του περιστρέφονται γύρω από το γεγονός ότι το SPI επιτρέπει μόνο έναν ελεγκτή και χρησιμοποιεί 4 καλώδια. Κατάφερα να το κάνω να λειτουργήσει χρησιμοποιώντας τον ακόλουθο κώδικα:

# 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

Το τρίτο πρωτόκολλο στο οποίο θέλω να αναφερθώ είναι το Inter-Integrate Circuit ή I2C, το οποίο χρειάζεται μόνο 2 καλώδια, είναι απλό στην υλοποίηση και απαιτεί χαμηλή ισχύ. Όπως και το UART, το I2C έχει περιορισμένη εμβέλεια και περιορισμένο εύρος ζώνης, αλλά σε αντίθεση με το UART, το I2C μπορεί να επικοινωνήσει με πολλές συσκευές. Το πιο σημαντικό, όμως, είναι ότι μπορώ να κάνω το I2C να λειτουργήσει με τον ακόλουθο κώδικα:

# 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')

Με βάση το γεγονός ότι το I2C είναι λιγότερο πολύπλοκο και υποστηρίζει πολλαπλές συσκευές σε έναν ενιαίο δίαυλο, είναι πιθανώς η καλύτερη επιλογή για απλούστερες εφαρμογές ήχου που απαιτούν μικρότερες μεταφορές δεδομένων. Ωστόσο, το SPI μπορεί να είναι πιο κατάλληλο για αναπαραγωγή ήχου υψηλότερης ποιότητας, καθώς υποστηρίζει ταχύτερους ρυθμούς μεταφοράς δεδομένων.

I2S

Το τέταρτο και τελευταίο πρωτόκολλο που θέλω να συζητήσω είναι το Inter-Integrated Sound ή I2S, το οποίο είναι ένα πρωτόκολλο που έχει βελτιστοποιηθεί για τη μεταφορά δεδομένων ήχου. Το I2S είναι μακράν το ιδανικό πρωτόκολλο για την παραγωγή ήχου από το Pico W, επειδή παρέχει ήχο υψηλής πιστότητας, μεταφέρει δεδομένα γρήγορα, είναι εξαιρετικά αξιόπιστο και είναι εύκολο στη χρήση. Επιπλέον, υπάρχει μεγάλη ποικιλία εξαρτημάτων υλικού που υποστηρίζουν το I2S, όπως το Pimoroni Pico Audio Pack.

Πακέτο ήχου Pimoroni Pico

Για να ακούσω πώς ακούγεται το I2S, πρέπει να μεταβώ από το MicroPython στο CircuitPython, καθώς το I2S δεν υποστηρίζεται ακόμα στο MicroPython. Αλλά μπορείτε να σπρώξετε απαλά το Pico Audio Pack στις κεφαλίδες του Pico W, φροντίζοντας η πλευρά "USB" του Audio Pack να είναι στραμμένη προς την κατεύθυνση του micro USB του Pico W.

Συνδέστε τα ακουστικά σας ή μια άλλη συσκευή που θέλετε και αυτό είναι όλο!

Και κάτι ακόμα: ίσως να θέλετε να χαμηλώσετε λίγο την ένταση του ήχου σας για αυτό (θα με ευχαριστείτε αργότερα!)

Και εδώ είναι το δείγμα κώδικα:

# 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

Αφού το Pico W παρουσιάζει τις ασύρματες δυνατότητες, επιτρέψτε μου να σας δείξω κάτι που χρησιμοποιεί την ασύρματη συνδεσιμότητα.

Αυτός ο κώδικας παίρνει ένα .mp3 από μια διεύθυνση URL και στη συνέχεια το αναπαράγει από ένα αρχείο μόλις ολοκληρωθεί η λήψη:

# 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"

Σημειώστε ότι η λήψη του .mp3 ενδέχεται να διαρκέσει περισσότερο από την πραγματική διάρκεια του ήχου. Για να λειτουργήσει αυτό, έπρεπε επίσης να ενεργοποιήσω τα δικαιώματα εγγραφής και να αυξήσω τη μνήμη για τη λήψη μέσω της συλλογής σκουπιδιών στο boot.py:

# PiCockpit.com

import gc
import storage

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

Προσθέστε εξωτερικό αποθηκευτικό χώρο, βγάλτε μερικά κουμπιά και δοκιμάστε να κάνετε streaming μουσικής από κάποιους διαδικτυακούς ραδιοφωνικούς σταθμούς. Τότε τίποτα δεν μπορεί να σας εμποδίσει να απολαύσετε την αγαπημένη σας μουσική με το Raspberry Pi Pico W.

Συμπέρασμα

Το Pico W διαθέτει πολλές διαφορετικές επιλογές για την παραγωγή ήχου, απλώς φροντίστε να προσέξετε τον περιορισμένο χώρο που προσφέρει το Pico W.

Αυτή η ανάρτηση βασίζεται σε ένα άρθρο που γράφτηκε αρχικά από τη Zita B.

3 Σχόλια

  1. Rob Frohne στις Δεκέμβριος 19, 2023 στις 9:23 μμ

    Μπορώ να χρησιμοποιήσω το UART για να στείλω κομμάτια ήχου μέσω USB. Μπορείτε να έχετε σχεδόν 6Mbps με το Pico USB. https://github.com/earlephilhower/arduino-pico/discussions/1765

  2. Prof Patrick Palmer στις Φεβρουάριος 23, 2024 στις 8:07 μμ

    Αυτό είναι εξαιρετικά χρήσιμο! Καλύπτετε όλες τις μεθόδους και όχι μόνο μια "αγαπημένη". Ακριβώς ό,τι χρειαζόμουν για τη διδασκαλία της τάξης μου στα Προχωρημένα Αναλογικά Ηλεκτρονικά, όπου χρησιμοποιώ κάπου το PICO για τα περισσότερα πειράματα. Βασικοί τόνοι σε PWM μέχρι και λειτουργία I2S υψηλής ποιότητας! Ελπίζω να δουν και άλλοι το σχόλιό μου!

  3. HarloTek στις Μάρτιος 9, 2024 στις 11:52 πμ

    Τι γίνεται με το Bluetooth;

Αφήστε ένα σχόλιο