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 参数相关(主要是通信方式和数据传输格式等等)。

  1. /**
  2. ******************************************************************************
  3. * @文件 sample_codec.c
  4. * @版本 V1.0.0
  5. * @日期
  6. * @概要 xr872 sample codec
  7. * @作者 lmx
  8. ******************************************************************************
  9. * @注意
  10. *
  11. ******************************************************************************
  12. */
  13. #include <stdbool.h>
  14. #include "audio_arch.h"
  15. #include "driver/chip/hal_i2c.h"
  16. #include "sample.h"
  17. /*********************************************************************************
  18. 功能: 配置 codec 的时钟源、频率、采样率
  19. 说明:
  20. 1.时钟源和频率通过 board_config.c -> struct snd_card_board_config 配置
  21. 2.采样率通过用户层的 struct pcm_config 指定
  22. 3.两者在用户调用 snd_pcm_open()->HAL_SndCard_Open() 时触发此接口
  23. *********************************************************************************/
  24. 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)
  25. {
  26. switch(sysclk_src)
  27. {
  28. case SYSCLK_SRC_OSC: // 设置晶振为系统时钟源
  29. break;
  30. case SYSCLK_SRC_MCLK: // 设置 MCLK 为系统时钟源
  31. break;
  32. case SYSCLK_SRC_BCLK: // 设置 BCLK 为系统时钟源
  33. break;
  34. case SYSCLK_SRC_PLL: // 设置 PLL 为系统时钟源
  35. break;
  36. default: // 无效的配置
  37. return HAL_INVALID;
  38. }
  39. return HAL_OK;
  40. }
  41. /*********************************************************************************
  42. 功能: 配置 codec 的主从关系、数据传输格式、时钟极性
  43. 说明:
  44. 1.通过 board_config.c -> struct snd_card_board_config 配置
  45. 2.如果有多颗 codec 只能配置其中一颗 Master 其它的需要为 Slave
  46. 3.用户调用 snd_pcm_open()->HAL_SndCard_Open() 时触发此接口
  47. *********************************************************************************/
  48. static int sample_dai_set_fmt(uint32_t fmt)
  49. {
  50. // 配置 codec I2S 接口的主从关系
  51. switch (fmt & I2S_ROLE_MASK)
  52. {
  53. case DAIFMT_CBM_CFM: // 设置 Codec 为 I2S Master : BCLK、LRCK output
  54. break;
  55. case DAIFMT_CBS_CFS: // 设置 Codec 为 I2S Slave : BCLK、LRCK output
  56. break;
  57. default:
  58. return HAL_INVALID;
  59. }
  60. // 配置 I2S 的数据格式
  61. switch (fmt & I2S_FORMAT_MASK)
  62. {
  63. case DAIFMT_I2S: // 设置为 I2S 格式
  64. break;
  65. case DAIFMT_RIGHT_J: // 设置为 I2S 右对齐格式
  66. break;
  67. case DAIFMT_LEFT_J: // 设置为 I2S 左对齐格式
  68. break;
  69. case DAIFMT_DSP_A: // 设置为 PCM-A 格式
  70. break;
  71. case DAIFMT_DSP_B: // 设置为 PCM-B 格式
  72. break;
  73. default: // 格式错误
  74. return HAL_INVALID;
  75. }
  76. // 配置时钟的极性
  77. switch (fmt & I2S_POLARITY_MASK)
  78. {
  79. case DAIFMT_NB_NF: // BCLK: 正常极性 LRCLK: 正常极性
  80. break;
  81. case DAIFMT_NB_IF: // BCLK: 正常极性 LRCLK: 反相极性
  82. break;
  83. case DAIFMT_IB_NF: // BCLK: 反相极性 LRCLK: 正常极性
  84. break;
  85. case DAIFMT_IB_IF: // BCLK: 反相极性 LRCLK: 反相极性
  86. break;
  87. default: // 格式错误
  88. return HAL_INVALID;
  89. }
  90. return HAL_OK;
  91. }
  92. /*********************************************************************************
  93. 功能: 配置增益或者音量
  94. 说明: 由用户通过 audio_manager_handler() 时触发此接口
  95. *********************************************************************************/
  96. static int sample_dai_set_volume(Audio_Device device, uint16_t volume)
  97. {
  98. // volume & VOLUME_SET_MASK == 1 : 设置的是 Gain, 反之设置的是 Level
  99. // volume & ~VOLUME_SET_MASK : 设置具体的值
  100. switch(device)
  101. {
  102. case AUDIO_IN_DEV_AMIC: // 设置模拟麦的音量
  103. break;
  104. case AUDIO_IN_DEV_DMIC: // 设置数字买的音量
  105. break;
  106. default: // 参数无效
  107. return HAL_INVALID;
  108. }
  109. return HAL_OK;
  110. }
  111. /*********************************************************************************
  112. 功能: 设置音频路径, 有些芯片支持数字麦克风也支持模拟麦克风, 可以通过此接口进行切换
  113. 说明: 由用户通过 audio_manager_handler() 时触发此接口
  114. *********************************************************************************/
  115. static int sample_dai_set_route(Audio_Device device, Audio_Dev_State state)
  116. {
  117. ES7210_DBG("--->%s\n",__FUNCTION__);
  118. // 设置为录音
  119. if(device & AUDIO_IN_DEV_ALL)
  120. {
  121. switch(device)
  122. {
  123. case AUDIO_IN_DEV_AMIC:
  124. break;
  125. case AUDIO_IN_DEV_DMIC:
  126. break;
  127. case AUDIO_IN_DEV_LINEIN:
  128. default:
  129. return HAL_INVALID;
  130. }
  131. }
  132. else // 设置为播放
  133. {
  134. switch(device)
  135. {
  136. case AUDIO_OUT_DEV_HP:
  137. break;
  138. case AUDIO_OUT_DEV_SPK:
  139. break;
  140. default:
  141. return HAL_INVALID;
  142. }
  143. }
  144. return HAL_OK;
  145. }
  146. /*********************************************************************************
  147. 功能: 配置硬件参数: 播放或者录音, 通道数, 采样率, 数据位宽
  148. 说明: 用户调用 snd_pcm_open()->HAL_SndCard_Open() 时触发此接口
  149. *********************************************************************************/
  150. static int sample_dai_hw_params(Audio_Stream_Dir dir, struct pcm_config *pcm_cfg)
  151. {
  152. // common init
  153. // set sample rate: pcm_cfg->rate
  154. // set channels
  155. // set sample resorution and slot width
  156. if(dir == PCM_OUT){
  157. // TX enable, Globle enable
  158. }
  159. else{
  160. // RX enable, Globle enable
  161. }
  162. return HAL_OK;
  163. }
  164. /*********************************************************************************
  165. 功能: 释放硬件参数
  166. 说明: 用户调用 snd_pcm_close()->HAL_SndCard_Close() 时触发此接口
  167. *********************************************************************************/
  168. static int sample_dai_hw_free(Audio_Stream_Dir dir)
  169. {
  170. ES7210_DBG("--->%s\n",__FUNCTION__);
  171. // globle disable
  172. // soft reset
  173. return HAL_OK;
  174. }
  175. /*********************************************************************************
  176. 功能: 控制指令
  177. 说明:
  178. 1.CODEC_IOCTL_PCM_READ\CODEC_IOCTL_PCM_WRITE 作为读写音频数据的补充接口,
  179. 有些 Codec 不需走外部 I2S 接口, 如 SOC 内部 codec 是通过 SOC 内部总线传输数据,
  180. 因此可以在这里实现音频数据的读取和写入功能, 设置 XRADIO_PLATFORM_NULL 即可不,
  181. 需要平台的接口读写音频数据, 就能被 snd_pcm_read()\snd_pcm_write() 触发回调.
  182. 2.CODEC_IOCTL_HW_CONFIG\CODEC_IOCTL_SW_CONFIG 作为配置的补充接口,满足差异需求,
  183. 通过 audio_maneger_ioctl() 接口触发此接口.
  184. *********************************************************************************/
  185. static int sample_codec_ioctl(uint32_t cmd, uint32_t cmd_param[], uint32_t cmd_param_len)
  186. {
  187. switch(cmd){
  188. case CODEC_IOCTL_HW_CONFIG: // 硬件配置
  189. break;
  190. case CODEC_IOCTL_SW_CONFIG: // 软件配置
  191. break;
  192. case CODEC_IOCTL_PCM_READ: // 读取音频数据
  193. break;
  194. case CODEC_IOCTL_PCM_WRITE: // 写入音频数据
  195. break;
  196. default:
  197. return HAL_INVALID;
  198. }
  199. return HAL_OK;
  200. }
  201. /*********************************************************************************
  202. 功能: 用于读取指定寄存器的值
  203. 说明:
  204. 1. 用户调用 audio_manager_reg_read() 时触发此接口
  205. 2. 主要是用于控制台命令读取寄存器
  206. *********************************************************************************/
  207. static int sample_codec_reg_read(uint32_t reg)
  208. {
  209. return 0x00;
  210. }
  211. /*********************************************************************************
  212. 功能: 用于写入指定的值到指定寄存器
  213. 说明:
  214. 1. 用户调用 audio_manager_reg_write() 时触发此接口
  215. 2. 主要是用于控制台命令写入寄存器
  216. *********************************************************************************/
  217. static int sample_codec_reg_write(uint32_t reg, uint32_t val)
  218. {
  219. return HAL_OK;
  220. }
  221. /*********************************************************************************
  222. 功能: 打开 codec
  223. 说明: 设置完所有配置之后, 会由 snd_pcm_open() 触发, 在这个位置可以启动 codec 工作
  224. *********************************************************************************/
  225. static int sample_codec_open(Audio_Stream_Dir dir)
  226. {
  227. return HAL_OK;
  228. }
  229. /*********************************************************************************
  230. 功能: 关闭 codec
  231. 说明: 当用于调用 snd_pcm_close() 触发, 在这个位置可以停止 codec 工作
  232. *********************************************************************************/
  233. static int sample_codec_close(Audio_Stream_Dir dir)
  234. {
  235. return HAL_OK;
  236. }
  237. /*********************************************************************************
  238. 功能: 初始化 codec
  239. 说明: 当加载驱动程序是会被 HAL_SndCard_Register() 调用
  240. *********************************************************************************/
  241. static int sample_codec_init(void)
  242. {
  243. ES7210_DBG("--->%s\n",__FUNCTION__);
  244. return HAL_OK;
  245. }
  246. /*********************************************************************************
  247. 功能: 释放 codec
  248. 说明: 当卸载驱动程序是会被 HAL_SndCard_Unregister() 调用
  249. *********************************************************************************/
  250. static void sample_codec_deinit(void)
  251. {
  252. ES7210_DBG("--->%s\n",__FUNCTION__);
  253. }
  254. // SOC 的数字接口(I2S)相关的回调, 配置信息需要双方一致
  255. static const struct codec_dai_ops sample_codec_dai_ops = {
  256. .set_sysclk = sample_dai_set_sysclk,
  257. .set_fmt = sample_dai_set_fmt,
  258. .set_volume = sample_dai_set_volume,
  259. .set_route = sample_dai_set_route,
  260. .hw_params = sample_dai_hw_params,
  261. .hw_free = sample_dai_hw_free,
  262. };
  263. // codec 相关的回调接口
  264. static const struct codec_ops sample_codec_ops = {
  265. .open = sample_codec_open,
  266. .close = sample_codec_close,
  267. .reg_read = sample_codec_reg_read,
  268. .reg_write = sample_codec_reg_write,
  269. .ioctl = sample_codec_ioctl,
  270. };
  271. // codec 驱动程序接口
  272. static struct codec_driver sample_codec_drv = {
  273. .name = ES7210_CODEC_NAME, // codec 名称
  274. .codec_attr = XRADIO_CODEC_ES7210, // 用于标识 codec , 便于与 I2S 关联
  275. .init = sample_codec_init, // 驱动加载时的初始化接口
  276. .deinit = sample_codec_deinit, // 驱动卸载时的释放接口
  277. .dai_ops = &sample_codec_dai_ops, // 配置数字接口时的回调接口(主要作用是保证数字接口和Codec的配置保持一致)
  278. .codec_ops = &sample_codec_ops, // 配置 codec 时的回调接口
  279. };
  280. /*********************************************************************************
  281. 功能: 注册 codec
  282. 说明: 由 board.c board_soundcard_init()->hal_codec.c HAL_SndCard_CodecRegisterSample1()->sample_codec_register 调用
  283. *********************************************************************************/
  284. HAL_Status sample_codec_register(void)
  285. {
  286. // 配置 I2C 总线
  287. // 通过 I2C 寻找芯片, 读取芯片 ID 确认
  288. // 调用下面的代码将此 codec 挂接在 hal_snd_codec_list 链表
  289. list_add(&sample_codec_drv.node, &hal_snd_codec_list);
  290. return HAL_OK;
  291. }
  292. /*********************************************************************************
  293. 功能: 注册 codec
  294. 说明: 由 board.c board_soundcard_deinit()->hal_codec.c HAL_SndCard_CodecUnregisterSample1()->sample_codec_unregister 调用
  295. *********************************************************************************/
  296. HAL_Status sample_codec_unregister(void)
  297. {
  298. struct codec_driver *codec_drv_ptr;
  299. // 先检查该 codec 是否在链表内
  300. if(list_empty(&hal_snd_codec_list)){
  301. return HAL_OK;
  302. }
  303. // 从 hal_snd_codec_list 移除此 codec
  304. list_for_each_entry(codec_drv_ptr, &hal_snd_codec_list, node){
  305. if(codec_drv_ptr == &sample_codec_drv){
  306. list_del(&sample_codec_drv.node);
  307. break;
  308. }
  309. }
  310. return HAL_OK;
  311. }

