我有一个 Raspberry Pi 3B 和 Suptronics X920 扩展板,它使用 PCM5122 DAC。所以我在通过该板播放声音时遇到问题。
配置文件除显示配置部分外均为默认:
kernel=u-boot-dtok.bin
framebuffer_depth=16
# Prevent the firmware from loading HAT overlays now that we handle pin muxing.
# ourselves. See:
# https://www.raspberrypi.org/documentation/configuration/device-tree.md#part3.4
dtoverlay=
dtparam=i2c_arm=on
dtparam=spi=on
dtparam=audio=on
# pwm and I2S are mutually-exclusive since they share hardware clocks.
dtoverlay=pwm-2chan-with-clk,pin=18,func=2,pin2=13,func2=4
dtoverlay=generic-i2s
start_x=1
# Tell U-boot to always use the "serial0" interface for the console, which is
# set to whichever uart (uart0 or uart1) is set to the header pins. This doesn't
# interfere with the uart selected for Bluetooth.
dtoverlay=chosen-serial0
# Enable skip-init on the UART interfaces, so U-Boot doesn't attempt to
# re-initialize them.
dtoverlay=rpi-uart-skip-init
# Add pin devices to the system for use by the runtime pin configuration driver.
dtoverlay=runtimepinconfig
dtoverlay=uart1
dtoverlay=bcm2710-rpi-3-b-spi0-pin-reorder
# Tell the I2S driver to use the cprman clock.
dtoverlay=bcm2710-rpi-3-b-i2s-use-cprman
# Uncomment to disable serial port on headers, use GPIO14 and GPIO15
# as gpios and to allow the core_freq to change at runtime.
enable_uart=1
core_freq=400
# Support official RPi display.
dtoverlay=i2c-rtc,ds3231
dtoverlay=rpi-ft5406
hdmi_force_hotplug=1
# Set framebuffer to support RGBA colors.
framebuffer_swap=0
# Waveshare display settings
max_usb_current=1
hdmi_group=2
hdmi_mode=87
hdmi_cvt 1024 600 60 6 0 0 0
hdmi_drive=1
这是播放声音文件的代码:
fun playSound(file: File) {
val audioEncoding = AudioFormat.ENCODING_PCM_16BIT
val sampleRate = 16000
val audioOutputFormat = AudioFormat.Builder()
.setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
.setEncoding(audioEncoding)
.setSampleRate(16000)
.build()
val audioOutputBufferSize = AudioTrack.getMinBufferSize(sampleRate, audioOutputFormat.channelMask, audioEncoding)
val audioOutputDevice = findAudioDevice(AudioManager.GET_DEVICES_OUTPUTS, AudioDeviceInfo.TYPE_BUS)
val audioTrack = AudioTrack.Builder()
.setAudioFormat(audioOutputFormat)
.setBufferSizeInBytes(audioOutputBufferSize)
.setTransferMode(AudioTrack.MODE_STREAM)
.build()
audioTrack.preferredDevice = audioOutputDevice
val buffer = ByteArray(audioOutputBufferSize)
audioTrack.play()
audioTrack.setVolume(1f)
val stream = file.inputStream().buffered()
try {
while (stream.read(buffer) > 0) {
val out = audioTrack.write(buffer, 0, buffer.size, AudioTrack.WRITE_BLOCKING)
d { "audioTrack.write = $out" }
}
} catch (error: Throwable) {
e(error) { "Error playing audio $file" }
} finally {
stream.close()
}
audioTrack.stop()
audioTrack.release()
}
private fun findAudioDevice(deviceFlag: Int, deviceType: Int): AudioDeviceInfo? {
val manager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
val adis = manager.getDevices(deviceFlag)
for (adi in adis) {
if (adi.type == deviceType) {
return adi
}
}
return null
}
我已经使用常规 Raspberry Pi 音频输出(即
AudioDeviceInfo.TYPE_BUILTIN_SPEAKER
)测试了代码,并且工作正常。但使用 AudioDeviceInfo.TYPE_BUS
它只是不发出声音,没有任何错误。
我尝试了各种配置选项,例如
dtoverlay=hifiberry
或dtoverlay=hifiberry-dacplus
,但没有成功。
请帮忙。
看起来您可能正在使用 Google Assistant 示例的一些代码,并且您正确地假设
TYPE_BUS
是您启用音频路由以使用 I2S 总线而不是内置总线所需的内容。音频插孔。
然而,这可能不是故事的全部。 DAC 可能需要额外的配置命令和/或外部触发器。例如,查看具有相同 DAC 的类似 HAT,也有一个用于 DAC 设置命令的 I2C 总线连接。我们的 Assistant 示例使用 VoiceHAT 驱动程序 来完成该外设上 DAC 所需的额外触发。
在 Raspbian 中,您通过
dtoverlay
启用的驱动程序可能会处理这两部分。在这里,您的代码需要手动管理设置位。查看 Assistant 示例中如何使用 VoiceHAT 驱动程序作为示例。
此外,请确保您没有启用任何 I2S 引脚作为 GPIO 或 PWM,因为这将禁用音频路由根据文档。
旁注: Android Things 不支持通过
config.txt
进行内核更改,因此添加驱动程序预计不会产生任何效果。
自从我弄清楚这一点以来已经有一段时间了,所以我发布了对我有用的代码,以便其他人花更少的时间埋在手册中。
在我花了几个小时阅读手册并皱着眉头查看电路板原理图后,我发现 PCM5122 芯片需要一些预配置。
事实证明,该芯片具有复杂的时钟方案。从数据表:
串行音频接口通常有 4 个连接:SCK(系统主时钟)、BCK(位时钟)、LRCK(左 右字时钟)和 DIN(数据)。该器件有一个内部 PLL,用于获取 SCK 或 BCK 以及 创建内插处理器和 DAC 时钟所需的更高速率时钟。这允许设备 使用或不使用外部 SCK 均可操作。
所以,长话短说,芯片的 PLL 操作取决于物理连接到 Raspberry 板的引脚 - SCK、BCK 或两者:
就我而言,是 BCK。我们需要使用第13个寄存器来选择PLL时钟源:
解释完所有内容后,我将发布我使用的完整驱动程序以及一些附加配置。您可以在链接的手册中找到所有信息。希望有帮助。
class SuptronicsX920AudioDevice private constructor(
private val busDevice: AudioDeviceInfo,
private val i2cDevice: I2cDevice) : AudioDevice {
private var audioTrack: AudioTrack? = null
private var leftVolume = 1f
private var rightVolume = 1f
companion object {
private const val ERROR_DETECT_REG = 37
private const val ERROR_DETECT_IDCM_BIT = 3
private const val PLL_SOURCE_REG = 13
private const val PLL_SOURCE_BCK_BIT = 4
private const val AUTO_MUTE_REG = 65
private const val DIGITAL_VOLUME_LEFT_REG = 61
private const val DIGITAL_VOLUME_RIGHT_REG = 62
fun create(busDevice: AudioDeviceInfo, i2cDevice: I2cDevice): Either<IOException, SuptronicsX920AudioDevice> {
return try {
// Ignore BCK\SCK missing errors as they turn device into Power down mode
riseRegBit(i2cDevice, ERROR_DETECT_REG, ERROR_DETECT_IDCM_BIT)
// Select BCK as the source for PLL
riseRegBit(i2cDevice, PLL_SOURCE_REG, PLL_SOURCE_BCK_BIT)
// Disable auto mute for both channels
i2cDevice.writeRegByte(AUTO_MUTE_REG, 0)
// Set the maximum gain for both channels
i2cDevice.writeRegByte(DIGITAL_VOLUME_LEFT_REG, 0)
i2cDevice.writeRegByte(DIGITAL_VOLUME_RIGHT_REG, 0)
SuptronicsX920AudioDevice(busDevice, i2cDevice).right()
} catch (ioe: IOException) {
e(ioe) { "Unable to configure PCM512x for Suptronics x920" }
ioe.left()
}
}
private fun riseRegBit(i2cDevice: I2cDevice, regAddress: Int, bitAddress: Int) {
val value = i2cDevice.readRegByte(regAddress)
i2cDevice.writeRegByte(regAddress, value or (1 shl bitAddress).toByte())
}
}
override fun play(stream: InputStream, audioFormat: AudioFormat) {
val audioOutputBufferSize = AudioTrack.getMinBufferSize(
audioFormat.sampleRate,
audioFormat.channelMask,
audioFormat.encoding)
val buffer = ByteArray(audioOutputBufferSize)
audioTrack = AudioTrack.Builder()
.setAudioFormat(audioFormat)
.setBufferSizeInBytes(audioOutputBufferSize)
.setTransferMode(AudioTrack.MODE_STREAM)
.build()
audioTrack?.apply {
preferredDevice = busDevice
setStereoVolume(leftVolume, rightVolume)
play()
var bytes = 0
try {
while (stream.read(buffer) > 0) {
bytes += write(buffer, 0, buffer.size, AudioTrack.WRITE_BLOCKING)
}
} catch (error: Throwable) {
e(error) { "Error playing audio" }
}
d { "$bytes of audio track written" }
}
stop()
audioTrack = null
}
override fun stop() {
audioTrack?.apply {
if (state != AudioTrack.STATE_UNINITIALIZED) {
try {
pause()
flush()
release()
d { "Audio stopped" }
} catch (error: Throwable) {
e(error) { "Can't stop track properly" }
}
}
}
}
override fun setVolume(leftVolume: Float, rightVolume: Float) {
this.leftVolume = leftVolume
this.rightVolume = rightVolume
audioTrack?.apply { setStereoVolume(leftVolume, rightVolume) }
}
override fun close() {
stop()
i2cDevice.close()
}
}
不确定是否值得尝试声称支持 HifiBerry 的 AOSP 变体?
https://xdaforums.com/t/dev-rom-aosp-android-13-for-raspberry-pi-4-b.4481977/