概念先知道
- Codec(编解码器):负责模拟音频信号与数字 PCM 数据之间的转换——录音是麦克风模拟信号 → 数字 PCM,播放是数字 PCM → 喇叭模拟信号。BL616/BL618 内置音频编解码器(xcodec)。
- PCM:音频的原始数字格式,记录每个采样点的幅值;例程用 16KHz、16bit、单声道 PCM 测试。
- 采样率/位深:每秒采多少个点(16K = 16000 点/秒)和每个点用多少位表示(16bit)。麦克风与喇叭两端必须一致。
- DMA 传输:编解码数据量大,例程通过 DMA 通道异步读写(
xcodec_output_write_async/xcodec_input_read_async),避免 CPU 忙等。
例程功能简介
本页对应博流官方 SDK 的 codec 例程(examples/audio/codec),演示 Ai-M6x 内置音频编解码器的使用:
codec_speaker:初始化 xcodec 输出通道,通过 DMA 播放一段内置 16K/16bit/单声道 PCM 测试音;codec_mic:初始化输入通道,采集麦克风数据并统计打印;codec_loop:回环测试(当前版本 README 注明 loopback 尚待完善);codec_eq:EQ/增益/限幅等音频处理参数调试;- 例程使用 xcodec 驱动 API(
xcodec_init/xcodec_output_open/xcodec_output_write_async等)。 - 同族例程(
examples/audio/):player(播放器,见「音频播放」页)、minialsa(ALSA 风格接口)。
注意
音频引脚与功放配置需要按板子调整(见 README 的 multimedia_port.c 适配说明:g_pa_delay_2、CONFIG_AUDIO_PA_PIN、麦克风/喇叭输入输出引脚等)。使用外挂 ES8388 时需设置 CONFIG_CODEC_USE_ES8388=1。
操作步骤
本页需要带麦克风/喇叭的开发板。在终端进入 SDK 的音频编解码例程目录(前提:已按快速开始(Linux)或Windows搭建好环境):
cd examples/audio/codec执行编译命令。Ai-M62(BL616)与 Ai-M61(BL618)同属一个系列,统一填写引脚最少的 bl616 即可:
make CHIP=bl616 BOARD=bl616dk用 USB 线连接开发板,按住 BOOT 键(Ai-M61-32S-Kit 为 IO2)不放、短按 EN/RST 进入下载模式,然后执行烧录(把串口号换成实际值):
make flash CHIP=bl616 COMX=/dev/ttyUSB0打开串口助手(波特率 2000000),在 bouffalolab /> 提示符输入(例程会播放一段 16K 16bit 单声道 PCM 测试音):
codec_speaker输入 codec_mic 开始采集麦克风数据并打印采样信息(按 Ctrl+C 停止):
codec_mic回环测试:codec_loop;EQ/增益调试:codec_eq(配合 MSP_PEQ、MSP_GAIN 等子命令)。
代码执行流程
喇叭播放测试的完整流程如下(麦克风采集流程类似,方向相反):
例程调用的 API 介绍
xcodec_output_open(&codec, &ch, 0)
打开输出(播放)通道,配置采样率、位深、声道等参数(在 xcodec_ch_cfg 中设置)。
参数:
codec:编解码器设备ch:输出通道句柄id:通道编号(0)
返回值:0 成功;负值失败
xcodec_output_write_async(&ch, buf, len)
异步写入 PCM 数据到输出通道(内部经 DMA 送往喇叭),写满后返回实际写入长度,需循环调用直到写完。
参数:
ch:输出通道句柄buf:PCM 数据len:待写入长度
返回值:本次实际写入长度
xcodec_input_open / xcodec_input_read_async(&codec, &ch, 0) / (&ch, buf, len)
打开输入(录音)通道并异步读取麦克风采集的 PCM 数据,与输出接口对称。
参数:
codec/ch:设备与输入通道buf/len:接收缓冲区与期望长度
返回值:open 返回 0 成功;read_async 返回实际读取长度
完整代码
以下为 codec/main.c 完整源码,与官方示例(examples/audio/codec)一致(实际播放/录音逻辑在 app/ 目录的 codec_speaker.c、codec_mic.c 等文件中),默认折叠,点击展开:
📜 点击展开 codec/main.c 完整代码
/****************************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
#include <lwip/tcpip.h>
#include <lwip/sockets.h>
#include <lwip/netdb.h>
#include "bl_fw_api.h"
#include "fhost_api.h"
#include "wifi_mgmr_ext.h"
#include "wifi_mgmr.h"
#include "bflb_irq.h"
#include "bflb_uart.h"
#include "bl616_glb.h"
#include "rfparam_adapter.h"
#include "board.h"
#include "shell.h"
#define DBG_TAG "MAIN"
#include "log.h"
#include "async_event.h"
struct bflb_device_s *gpio;
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define WIFI_STACK_SIZE (1536)
#define TASK_PRIORITY_FW (16)
/****************************************************************************
* Private Types
****************************************************************************/
/****************************************************************************
* Private Data
****************************************************************************/
static struct bflb_device_s *uart0;
extern void shell_init_with_task(struct bflb_device_s *shell);
extern void wifi_event_handler(async_input_event_t ev, void *priv);
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/****************************************************************************
* Functions
****************************************************************************/
void wifi_start_firmware_task(void *param)
{
LOG_I("Starting wifi ...\r\n");
/* set ble controller EM Size */
GLB_Set_EM_Sel(GLB_WRAM160KB_EM0KB);
if (0 != rfparam_init(NULL, NULL, NULL)) {
LOG_I("PHY RF init failed!\r\n");
vTaskDelete(NULL);
}
async_register_event_filter(EV_WIFI, wifi_event_handler, NULL);
wifi_task_create();
LOG_I("Starting fhost ...\r\n");
fhost_init();
vTaskDelete(NULL);
}
void wifi_event_handler(async_input_event_t ev, void *priv)
{
uint32_t code = ev->code;
switch (code) {
case CODE_WIFI_ON_INIT_DONE: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_INIT_DONE\r\n", __func__);
wifi_mgmr_task_start();
} break;
case CODE_WIFI_ON_MGMR_DONE: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_MGMR_DONE\r\n", __func__);
} break;
case CODE_WIFI_ON_SCAN_DONE: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_SCAN_DONE\r\n", __func__);
wifi_mgmr_sta_scanlist();
} break;
case CODE_WIFI_ON_CONNECTED: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_CONNECTED\r\n", __func__);
void mm_sec_keydump();
mm_sec_keydump();
} break;
#ifdef CODE_WIFI_ON_GOT_IP_ABORT
case CODE_WIFI_ON_GOT_IP_ABORT: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_GOT_IP_ABORT\r\n", __func__);
} break;
#endif
#ifdef CODE_WIFI_ON_GOT_IP_TIMEOUT
case CODE_WIFI_ON_GOT_IP_TIMEOUT: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_GOT_IP_TIMEOUT\r\n", __func__);
} break;
#endif
case CODE_WIFI_ON_GOT_IP: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_GOT_IP\r\n", __func__);
} break;
case CODE_WIFI_ON_DISCONNECT: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_DISCONNECT\r\n", __func__);
} break;
case CODE_WIFI_ON_AP_STARTED: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_AP_STARTED\r\n", __func__);
} break;
case CODE_WIFI_ON_AP_STOPPED: {
LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_AP_STOPPED\r\n", __func__);
} break;
case CODE_WIFI_ON_AP_STA_ADD: {
LOG_I("[APP] [EVT] [AP] [ADD] %lld\r\n", xTaskGetTickCount());
} break;
case CODE_WIFI_ON_AP_STA_DEL: {
LOG_I("[APP] [EVT] [AP] [DEL] %lld\r\n", xTaskGetTickCount());
} break;
default: {
LOG_I("[APP] [EVT] Unknown code %u \r\n", code);
}
}
}
int main(void)
{
board_init();
uart0 = bflb_device_get_by_name("uart0");
shell_init_with_task(uart0);
tcpip_init(NULL, NULL);
xTaskCreate(wifi_start_firmware_task, "wifi init", 1024, NULL, 10, NULL);
#if defined(CONFIG_CODEC_USE_I2S)
extern msp_i2s_port_init(void);
msp_i2s_port_init();
#endif
vTaskStartScheduler();
while (1) {
}
}FAQ
codec_speaker 没有声音
检查喇叭/PA 接线与 multimedia_port.c 的引脚配置(CONFIG_AUDIO_PA_PIN、g_pa_delay_2);再确认输出引脚(OUTPUT_POSITIVE/NEGATIVE_PIN)与板子一致,内置 codec 与外部 ES8388 不要混用配置。
codec_mic 采不到数据
确认麦克风接在输入引脚(INPUT_POSITIVE/NEGATIVE_PIN)上,且配置的是板子实际使用的 codec(内置或 ES8388);不使用的差分输入引脚建议设为 255。
采样率与预期不符
例程使用 16KHz/16bit/单声道;如需其他采样率,修改 codec_speaker.c / codec_mic.c 中通道配置(xcodec_ch_cfg)的采样率与位深,注意内置 codec 支持的范围。
遇到问题?
如有其他问题,请到统一的提问与讨论区:Ai-Thinker Discussions