二、将驱动程序添加到 SDK 中

audio_arch.h 和 hal_snd_card.h 主要是增加支持新的 sample 声卡注册和卸载接口

  1. --- xradio-skylark-sdk\src\driver\chip\codec\audio_arch.h 2021-03-17 09:47:37.000000000 +0800
  2. +++ xradio-skylark-sdk\src\driver\chip\codec\audio_arch.h 2021-03-16 14:08:17.000000000 +0800
  3. @@ -175,12 +175,15 @@
  4. HAL_Status ac107_pdm_set_volume_level(Audio_Device device, uint16_t volume);
  5. HAL_Status ac107_pdm_set_volume_gain(Audio_Device device, uint16_t volume);
  6. HAL_Status ac101_codec_register(void);
  7. HAL_Status ac101_codec_unregister(void);
  8. +HAL_Status sample_codec_register(void);
  9. +HAL_Status sample_codec_unregister(void);
  10. +
  11. HAL_Status xradio_i2s_register(void);
  12. HAL_Status xradio_i2s_unregister(void);
  13. /*****************************************************************************************/
  1. --- xradio-skylark-sdk\include\driver\chip\hal_snd_card.h 2021-03-17 09:46:58.000000000 +0800
  2. +++ xradio-skylark-sdk\include\driver\chip\hal_snd_card.h 2021-03-16 14:06:03.000000000 +0800
  3. @@ -47,12 +47,13 @@
  4. /* Codec name */
  5. #define XRADIO_CODEC_NULL_NAME "xradio_codec_null"
  6. #define XRADIO_INTERNAL_CODEC_NAME "xradio_internal_codec"
  7. #define AC107_CODEC_NAME "ac107_codec"
  8. #define AC101_CODEC_NAME "ac101_codec"
  9. +#define SAMPLE_CODEC_NAME "sample_codec"
  10. /* Platform name */
  11. #define XRADIO_PLATFORM_I2S_NAME "xradio_platform_i2s"
  12. /* Snd card suffix name */
  13. #define SND_CARD_SUFFIX "_sound_card"
  14. @@ -144,12 +145,13 @@
  15. /* codec driver select */
  16. typedef enum {
  17. XRADIO_CODEC_NULL,
  18. XRADIO_CODEC_INTERNAL,
  19. XRADIO_CODEC_AC107,
  20. XRADIO_CODEC_AC101,
  21. + XRADIO_CODEC_SAMPLE,
  22. }Codec_Attr;
  23. /* platform driver select */
  24. typedef enum {
  25. XRADIO_PLATFORM_NULL,
  26. XRADIO_PLATFORM_I2S,
  27. @@ -159,13 +161,14 @@
  28. /* Sound Card number */
  29. typedef enum {
  30. SND_CARD_0,
  31. SND_CARD_1,
  32. SND_CARD_2,
  33. SND_CARD_3,
  34. - SND_CARD_MAX = SND_CARD_3,
  35. + SND_CARD_4,
  36. + SND_CARD_MAX = SND_CARD_4,
  37. }Snd_Card_Num;
  38. /* Volume set flag */
  39. #define VOLUME_SET_LEVEL (0x0000)
  40. #define VOLUME_SET_GAIN (0x8000)
  41. @@ -427,12 +430,14 @@
  42. HAL_Status HAL_SndCard_CodecRegisterInternal(void);
  43. HAL_Status HAL_SndCard_CodecUnregisterInternal(void);
  44. HAL_Status HAL_SndCard_CodecRegisterAc107(void);
  45. HAL_Status HAL_SndCard_CodecUnregisterAc107(void);
  46. HAL_Status HAL_SndCard_CodecRegisterAc101(void);
  47. HAL_Status HAL_SndCard_CodecUnregisterAc101(void);
  48. +HAL_Status HAL_SndCard_CodecRegisterSample(void);
  49. +HAL_Status HAL_SndCard_CodecUnregisterSample(void);
  50. HAL_Status HAL_SndCard_PlatformRegisterI2S(void);
  51. HAL_Status HAL_SndCard_PlatformUnregisterI2S(void);
  52. HAL_Status HAL_SndCard_Open(Snd_Card_Num card_num, Audio_Stream_Dir stream_dir, struct pcm_config *pcm_cfg);
  53. HAL_Status HAL_SndCard_Close(Snd_Card_Num card_num, Audio_Stream_Dir stream_dir);
  54. HAL_Status HAL_SndCard_SetVolume(Snd_Card_Num card_num, Audio_Device dev, uint16_t volume);
  55. --- xradio-skylark-sdk\src\driver\chip\hal_snd_card.c 2021-03-17 09:47:02.000000000 +0800
  56. +++ xradio-skylark-sdk\src\driver\chip\hal_snd_card.c 2021-03-16 20:25:11.000000000 +0800
  57. @@ -891,17 +891,27 @@
  58. HAL_Status HAL_SndCard_CodecUnregisterAc101(void)
  59. {
  60. return ac101_codec_unregister();
  61. }
  62. +HAL_Status HAL_SndCard_CodecRegisterSample(void)
  63. +{
  64. + return sample_codec_register();
  65. +}
  66. +
  67. +HAL_Status HAL_SndCard_CodecUnregisterSample(void)
  68. +{
  69. + return sample_codec_unregister();
  70. +}
  71. +
  72. HAL_Status HAL_SndCard_PlatformRegisterI2S(void)
  73. {
  74. return xradio_i2s_register();
  75. }
  76. HAL_Status HAL_SndCard_PlatformUnregisterI2S(void)
  77. {
  78. return xradio_i2s_unregister();
  79. }

board.c 主要是根据 PRJCONF_SAMPLE_SOUNDCARD_EN 开关来决定是否将新的 sample 声卡注册到系统中(通过上面新增的接口来注册和释放)。

  1. --- xradio-skylark-sdk\project\common\board\board.c 2021-03-17 09:46:53.000000000 +0800
  2. +++ xradio-skylark-sdk\project\common\board\board.c 2021-03-16 14:06:19.000000000 +0800
  3. @@ -80,13 +80,15 @@
  4. HAL_SndCard_CodecRegisterAc107();
  5. #endif
  6. #if PRJCONF_AC101_SOUNDCARD_EN
  7. HAL_SndCard_CodecRegisterAc101();
  8. #endif
  9. //Add other codec register here
  10. -
  11. +#if PRJCONF_SAMPLE_SOUNDCARD_EN
  12. + HAL_SndCard_CodecRegisterSample();
  13. +#endif
  14. /* Platform register */
  15. #if PRJCONF_PLATFORM_I2S_EN
  16. HAL_SndCard_PlatformRegisterI2S();
  17. #endif
  18. //Add other platform register here
  19. @@ -115,13 +117,15 @@
  20. HAL_SndCard_CodecUnregisterAc107();
  21. #endif
  22. #if PRJCONF_AC101_SOUNDCARD_EN
  23. HAL_SndCard_CodecUnregisterAc101();
  24. #endif
  25. //Add other codec unregister here
  26. -
  27. +#if PRJCONF_SAMPLE_SOUNDCARD_EN
  28. + HAL_SndCard_CodecUnregisterSample();
  29. +#endif
  30. return HAL_OK;
  31. }
  32. #endif
  33. /* mmc card */
  34. #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 平台驱动。

  1. --- xradio-skylark-sdk\project\common\prj_conf_opt.h 2021-03-17 09:46:53.000000000 +0800
  2. +++ xradio-skylark-sdk\project\common\prj_conf_opt.h 2021-03-16 14:06:26.000000000 +0800
  3. @@ -222,13 +222,13 @@
  4. /* swd enable/disable */
  5. #ifndef PRJCONF_SWD_EN
  6. #define PRJCONF_SWD_EN 0
  7. #endif
  8. -#if PRJCONF_AC107_SOUNDCARD_EN || PRJCONF_AC101_SOUNDCARD_EN || PRJCONF_I2S_NULL_SOUNDCARD_EN
  9. +#if PRJCONF_AC107_SOUNDCARD_EN || PRJCONF_AC101_SOUNDCARD_EN || PRJCONF_SAMPLE_SOUNDCARD_EN || PRJCONF_I2S_NULL_SOUNDCARD_EN
  10. #define PRJCONF_PLATFORM_I2S_EN 1
  11. #else
  12. #define PRJCONF_PLATFORM_I2S_EN 0
  13. #endif

