【Ai-WB2中级篇】DAC数模转换

[复制链接]
查看1218 | 回复3 | 2024-9-2 17:05:19 | 显示全部楼层 |阅读模式
数模转换器(diaital-to-anal0g converter,通常称为DAC)是一种数字与模拟转换器,FIFO深度为1,支持2路DAC调制输出。可用于音频播放,常规的模拟信号调制。
本文将详细介绍如何使用Ai-WB2的DAC模块播放音频
一:DAC介绍
BL602芯片内置一个10bits的数字模拟转换Q器(DAC),FIFO深度为1,支持2路DAC调制输出。 可用于音频播放,变送器电压调制。其特性如下:
      · DAC调制精度为10-bits
      · DAC的输入时钟可选为32k、16k、8k或512k
      · 支持DMA将内存搬运至DAC调制寄存器
      · 支持双声道播放DMA搬运模式
      · DAC的输出引脚固定为ChannelA为GPI013,Channel为GPI014
DAC的基本功能框图如下:
1.png
      · DAC模块支持最多两路调制输出
      · DAC模块支持双声道DMA数据搬运模式
      · DAC模块支持长度为32-bit的DMA数据接口,其中高16位将会调制在ChannelA的引脚上,低16位调制在ChannelB引脚
二:DAC驱动API介绍
DAC的HOSAL层驱动API在文件 components/platform/hosal/include/hosal_dac.h 中定义,常用的AP!如下:
· int hosal_dac_init(hosal_dac_dev_t *dac):初始化DAC。参数说明如下:
      · dac:DAC设备实例。其定义如下:
  1.   typedef struct {
  2.       uint8_t  port;                    /**< @brief dac id */
  3.       hosal_dac_config_t config;        /**< @brief dac config */
  4.       hosal_dac_cb_t cb;                /**< @brief dma callback */
  5.       hosal_dma_chan_t dma_chan;        /**< @brief dac dma channel */
  6.       void    *arg;                     /**< @brief arg data */
  7.       void    *priv;                    /**< @brief priv data */
  8.   } hosal_dac_dev_t;
复制代码
其中,hosal_dac_config_t为DAC配置,其定义如下:
  1. typedef struct {
  2.     uint8_t  dma_enable;        /**< @brief 1: use dma, 0: no dma */
  3.     uint32_t  pin;              /**< @brief dac pin */
  4.     uint32_t  freq;             /**< @brief dac freq */
  5. } hosal_dac_config_t;
复制代码
hosal _dac_cb_t为DAC回调函数,定义如下:
  1. typedef void (*hosal_dac_cb_t)(void *arg)
复制代码
      · 返回值:成功时,返回0;否则返回非零值
· int hosal_dac_finalize(hosal_dac_dev_t *dac):销毁DAC实例并释放相关资源。参数说明如下:
      · dac:DAC设备实例。
      · 返回值:成功时,返回0;否则返回非零值
· int hosal_dac_start(hosal_dac_dev_t *dac):启动DAC。参数说明如下:
      · dac:DAC设备实例。
      · 返回值:成功时,返回0;否则返回非零值
· int hosal_dac_stop(hosal_dac_dev_t *dac):停止DAC。参数说明如下:
      · dac:DAC设备实例。
      · 返回值:成功时,返回0;否则返回非零值
· int hosal_dac_set_value(hosal_dac_dev_t *dac,uint32_t data):设置DAC输出电压,单位为UV。参数说明如下:
      · dac:DAC设备实例。
      · data:输出电压大小。单位为V。比如输出1V,则参数的值为1000000。返回值:成功时,返回0;否则返回非零值
· int hosal_dac_get_value(hosal_dac_dev_t *dac):査询最新的DAC端口输出电压值。单位为V。参数说明如下:
      · dac:DAC设备实例。
      · 返回值:返回最新电压值
· int hosal_dac_dma_cb_reg(hosal_dac_dev_t *dac, hosal_dac_cb_t callback, void *arg):注册DMA模式下的回调函数。参数说明如下:
      · dac:DAC设备实例。
      · callback:回调函数
      · arg:回调函数参数
      · 返回值:成功时,返回0;否则返回非零值
· int hosal_dac_dma_start(hosal_dac_dev_t *dac, uint32_t *data, uint32_t size):启动DMA方式输出。参数说明如下:
      · dac:DAC设备实例。
      · data:需要传输的数据
      · size:数据长度
      · 返回值:成功时,返回0;否则返回非零值
· int hosal_dac_dma_stop(hosal_dac_dev_t *dac):停止DMA传输。参数说明如下:
      · dac:DAC设备实例。
      · 返回值:成功时,返回0;否则返回非零值
另外,HAL层的驱动API位于components/platform/hosal/b1602_hal/bl_dac.h 。
三:音频数据转换
1. 音频格式转换
由于DAC不支持WAV、MP3等音频数据解码,因此需要将这些格式的音频数据转换成RAW格式数据或PCM数据。下面以WAV格式音频为例,详细介绍如何将WAV格式音频数据转换成C语言数组。
第一步,准备软件工具:
      · 下载并安装音频处理工具软件:Audacity
      · 下载并安装:bin2c工具软件
