编译:陈拓 chentuo@ms.xab.ac.cn 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添加基本的音频输出。

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 Name填,用户名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请看我的《树莓派ZeroW的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音频播放器播放内置的音频文件。运行:

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. 树莓派介绍
    2. 电脑连接树莓派PiZeroW
    3. 树莓派ZeroW的GPIO控制
    4. 树莓派 Zero W+温度传感器DS18B20
    5. Adding Basic Audio Ouput to Raspberry Pi Zero
      https://learn.adafruit.com/adding-basic-audio-ouput-to-raspberry-pi-zero

树莓派Zero W添加音频输出的更多相关文章

  1. 树莓派3b配置耳机音频输出

    耳机输出 amixer cset numid=3 1 然后,播放既可,有杂音. hdmi输出 amixer cset numid=3 2

  2. Windows 10 IoT Serials 5 - 如何为树莓派应用程序添加语音识别与交互功能

    都说语音是人机交互的重要手段,虽然个人觉得在大庭广众之下,对着手机发号施令会显得有些尴尬.但是在资源受限的物联网应用场景下(无法外接鼠标键盘显示器),如果能够通过语音来控制设备,与设备进行交互,那还是 ...

  3. 树莓派Zero W GPIO控制

    作者:陈拓 chentuo@ms.xab.ac.cn 2018.06.09/2018.07.05 0.  概述 本文介绍树莓派 Zero W的GPIO控制,并用LED看效果. 0.1 树莓派GPIO编 ...

  4. windows下一根数据线玩转树莓派zero(w)

    买了个树莓派zero w,想着用一根普通micro数据线达成 供电+ssh+通过usb共享网络+远程桌面 的目标 通过用静态ip免去了用benjour的连接不稳定方法,下面开始细说 需要的硬件: 树莓 ...

  5. 树莓派 Zero W+温度传感器DS18B20

    树莓派 Zero W+温度传感器DS18B20 作者:陈拓chentuo@ms.xab.ac.cn 2018.05.28/2018.06.01 0.  概述 用树莓派 Zero W读取DS18B20温 ...

  6. 树莓派Zero W无键盘无屏幕初始化

    买了个树莓派Zero W,结果发现没mini hdmi线 淘宝了一根mini hdmi,等了5天,拿到手后发现zero还没地接键盘,这下脑袋大了 查了下资料,发现树莓的系统,可以这boot分区中加入一 ...

  7. 无显示器安装raspberry zero w树莓派 zero w

    笔者的环境 1. Macbook 电脑用于烧录树莓派系统到SD卡 2. 树莓派 zero w 把SD卡放进读卡器,插到Mac电脑上, 打开命令行工具Terminal diskutil list列出di ...

  8. 手把手教你修改pcduino系统默认的音频输出

    最近要搞个小玩意儿,要用到pcduino的音频输出,但是系统默认的是输出到hdmi的音频,我的显示器上没有喇叭,只能搞个USB声卡.但是系统默认又不是输出到USB,这里我手把手叫你怎么设置系统默认声卡 ...

  9. 解决:编辑一条彩信,附件选择添加音频,返回到编辑界面选择play,不能播放,没有声音

    [操作步骤]:编辑一条彩信,附件选择添加音频(外部音频),返回到编辑界面选择play,菜单键选择view slideshow [测试结果]:不能播放,没有声音 [预期结果]:可以播放 根据以往的经验( ...

随机推荐

  1. HttpPostedFile类

    在研究HttpRequest的时候,搞文件上传的时候,经常碰到返回HttpPostedFile对象的情况,这个对象才是真正包含文件内容的东西. 经常要获取的最重要的内容是FileName属性与Sava ...

  2. 打开安装 好的Microsoft Dynamics CRM 4.0 报错误为 Caller does not have enough privilege to set CallerOriginToken to the specified value 的解决办法

    If you installed CRM 4.0 on box where you also have SQL and used a domain account as service account ...

  3. [boost] : asser库用法

    基本用法 需要包含头文件#include <boost/assert.hpp> assert库定义了两个断言宏 BOOST_ASSERT BOOSE_ASSERT_MSG 第一种形式等价于 ...

  4. golang显示本机IP代码

    package main import ( "fmt" "net" ) func main() { addrs, err := net.InterfaceAdd ...

  5. C/C++基础----string, vector, array

    1 using声明 方便使用命名空间中的成员,不用每次xxx::yyy 头文件不应该包含using声明(不经意间包含了一些名字) 2 string 表3.1:初始化string对象的方式 string ...

  6. 页面引用jquery中文变乱码的问题

    <script type="text/javascript" src="Js/JavaScript.js"></script>   方法 ...

  7. jquery拖动分页

    scrollpagination.js /* ** Anderson Ferminiano ** contato@andersonferminiano.com -- feel free to cont ...

  8. 6.12-PrepareStatement,JdbcUtil 读取数据库配置文件properties,dao模式

    一.PrepareStatement 防止sql注入 PrepareStatement 是预编译sql语句 更加灵活,更有效率 executeUpdate() 做增删改 executeQuery() ...

  9. tcp 大文件上传 ,切换目录 及登陆文件加盐处理

    实现大文件的传输 服务器 import socketimport jsonimport structsk = socket.socket()sk.bind(("127.0.0.1" ...

  10. oracle库和表空间

    完整的Oracle数据库通常由两部分组成:Oracle数据库和数据库实例. 1) 数据库是一系列物理文件的集合(数据文件,控制文件,联机日志,参数文件等): 2) Oracle数据库实例则是一组Ora ...