三、在应用程序中启用该声卡

启用 PRJCONF_SAMPLE_SOUNDCARD_EN 开关,即可完成 sample 声卡的接入,同时启用 PRJCONF_MMC_EN 开关,便于将录音的数据保存到 TF 卡中。

  1. --- xradio-skylark-sdk\project\zhc_application\zhc_answer_demo\prj_config.h 2021-03-17 09:47:37.000000000 +0800
  2. +++ xradio-skylark-sdk\project\zhc_application\zhc_answer_demo\prj_config.h 2021-03-16 14:08:07.000000000 +0800
  3. @@ -107,22 +107,25 @@
  4. #define PRJCONF_CE_EN 1
  5. /* spi enable/disable */
  6. #define PRJCONF_SPI_EN 0
  7. /* mmc enable/disable */
  8. -#define PRJCONF_MMC_EN 0
  9. +#define PRJCONF_MMC_EN 1
  10. /* mmc detect mode */
  11. #define PRJCONF_MMC_DETECT_MODE CARD_ALWAYS_PRESENT
  12. /* Xradio internal codec sound card enable/disable */
  13. #define PRJCONF_INTERNAL_SOUNDCARD_EN 0
  14. /* AC107 sound card enable/disable */
  15. #define PRJCONF_AC107_SOUNDCARD_EN 0
  16. +
  17. +/* SAMPLE sound card enable/disable */
  18. +#define PRJCONF_SAMPLE_SOUNDCARD_EN 1
  19. /*
  20. * project service feature
  21. */
  22. /* console enable/disable */

