【随笔记】XR872 Codec 驱动移植和应用程序实例(附芯片调试方法)
XR872 的 SDK 是我目前接触过那么多款 MCU 的 SDK 中,唯一一个将框架和 RTOS 结合的非常完美的 SDK 。无论是代码风格还是框架的设计,看起来都很赏心悦目,而且是源码开源。希望能有更多的机会可以在项目上应用这款芯片,便于更加深入的理解和学习它的框架设计思想,当然,如果有一段连续较长的闲暇时间,那就更好了。
官方 SDK 和 文档链接:
SDK: https://github.com/XradioTech/xradio-skylark-sdk.git
DOC: https://docs.xradiotech.com
这一篇是应用篇,主要是介绍如何移植一款新的 Codec 驱动到 xr872 SDK 中,由于框架写的简单,且又与 Linux 框架非常相似,所以很容易的可以借鉴已有的驱动来移植新的 Codec 驱动,以下的修改方法都是通过搜索 AC107 相关的关键词,来了解所需要修改的地方并实际应用。
文中最后,会给出调试芯片的经验总结,而下一篇将会介绍它的 Audio 驱动框架。
一、 参考已有的 Codec 驱动程序框架编写 Codec 驱动
以下为参考驱动程序的路径,示例源码则是根据驱动精简过后得到的框架代码,按照注释说明去实现框架里面的各个回调接口即可:
xradio-skylark-sdk\src\driver\chip\codec\ac107.c
xradio-skylark-sdk\src\driver\chip\codec\ac101.c
xradio-skylark-sdk\src\driver\chip\codec\xradio_internal_codec.c
最主要的是 struct codec_ops 和 struct codec_dai_ops 这两个回调接口组,前者是与 Codec 初始化相关,后者与 I2S 参数相关(主要是通信方式和数据传输格式等等)。
/**
******************************************************************************
* @文件 sample_codec.c
* @版本 V1.0.0
* @日期
* @概要 xr872 sample codec
* @作者 lmx
******************************************************************************
* @注意
*
******************************************************************************
*/#include <stdbool.h>
#include "audio_arch.h"
#include "driver/chip/hal_i2c.h"
#include "sample.h"/*********************************************************************************
功能: 配置 codec 的时钟源、频率、采样率
说明: 1.时钟源和频率通过 board_config.c -> struct snd_card_board_config 配置2.采样率通过用户层的 struct pcm_config 指定3.两者在用户调用 snd_pcm_open()->HAL_SndCard_Open() 时触发此接口
*********************************************************************************/
static int sample_dai_set_sysclk(Codec_Sysclk_Src sysclk_src, Codec_Pllclk_Src pllclk_src, uint32_t pll_freq_in, uint32_t sample_rate)
{switch(sysclk_src){case SYSCLK_SRC_OSC: // 设置晶振为系统时钟源break;case SYSCLK_SRC_MCLK: // 设置 MCLK 为系统时钟源break;case SYSCLK_SRC_BCLK: // 设置 BCLK 为系统时钟源break;case SYSCLK_SRC_PLL: // 设置 PLL 为系统时钟源break;default: // 无效的配置return HAL_INVALID;}return HAL_OK;
}/*********************************************************************************
功能: 配置 codec 的主从关系、数据传输格式、时钟极性
说明: 1.通过 board_config.c -> struct snd_card_board_config 配置2.如果有多颗 codec 只能配置其中一颗 Master 其它的需要为 Slave3.用户调用 snd_pcm_open()->HAL_SndCard_Open() 时触发此接口
*********************************************************************************/
static int sample_dai_set_fmt(uint32_t fmt)
{// 配置 codec I2S 接口的主从关系switch (fmt & I2S_ROLE_MASK) {case DAIFMT_CBM_CFM: // 设置 Codec 为 I2S Master : BCLK、LRCK outputbreak;case DAIFMT_CBS_CFS: // 设置 Codec 为 I2S Slave : BCLK、LRCK outputbreak;default:return HAL_INVALID;}// 配置 I2S 的数据格式switch (fmt & I2S_FORMAT_MASK) {case DAIFMT_I2S: // 设置为 I2S 格式break;case DAIFMT_RIGHT_J: // 设置为 I2S 右对齐格式break;case DAIFMT_LEFT_J: // 设置为 I2S 左对齐格式break;case DAIFMT_DSP_A: // 设置为 PCM-A 格式break;case DAIFMT_DSP_B: // 设置为 PCM-B 格式break;default: // 格式错误return HAL_INVALID;}// 配置时钟的极性switch (fmt & I2S_POLARITY_MASK) {case DAIFMT_NB_NF: // BCLK: 正常极性 LRCLK: 正常极性break;case DAIFMT_NB_IF: // BCLK: 正常极性 LRCLK: 反相极性break;case DAIFMT_IB_NF: // BCLK: 反相极性 LRCLK: 正常极性break;case DAIFMT_IB_IF: // BCLK: 反相极性 LRCLK: 反相极性break;default: // 格式错误return HAL_INVALID;}return HAL_OK;
}/*********************************************************************************
功能: 配置增益或者音量
说明: 由用户通过 audio_manager_handler() 时触发此接口
*********************************************************************************/
static int sample_dai_set_volume(Audio_Device device, uint16_t volume)
{// volume & VOLUME_SET_MASK == 1 : 设置的是 Gain, 反之设置的是 Level// volume & ~VOLUME_SET_MASK : 设置具体的值switch(device){case AUDIO_IN_DEV_AMIC: // 设置模拟麦的音量break;case AUDIO_IN_DEV_DMIC: // 设置数字买的音量break;default: // 参数无效return HAL_INVALID;}return HAL_OK;
}/*********************************************************************************
功能: 设置音频路径, 有些芯片支持数字麦克风也支持模拟麦克风, 可以通过此接口进行切换
说明: 由用户通过 audio_manager_handler() 时触发此接口
*********************************************************************************/
static int sample_dai_set_route(Audio_Device device, Audio_Dev_State state)
{ES7210_DBG("--->%s\n",__FUNCTION__);// 设置为录音if(device & AUDIO_IN_DEV_ALL){switch(device){case AUDIO_IN_DEV_AMIC:break;case AUDIO_IN_DEV_DMIC:break;case AUDIO_IN_DEV_LINEIN:default:return HAL_INVALID;}}else // 设置为播放{switch(device){case AUDIO_OUT_DEV_HP:break;case AUDIO_OUT_DEV_SPK:break;default:return HAL_INVALID;}}return HAL_OK;
}/*********************************************************************************
功能: 配置硬件参数: 播放或者录音, 通道数, 采样率, 数据位宽
说明: 用户调用 snd_pcm_open()->HAL_SndCard_Open() 时触发此接口
*********************************************************************************/
static int sample_dai_hw_params(Audio_Stream_Dir dir, struct pcm_config *pcm_cfg)
{// common init// set sample rate: pcm_cfg->rate // set channels // set sample resorution and slot width if(dir == PCM_OUT){// TX enable, Globle enable }else{// RX enable, Globle enable}return HAL_OK;
}/*********************************************************************************
功能: 释放硬件参数
说明: 用户调用 snd_pcm_close()->HAL_SndCard_Close() 时触发此接口
*********************************************************************************/
static int sample_dai_hw_free(Audio_Stream_Dir dir)
{ES7210_DBG("--->%s\n",__FUNCTION__);// globle disable // soft reset return HAL_OK;
}/*********************************************************************************
功能: 控制指令
说明: 1.CODEC_IOCTL_PCM_READ\CODEC_IOCTL_PCM_WRITE 作为读写音频数据的补充接口,有些 Codec 不需走外部 I2S 接口, 如 SOC 内部 codec 是通过 SOC 内部总线传输数据,因此可以在这里实现音频数据的读取和写入功能, 设置 XRADIO_PLATFORM_NULL 即可不,需要平台的接口读写音频数据, 就能被 snd_pcm_read()\snd_pcm_write() 触发回调.2.CODEC_IOCTL_HW_CONFIG\CODEC_IOCTL_SW_CONFIG 作为配置的补充接口,满足差异需求,通过 audio_maneger_ioctl() 接口触发此接口.
*********************************************************************************/
static int sample_codec_ioctl(uint32_t cmd, uint32_t cmd_param[], uint32_t cmd_param_len)
{switch(cmd){case CODEC_IOCTL_HW_CONFIG: // 硬件配置break;case CODEC_IOCTL_SW_CONFIG: // 软件配置break;case CODEC_IOCTL_PCM_READ: // 读取音频数据break;case CODEC_IOCTL_PCM_WRITE: // 写入音频数据break;default:return HAL_INVALID;}return HAL_OK;
}/*********************************************************************************
功能: 用于读取指定寄存器的值
说明: 1. 用户调用 audio_manager_reg_read() 时触发此接口2. 主要是用于控制台命令读取寄存器
*********************************************************************************/
static int sample_codec_reg_read(uint32_t reg)
{return 0x00;
}/*********************************************************************************
功能: 用于写入指定的值到指定寄存器
说明: 1. 用户调用 audio_manager_reg_write() 时触发此接口2. 主要是用于控制台命令写入寄存器
*********************************************************************************/
static int sample_codec_reg_write(uint32_t reg, uint32_t val)
{return HAL_OK;
}/*********************************************************************************
功能: 打开 codec
说明: 设置完所有配置之后, 会由 snd_pcm_open() 触发, 在这个位置可以启动 codec 工作
*********************************************************************************/
static int sample_codec_open(Audio_Stream_Dir dir)
{return HAL_OK;
}/*********************************************************************************
功能: 关闭 codec
说明: 当用于调用 snd_pcm_close() 触发, 在这个位置可以停止 codec 工作
*********************************************************************************/
static int sample_codec_close(Audio_Stream_Dir dir)
{return HAL_OK;
}/*********************************************************************************
功能: 初始化 codec
说明: 当加载驱动程序是会被 HAL_SndCard_Register() 调用
*********************************************************************************/
static int sample_codec_init(void)
{ES7210_DBG("--->%s\n",__FUNCTION__);return HAL_OK;
}/*********************************************************************************
功能: 释放 codec
说明: 当卸载驱动程序是会被 HAL_SndCard_Unregister() 调用
*********************************************************************************/
static void sample_codec_deinit(void)
{ES7210_DBG("--->%s\n",__FUNCTION__);
}// SOC 的数字接口(I2S)相关的回调, 配置信息需要双方一致
static const struct codec_dai_ops sample_codec_dai_ops = {.set_sysclk = sample_dai_set_sysclk,.set_fmt = sample_dai_set_fmt,.set_volume = sample_dai_set_volume,.set_route = sample_dai_set_route,.hw_params = sample_dai_hw_params,.hw_free = sample_dai_hw_free,
};// codec 相关的回调接口
static const struct codec_ops sample_codec_ops = {.open = sample_codec_open,.close = sample_codec_close,.reg_read = sample_codec_reg_read,.reg_write = sample_codec_reg_write,.ioctl = sample_codec_ioctl,
};// codec 驱动程序接口
static struct codec_driver sample_codec_drv = {.name = ES7210_CODEC_NAME, // codec 名称.codec_attr = XRADIO_CODEC_ES7210, // 用于标识 codec , 便于与 I2S 关联.init = sample_codec_init, // 驱动加载时的初始化接口.deinit = sample_codec_deinit, // 驱动卸载时的释放接口.dai_ops = &sample_codec_dai_ops, // 配置数字接口时的回调接口(主要作用是保证数字接口和Codec的配置保持一致).codec_ops = &sample_codec_ops, // 配置 codec 时的回调接口
};/*********************************************************************************
功能: 注册 codec
说明: 由 board.c board_soundcard_init()->hal_codec.c HAL_SndCard_CodecRegisterSample1()->sample_codec_register 调用
*********************************************************************************/
HAL_Status sample_codec_register(void)
{// 配置 I2C 总线// 通过 I2C 寻找芯片, 读取芯片 ID 确认// 调用下面的代码将此 codec 挂接在 hal_snd_codec_list 链表list_add(&sample_codec_drv.node, &hal_snd_codec_list);return HAL_OK;
}/*********************************************************************************
功能: 注册 codec
说明: 由 board.c board_soundcard_deinit()->hal_codec.c HAL_SndCard_CodecUnregisterSample1()->sample_codec_unregister 调用
*********************************************************************************/
HAL_Status sample_codec_unregister(void)
{struct codec_driver *codec_drv_ptr;// 先检查该 codec 是否在链表内if(list_empty(&hal_snd_codec_list)){return HAL_OK;}// 从 hal_snd_codec_list 移除此 codeclist_for_each_entry(codec_drv_ptr, &hal_snd_codec_list, node){if(codec_drv_ptr == &sample_codec_drv){list_del(&sample_codec_drv.node);break;}}return HAL_OK;
}
二、将驱动程序添加到 SDK 中
audio_arch.h 和 hal_snd_card.h 主要是增加支持新的 sample 声卡注册和卸载接口
--- xradio-skylark-sdk\src\driver\chip\codec\audio_arch.h 2021-03-17 09:47:37.000000000 +0800
+++ xradio-skylark-sdk\src\driver\chip\codec\audio_arch.h 2021-03-16 14:08:17.000000000 +0800
@@ -175,12 +175,15 @@HAL_Status ac107_pdm_set_volume_level(Audio_Device device, uint16_t volume);HAL_Status ac107_pdm_set_volume_gain(Audio_Device device, uint16_t volume);HAL_Status ac101_codec_register(void);HAL_Status ac101_codec_unregister(void);+HAL_Status sample_codec_register(void);
+HAL_Status sample_codec_unregister(void);
+HAL_Status xradio_i2s_register(void);HAL_Status xradio_i2s_unregister(void);/*****************************************************************************************/
--- xradio-skylark-sdk\include\driver\chip\hal_snd_card.h 2021-03-17 09:46:58.000000000 +0800
+++ xradio-skylark-sdk\include\driver\chip\hal_snd_card.h 2021-03-16 14:06:03.000000000 +0800
@@ -47,12 +47,13 @@/* Codec name */#define XRADIO_CODEC_NULL_NAME "xradio_codec_null"#define XRADIO_INTERNAL_CODEC_NAME "xradio_internal_codec"#define AC107_CODEC_NAME "ac107_codec"#define AC101_CODEC_NAME "ac101_codec"
+#define SAMPLE_CODEC_NAME "sample_codec"/* Platform name */#define XRADIO_PLATFORM_I2S_NAME "xradio_platform_i2s"/* Snd card suffix name */#define SND_CARD_SUFFIX "_sound_card"
@@ -144,12 +145,13 @@/* codec driver select */typedef enum {XRADIO_CODEC_NULL,XRADIO_CODEC_INTERNAL,XRADIO_CODEC_AC107,XRADIO_CODEC_AC101,
+ XRADIO_CODEC_SAMPLE,}Codec_Attr;/* platform driver select */typedef enum {XRADIO_PLATFORM_NULL,XRADIO_PLATFORM_I2S,
@@ -159,13 +161,14 @@/* Sound Card number */typedef enum {SND_CARD_0,SND_CARD_1,SND_CARD_2,SND_CARD_3,
- SND_CARD_MAX = SND_CARD_3,
+ SND_CARD_4,
+ SND_CARD_MAX = SND_CARD_4,}Snd_Card_Num;/* Volume set flag */#define VOLUME_SET_LEVEL (0x0000)#define VOLUME_SET_GAIN (0x8000)
@@ -427,12 +430,14 @@HAL_Status HAL_SndCard_CodecRegisterInternal(void);HAL_Status HAL_SndCard_CodecUnregisterInternal(void);HAL_Status HAL_SndCard_CodecRegisterAc107(void);HAL_Status HAL_SndCard_CodecUnregisterAc107(void);HAL_Status HAL_SndCard_CodecRegisterAc101(void);HAL_Status HAL_SndCard_CodecUnregisterAc101(void);
+HAL_Status HAL_SndCard_CodecRegisterSample(void);
+HAL_Status HAL_SndCard_CodecUnregisterSample(void);HAL_Status HAL_SndCard_PlatformRegisterI2S(void);HAL_Status HAL_SndCard_PlatformUnregisterI2S(void);HAL_Status HAL_SndCard_Open(Snd_Card_Num card_num, Audio_Stream_Dir stream_dir, struct pcm_config *pcm_cfg);HAL_Status HAL_SndCard_Close(Snd_Card_Num card_num, Audio_Stream_Dir stream_dir);HAL_Status HAL_SndCard_SetVolume(Snd_Card_Num card_num, Audio_Device dev, uint16_t volume);--- xradio-skylark-sdk\src\driver\chip\hal_snd_card.c 2021-03-17 09:47:02.000000000 +0800
+++ xradio-skylark-sdk\src\driver\chip\hal_snd_card.c 2021-03-16 20:25:11.000000000 +0800
@@ -891,17 +891,27 @@HAL_Status HAL_SndCard_CodecUnregisterAc101(void){return ac101_codec_unregister();}+HAL_Status HAL_SndCard_CodecRegisterSample(void)
+{
+ return sample_codec_register();
+}
+
+HAL_Status HAL_SndCard_CodecUnregisterSample(void)
+{
+ return sample_codec_unregister();
+}
+HAL_Status HAL_SndCard_PlatformRegisterI2S(void){return xradio_i2s_register();}HAL_Status HAL_SndCard_PlatformUnregisterI2S(void){return xradio_i2s_unregister();}
board.c 主要是根据 PRJCONF_SAMPLE_SOUNDCARD_EN 开关来决定是否将新的 sample 声卡注册到系统中(通过上面新增的接口来注册和释放)。
--- xradio-skylark-sdk\project\common\board\board.c 2021-03-17 09:46:53.000000000 +0800
+++ xradio-skylark-sdk\project\common\board\board.c 2021-03-16 14:06:19.000000000 +0800
@@ -80,13 +80,15 @@HAL_SndCard_CodecRegisterAc107();#endif#if PRJCONF_AC101_SOUNDCARD_ENHAL_SndCard_CodecRegisterAc101();#endif//Add other codec register here
-
+#if PRJCONF_SAMPLE_SOUNDCARD_EN
+ HAL_SndCard_CodecRegisterSample();
+#endif/* Platform register */#if PRJCONF_PLATFORM_I2S_ENHAL_SndCard_PlatformRegisterI2S();#endif//Add other platform register here@@ -115,13 +117,15 @@HAL_SndCard_CodecUnregisterAc107();#endif#if PRJCONF_AC101_SOUNDCARD_ENHAL_SndCard_CodecUnregisterAc101();#endif//Add other codec unregister here
-
+#if PRJCONF_SAMPLE_SOUNDCARD_EN
+ HAL_SndCard_CodecUnregisterSample();
+#endifreturn HAL_OK;}#endif/* mmc card */#if PRJCONF_MMC_EN
prj_conf_opt.h 是所有工程的公共配置文件,外置的 Codec 芯片基本是需要通过 I2S 进行音频数据传输,因此这里需要增加 PRJCONF_SAMPLE_SOUNDCARD_EN 开关判断,如果该宏在用户工程中被打开了,就将关联的 I2S 平台驱动的宏开关 PRJCONF_PLATFORM_I2S_EN 也同时打开,该宏打开之后,就会在 board.c 中将 I2S 平台驱动也注册到系统中。
也可以不必修改这里,是否要使用 I2S 平台驱动,可以在应用程序的 prj_config.h 中手动将 PRJCONF_PLATFORM_I2S_EN 打开,即可将使用 I2S 平台驱动。
--- xradio-skylark-sdk\project\common\prj_conf_opt.h 2021-03-17 09:46:53.000000000 +0800
+++ xradio-skylark-sdk\project\common\prj_conf_opt.h 2021-03-16 14:06:26.000000000 +0800
@@ -222,13 +222,13 @@/* swd enable/disable */#ifndef PRJCONF_SWD_EN#define PRJCONF_SWD_EN 0#endif-#if PRJCONF_AC107_SOUNDCARD_EN || PRJCONF_AC101_SOUNDCARD_EN || PRJCONF_I2S_NULL_SOUNDCARD_EN
+#if PRJCONF_AC107_SOUNDCARD_EN || PRJCONF_AC101_SOUNDCARD_EN || PRJCONF_SAMPLE_SOUNDCARD_EN || PRJCONF_I2S_NULL_SOUNDCARD_EN#define PRJCONF_PLATFORM_I2S_EN 1#else#define PRJCONF_PLATFORM_I2S_EN 0#endif
三、在应用程序中启用该声卡
启用 PRJCONF_SAMPLE_SOUNDCARD_EN 开关,即可完成 sample 声卡的接入,同时启用 PRJCONF_MMC_EN 开关,便于将录音的数据保存到 TF 卡中。
--- xradio-skylark-sdk\project\zhc_application\zhc_answer_demo\prj_config.h 2021-03-17 09:47:37.000000000 +0800
+++ xradio-skylark-sdk\project\zhc_application\zhc_answer_demo\prj_config.h 2021-03-16 14:08:07.000000000 +0800
@@ -107,22 +107,25 @@#define PRJCONF_CE_EN 1/* spi enable/disable */#define PRJCONF_SPI_EN 0/* mmc enable/disable */
-#define PRJCONF_MMC_EN 0
+#define PRJCONF_MMC_EN 1/* mmc detect mode */#define PRJCONF_MMC_DETECT_MODE CARD_ALWAYS_PRESENT/* Xradio internal codec sound card enable/disable */#define PRJCONF_INTERNAL_SOUNDCARD_EN 0/* AC107 sound card enable/disable */#define PRJCONF_AC107_SOUNDCARD_EN 0
+
+/* SAMPLE sound card enable/disable */
+#define PRJCONF_SAMPLE_SOUNDCARD_EN 1/** project service feature*//* console enable/disable */
board_config.c 这里的修改主要是关联 Codec 驱动 和 platform 驱动 ,即指明哪一个 Codec 芯片和哪一个 I2S 接口使用什么数据格式,以及主从关系进行音频数据传输。
--- xradio-skylark-sdk\project\zhc_application\zhc_answer_demo\board_t1\board_config.c 2021-03-17 09:47:37.000000000 +0800
+++ xradio-skylark-sdk\project\zhc_application\zhc_answer_demo\board_t1\board_config.c 2021-03-16 14:38:42.000000000 +0800
@@ -322,12 +322,28 @@.codec_pllclk_src = 0,.codec_pll_freq_in = 0,.i2s_fmt = DAIFMT_CBS_CFS | DAIFMT_I2S | DAIFMT_NB_NF,};#endif+#if PRJCONF_SAMPLE_SOUNDCARD_EN
+__xip_rodata const static struct snd_card_board_config sample_codec_snd_card = {
+ .card_num = SND_CARD_4,
+ .card_name = HAL_SND_CARD_NAME(SAMPLE_CODEC_NAME, SND_CARD_SUFFIX),
+ .codec_link = XRADIO_CODEC_SAMPLE,
+ .platform_link = XRADIO_PLATFORM_I2S,
+
+ .pa_switch_ctl = NULL,
+
+ .codec_sysclk_src = SYSCLK_SRC_MCLK,
+ .codec_pllclk_src = 0,
+ .codec_pll_freq_in = 0,
+ .i2s_fmt = DAIFMT_CBS_CFS | DAIFMT_I2S | DAIFMT_NB_NF,
+};
+#endif
+#if PRJCONF_I2S_NULL_SOUNDCARD_EN__xip_rodata const static struct snd_card_board_config xradio_i2s_null_snd_card = {.card_num = SND_CARD_3,.card_name = HAL_SND_CARD_NAME(XRADIO_CODEC_NULL_NAME, SND_CARD_SUFFIX),.codec_link = XRADIO_CODEC_NULL,.platform_link = XRADIO_PLATFORM_I2S,
@@ -349,12 +365,16 @@#if PRJCONF_AC107_SOUNDCARD_EN&ac107_codec_snd_card,#endif#if PRJCONF_AC101_SOUNDCARD_EN&ac101_codec_snd_card,
#endif
+
+#if PRJCONF_SAMPLE_SOUNDCARD_EN
+ &sample_codec_snd_card,
+#endif#if PRJCONF_I2S_NULL_SOUNDCARD_EN&xradio_i2s_null_snd_card,#endif};
四、应用程序实例
参考:xradio-skylark-sdk\project\example\audio_record\main.c
/** Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved.** Redistribution and use in source and binary forms, with or without* modification, are permitted provided that the following conditions* are met:* 1. Redistributions of source code must retain the above copyright* notice, this list of conditions and the following disclaimer.* 2. Redistributions in binary form must reproduce the above copyright* notice, this list of conditions and the following disclaimer in the* documentation and/or other materials provided with the* distribution.* 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of* its contributors may be used to endorse or promote products derived* from this software without specific prior written permission.** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/#include <stdio.h>
#include <string.h>#include "common/framework/platform_init.h"
#include "kernel/os/os_time.h"
#include "fs/fatfs/ff.h"
#include "common/framework/fs_ctrl.h"
#include "audio/pcm/audio_pcm.h"
#include "audio/manager/audio_manager.h"#define SAVE_RECORD_DATA_DURATION_MS 15000static void audio_driver_record(void)
{int ret;FIL file;void *data;unsigned int len;unsigned int writeLen;struct pcm_config config;unsigned int tick;unsigned int startTime;unsigned int nowTime;f_unlink("record/audio.pcm");f_open(&file, "record/audio.pcm", FA_CREATE_ALWAYS | FA_READ | FA_WRITE);config.channels = 1;config.format = PCM_FORMAT_S16_LE;config.period_count = 2;config.period_size = 1024;config.rate = 8000;ret = snd_pcm_open(SND_CARD_4, PCM_IN, &config);if (ret) {printf("snd_pcm_open fail.\n");goto err;}len = config.channels * config.period_count * config.period_size;data = malloc(len);if (data == NULL) {goto err1;}tick = OS_GetTicks();startTime = OS_TicksToMSecs(tick);printf("===start record pcm by audio driver, last for %dms===\n", SAVE_RECORD_DATA_DURATION_MS);while (1) {ret = snd_pcm_read(SND_CARD_4, data, len);if (ret != len) {printf("snd_pcm_read fail.\n");}f_write(&file, data, len, &writeLen);tick = OS_GetTicks();nowTime = OS_TicksToMSecs(tick);if ((nowTime - startTime) > SAVE_RECORD_DATA_DURATION_MS) {break;}}printf("record pcm over.\n");free(data);err1:snd_pcm_close(SND_CARD_4, PCM_IN);
err:f_close(&file);return;
}int main(void)
{platform_init();if (fs_ctrl_mount(FS_MNT_DEV_TYPE_SDCARD, 0) != 0) {printf("mount fail\n");return -1;}printf("audio record start.\n");/* set record volume */audio_manager_handler(SND_CARD_4, AUDIO_MANAGER_SET_VOLUME_LEVEL, AUDIO_IN_DEV_AMIC, 3);printf("start to use audio driver to record pcm\n");audio_driver_record();printf("audio record over.\n");return 0;
}
五、调试 Codec 芯片的方法
一、准备资料
- 芯片规格书和芯片数据手册
- 芯片的硬件参考设计原理图
- 芯片的参考驱动代码,不限平台
二、电路确认
- 确认基本的硬件电路连接是否正确(参考芯片的参考设计原理图)
- 确认供电和复位是否需要控制,确保供电电压和上电时序正常(如果有时序要求的话)
三、控制通讯
- 确认 I2C 芯片地址,通常 I2C 器件都可以通过相关引脚拉高拉低来配置 I2C 地址,所以需要阅读芯片规格书和结合实际电路设计确认。
- 确认 I2C 通讯波形,通过示波器观察 I2C 波形的幅度(可能低电平不够低或者高电平不够高)、形状(可能会出现类似三角波的情况)是否正常,可以通过调整上拉电阻改善。
- 确认 I2C 初始化正常,如读取芯片 ID,或通过逻辑分析仪抓取 I2C 的读写数据,对比初始化代码,确认初始化的寄存器和值符合芯片要求。(通常较为完善的I2C控制器驱动程序,在I2C通信异常时都会有相关打印信息输出)
四、数据通信
- 确认数据传输格式和主从关系,确保 Codec 芯片和 SOC 的 I2S 保持一致。
- 测量 MCLK(若需要)、LRCLK、BCLK 时钟频率、占空比是否正确,波形是否正常。(例如 I2S 和 TDM 的时序波形不一样)
- 播放测试,可以通过播放指定的音频文件,与逻辑分析仪中抓取 I2S 的数据对比,如果完全一致则认为通讯正常。
- 录音测试,确认 Codec 芯片是否支持输出固定数据,如果不支持可以通过电脑的 3.5 mm 接口替代麦克风(AMIC),将所录的音频文件和音源文件通过 Adobe Audition 对比频谱确认。
五、音频质量
- 播放测试,播放 1KHz 和扫频信号,在 Codec 的输出端(功放前)使用示波器测量,与播放的频率对比进行确认是否一致。
- 录音测试,在安静的环境下进行录音,以及在安静环境下播放扫频信号,通过 Adobe Audition 分析频谱确认前者底噪是否干净和后者谐波是否严重。
- 录音时延测试,通过录音外部喇叭长时间播放有间歇性声音的音频(如 10 小时音频文件),完成后对比音源文件,观察所录的音频在最后几分钟的间歇性声音相差多少。
六、功耗确认
主要是关注 Codec 停止工作的时候,该芯片工作电流会有多少,是否和芯片规格书相符,是否符合项目需求。
不符合的话,需要检查驱动,是否有按照要求设置某些寄存器或控制某些GPIO电平状态。
【随笔记】XR872 Codec 驱动移植和应用程序实例(附芯片调试方法)相关推荐
- AM335x(TQ335x)学习笔记——GPIO关键驱动移植
或按照S5PV210学习秩序.我们首先解决的关键问题.TQ335x有六个用户按钮,每个上.下.剩下.对.Enter和ESC. 我想开始学习S5PV210当同一,写输入子系统驱动器的关键问题要解决,但浏 ...
- AM335x(TQ335x)学习笔记——Nand网卡驱动移植
移植完成声卡驱动之后本想再接再励,移植网卡驱动,但没想到的是TI维护的内核太健壮,移植网卡驱动跟之前移植按键驱动一样简单,Nand驱动也是如此,于是,本人将Nand和网卡放在同一篇文章中介绍.介绍之前 ...
- OpenCV将现有算法移植到G-API的实例(附完整代码)
OpenCV将现有算法移植到G-API的实例 OpenCV将现有算法移植到G-API的实例 OpenCV将现有算法移植到G-API的实例 #include "opencv2/opencv_m ...
- linux声卡驱动arm,AM335x(TQ335x)学习笔记——WM8960声卡驱动移植
Step3. 调整WM8960驱动结构 内核中自带的WM8960驱动结构很旧,编写Machine是需要过多的了解Codec芯片内部细节,本文对WM8960的驱动结构进行了调整,可以使Machine忽略 ...
- android+wifi驱动移植,全志平台ap6476 wifi模组调试(2)驱动移植 配置文件修改
1. 前言 基于上篇的环境,继续修改: 这里主要是修改驱动模块和配置文件 2. driver修改 当前broadcom系统的ap6xxx模组,wifi是共用同一份驱动,增加同系列的一款wifi的支持, ...
- STM32F1网络编程-W5500网卡驱动移植
W5500网卡驱动移植 1.W5500介绍 W5500 芯片是硬连线 TCP/IP 嵌入式以太网控制器,可提供与嵌入式系统的更轻松的 Internet 连接. W5500 使用户只需使用嵌入了 TCP ...
- I.MX6Q(TQIMX6Q/TQE9)学习笔记——新版BSP之声卡驱动移植
经过前面的移植,tqimx6q已经可以正常驱动触摸屏了,本文我们来移植声卡驱动. DTS编写 由于tqimx6q搭载的声卡是sgtl5000芯片,因此,参考dts目录下其它开发板的相应信息,我们可以在 ...
- AM335x(TQ335x)学习笔记——Nandamp;amp;网卡驱动移植
移植完成声卡驱动之后本想再接再励,移植网卡驱动,但没想到的是TI维护的内核太健壮,移植网卡驱动跟之前移植按键驱动一样简单,Nand驱动也是如此,于是,本人将Nand和网卡放在同一篇文章中介绍.介绍之前 ...
- RT3070驱动移植笔记 海思3515
今天将rt3070wifi模块安装到海思3515开发板上,需要移植驱动程序.下面就移植过程做一下笔记 首先下载源码,可以到我的资源中下载http://download.csdn.net/detail/ ...
最新文章
- modin pandas 加速
- cocos2d 从v1.x升级到v2.x需要注意的几个地方
- js插件---IUpload文件上传插件(包括图片)
- SQL2005数据导入错误:0xc00470fe 产品级别对于..
- 教你如何在面试中用「10分钟快速分析」一款产品
- html页面渲染vue组件,Vue组件页面渲染的基本流程
- (转)扩展KMP算法模板
- php mktime 时间不对_PHP 语言需要避免的 10 大误区
- 软件测试(功能、接口、性能、自动化)详解
- 计算机设备硬件维护税收编码,自动化设备的税收编码是多少
- JAVA文件传输原理及介绍—狂神说
- ad中电容用什么封装_电容器是怎么工作的?它在电路中究竟起什么作用?
- 梁宁-产品思维30讲-痛点、爽点和痒点都是产品机会
- JS文本中间显示省略号
- java蓝桥杯练习 学做菜
- 半监督异常检测(Anomaly Detection)的研究线
- Android 系统开机logo的修改
- 中国财团买得了 Opera,买不了仙童半导体?
- 干货 | 飞凌嵌入式OKT507-C开发板如何在Android系统上进行OTA升级
- 读书笔记---《编写可读代码的艺术》