代码之家  ›  专栏  ›  技术社区  ›  4emodan

带Android的PCM5122 DAC

  •  0
  • 4emodan  · 技术社区  · 7 年前

    我有一个树莓皮3B和 Suptronics X920 Expansion Board 它使用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 没有运气。

    请帮忙。

    2 回复  |  直到 7 年前
        1
  •  2
  •   devunwired    7 年前

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

    然而,这可能不是全部。DAC可能需要额外的配置命令和/或外部触发器。查看 similar HAT with the same DAC 例如,DAC设置命令也有I2C总线连接。我们的助理样本使用 VoiceHAT driver 在该外设上完成DAC所需的额外触发。

    在Raspbian中,通过以下方式启用的驱动程序 dtoverlay 可能会处理好这两件事。在这里,您的代码需要手动管理设置位。以助手示例中的VoiceHAT驱动程序为例。

    此外,确保未将任何I2S引脚启用为GPIO或PWM,因为这将禁用音频路由 per the documentation .

    旁注: Android Things不支持通过 config.txt ,因此在那里添加驱动程序预计不会产生任何效果。

        2
  •  0
  •   4emodan    6 年前

    我发现这一点已经有一段时间了,所以我发布了适合我的代码,以便其他人花更少的时间在手册中。

    在我花了几个小时阅读 manual 我皱着眉头看着电路板的示意图,发现PCM5122芯片需要一些预配置。

    事实证明,该芯片有一个复杂的时钟方案。从数据表中:

    串行音频接口通常有4个连接:SCK(系统主时钟)、BCK(位时钟)、LRCK(左 右字时钟)和DIN(数据)。该设备有一个内部PLL,用于获取SCK或BCK,以及 创建插值处理器和DAC时钟所需的更高速率时钟。这允许设备 使用或不使用外部SCK进行操作。

    因此,长话短说,芯片的PLL操作取决于物理连接到Raspberry板的引脚-SCK、BCK或两者:

    enter image description here

    我的情况是BCK。我们需要选择第13个寄存器的PLL时钟源:

    enter image description here

    在解释完所有这些之后,我将发布我在一些附加配置中使用的完整驱动程序。您可以在链接的手册中找到的所有信息。希望有帮助。

    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()
        }
    }