本次目标通过INMP441全向麦克风模块,将其中采集到的音频,通过auracast广播出去,然后其中nrf52840 dongle接收到音频从电脑上播放出来采集到的声音。方案流程图

本次比较匆忙的完成此次链路的测评,给大家带来自己实现auracast的一些实现方法,其中有写关于auracast的相关概念没有讲清楚,等下次遇到带有auracast的功能,将详细的给大家带来其中仔细讲解--立flag

硬件平台与外设
本项目包含发射端(广播)和接收端(监听)两个主要硬件实体:- 广播端 (Broadcaster):
主控芯片: Nordic nRF54L15-DK(负责 I2S 驱动、LC3 编码与蓝牙 LE Audio 广播)。
音频采集模块: INMP441 全向 I2S 硅麦克风(高信噪比,支持 24/32-bit I2S 输出)。
- 接收端 (Receiver/Sink):
主控芯片: Nordic nRF52840 Dongle(负责蓝牙周期性广播同步、LC3 解码与 USB UAC2 音频类传输)。
播放终端: 个人电脑(PC),通过 USB 识别 Dongle 为标准音频输入设备。
系统架构与数据流向
整个系统的端到端(End-to-End)数据流向呈现“漏斗状”的生产-消费模型,主要分为以下几个阶段:声学采集 (I2S): 环境声音 -> INMP441->(I2S I2S_RX)->nRF54L15 内存池。
编码与广播 (ISO Tx): nRF54L15 (PCM 数据 16kHz)-> LC3 编码器 -> 蓝牙底层的 BIG/BIS (广播等时流) -> 天线。
同步与解码 (ISO Rx): 天线-> nRF52840 Dongle 扫描并同步 PA/BIG ->获取 LC3 载荷 -> LC3 解码器->还原出 PCM 数据。
PC 端播放 (USB UAC2): Dongle 内部 PCM 数据->USB Audio Class 2.0 驱动-> 电脑 USB 接口 -> 操作系统识别为“USB 麦克风”->电脑扬声器播放。
主要代码实现
麦克风采集我们使用16k采样率和16位,这样可以降低我们的设备的资源消耗在LC3编码中,其中支持帧长7.5ms和10ms,,这里我们选择帧长10ms
#define I2S_NODE DT_NODELABEL(i2s20) #define MIC_SAMPLE_RATE 16000 /* 麦克风采样率:16kHz */ #define MIC_WORD_SIZE 16 /* 采样位宽:16 bit */ #define MIC_CHANNELS 1 /* 通道数:单声道 */ #define MIC_FRAME_SAMPLES 160 /* 10ms数据(16000 * 0.01) = 160个采样点 */ #define MIC_FRAME_BYTES (MIC_FRAME_SAMPLES * sizeof(int16_t)) /* 160 * 2 = 320 字节 */之后我们创建线程,来接收我们的麦克风音频数据
static void mic_i2s_thread(void *a, void *b, void *c)
{
const struct device *i2s_dev = DEVICE_DT_GET(I2S_NODE); // 获取设备实例
int ret;
struct i2s_config cfg = {
.word_size = MIC_WORD_SIZE,
.channels = MIC_CHANNELS,
.format = I2S_FMT_DATA_FORMAT_I2S,
.options = I2S_OPT_BIT_CLK_MASTER | I2S_OPT_FRAME_CLK_MASTER,
.frame_clk_freq = MIC_SAMPLE_RATE, // 16000 Hz
.mem_slab = &mic_rx_slab,
.block_size = MIC_FRAME_BYTES, /
.timeout = 1000, // 读写超时时间
};
if (!device_is_ready(i2s_dev)) { return; }
ret = i2s_configure(i2s_dev, I2S_DIR_RX, &cfg);
ret = i2s_trigger(i2s_dev, I2S_DIR_RX, I2S_TRIGGER_START);数据传输,这里我们将其中采集到的数据消息队列传入到LC3编码中while (1) {
void *mem_block;
size_t block_size_out;
int16_t frame[MIC_FRAME_SAMPLES];
ret = i2s_read(i2s_dev, &mem_block, &block_size_out);
if (ret < 0) {
k_msleep(10);
continue;
}
memcpy(frame, mem_block, MIN(block_size_out, sizeof(frame)));
ret = k_msgq_put(&mic_frame_q, frame, K_NO_WAIT);
if (ret != 0) {
printk("mic_frame_q full, drop one frame\n");
}
k_mem_slab_free(&mic_rx_slab, mem_block);
}
}最后就是调用ISO接口将我们的编码好的LC3数据传输,接下来是本次项目的演示视频
总结
此次测评的所有代码NRF_Code.zip
我要赚赏金
