樹莓派Zero W添加音頻輸出

樹莓派Zero W添加音頻輸出

編譯:陳拓[email protected] 2018.06.07/2018.07.14

原文:Adding Basic Audio Ouput to Raspberry Pi Zero
https://learn.adafruit.com/adding-basic-audio-ouput-to-raspberry-pi-zero

0. 概述

爲了保持樹莓派Zero W低成本和儘可能小,Pi Zero W不包括3.5mm音頻插座。也沒有音頻輸出端子。下面我們爲Pi Zero W添加基本的音頻輸出。

本文的另一個重點是GPIO引腳複用,也就是通過設置改變引腳的功能。

1. 原理

在其他的樹莓派上音頻是怎樣工作的呢?用於PI的Broadcom芯片組沒有真正的模擬輸出。作爲替代,用兩個PWM引腳(脈寬調製)以非常高的速度工作,並且有濾波。PWM頻率至少10倍於我們想要重現的音頻最高頻率。然後,通過調整PWM的佔空比,我們就可以“僞造”音頻信號。

音頻是20Hz到20kHz,從PI輸出的PWM是50MHz,所以我們可以很容易地過濾高50MHz輸出(無論如何都聽不到)。

看其他樹莓派音頻輸出示意圖,我們可以看到PWM0OUT和PWM1OUT是左右通道。R21和R20是分壓器,使3.3V信號下降到約1.1V MAX(這是音頻線路電平所需的最大峯值到峯值電壓)。

C20/C26與R21/R27一起工作,產生一個“RC低通濾波器”。你可以用1/(2*pi*RC) = 1/(2*pi*270*33*10-9) = 17865 Hz計算截止頻率,這非常接近20kHz。

C48/C34作爲直流濾波電容器,它只允許交流通過揚聲器和耳機。

BAV99 是ESD保護二極管。保護樹莓派防止來自PWM引腳的靜電。

 

2. Pi Zero W音頻

[https://learn.adafruit.com/adding-basic-audio-ouput-to-raspberry-pi-zero/pi-zero-pwm-audio]

2.1 BCM2835 GPIO功能

在PI Zero W的PCB板上,沒有引出PIN PWM0(引腳#40)和PWM1(引腳#45)。但是可以通過GPIO#18(ALT5)訪問PWM0,通過GPIO#13(ALT0)或GPIO#19(ALT5)訪問PWM1,請參閱下面的引腳和備用功能的完整列表:https://elinux.org/RPi_BCM2835_GPIOs

BCM2835 GPIO功能

 

GPIO ALT就像單片機的引腳複用。

2.2 在PI Zero W上實現PWM

  • 用ssh登錄到命令行控制檯。

用putty連接電腦和Pi Zero W,看本文最後的參考文檔Host Nameraspberrypi.local,端口22,用戶名pi,密碼raspberry

注意:boot分區有一個名爲ssh的空文本文件,這個ssh文件容易丟失,如果ssh不能登錄了,先檢查ssh是否丟失。

  • 設置引腳和PWM的方法

有兩種選擇,簡單的方法是使用DTO來設置所有引腳和PWM,但是,我們還沒有測試過。另一種方法是手動設置引腳GPIO alt功能,我們已經測試過了,但難度更大!

選擇1. 使用設備樹覆蓋(Device Tree Overlay - DTO)

有兩個建議的DTO選項,第一個使用了最近加入的DTO:

1) 有一種簡單的方法來配置PWM音頻的PI GPIO引腳。只需將以下行添加到/boot/config.txt,即可在引導是重新配置引腳,無需任何外部軟件或服務:

dtoverlay=pwm-2chan,pin=18,func=2,pin2=13,func2=4

將左通道映射到引腳33(BCM = 13),將右通道映射到引腳12(BCM = 18)。

我們先用這個方法試試:

命令: sudo nano /boot/config.txt

 

輸入後,使用組合鍵“Ctrl + X”,然後輸入“Y”,回車保存修改。

設置好/boot/config.txt以後,跳轉到2.3 低通濾波器接線。

2) 或者,你可以製作自己的DTO,參考下面的文章

嗨,使用dtoValay,我讓PWM音頻工作在我的pi-zero上:

https://hackaday.io/project/9467-pigrrl-zero/log/35090-pi-zero-pwm-audio-device-tree-overlay