board_config.c 这里的修改主要是关联 Codec 驱动 和 platform 驱动 ,即指明哪一个 Codec 芯片和哪一个 I2S 接口使用什么数据格式,以及主从关系进行音频数据传输。

  1. --- xradio-skylark-sdk\project\zhc_application\zhc_answer_demo\board_t1\board_config.c 2021-03-17 09:47:37.000000000 +0800
  2. +++ xradio-skylark-sdk\project\zhc_application\zhc_answer_demo\board_t1\board_config.c 2021-03-16 14:38:42.000000000 +0800
  3. @@ -322,12 +322,28 @@
  4. .codec_pllclk_src = 0,
  5. .codec_pll_freq_in = 0,
  6. .i2s_fmt = DAIFMT_CBS_CFS | DAIFMT_I2S | DAIFMT_NB_NF,
  7. };
  8. #endif
  9. +#if PRJCONF_SAMPLE_SOUNDCARD_EN
  10. +__xip_rodata const static struct snd_card_board_config sample_codec_snd_card = {
  11. + .card_num = SND_CARD_4,
  12. + .card_name = HAL_SND_CARD_NAME(SAMPLE_CODEC_NAME, SND_CARD_SUFFIX),
  13. + .codec_link = XRADIO_CODEC_SAMPLE,
  14. + .platform_link = XRADIO_PLATFORM_I2S,
  15. +
  16. + .pa_switch_ctl = NULL,
  17. +
  18. + .codec_sysclk_src = SYSCLK_SRC_MCLK,
  19. + .codec_pllclk_src = 0,
  20. + .codec_pll_freq_in = 0,
  21. + .i2s_fmt = DAIFMT_CBS_CFS | DAIFMT_I2S | DAIFMT_NB_NF,
  22. +};
  23. +#endif
  24. +
  25. #if PRJCONF_I2S_NULL_SOUNDCARD_EN
  26. __xip_rodata const static struct snd_card_board_config xradio_i2s_null_snd_card = {
  27. .card_num = SND_CARD_3,
  28. .card_name = HAL_SND_CARD_NAME(XRADIO_CODEC_NULL_NAME, SND_CARD_SUFFIX),
  29. .codec_link = XRADIO_CODEC_NULL,
  30. .platform_link = XRADIO_PLATFORM_I2S,
  31. @@ -349,12 +365,16 @@
  32. #if PRJCONF_AC107_SOUNDCARD_EN
  33. &ac107_codec_snd_card,
  34. #endif
  35. #if PRJCONF_AC101_SOUNDCARD_EN
  36. &ac101_codec_snd_card,
  37. #endif
  38. +
  39. +#if PRJCONF_SAMPLE_SOUNDCARD_EN
  40. + &sample_codec_snd_card,
  41. +#endif
  42. #if PRJCONF_I2S_NULL_SOUNDCARD_EN
  43. &xradio_i2s_null_snd_card,
  44. #endif
  45. };