第二步,将音频数据转换成raw格式音频数据。
1 ) 启动 Audacitya 并加载WAV格式音频文件
2.png
2 ) 重采样。由于BL602的DAC最大支持32K的采样率,最小支持8K采样率,因此需要在这范围内进行重新采样。
3.png
4.png
注意:如果音频有多个声道,则需要合并成单个声道或删除多声道
3 ) 导出RAW格式音频文件
5.png
6.png
2. 音频数据生成C数组
在生成C数组之前,需要进一步转换RAW格式数据。下面为BL602提供的一个Python脚本:
  1.    
  2. import os
  3. import sys
  4. import numpy as np
  5. import math

  6. if __name__ == "__main__":
  7.     data_list_deal = []
  8.     path =  sys.argv[1]
  9.     path_save = sys.argv[2]
  10.     bytes_num = os.path.getsize(path)

  11.     save_file = open(path_save, "wb")

  12.     with open(path, 'rb') as f:
  13.         for i in range(bytes_num // 2):
  14.             data = f.read(2)
  15.             data_int = int.from_bytes(data, byteorder='little', signed=True)
  16.             data_int = int((data_int +32768 ) * 0.0156099794003204)
  17.             new_data = data_int.to_bytes(2, byteorder='little', signed=False)
  18.             save_file.write(new_data)
  19.             save_file.write(new_data)
  20.     save_file.close()
复制代码
在命令行中运行Python脚本:
      python test.raw test.bin
最后,使用 bin2c 工具转换成C语言数组:
  1. bin2c.exe --const test.bin > test.h
复制代码
生成的内容如下:
  1. #ifdef __cplusplus
  2. extern "C" {
  3. #endif

  4. const unsigned char imageBytes[] = {
  5. 0x80,0x44,0x30,0xec,0xa0,0xb3,0x16,0xe3,0x40,0xf0,0x78,0xe4,0x40,0x6a,0x31,0xde,
  6. 0x00,0x88,0x11,0xd7,0xc0,0xb3,0xf0,0xd9,0x00,0xfb,0xa1,0xdb,0x80,0x3b,0x00,0xdc,
  7. 0x00,0x23,0x83,0xdf,0x40,0xea,0xf6,0xdc,0x40,0x19,0x68,0xd5,0x40,0xb2,0x68,0xcf,
  8. 0x00,0x47,0x55,0xc9,0x00,0xed,0x82,0xc8,0x80,0x7e,0x57,0xcd,0x00,0xe9,0x35,0xcc,
  9. 0x80,0x28,0xee,0xc6,0x80,0x00,0x41,0xc4,0x40,0xa0,0x35,0xc1,0x40,0x16,0x79,0xc0,
  10. ....
  11. 0xf0,0xeb,0xad,0xfb,0xb0,0xff,0x92,0xf5,0x60,0xcc,0x30,0xf6,0x00,0xe5,0xf7,0xf9,
  12. 0x34,0x89,0xe2,0xfc,0x52,0x31,0xcd,0x01,0xe0,0x90,0x58,0x03,0x58,0xb0,0x5f,0xfc,
  13. 0xf0,0xde,0x64,0xf8
  14. };

  15. #ifdef __cplusplus
  16. }
  17. #endif
复制代码
生成的数据每行16个数据,可以根据行数计算数据长度。或者在代码中通过( sizeof )来计算数组长度。生成的数组名称可以根据需要更改。
四:DAC使用示例
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <FreeRTOS.h>
  4. #include <task.h>
  5. #include <stdio.h>
  6. #include <stdbool.h>
  7. #include <hosal_wdg.h>
  8. #include <blog.h>
  9. #include "hosal_dac.h"
  10. #include "audio_data.h"

  11. #define TAG "dac_demo"

  12. #define DAC_PIN         14

  13. static hosal_dac_dev_t dac0;

  14. /* dac dma callback */
  15. static void user_cb(void *arg)
  16. {
  17.     hosal_dac_dev_t *dac = (hosal_dac_dev_t *)arg;
  18.     hosal_dac_dma_stop(dac);
  19.     hosal_dac_finalize(dac);
  20.     blog_info("success\r\n");
  21. }

  22. void dac_demo_init(void)
  23. {
  24.     int ret = 0;
  25.     uint32_t *p_u32addr;
  26.     uint32_t bufsize;

  27.     dac0.port = 0;                   /* only one dac,so port must be 0 */
  28.     dac0.config.dma_enable = 1;
  29.     dac0.config.pin = DAC_PIN;              /* for 602 :only two channel:0-->channel A -->gpio13, 1-->channel B-->gpio14 */
  30.                                             /* for 702 :only two channel:0-->channel A -->gpio11, 1-->channel B-->gpio17*/
  31.     dac0.config.freq = 32000;               /* if you play audio ,freq must same as audio sample rate ,only support 8k,16k,32k */

  32.     p_u32addr = (uint32_t *)audio_32k;
  33.     bufsize = (uint32_t)audio_32k_len;
  34.     hosal_dma_init();
  35.     ret = hosal_dac_init(&dac0);
  36.     if (ret != 0) {
  37.         hosal_dac_finalize(&dac0);
  38.         blog_error("dac init failed \r\n");
  39.         return;
  40.     }
  41.    
  42.     hosal_dac_dma_cb_reg(&dac0, user_cb, &dac0);

  43.     ret = hosal_dac_dma_start(&dac0, p_u32addr, bufsize);

  44.     if (ret != 0) {
  45.         hosal_dac_finalize(&dac0);
  46.         blog_error("dac start failed \r\n");
  47.         return;
  48.     }
  49. }

  50. void main(void) {
  51.    dac_demo_init();
  52. }
复制代码
注意:请将喇叭外接功放或直接连接DAC引I脚

本帖被以下淘专辑推荐:

用心做好保姆工作
回复

使用道具 举报

iiv | 2024-9-2 18:10:32 | 显示全部楼层
这就去学习,很棒
回复 支持 反对

使用道具 举报

bzhou830 | 2024-9-3 08:35:20 | 显示全部楼层
更太快了,跟不上
选择去发光,而不是被照亮
回复 支持 反对

使用道具 举报

djy876 | 2024-9-9 16:01:10 | 显示全部楼层
学习打卡,这个增加难度了,想搞清楚实例还要再去学PY
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则