【问题标题】:PCM5122 DAC with Android ThingsPCM5122 DAC 与 Android Things
【发布时间】:2018-05-12 04:43:15
【问题描述】:

我有一个使用 PCM5122 DAC 的 Raspberry Pi 3B 和 Suptronics X920 Expansion Board。所以我无法通过该板播放声音。

配置文件是默认的,除了显示配置部分:

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=hifiberrydtoverlay=hifiberry-dacplus,但没有成功。

请帮忙。

【问题讨论】:

    标签: raspberry-pi raspberry-pi3 android-things


    【解决方案1】:

    看起来您可能正在使用 Google Assistant sample 的某些代码,并且您正确地假设 TYPE_BUS 是启用音频路由以使用 I2S 总线而不是内置的音频插孔。

    但是,这可能不是故事的全部。 DAC 可能需要额外的配置命令和/或外部触发器。例如,查看similar HAT with the same DAC,还有一个用于 DAC 设置命令的 I2C 总线连接。我们的 Assistant 示例使用 VoiceHAT driver 来完成该外设上的 DAC 所需的额外触发。

    在 Raspbian 中,您通过 dtoverlay 启用的驱动程序可能会处理这两个部分。在这里,您的代码将需要手动管理设置位。看看 VoiceHAT 驱动程序是如何在 Assistant 示例中使用的。

    另外,请确保您没有将任何 I2S 引脚启用为 GPIO 或 PWM,因为这将禁用音频路由 per the documentation

    旁注:Android Things 不支持通过config.txt 进行内核更改,因此在那里添加驱动程序预计不会有任何影响。

    【讨论】:

    • 谢谢!你真的帮了我。我将尝试创建一个驱动程序来处理电路板配置。
    • 嘿@4emodan,您是否设法为PCM5122 编写驱动程序?当我尝试连接 SPH0645 I2S 麦克风时,如果您能就如何为其编写驱动程序提供建议,那将非常有用。谢谢!
    • @amg 我已经在下面发布了 PCM5122 的完整驱动程序。所有的预配置都在create() 函数中进行。
    【解决方案2】:

    自从我弄清楚这一点以来已经有一段时间了,所以我发布了对我有用的代码,以便其他人花更少的时间埋在手册上。

    在我花了几个小时阅读manual 并对电路板的原理图皱眉之后,我发现 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()
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多