四、应用程序实例

参考:xradio-skylark-sdk\project\example\audio_record\main.c

  1. /*
  2. * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  7. * 1. Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * 2. Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the
  12. * distribution.
  13. * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of
  14. * its contributors may be used to endorse or promote products derived
  15. * from this software without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  18. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  19. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  20. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  21. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  22. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  23. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  24. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  25. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  27. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. */
  29. #include <stdio.h>
  30. #include <string.h>
  31. #include "common/framework/platform_init.h"
  32. #include "kernel/os/os_time.h"
  33. #include "fs/fatfs/ff.h"
  34. #include "common/framework/fs_ctrl.h"
  35. #include "audio/pcm/audio_pcm.h"
  36. #include "audio/manager/audio_manager.h"
  37. #define SAVE_RECORD_DATA_DURATION_MS 15000
  38. static void audio_driver_record(void)
  39. {
  40. int ret;
  41. FIL file;
  42. void *data;
  43. unsigned int len;
  44. unsigned int writeLen;
  45. struct pcm_config config;
  46. unsigned int tick;
  47. unsigned int startTime;
  48. unsigned int nowTime;
  49. f_unlink("record/audio.pcm");
  50. f_open(&file, "record/audio.pcm", FA_CREATE_ALWAYS | FA_READ | FA_WRITE);
  51. config.channels = 1;
  52. config.format = PCM_FORMAT_S16_LE;
  53. config.period_count = 2;
  54. config.period_size = 1024;
  55. config.rate = 8000;
  56. ret = snd_pcm_open(SND_CARD_4, PCM_IN, &config);
  57. if (ret) {
  58. printf("snd_pcm_open fail.\n");
  59. goto err;
  60. }
  61. len = config.channels * config.period_count * config.period_size;
  62. data = malloc(len);
  63. if (data == NULL) {
  64. goto err1;
  65. }
  66. tick = OS_GetTicks();
  67. startTime = OS_TicksToMSecs(tick);
  68. printf("===start record pcm by audio driver, last for %dms===\n", SAVE_RECORD_DATA_DURATION_MS);
  69. while (1) {
  70. ret = snd_pcm_read(SND_CARD_4, data, len);
  71. if (ret != len) {
  72. printf("snd_pcm_read fail.\n");
  73. }
  74. f_write(&file, data, len, &writeLen);
  75. tick = OS_GetTicks();
  76. nowTime = OS_TicksToMSecs(tick);
  77. if ((nowTime - startTime) > SAVE_RECORD_DATA_DURATION_MS) {
  78. break;
  79. }
  80. }
  81. printf("record pcm over.\n");
  82. free(data);
  83. err1:
  84. snd_pcm_close(SND_CARD_4, PCM_IN);
  85. err:
  86. f_close(&file);
  87. return;
  88. }
  89. int main(void)
  90. {
  91. platform_init();
  92. if (fs_ctrl_mount(FS_MNT_DEV_TYPE_SDCARD, 0) != 0) {
  93. printf("mount fail\n");
  94. return -1;
  95. }
  96. printf("audio record start.\n");
  97. /* set record volume */
  98. audio_manager_handler(SND_CARD_4, AUDIO_MANAGER_SET_VOLUME_LEVEL, AUDIO_IN_DEV_AMIC, 3);
  99. printf("start to use audio driver to record pcm\n");
  100. audio_driver_record();
  101. printf("audio record over.\n");
  102. return 0;
  103. }