無論哪種方式使用DTO,下一步就是低通濾波器(看本文後面):[https://learn.adafruit.com/adding-basic-audio-ouput-to-raspberry-pi-zero/pi-zero-pwm-audio#low-pass-filter-wiring]

選擇2. 手動分配PWM引腳

如果您想設置單獨的引腳和用途,則可以手動設置引腳功能。

  • 恢復做選擇1時修改的/boot/config.txt

pi@raspberrypi:~ $ sudo nano /boot/config.txt

  • 重新啓動Pi Zero W

pi@raspberrypi:~ $ sudo shutdown -h now

等待電源指示燈熄滅後,關閉電源

重新上電,登錄ssh。

  • 使用wiringpi的GPIO實用工具

在我們開始之前,先使用wiringpi的GPIO實用工具列出所有GPIO引腳和它們的當前功能/替代設置,請看: http://wiringpi.com/download-and-install/

上的文章:Wiring Pi -GPIO Interface library for the Raspberry Pi。

下載和安裝Wiring Pi請看參考文檔《樹莓派GPIO控制》一文。

讀取所有引腳的狀態

pi@raspberrypi:~ $ gpio readall

這兩個引腳的MODE類型爲IN - 這意味着它們只是簡單的輸入。

  • 下載更改GPIO ALT的工具

我們可以在Pi論壇中使用TimG的一個非常方便的工具來手動調整GPIO ALT(https://www.raspberrypi.org/forums/viewtopic.php?f=44&t=39138)。下載地址:https://learn.adafruit.com/pages/6577/elements/1960154/download

以下是完整的代碼:

    /*

    Utility to switch Raspberry-Pi GPIO pin functions

    Tim Giles 01/04/2013

 

    Usage:

    $ gpio_alt -p PIN_NUMBER -f ALT_NUMBER

 

    Based on RPi code from Dom and Gert, 15-Feb-2013, <http://elinux.org/RPi_Low-level_peripherals#C_2>

    and Gnu getopt() example <http://www.gnu.org/software/libc/manual/html_node/Example-of-Getopt.html#Example-of-Getopt>

    */

 

    #include <ctype.h>

    #include <stdio.h>

    #include <stdlib.h>

    #include <unistd.h>

    #include <fcntl.h>

    #include <sys/mman.h>

 

    #define BCM2708_PERI_BASE        0x20000000

    #define GPIO_BASE                (BCM2708_PERI_BASE + 0x200000) /* GPIO controller */

    #define PAGE_SIZE (4*1024)

    #define BLOCK_SIZE (4*1024)

 

    int  mem_fd;

    void *gpio_map;

    volatile unsigned *gpio;

    void setup_io();

 

    // GPIO setup macros. Always use INP_GPIO(x) before using OUT_GPIO(x) or SET_GPIO_ALT(x,y)

    #define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))

    #define OUT_GPIO(g) *(gpio+((g)/10)) |=  (1<<(((g)%10)*3))

    #define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3))

 

    #define GPIO_SET *(gpio+7)  // sets   bits which are 1 ignores bits which are 0

    #define GPIO_CLR *(gpio+10) // clears bits which are 1 ignores bits which are 0

 

    int main (int argc, char **argv) {

      int opt, flag, n_pin, n_alt;

      flag=0;

 

      while ((opt = getopt (argc, argv, "hp:f:")) != -1) {

        switch (opt) {

        case 'h':

          break;

        case 'p':

          n_pin = atoi(optarg); flag |= 0b0001; break;

        case 'f':

          n_alt = atoi(optarg); flag |= 0b0010; break;

        case '?':

        // getopt() prints error messages, so don't need to repeat them here

          return 1;

        default:

          abort ();

        }

      }

    

      if (flag != 0b0011) {

        fprintf (stderr, "Usage:\n$ gpio_alt -p PIN_NUM -f FUNC_NUM\n");

        return 1;

      }

    

      setup_io(); // Set up gpi pointer for direct register access

      INP_GPIO(n_pin);  // Always use INP_GPIO(x) before using SET_GPIO_ALT(x,y)

      SET_GPIO_ALT(n_pin, n_alt);

    

      printf("Set pin %i to alternative-function %i\n", n_pin, n_alt);

    

      return 0;

    }

 

    void setup_io() {

       /* open /dev/mem */

       if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {

          printf("can't open /dev/mem \n");

          exit(-1);

       }

 

       /* mmap GPIO */

       gpio_map = mmap(

          NULL,             //Any adddress in our space will do

          BLOCK_SIZE,       //Map length

          PROT_READ|PROT_WRITE,// Enable reading & writting to mapped memory

          MAP_SHARED,       //Shared with other processes

          mem_fd,           //File to map

          GPIO_BASE         //Offset to GPIO peripheral

       );

 

       close(mem_fd); //No need to keep mem_fd open after mmap

 

       if (gpio_map == MAP_FAILED) {

          printf("mmap error %d\n", (int)gpio_map);//errno also set!

          exit(-1);

       }

 

       // Always use volatile pointer!

       gpio = (volatile unsigned *)gpio_map;

    }

在Pi ZeroW上,創建一個新文件並進行編輯(不管在什麼目錄):

pi@raspberrypi:~ $ nano gpio_alt.c

然後粘貼上面的整個代碼。保存...

  • 編譯並安裝

編譯

pi@raspberrypi:~ $ gcc -o gpio_alt gpio_alt.c

改變所有者

pi@raspberrypi:~ $ sudo chown root:root gpio_alt

給gpio_alt以suid權限,可以像root用戶一樣操作

pi@raspberrypi:~ $ sudo chmod u+s gpio_alt

將gpio_alt移動到/usr/local/bin/

pi@raspberrypi:~ $ sudo mv gpio_alt /usr/local/bin/

  • 現在你可以設置兩個GPIO 的ALT功能了!

pi@raspberrypi:~ $ gpio_alt -p 13 -f 0

pi@raspberrypi:~ $ gpio_alt -p 18 -f 5

現在回到wiringPi檢查我們是否做到了!

pi@raspberrypi:~ $ gpio readall

你可以看到新的ALT設置。當然,用選擇1,設置/boot/config.txt的方法也可以看到這張圖,殊途同歸嘛。

2.3 低通濾波器接線

現在按照第1節的電路原理圖,將低通濾波電路連接到GPIO#13和GPIO#18上,作爲PWM1和PWM0。4個二極管可以忽略,跳過二極管。元件的參數不用太嚴格。

不必做的這麼正規,我利用手頭的元件,用洞洞板焊了一個低通濾波器,用起來挺好。看,就是這個:

3.5mm音頻插座,左右聲道和地接線。

用杜邦線和Pi Zero W連接起來,插上耳機:

2.4 設置音頻輸出

你還需要設置Pi Zero W,這樣音頻纔會通過“耳機插孔”(PWM輸出)流出,而不是HDMI。從控制檯運行:

pi@raspberrypi:~ $ sudo raspi-config

轉到Advanced Options

然後是音頻

強制3.5mm耳機

選擇OK,回車,然後Finish退出。這個過程你只需要做一次。

2.5 測試

你現在可以播放音頻了!要重新啓動一下Pi Zero W,注意,不要用reboot命令重啓,容易丟失ssh文件!

關機:

  • pi@raspberrypi:~ $ sudo shutdown -h now
  • 等待電源指示燈熄滅後,關閉電源

插入有源揚聲器或耳機,重新上電,登錄ssh。

我們使用內置的aplay(該命令是ALSA系統的一部分)音頻播放器播放內置的音頻文件。運行:

pi@raspberrypi:~ $ aplay /usr/share/sounds/alsa/Front_Center.wav

你聽到聲音了嗎,音質還不錯哦。

2.6 調整音量

你可能會注意到一點點嗡嗡聲,或者可能只是聲音不大。要獲得最佳音質,你需要調節音頻電平。你可以用alsamixer或amixer調節音量,但我認爲alsamixer更容易。

運行pi@raspberrypi:~ $ alsamixer

在終端上,音量是用ascii字符排列的圖像界面來表現的,用4個方向箭頭鍵調節,點擊Esc保存並退出。

現在我們再回過頭去試試第二種方法,雖然有點麻煩,但是對於機制的理解更好。

3. 自動化

如果你希望在啓動時自動設置alt,我可以這樣做。

  • 首先創建一個shell腳本pwmaudio.sh

pi@raspberrypi:~ $ sudo nano /root/pwmaudio.sh

內容:

#!/bin/bash

/usr/local/bin/gpio_alt -p 13 -f 0

/usr/local/bin/gpio_alt -p 18 -f 5

  • 修改權限

運行:

pi@raspberrypi:~ $ sudo chmod +x /root/pwmaudio.sh

  • 創建另一個腳本pwmaudio.service

sudo nano /lib/systemd/system/pwmaudio.service

內容:

[Unit]

Description=PWM Audio Service

 

[Service]

ExecStart=/root/pwmaudio.sh

StandardOutput=null

 

[Install]

WantedBy=multi-user.target

Alias=pwmaudio.service

保存文件。

  • 啓用該服務enable

pi@raspberrypi:~ $ sudo systemctl enable pwmaudio.service

  • 啓動該服務start

pi@raspberrypi:~ $ sudo systemctl start pwmaudio.service

  • 重新啓動pi

注意:慎用sudo reboot,容易丟失文件,ssh文件很容易丟失。

pi@raspberrypi:~ $ sudo shutdown -h now

等待電源指示燈熄滅後,重新上電。

  • 測試

重新登錄。運行gpio readall命令來驗證是否已設置了alts

成功!

 

參考文檔

  1. 樹莓派介紹https://blog.csdn.net/chentuo2000/article/details/81051241
  2. 電腦連接樹莓派Zero W
    https://blog.csdn.net/chentuo2000/article/details/81051308
  3. 樹莓派GPIO控制https://blog.csdn.net/chentuo2000/article/details/81051645
  4. 樹莓派 Zero W+溫度傳感器DS18B20
    https://blog.csdn.net/chentuo2000/article/details/81051701
  5. Pi Zero PWM Audio - device tree overlay
    https://hackaday.io/project/9467-piboy-zero/log/35090-pi-zero-pwm-audio-device-tree-overlay

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章