数模转换器(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的基本功能框图如下:
· 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设备实例。其定义如下:
- typedef struct {
- uint8_t port; /**< @brief dac id */
- hosal_dac_config_t config; /**< @brief dac config */
- hosal_dac_cb_t cb; /**< @brief dma callback */
- hosal_dma_chan_t dma_chan; /**< @brief dac dma channel */
- void *arg; /**< @brief arg data */
- void *priv; /**< @brief priv data */
- } hosal_dac_dev_t;
复制代码 其中,hosal_dac_config_t为DAC配置,其定义如下:
- typedef struct {
- uint8_t dma_enable; /**< @brief 1: use dma, 0: no dma */
- uint32_t pin; /**< @brief dac pin */
- uint32_t freq; /**< @brief dac freq */
- } hosal_dac_config_t;
复制代码 hosal _dac_cb_t为DAC回调函数,定义如下:
- 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 ) 重采样。由于BL602的DAC最大支持32K的采样率,最小支持8K采样率,因此需要在这范围内进行重新采样。
注意:如果音频有多个声道,则需要合并成单个声道或删除多声道
3 ) 导出RAW格式音频文件
2. 音频数据生成C数组
在生成C数组之前,需要进一步转换RAW格式数据。下面为BL602提供的一个Python脚本:
-
- import os
- import sys
- import numpy as np
- import math
- if __name__ == "__main__":
- data_list_deal = []
- path = sys.argv[1]
- path_save = sys.argv[2]
- bytes_num = os.path.getsize(path)
- save_file = open(path_save, "wb")
- with open(path, 'rb') as f:
- for i in range(bytes_num // 2):
- data = f.read(2)
- data_int = int.from_bytes(data, byteorder='little', signed=True)
- data_int = int((data_int +32768 ) * 0.0156099794003204)
- new_data = data_int.to_bytes(2, byteorder='little', signed=False)
- save_file.write(new_data)
- save_file.write(new_data)
- save_file.close()
复制代码 在命令行中运行Python脚本:
python test.raw test.bin
最后,使用 bin2c 工具转换成C语言数组:
- bin2c.exe --const test.bin > test.h
复制代码 生成的内容如下:
- #ifdef __cplusplus
- extern "C" {
- #endif
- const unsigned char imageBytes[] = {
- 0x80,0x44,0x30,0xec,0xa0,0xb3,0x16,0xe3,0x40,0xf0,0x78,0xe4,0x40,0x6a,0x31,0xde,
- 0x00,0x88,0x11,0xd7,0xc0,0xb3,0xf0,0xd9,0x00,0xfb,0xa1,0xdb,0x80,0x3b,0x00,0xdc,
- 0x00,0x23,0x83,0xdf,0x40,0xea,0xf6,0xdc,0x40,0x19,0x68,0xd5,0x40,0xb2,0x68,0xcf,
- 0x00,0x47,0x55,0xc9,0x00,0xed,0x82,0xc8,0x80,0x7e,0x57,0xcd,0x00,0xe9,0x35,0xcc,
- 0x80,0x28,0xee,0xc6,0x80,0x00,0x41,0xc4,0x40,0xa0,0x35,0xc1,0x40,0x16,0x79,0xc0,
- ....
- 0xf0,0xeb,0xad,0xfb,0xb0,0xff,0x92,0xf5,0x60,0xcc,0x30,0xf6,0x00,0xe5,0xf7,0xf9,
- 0x34,0x89,0xe2,0xfc,0x52,0x31,0xcd,0x01,0xe0,0x90,0x58,0x03,0x58,0xb0,0x5f,0xfc,
- 0xf0,0xde,0x64,0xf8
- };
- #ifdef __cplusplus
- }
- #endif
复制代码 生成的数据每行16个数据,可以根据行数计算数据长度。或者在代码中通过( sizeof )来计算数组长度。生成的数组名称可以根据需要更改。
四:DAC使用示例
- #include <stdio.h>
- #include <string.h>
- #include <FreeRTOS.h>
- #include <task.h>
- #include <stdio.h>
- #include <stdbool.h>
- #include <hosal_wdg.h>
- #include <blog.h>
- #include "hosal_dac.h"
- #include "audio_data.h"
- #define TAG "dac_demo"
- #define DAC_PIN 14
- static hosal_dac_dev_t dac0;
- /* dac dma callback */
- static void user_cb(void *arg)
- {
- hosal_dac_dev_t *dac = (hosal_dac_dev_t *)arg;
- hosal_dac_dma_stop(dac);
- hosal_dac_finalize(dac);
- blog_info("success\r\n");
- }
- void dac_demo_init(void)
- {
- int ret = 0;
- uint32_t *p_u32addr;
- uint32_t bufsize;
- dac0.port = 0; /* only one dac,so port must be 0 */
- dac0.config.dma_enable = 1;
- dac0.config.pin = DAC_PIN; /* for 602 :only two channel:0-->channel A -->gpio13, 1-->channel B-->gpio14 */
- /* for 702 :only two channel:0-->channel A -->gpio11, 1-->channel B-->gpio17*/
- dac0.config.freq = 32000; /* if you play audio ,freq must same as audio sample rate ,only support 8k,16k,32k */
- p_u32addr = (uint32_t *)audio_32k;
- bufsize = (uint32_t)audio_32k_len;
- hosal_dma_init();
- ret = hosal_dac_init(&dac0);
- if (ret != 0) {
- hosal_dac_finalize(&dac0);
- blog_error("dac init failed \r\n");
- return;
- }
-
- hosal_dac_dma_cb_reg(&dac0, user_cb, &dac0);
- ret = hosal_dac_dma_start(&dac0, p_u32addr, bufsize);
- if (ret != 0) {
- hosal_dac_finalize(&dac0);
- blog_error("dac start failed \r\n");
- return;
- }
- }
- void main(void) {
- dac_demo_init();
- }
复制代码 注意:请将喇叭外接功放或直接连接DAC引I脚
|
|