五、调试 Codec 芯片的方法

一、准备资料

  1. 芯片规格书和芯片数据手册
  2. 芯片的硬件参考设计原理图
  3. 芯片的参考驱动代码,不限平台

二、电路确认

  1. 确认基本的硬件电路连接是否正确(参考芯片的参考设计原理图)
  2. 确认供电和复位是否需要控制,确保供电电压和上电时序正常(如果有时序要求的话)

三、控制通讯

  1. 确认 I2C 芯片地址,通常 I2C 器件都可以通过相关引脚拉高拉低来配置 I2C 地址,所以需要阅读芯片规格书和结合实际电路设计确认。
  2. 确认 I2C 通讯波形,通过示波器观察 I2C 波形的幅度(可能低电平不够低或者高电平不够高)、形状(可能会出现类似三角波的情况)是否正常,可以通过调整上拉电阻改善。
  3. 确认 I2C 初始化正常,如读取芯片 ID,或通过逻辑分析仪抓取 I2C 的读写数据,对比初始化代码,确认初始化的寄存器和值符合芯片要求。(通常较为完善的I2C控制器驱动程序,在I2C通信异常时都会有相关打印信息输出)

四、数据通信

  1. 确认数据传输格式和主从关系,确保 Codec 芯片和 SOC 的 I2S 保持一致。
  2. 测量 MCLK(若需要)、LRCLK、BCLK 时钟频率、占空比是否正确,波形是否正常。(例如 I2S 和 TDM 的时序波形不一样)
  3. 播放测试,可以通过播放指定的音频文件,与逻辑分析仪中抓取 I2S 的数据对比,如果完全一致则认为通讯正常。
  4. 录音测试,确认 Codec 芯片是否支持输出固定数据,如果不支持可以通过电脑的 3.5 mm 接口替代麦克风(AMIC),将所录的音频文件和音源文件通过 Adobe Audition 对比频谱确认。

五、音频质量

  1. 播放测试,播放 1KHz 和扫频信号,在 Codec 的输出端(功放前)使用示波器测量,与播放的频率对比进行确认是否一致。
  2. 录音测试,在安静的环境下进行录音,以及在安静环境下播放扫频信号,通过 Adobe Audition 分析频谱确认前者底噪是否干净和后者谐波是否严重。
  3. 录音时延测试,通过录音外部喇叭长时间播放有间歇性声音的音频(如 10 小时音频文件),完成后对比音源文件,观察所录的音频在最后几分钟的间歇性声音相差多少。

六、功耗确认

主要是关注 Codec 停止工作的时候,该芯片工作电流会有多少,是否和芯片规格书相符,是否符合项目需求。

不符合的话,需要检查驱动,是否有按照要求设置某些寄存器或控制某些GPIO电平状态。

【随笔记】XR872 Codec 驱动移植和应用程序实例(附芯片调试方法)的更多相关文章

  1. AM335x(TQ335x)学习笔记——GPIO关键驱动移植

    或按照S5PV210学习秩序.我们首先解决的关键问题.TQ335x有六个用户按钮,每个上.下.剩下.对.Enter和ESC. 我想开始学习S5PV210当同一,写输入子系统驱动器的关键问题要解决,但浏 ...

  2. AM335x(TQ335x)学习笔记——Nand&&网卡驱动移植

    移植完成声卡驱动之后本想再接再励,移植网卡驱动,但没想到的是TI维护的内核太健壮,移植网卡驱动跟之前移植按键驱动一样简单,Nand驱动也是如此,于是,本人将Nand和网卡放在同一篇文章中介绍.介绍之前 ...

  3. AM335x(TQ335x)学习笔记——WM8960声卡驱动移植

    经过一段时间的调试,终于调好了TQ335x的声卡驱动.TQ335x采用的Codec是WM8960,本文来总结下WM8960驱动在AM335x平台上的移植方法.Linux声卡驱动架构有OSS和ALSA两 ...

  4. AM335x(TQ335x)学习笔记——GPIO按键驱动移植

    还是按照S5PV210的学习顺序来,我们首先解决按键问题.TQ335x有六个用户按键,分别是上.下.左.右.Enter和ESC.开始我想到的是跟学习S5PV210时一样,编写输入子系统驱动解决按键问题 ...

  5. AM335x(TQ335x)学习笔记——Nand&amp;&amp;网卡驱动移植

    移植完毕声卡驱动之后本想再接再励,移植网卡驱动,但没想到的是TI维护的内核太健壮,移植网卡驱动跟之前移植按键驱动一样简单,Nand驱动也是如此,于是,本人将Nand和网卡放在同一篇文章中介绍.介绍之前 ...

  6. AM335x(TQ335x)学习笔记——LCD驱动移植

    TI的LCD控制器驱动是非常完善的,共通的地方已经由驱动封装好了,与按键一样,我们可以通过DTS配置完成LCD的显示.下面,我们来讨论下使用DTS方式配置内核完成LCD驱动的思路. (1)初步分析 由 ...

  7. DM9000驱动移植在mini2440(linux2.6.29)和FS4412(linux3.14.78)上的实现(deep dive)篇一

    关于dm9000的驱动移植分为两篇,第一篇在mini2440上实现,基于linux2.6.29,也成功在在6410上移植了一遍,和2440非常类似,第二篇在fs4412(Cortex A9)上实现,基 ...

  8. kernel 4.4.12 EETI eGTouch 电容屏驱动移植

    kernel 4.4.12 EETI eGTouch 电容屏驱动移植: 在make menuconfig 里面添加如下选项: 添加通过事件上报接口节点: Device Drivers ---> ...

  9. AM335x kernel 4.4.12 i2c eeprom AT24c02驱动移植

    kernel 4.4.12 i2c eeprom AT24c02驱动移植 在kernel make menuconfig ARCH=ARM 中打开: Device Drivers ---> Mi ...

  10. wifi 驱动移植范例

    .改Makefile:  里面没有dm6441平台的,我看到有dm6446的,所以就在这里改了 ifeq ($(PLATFORM),DM6446) LINUX_SRC = /root/work/lin ...

随机推荐

  1. Scrapy 如何传递 get请求的params

    我们都知道 在requests中可以使用 requests.get(url,params)的方式传值 那么在scrapy中如何传值呢 直接看代码 from urllib.parse import ur ...

  2. clang在编译时指定目标文件所需的最低macOS版本

    调研这个的原因,是因为有个同事在macOS 12.2上打包好的程序,放在macOS 10.15上运行时报错: Dyld Error Message:  Symbol not found: __ZNKS ...

  3. 【JAVA】详解在JAVA中int与Integer的区别以及背后的原因。

    区别 首先我们要明确,这两点之间有什么区别? 主要有以下几点: 数据类型不同:int是基础数据类型,而 Integer是包装数据类型: 默认值不同:int的默认值是 0,而 Integer的默认值是 ...

  4. 【题解】CF1715A Crossmarket

    题面传送门 解决思路 首先,我们让 Megan 先走,因为他可以留下传送门.可以得知,不管怎么走,他到达终点所耗费的能量一定是 \(n+m-2\) . 然后,Stanley 走时就可以利用传送门.考虑 ...

  5. tomcat 随windows启动

    有时候服务器会突然断电,维护管理员只会帮我们启动服务器,但是不会不会帮我们启动Tomcat. 1.进入tomcat的bin文件夹,找到startup.bat,右键生成快捷方式到桌面. 2.点击桌面左下 ...

  6. JVM面试点汇总

    JVM面试点汇总 我们会在这里介绍我所涉及到的JVM相关的面试点内容,本篇内容持续更新 我们会介绍下述JVM的相关面试点: JVM内存结构 内存溢出问题 方法区与永久代和元空间 JVM内存参数 JVM ...

  7. O-MVLL代码混淆方式

    在介绍O-MVLL之前,首先介绍什么是代码混淆以及基于LLVM的代码混淆,O-MVLL项目正是基于此而开发来的. 有关O-MVLL的概括介绍以及安装和基本使用方式,可参见另一篇随笔 https://w ...

  8. Java中将 int[] 数组 转换为 List(ArrayList)

    前言 说起数组转换成 ArrayList,很多同学第一反应就是遍历数组,将元素逐个添加到 ArrayList 中,但是这个看着就lower,一般不会这么答. 所以马上就会想到Arrays工具类的 as ...

  9. 现代 CSS 高阶技巧,完美的波浪进度条效果!

    本文是 CSS Houdini 之 CSS Painting API 系列第三篇. 现代 CSS 之高阶图片渐隐消失术 现代 CSS 高阶技巧,像 Canvas 一样自由绘图构建样式! 在上两篇中,我 ...

  10. redis集合 实现 队列

    先说一下需求:用队列解决 流量削峰,主要应用场景:商城秒杀功能. 以下是业务流程图可以参考一下: 然后本地实现思路 截图下单页面 每次购买数量会减少1,设置了1000个库存,用户id 是随机生成的. ...