当前位置: 首页 > news >正文

基于瑞萨 RA6M5 开发板的声源定位系统设计与实现

目录

前言

一、项目概述

1.1 项目背景与意义

1.2 核心功能与技术指标

1.2.1 基础功能

1.2.2 扩展功能

1.2.3 技术指标

二、硬件选型与系统架构

2.1 核心硬件选型

2.1.1 瑞萨 CPKIOT-RA6M5 开发板

2.1.2 SPIEED MicArray 麦克风阵列

2.1.3 74HC4051D 8:1 模拟多路复用器

2.1.4 SSD1306 OLED 显示屏

2.1.5 其他硬件

2.2 系统整体架构

2.2.1 架构框图

2.2.2 模块连接关系

2.3 引脚配置详情

三、开发环境搭建

3.1 软件工具选型

3.1.1 集成开发环境(IDE)

3.1.2 调试工具

3.1.3 辅助工具

3.2 开发环境配置步骤

3.2.1 安装 e² studio

3.2.2 安装 FSP 软件包

3.2.3 配置调试器

3.2.4 新建项目

四、软件设计与代码实现

4.1 软件整体架构

4.1.1 软件架构框图

4.1.2 源代码文件结构

4.1.3 源代码栈区结构

4.2 核心技术原理

4.2.1 I2S 通讯协议

4.2.2 声源方向计算原理

4.2.3 滤波算法

4.3 SSI 与 GPT 栈区模块设置

4.3.1 SSI 与 GPT 模块的核心作用

(1)SSI 模块作用

(2)GPT 模块作用

(3) 栈区设置的意义

4.3.2 栈区设置核心参数计算

(1) 系统内存资源

(2)SSI 模块栈区需求计算

(3)GPT 模块栈区需求计算

(4)系统总栈区配置

4.3.3 FSP 图形化配置步骤

(1)栈区全局配置

(2)GPT 模块配置(生成 I2S 时钟)

(3)SSI 模块配置(I2S 音频接收)

(4)配置生成与验证

4.3 核心代码实现

4.3.1 初始化模块代码

4.3.2 数据采集模块代码

4.3.3 信号处理模块代码

4.3.4 外设驱动模块代码

4.3.5 主控制模块代码

五、系统测试与结果分析

5.1 测试环境搭建

5.1.1 硬件连接验证

5.1.2 测试工具与场景

5.2 测试结果与分析

5.2.1 静态测试结果

5.2.2 动态测试结果

5.2.3 噪声干扰测试

5.3 常见问题与解决方案

六、项目优化与拓展方向

6.1 现有系统优化点

6.2 功能拓展方向

总结


前言

        在智能语音交互、安防监控、智能家居等领域,精准的声源定位技术是实现设备智能化升级的核心支撑。传统单麦克风设备仅能采集声音信号,无法获取声源方位信息,而基于麦克风阵列的声源定位系统通过多麦克风协同工作,结合信号处理算法,可实时检测声源位置并分析音频特征,广泛应用于智能音箱自动转向、会议系统定向拾音、机器人听觉导航等场景。

        本文将详细介绍基于瑞萨 RA6M5 开发板的声源定位系统设计全过程,包括硬件选型、系统架构设计、软件开发、代码实现、测试验证等核心内容,全程采用 C 语言开发,适合嵌入式工程师、电子信息专业学生及技术爱好者参考学习。下面就让我们正式开始吧!


一、项目概述

1.1 项目背景与意义

        随着嵌入式技术与音频信号处理技术的快速发展,声源定位系统在消费电子、工业控制、安防监控等领域的应用日益广泛。例如,智能音箱通过声源定位可自动转向发声者,提升语音交互体验;会议系统借助定向拾音技术可突出发言人声音,抑制环境噪声;安防系统通过异常声音定位可快速响应危险信号。

        本项目基于瑞萨 RA6M5 开发板构建声源定位系统,结合 SPIEED 麦克风阵列、74HC4051D 多路复用器、SSD1306 OLED 显示屏等硬件,实现高精度、低延迟的声源定位功能,同时支持声强检测、LED 方向指示、屏幕信息显示等扩展功能,为相关领域的技术研发提供参考方案。

1.2 核心功能与技术指标

1.2.1 基础功能

  • 声源采集:通过 7 路麦克风阵列实现声音信号同步采集,支持 360° 全向拾音;
  • 噪声抑制:采用最大值滤波算法,抑制环境噪声与电磁干扰;
  • 方向计算:基于声音信号的时间差(TDOA)原理,计算声源水平角度,定位精度≤±15°;
  • 信息显示:通过 OLED 显示屏实时显示麦克风编号、声源角度范围及声强值。

1.2.2 扩展功能

  • LED 方向指示:12 颗 SK9822 LED 灯根据声源位置点亮,支持不同声强对应不同颜色显示;
  • 按键控制:通过用户按键实现工作模式切换与当前声源位置锁定;
  • 声强分级:根据声强大小分为中等强度(40000-80000)和高强度(>80000),分别对应绿色和蓝色 LED 指示。

1.2.3 技术指标

  • 定位角度范围:0°-360°(水平方向);
  • 定位精度:≤±15°;
  • 采样率:7.8KHz(满足人声频率采集需求);
  • 声强检测范围:0-140dB SPL;
  • 屏幕刷新频率:约 1.25Hz(每 0.8 秒刷新一次);
  • 工作电压:3.3V-5V。

二、硬件选型与系统架构

2.1 核心硬件选型

2.1.1 瑞萨 CPKIOT-RA6M5 开发板

        作为系统主控单元,RA6M5 开发板搭载 32 位 Arm Cortex-M33 内核,主频高达 200MHz,集成 2MB Flash 和 256KB SRAM,具备丰富的外设接口(SSI、I2C、GPIO 等),支持音频信号采集与处理、外设驱动控制等功能。

        开发板关键特性:

  • 内置 J-LINK 调试器,支持程序烧录与在线调试;
  • 提供预留音频模块接口(SSIE),兼容 I2S 音频传输协议;
  • 支持 5V 与 3.3V 双电源供电,满足多外设供电需求;
  • 配备用户按键与 LED,便于功能扩展与状态指示。

2.1.2 SPIEED MicArray 麦克风阵列

        采用 7 颗 MSM261S4030H0 数字麦克风组成阵列,其中 6 颗均匀分布在圆周,1 颗位于中心,支持 360° 全向拾音。阵列板集成 12 颗 SK9822 LED 灯,可用于声源方向可视化指示。

        麦克风阵列关键参数:

  • 声压级:140dB SPL;
  • 灵敏度:-26dBFS @1kHz 1Pa;
  • 信噪比:57dB(20kHz 带宽,A 加权);
  • 时钟频率:1.0-4.0MHz(正常模式);
  • 通讯协议:I2S(左对齐模式,双声道采集)。

2.1.3 74HC4051D 8:1 模拟多路复用器

        由于 RA6M5 开发板仅提供 1 个 SSI_RXD0 接收口,而麦克风阵列有 3 个数据发送口(MIC_D0、MIC_D1、MIC_D2),需通过多路复用器实现时分复用,依次接收 6 路麦克风数据。

        74HC4051D 关键特性:

  • 8 通道模拟开关,支持双向数据传输;
  • 导通电阻低(典型值 80Ω@VCC-VEE=4.5V);
  • 逻辑电平兼容 5V 与 3.3V;
  • 3 个数字选择输入(S0-S2),支持通道切换控制。

2.1.4 SSD1306 OLED 显示屏

        采用 0.96 寸 OLED 显示屏,分辨率 128×64,支持 I2C 通讯协议,具有低功耗、高对比度、响应速度快等优点,用于显示麦克风编号、声源角度、声强值等信息。

        显示屏关键参数:

  • 工作电压:1.65V-3.3V(逻辑电路),7V-15V(面板驱动);
  • 内置 128×64 位 SRAM 显示缓冲区;
  • 支持 256 级亮度调节;
  • I2C 从机地址:0x78(默认)。

2.1.5 其他硬件

  • 杜邦线若干:用于硬件模块之间的信号连接;
  • 5V 电源适配器:为开发板与麦克风阵列供电;
  • 面包板:用于 74HC4051D 多路复用器的焊接与固定。

2.2 系统整体架构

        系统采用 “主控单元 + 采集单元 + 信号处理单元 + 显示单元” 的架构设计,各模块功能分工明确,协同工作实现声源定位功能。

2.2.1 架构框图

2.2.2 模块连接关系

  • 电源连接:开发板 5V 输出接麦克风阵列 VIN 引脚,3.3V 输出接 74HC4051D VCC 引脚与 OLED 显示屏 VCC 引脚;所有模块 GND 引脚共地。
  • 通讯连接:
    • 麦克风阵列 I2S 接口(MIC_WS、MIC_CK、MIC_D0-D3)与开发板 SSI 接口(SSI_FS0_A、SSI_BCK0_A、SSI_RTX0_A)通过 74HC4051D 连接;
    • 麦克风阵列 LED 控制引脚(LED_CK、LED_DA)与开发板 GPIO 引脚(P000、P401)连接;
    • 74HC4051D 通道选择引脚(S0-S2)与开发板 GPIO 引脚(P113、P600、P103)连接;
    • OLED 显示屏 I2C 接口(SCL、SDA)与开发板 I2C 接口(IIC_SCL0_B、IIC_SDA0_B)连接。

2.3 引脚配置详情

瑞萨 RA6M5 开发板引脚外设模块外设引脚功能说明
5V麦克风阵列VIN麦克风阵列电源输入
3.3V74HC4051DVCC多路复用器电源输入
3.3VOLED 显示屏VCC显示屏电源输入
GND所有模块GND/VEE电源地
SSI_FS0_A麦克风阵列MIC_WSI2S 帧同步信号
SSI_BCK0_A麦克风阵列MIC_CKI2S 时钟信号
SSI_RTX0_A74HC4051DZ多路复用器公共输出端
P000麦克风阵列LED_CKLED 时钟信号
P401麦克风阵列LED_DALED 数据信号
P10374HC4051DS2通道选择输入 2
P60074HC4051DS1通道选择输入 1
P11374HC4051DS0通道选择输入 0
IIC_SCL0_BOLED 显示屏SCLI2C 时钟信号
IIC_SDA0_BOLED 显示屏SDAI2C 数据信号
BSP_IO_PORT_01_PIN_01用户按键KEY1工作模式切换

三、开发环境搭建

3.1 软件工具选型

3.1.1 集成开发环境(IDE)

        本项目采用瑞萨 e² studio,基于 Eclipse 开发,支持瑞萨 RA 系列 MCU 的代码编辑、编译、调试等功能,内置 Flexible Software Package(FSP)软件支持包,提供丰富的库函数与驱动模板。

        e² studio 的操作系统支持情况如下:

  • Windows:Windows 10 64-bit、Windows 11 64-bit;
  • Linux:Ubuntu 22.04 LTS、Ubuntu 24.04 LTS;
  • Mac OS:Mac OS 14(Sonoma)、Mac OS 15(Sequoin)。

3.1.2 调试工具

        使用开发板板载 J-LINK 调试器,通过 USB 接口连接电脑与开发板,支持程序烧录、在线调试、断点设置、变量监控等功能。

3.1.3 辅助工具

  • 串口助手:用于查看系统运行日志与调试信息;
  • 示波器:用于监测 I2S 信号与 GPIO 引脚电平变化;
  • 万用表:用于检测硬件连接与电源电压。

3.2 开发环境配置步骤

3.2.1 安装 e² studio

  1. 从瑞萨官网下载 e² studio 2025-04.1 版本安装包;
  2. 运行安装程序,按照向导完成安装,期间会自动安装 Microsoft Visual C++ 运行库;
  3. 安装完成后,启动 e² studio,选择工作空间路径,进入主界面。

3.2.2 安装 FSP 软件包

  1. 打开 e² studio,点击 “File”→“New”→“Renesas RA Project”;
  2. 在弹出的对话框中,选择 “RA6M5” 系列,点击 “Next”;
  3. 选择 FSP 版本(推荐 v4.4.0),点击 “Download” 下载并安装 FSP 软件包;
  4. 安装完成后,重启 e² studio 生效。

3.2.3 配置调试器

  1. 将开发板通过 USB 线连接电脑,电脑会自动识别 J-LINK 调试器;
  2. 打开 e² studio,点击 “Run”→“Debug Configurations”;
  3. 选择 “Renesas GDB Hardware Debugging”,点击 “New” 创建新配置;
  4. 在 “Debugger” 选项卡中,选择 “J-Link” 作为调试器,设置接口为 “SWD”,频率为 “1MHz”;
  5. 点击 “Apply”→“Debug”,验证调试器连接是否正常。

3.2.4 新建项目

  1. 点击 “File”→“New”→“Renesas RA Project”;
  2. 输入项目名称,选择保存路径,点击 “Next”;
  3. 选择 MCU 型号为 “R7FA6M5BH3CFP”,点击 “Next”;
  4. 选择 “Bare Metal” 项目类型,点击 “Next”;
  5. 选择 FSP 配置文件,点击 “Finish”,完成项目创建。

四、软件设计与代码实现

4.1 软件整体架构

        系统软件基于瑞萨 FSP 库开发,采用模块化设计思想,分为初始化模块、数据采集模块、信号处理模块、外设驱动模块、主控制模块等,各模块通过函数调用实现数据交互。

4.1.1 软件架构框图

[主控制模块]↓ ↑
[初始化模块]:I2C、SSI、GPIO、麦克风、LED、显示屏初始化↓ ↑
[数据采集模块]:I2S中断接收、通道切换、数据存储↓ ↑
[信号处理模块]:声道分离、声强计算、方向判断、滤波处理↓ ↑
[外设驱动模块]:LED控制、OLED显示、按键检测

4.1.2 源代码文件结构

4.1.3 源代码栈区结构

4.2 核心技术原理

4.2.1 I2S 通讯协议

        I2S(Inter-IC Sound)是数字音频传输的串行接口标准,用于麦克风阵列与开发板之间的音频数据传输,主要包含三类信号线:

  • 时钟线(SCK/MIC_CK):提供同步时钟,频率 = 2× 采样频率 × 位宽;
  • 帧同步线(LRCK/MIC_WS):指示当前传输声道,频率 = 采样频率;
  • 数据线(SD/MIC_D0-D3):传输音频数据,位宽 32 位,有效位深 24 位。

        在本系统中,麦克风阵列采用左对齐模式,双声道采集,WS 为低电平时传输左声道数据,高电平时传输右声道数据,数据在 SCK 下降沿发送,上升沿采样。下图分别为I2S 标准模式接口时序和左对齐模式接口时序:

4.2.2 声源方向计算原理

        采用最大值比较法结合时间差(TDOA)原理实现声源方向计算,核心步骤如下:

  1. 采集 6 路麦克风的音频数据,分离左右声道并计算各通道声强最大值;
  2. 找出全局声强最大值与次大值,记录对应的麦克风编号;
  3. 计算最大值与次大值的强度比,判断声源是否位于两个麦克风中间;
    • 强度比在 1.08-1.2 之间:取中间方向(每个麦克风对应 60° 范围);
    • 强度比 > 2:采用主方向(声强最大值对应的麦克风方向);
  4. 根据麦克风编号与强度比计算声源水平角度,映射到 12 个 LED 灯对应的方向(每个 LED 对应 30° 范围)。

4.2.3 滤波算法

        采用最大值滤波算法抑制环境噪声,核心逻辑:

  • 对每个麦克风采集的 1280 个数据样本进行遍历,滤除绝对值小于 80000 的噪声数据;
  • 取剩余数据的最大值作为该麦克风的有效声强值,提高声源检测的灵敏度与准确性。

4.3 SSI 与 GPT 栈区模块设置

4.3.1 SSI 与 GPT 模块的核心作用

(1)SSI 模块作用

        SSI 模块是瑞萨 RA 系列 MCU 的同步串行接口,支持 I2S、SPI、Microwire 等多种协议,本系统中配置为 I2S 从接收模式,核心作用:

  • 接收麦克风阵列输出的 32 位音频数据(24 位有效位深);
  • 配合 GPT 提供的 BCLK 时钟与 LRCK 帧同步信号,实现左右声道数据的准确分离;
  • 通过中断回调机制,在数据缓冲区满时触发处理逻辑,确保实时采集不丢包。
(2)GPT 模块作用

        GPT 模块为 SSI 提供高精度时钟信号,核心作用:

  • 生成 SSI 所需的 BCLK 时钟(频率 = 2× 采样频率 × 数据位宽);
  • 生成 LRCK 帧同步信号(频率 = 采样频率);
  • 确保时钟信号的稳定性,避免因时钟抖动导致的音频数据错位。
(3) 栈区设置的意义

        栈区(Stack)是 MCU 用于存储函数调用上下文、局部变量、中断现场保护的数据区域,SSI 与 GPT 模块的栈区设置核心目标:

  • 为 SSI 中断回调函数分配足够的栈空间,避免中断处理时栈溢出;
  • 确保 GPT 定时器中断(若启用)的栈空间独立,避免多中断嵌套导致的栈冲突;
  • 优化栈区大小,平衡内存占用与系统稳定性(栈区过大会浪费 RAM,过小会导致程序崩溃)。

4.3.2 栈区设置核心参数计算

(1) 系统内存资源

        瑞萨 RA6M5 开发板内置 256KB SRAM,内存分配规划:

  • 堆区(Heap):64KB(用于动态内存分配,如数据缓冲区);
  • 栈区(Stack):32KB(分为主栈 MSP 与进程栈 PSP,本系统使用 MSP);
  • 全局变量区:128KB(用于存储麦克风数据缓冲区、配置参数等);
  • 预留内存:32KB(用于 FSP 库运行、外设缓存等)。
(2)SSI 模块栈区需求计算

        SSI 模块的栈区占用主要来自中断回调函数i2s_callback,需考虑:

  • 局部变量大小:i2s_callback中局部变量包括event(4 字节)、current_ch(4 字节)、ch_max_val(3×4=12 字节)等,总计约 64 字节;
  • 函数调用栈:回调函数中调用separate_channelsmax_voice_value_find等函数,每个函数栈帧约 32 字节,嵌套深度 3 层,总计约 96 字节;
  • 中断现场保护:Cortex-M33 内核中断时自动保存 8 个寄存器(R0-R3、R12、LR、PC、PSR),每个寄存器 4 字节,总计 32 字节;
  • 预留空间:考虑最坏情况(如数据峰值处理),预留 128 字节。

SSI 模块最小栈区需求64 + 96 + 32 + 128 = 320 字节

(3)GPT 模块栈区需求计算

        GPT 模块配置为定时器模式,仅用于生成时钟信号,未启用中断(若启用中断需额外计算):

  • 无中断回调函数,仅初始化时占用栈空间;
  • 初始化函数R_GPT_OpenR_GPT_Start的栈帧总计约 64 字节;
  • 预留空间:32 字节。

GPT 模块最小栈区需求64 + 32 = 96 字节

(4)系统总栈区配置

        综合 SSI、GPT 及其他模块(OLED、LED 驱动)的栈区需求,设置系统总栈区大小为 8KB(8192 字节),完全满足各模块需求,同时避免内存浪费。

4.3.3 FSP 图形化配置步骤

        瑞萨 e² studio 通过 FSP Configurator 提供图形化配置界面,无需手动修改寄存器,以下是 SSI、GPT 及栈区的配置步骤:

(1)栈区全局配置
  1. 打开项目中的hal_data.c文件,点击顶部FSP Configuration按钮,进入 FSP 配置界面;
  2. 在左侧导航栏选择BSPSystem,右侧找到Stack/Heap Configuration
  3. 设置Main Stack Size (MSP)0x2000(8192 字节),Process Stack Size (PSP)0x0(本系统使用 MSP,禁用 PSP);
  4. 设置Heap Size0x10000(65536 字节),点击Apply保存配置。
(2)GPT 模块配置(生成 I2S 时钟)
  1. 在 FSP 配置界面左侧导航栏选择PeripheralsTimerGPT,点击Add添加 GPT 实例;
  2. 配置 GPT 核心参数:
    • Instance Name:命名为g_gpt0(与代码中全局变量一致);
    • Mode:选择Timer(定时器模式);
    • Clock Source:选择PCLK(外设时钟,频率 = 200MHz);
    • Prescaler:设置为12(分频系数 = 13,PCLK/13≈15.38MHz);
    • Period:设置为255(计数器最大值 = 255,周期 = 256×(1/15.38MHz)≈16.64μs);
  3. 配置输出比较通道(用于生成 BCLK 与 LRCK):
    • 点击Output Compare ChannelsAdd,添加 2 个通道;
    • 通道 1(BCLK):Channel选择0Output Action选择Toggle(电平翻转),Compare Value设置为127(占空比 50%);
    • 通道 2(LRCK):Channel选择1Output Action选择ToggleCompare Value设置为127
  4. 配置引脚映射:
    • 点击Pin Configuration,为 GPT0 Channel 0 分配引脚P001(对应 SSI_BCK0_A);
    • 为 GPT0 Channel 1 分配引脚P002(对应 SSI_FS0_A);
  5. 点击Apply保存配置,FSP 自动生成g_gpt0_ctrlg_gpt0_cfg等全局变量。

(3)SSI 模块配置(I2S 音频接收)
  1. 在 FSP 配置界面左侧导航栏选择PeripheralsSerialSSI,点击Add添加 SSI 实例;
  2. 配置 SSI 核心参数:
    • Instance Name:命名为g_i2s0(与代码中全局变量一致);
    • Mode:选择I2S Slave Receive(I2S 从机接收模式);
    • Data Bit Length:选择32 bits(数据位宽 32 位,有效位 24 位);
    • Frame Sync Format:选择Left Justified(左对齐模式,适配麦克风阵列协议);
    • Bit Order:选择MSB First(高位优先);
    • Clock Polarity:选择Rising Edge(上升沿采样);
    • Clock Phase:选择Data Valid on First Edge(第一边沿数据有效);
  3. 配置缓冲区与中断:
    • Receive Buffer Size:设置为4096(4×BUFF_SIZE,对应 32 位 ×1280 个数据);
    • Interrupt Priority:设置为2(中断优先级,高于普通外设,确保实时性);
    • Callback:选择User Defined,输入回调函数名i2s_callback
  4. 配置引脚映射:
    • SSI_BCK:选择P001(与 GPT0 Channel 0 绑定,接收 BCLK 时钟);
    • SSI_FS:选择P002(与 GPT0 Channel 1 绑定,接收 LRCK 帧同步);
    • SSI_RXD:选择P003(接收麦克风数据,连接 74HC4051D 输出端);
  5. 点击Apply保存配置,FSP 自动生成g_i2s0_ctrlg_i2s0_cfg等全局变量及中断向量表。

(4)配置生成与验证

  1. 点击 FSP 配置界面顶部Generate Code按钮,生成底层驱动代码;
  2. 检查项目src目录下的hal_data.c文件,确认g_gpt0_cfgg_i2s0_cfg等配置结构体已自动生成;
  3. 检查hal_entry.c文件,确认中断回调函数i2s_callback已声明,栈区大小配置__STACK_SIZE = 0x2000生效。

4.3 核心代码实现

4.3.1 初始化模块代码

        初始化模块负责 I2C、SSI、GPIO、麦克风阵列、LED、OLED 显示屏等外设的初始化,确保各模块正常工作。

Maix_mic_array.h 头文件关键代码

#ifndef MAIX_MIC_ARRAY_H
#define MAIX_MIC_ARRAY_H#include "hal_data.h"#define BUFF_SIZE 1280  // 每组麦克风数据存储区大小void MIC_Init(void);                          // 麦克风初始化函数
int32_t get_real_value(uint32_t raw);         // 获取声强真实值(32位转24位)
int32_t absolute(int32_t data);               // 获取有符号数绝对值
int32_t max_voice_value_find(uint32_t *g_dest); // 寻找最大声强值#endif /* MAIX_MIC_ARRAY_H */

Maix_mic_array.c 初始化函数实现

#include "Maix_mic_array.h"void MIC_Init(void)
{fsp_err_t err;// 初始化GPT定时器(为I2S提供BCLK时钟)err = R_GPT_Open(&g_gpt0_ctrl, &g_gpt0_cfg);assert(FSP_SUCCESS == err);err = R_GPT_Start(&g_gpt0_ctrl);assert(FSP_SUCCESS == err);// 初始化SSI模块(I2S模式)err = R_SSI_Open(&g_i2s0_ctrl, &g_i2s0_cfg);assert(FSP_SUCCESS == err);// 配置SSI控制寄存器,适配麦克风I2S协议(左对齐模式)R_SSI_Write(&g_i2s0_ctrl, SSI_CR, 0x0000C000); // 设置左对齐、无延迟、BCLK极性
}

sk9822.h 头文件关键代码

#ifndef SK9822_H
#define SK9822_H#include "hal_data.h"#define LED_NUM 12                              // LED灯数量
#define SK9822_CLK BSP_IO_PORT_00_PIN_00        // LED时钟引脚
#define SK9822_DATA BSP_IO_PORT_04_PIN_01       // LED数据引脚void sk9822_init(void);                        // LED初始化
void sk9822_send_data(uint32_t data);          // 发送32位LED数据
void sk9822_start_frame(void);                 // 发送开始帧(32位0)
void sk9822_stop_frame(void);                  // 发送结束帧(32位1)
uint32_t data_sk9822(uint8_t gray, uint8_t b, uint8_t g, uint8_t r); // 生成LED颜色数据
void sk9822_choose_led(uint8_t num, uint8_t gray, uint8_t b, uint8_t g, uint8_t r); // 点亮指定LED
void sk9822_voice_judge(int32_t data, uint8_t num); // 根据声强控制LED#endif /* SK9822_H */

sk9822.c 初始化函数实现

#include "sk9822.h"// 设置时钟线高电平
void SK9822_clk_set(void)
{R_IOPORT_PinWrite(&g_ioport_ctrl, SK9822_CLK, BSP_IO_LEVEL_HIGH);
}// 设置时钟线低电平
void SK9822_clk_clear(void)
{R_IOPORT_PinWrite(&g_ioport_ctrl, SK9822_CLK, BSP_IO_LEVEL_LOW);
}// 设置数据线高电平
void SK9822_data_set(void)
{R_IOPORT_PinWrite(&g_ioport_ctrl, SK9822_DATA, BSP_IO_LEVEL_HIGH);
}// 设置数据线低电平
void SK9822_data_clear(void)
{R_IOPORT_PinWrite(&g_ioport_ctrl, SK9822_DATA, BSP_IO_LEVEL_LOW);
}void sk9822_init(void)
{uint8_t index, cnt, dir, i = 0;uint32_t color;// 初始化GPIO引脚(时钟线与数据线)R_IOPORT_PinCfg(&g_ioport_ctrl, SK9822_CLK, IOPORT_CFG_OUTPUT | IOPORT_CFG_INIT_LOW);R_IOPORT_PinCfg(&g_ioport_ctrl, SK9822_DATA, IOPORT_CFG_OUTPUT | IOPORT_CFG_INIT_LOW);dir = 1; // 1-亮度增加,0-亮度减少cnt = 0;// 执行3次呼吸灯效果(初始化指示)while (i <= 3){if (cnt >= 31){dir = !dir;cnt = 0;i++;}cnt++;// 生成颜色数据(全白,亮度渐变)color = data_sk9822((0xE0 | (dir ? cnt : 31 - cnt)), 255, 255, 255);sk9822_start_frame();for (index = 0; index < LED_NUM; index++){sk9822_send_data(color);}sk9822_stop_frame();R_BSP_SoftwareDelay(20, BSP_DELAY_UNITS_MILLISECONDS);}
}

OLED.h 头文件关键代码

#ifndef OLED_H
#define OLED_H#include "hal_data.h"#define OLED_I2C_ADDR 0x78                     // OLED I2C地址
#define OLED_WIDTH 128                         // 屏幕宽度
#define OLED_HEIGHT 64                         // 屏幕高度void OLED_Init(void);                          // OLED初始化
void OLED_Clear(void);                         // 清屏
void OLED_SetPos(uint8_t x, uint8_t y);        // 设置显示位置
void OLED_ShowChar(uint8_t x, uint8_t y, uint8_t chr, uint8_t size); // 显示字符
void OLED_ShowString(uint8_t x, uint8_t y, uint8_t *str, uint8_t size); // 显示字符串
void OLED_ShowNum(uint8_t x, uint8_t y, uint32_t num, uint8_t len, uint8_t size); // 显示数字
void OLED_ShowChinese(uint8_t x, uint8_t y, uint8_t no); // 显示中文字符(16x16)#endif /* OLED_H */

OLED.c 初始化函数实现

#include "OLED.h"
#include "oledfont.h"// I2C写入命令
void OLED_WriteCmd(uint8_t cmd)
{fsp_err_t err;uint8_t data[2] = {0x00, cmd}; // 0x00-命令模式err = R_I2C_Master_Write(&g_i2c_master0_ctrl, data, 2, false);while (R_I2C_Master_BusyCheck(&g_i2c_master0_ctrl) == FSP_SUCCESS);assert(FSP_SUCCESS == err);
}// I2C写入数据
void OLED_WriteData(uint8_t data)
{fsp_err_t err;uint8_t buf[2] = {0x40, data}; // 0x40-数据模式err = R_I2C_Master_Write(&g_i2c_master0_ctrl, buf, 2, false);while (R_I2C_Master_BusyCheck(&g_i2c_master0_ctrl) == FSP_SUCCESS);assert(FSP_SUCCESS == err);
}void OLED_Init(void)
{fsp_err_t err;// 初始化I2C主控制器err = R_I2C_Master_Open(&g_i2c_master0_ctrl, &g_i2c_master0_cfg);assert(FSP_SUCCESS == err);// OLED初始化序列OLED_WriteCmd(0xAE); // 关闭显示OLED_WriteCmd(0xD5); // 设置显示时钟分频因子OLED_WriteCmd(0x80);OLED_WriteCmd(0xA8); // 设置多路复用率OLED_WriteCmd(0x3F);OLED_WriteCmd(0xD3); // 设置显示偏移OLED_WriteCmd(0x00);OLED_WriteCmd(0x40); // 设置显示起始行OLED_WriteCmd(0xA1); // 设置左右反置OLED_WriteCmd(0xC8); // 设置上下反置OLED_WriteCmd(0xDA); // 设置COM引脚硬件配置OLED_WriteCmd(0x12);OLED_WriteCmd(0x81); // 设置对比度控制OLED_WriteCmd(0xCF);OLED_WriteCmd(0xD9); // 设置预充电周期OLED_WriteCmd(0xF1);OLED_WriteCmd(0xDB); // 设置VCOMH取消选择级别OLED_WriteCmd(0x40);OLED_WriteCmd(0xA4); // 全局显示开启OLED_WriteCmd(0xA6); // 设置正常显示OLED_WriteCmd(0xAF); // 开启显示OLED_Clear();        // 清屏
}

4.3.2 数据采集模块代码

        数据采集模块通过 I2S 中断接收麦克风阵列数据,结合 74HC4051D 多路复用器实现多通道数据切换与采集。

74HC4051D 驱动代码(chip_eight_choose_one.c)

#include "chip_eight_choose_one.h"// 通道选择引脚宏定义
#define S2 BSP_IO_PORT_01_PIN_03
#define S1 BSP_IO_PORT_06_PIN_00
#define S0 BSP_IO_PORT_01_PIN_13// 初始化多路复用器
void chip_eight_choose_one_init(void)
{// 配置S0-S2为输出模式,初始低电平R_IOPORT_PinCfg(&g_ioport_ctrl, S2, IOPORT_CFG_OUTPUT | IOPORT_CFG_INIT_LOW);R_IOPORT_PinCfg(&g_ioport_ctrl, S1, IOPORT_CFG_OUTPUT | IOPORT_CFG_INIT_LOW);R_IOPORT_PinCfg(&g_ioport_ctrl, S0, IOPORT_CFG_OUTPUT | IOPORT_CFG_INIT_LOW);
}// 切换通道(0-2对应Y0-Y2)
void channel_choose(uint32_t channel)
{switch (channel){case 0: // Y0通道(MIC_D0)R_IOPORT_PinWrite(&g_ioport_ctrl, S2, BSP_IO_LEVEL_LOW);R_IOPORT_PinWrite(&g_ioport_ctrl, S1, BSP_IO_LEVEL_LOW);R_IOPORT_PinWrite(&g_ioport_ctrl, S0, BSP_IO_LEVEL_LOW);break;case 1: // Y1通道(MIC_D1)R_IOPORT_PinWrite(&g_ioport_ctrl, S2, BSP_IO_LEVEL_LOW);R_IOPORT_PinWrite(&g_ioport_ctrl, S1, BSP_IO_LEVEL_LOW);R_IOPORT_PinWrite(&g_ioport_ctrl, S0, BSP_IO_LEVEL_HIGH);break;case 2: // Y2通道(MIC_D2)R_IOPORT_PinWrite(&g_ioport_ctrl, S2, BSP_IO_LEVEL_LOW);R_IOPORT_PinWrite(&g_ioport_ctrl, S1, BSP_IO_LEVEL_HIGH);R_IOPORT_PinWrite(&g_ioport_ctrl, S0, BSP_IO_LEVEL_LOW);break;default:break;}
}

I2S 中断回调函数(hal_entry.c)

// 全局变量定义
uint32_t mic_datbuff[3][BUFF_SIZE];          // 麦克风数据缓冲区(3通道)
uint32_t mic_datbuff_div[2][BUFF_SIZE/2];    // 声道分离缓冲区(左/右)
int32_t max_value = 0;                       // 全局最大声强值
uint8_t led_num = 0;                         // LED灯编号(0-11)
uint8_t flag = 1;                            // 工作模式标志(1-工作,0-待机)
uint8_t flag1 = 0;                           // 数据更新标志// 声道分离函数(分离左右声道数据)
void separate_channels(uint32_t voice_channel)
{uint32_t i;for (i = 0; i < BUFF_SIZE; i++){if (i % 2 == 0){// 偶数索引-左声道mic_datbuff_div[0][i/2] = mic_datbuff[voice_channel][i];}else{// 奇数索引-右声道mic_datbuff_div[1][(i-1)/2] = mic_datbuff[voice_channel][i];}}
}// I2S中断回调函数(核心数据采集逻辑)
void i2s_callback(i2s_callback_args_t *p_args)
{static uint32_t current_ch = 0;          // 当前通道(0-2)static int32_t ch_max_val[3] = {0};      // 各通道最大声强值static int32_t ch_second_val[3] = {0};   // 各通道次大声强值static uint8_t ch_max_ch[3] = {0};       // 各通道最大声强声道(0-左,1-右)static uint8_t ch_second_ch[3] = {0};    // 各通道次大声强声道(0-左,1-右)i2s_event_t event = p_args->event;if (event == I2S_EVENT_RX_FULL){// 1. 分离当前通道左右声道数据separate_channels(current_ch);// 2. 计算左声道最大声强值int32_t left_max = max_voice_value_find(mic_datbuff_div[0]);// 计算右声道最大声强值int32_t right_max = max_voice_value_find(mic_datbuff_div[1]);// 3. 记录当前通道主次声强信息if (left_max > right_max){ch_max_val[current_ch] = left_max;ch_max_ch[current_ch] = 0;ch_second_val[current_ch] = right_max;ch_second_ch[current_ch] = 1;}else{ch_max_val[current_ch] = right_max;ch_max_ch[current_ch] = 1;ch_second_val[current_ch] = left_max;ch_second_ch[current_ch] = 0;}// 4. 准备处理下一通道current_ch++;if (current_ch >= 3){// 5. 完成3通道采集,计算全局主次声强int32_t global_max = 0;int32_t global_second = 0;uint8_t max_ch = 0;uint8_t second_ch = 0;uint8_t max_ch_num = 0;uint8_t second_ch_num = 0;// 找出全局最大声强值及对应通道for (uint8_t i = 0; i < 3; i++){if (ch_max_val[i] > global_max){global_second = global_max;second_ch = max_ch;second_ch_num = i;global_max = ch_max_val[i];max_ch = ch_max_ch[i];max_ch_num = i;}else if (ch_max_val[i] > global_second){global_second = ch_max_val[i];second_ch = ch_max_ch[i];second_ch_num = i;}}// 6. 计算声源方向编号uint8_t max_dir = 4 * max_ch_num + 2 * max_ch;uint8_t second_dir = 4 * second_ch_num + 2 * second_ch;max_value = global_max;// 7. 有效声源判断(声强>40000)if (global_max > 40000){// 计算强度比float b = (float)global_max / global_second;// 判断主次方向是否相邻(角度差<60°)if (abs(max_dir - second_dir) <= 2 || abs(max_dir - second_dir) >= 10){// 相邻方向,根据强度比修正if (b < 1.2 && b > 1.08){led_num = (max_dir + second_dir) / 2;}else if (b > 2){led_num = max_dir;}else{led_num = max_dir;}}else{led_num = max_dir;}// 方向编号限制在0-11led_num %= 12;flag1 = 1; // 设置数据更新标志}else{max_value = 0;led_num = 0;}// 重置通道索引current_ch = 0;}// 8. 切换到下一通道,启动数据采集channel_choose(current_ch);R_SSI_Read(&g_i2s0_ctrl, mic_datbuff[current_ch], 4 * BUFF_SIZE);}
}

4.3.3 信号处理模块代码

        信号处理模块负责声强计算、方向判断、滤波处理等功能,核心函数包括最大声强查找、绝对值计算、声强真实值转换等。

Maix_mic_array.c 信号处理函数实现

#include "Maix_mic_array.h"// 获取声强真实值(32位数据右移8位,保留24位有效数据)
int32_t get_real_value(uint32_t raw)
{return (int32_t)raw >> 8; // 符号位扩展
}// 获取有符号数绝对值
int32_t absolute(int32_t data)
{return data < 0 ? -data : data;
}// 寻找最大声强值(滤除小于80000的噪声)
int32_t max_voice_value_find(uint32_t *g_dest)
{uint32_t j = 0;int32_t max_temp = 0;int32_t data[BUFF_SIZE];// 转换数据并滤除噪声for (j = 0; j < BUFF_SIZE; j++){data[j] = get_real_value(g_dest[j]);if (absolute(data[j]) > 80000) // 滤除小信号噪声{if (absolute(data[j]) > max_temp){max_temp = absolute(data[j]);}}}return max_temp;
}

4.3.4 外设驱动模块代码

        外设驱动模块包括 LED 控制、OLED 显示、按键检测等功能,实现声源方向可视化与信息展示。

sk9822.c LED 控制函数实现

#include "sk9822.h"// 生成SK9822格式颜色数据(32位)
uint32_t data_sk9822(uint8_t gray, uint8_t b, uint8_t g, uint8_t r)
{uint32_t tem;gray &= 0x1F; // 亮度限制在0-31// 数据格式:111(控制位)+5位亮度 + 8位蓝色 + 8位绿色 + 8位红色tem = ((0xE0 | gray) << 24) | (b << 16) | (g << 8) | r;return tem;
}// 发送32位数据到SK9822
void sk9822_send_data(uint32_t data)
{uint32_t mask;// 从最高位(bit31)开始逐位发送for (mask = 0x80000000; mask > 0; mask >>= 1){SK9822_clk_clear(); // 时钟下降沿准备发送R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MICROSECONDS);if (data & mask){SK9822_data_set(); // 发送1}else{SK9822_data_clear(); // 发送0}SK9822_clk_set(); // 时钟上升沿锁存数据R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MICROSECONDS);}
}// 发送开始帧
void sk9822_start_frame(void)
{sk9822_send_data(0x00000000); // 32位0
}// 发送结束帧
void sk9822_stop_frame(void)
{sk9822_send_data(0xFFFFFFFF); // 32位1
}// 点亮指定LED(其他熄灭)
void sk9822_choose_led(uint8_t num, uint8_t gray, uint8_t b, uint8_t g, uint8_t r)
{uint8_t i;sk9822_start_frame();for (i = 0; i < LED_NUM; i++){if (i == num){sk9822_send_data(data_sk9822(gray, b, g, r));}else{sk9822_send_data(data_sk9822(0, 0, 0, 0)); // 其他LED熄灭}}sk9822_stop_frame();
}// 根据声强控制LED
void sk9822_voice_judge(int32_t data, uint8_t num)
{if (absolute(data) > 40000){// 中等强度(40000-80000):绿色if (absolute(data) < 80000){sk9822_choose_led(num, 5, 0, 200, 0);}// 高强度(>80000):蓝色else{sk9822_choose_led(num, 15, 200, 0, 0);}}else{// 声强不足:熄灭sk9822_choose_led(num, 0, 0, 0, 0);}
}

OLED.c 显示函数实现

#include "OLED.h"
#include "oledfont.h"// 设置显示位置(x:0-127,y:0-7)
void OLED_SetPos(uint8_t x, uint8_t y)
{OLED_WriteCmd(0xB0 + y);OLED_WriteCmd(((x & 0xF0) >> 4) | 0x10);OLED_WriteCmd(x & 0x0F);
}// 清屏
void OLED_Clear(void)
{uint8_t x, y;for (y = 0; y < 8; y++){OLED_SetPos(0, y);for (x = 0; x < 128; x++){OLED_WriteData(0x00);}}
}// 显示中文字符(16x16)
void OLED_ShowChinese(uint8_t x, uint8_t y, uint8_t no)
{uint8_t t, adder = 0;OLED_SetPos(x, y);for (t = 0; t < 16; t++){OLED_WriteData(Chinese[no][t]);adder += 1;}OLED_SetPos(x, y + 1);for (t = 0; t < 16; t++){OLED_WriteData(Chinese[no][t + 16]);adder += 1;}
}// 显示数字
void OLED_ShowNum(uint8_t x, uint8_t y, uint32_t num, uint8_t len, uint8_t size)
{uint8_t t, temp;uint8_t enshow = 0;for (t = 0; t < len; t++){temp = (num / oled_pow(10, len - t - 1)) % 10;if (enshow == 0 && t < len - 1){if (temp == 0){OLED_ShowChar(x + t * size, y, ' ', size);continue;}else{enshow = 1;}}OLED_ShowChar(x + t * size, y, temp + '0', size);}
}// 显示字符
void OLED_ShowChar(uint8_t x, uint8_t y, uint8_t chr, uint8_t size)
{uint8_t t, i;chr = chr - ' ';OLED_SetPos(x, y);for (t = 0; t < size; t++){OLED_WriteData(F8X16[chr][t]);}
}// 显示字符串
void OLED_ShowString(uint8_t x, uint8_t y, uint8_t *str, uint8_t size)
{uint8_t t = 0;while (str[t] != '\0'){OLED_ShowChar(x + t * size, y, str[t], size);t++;}
}// 幂运算函数(辅助显示数字)
uint32_t oled_pow(uint8_t m, uint8_t n)
{uint32_t result = 1;while (n--){result *= m;}return result;
}

4.3.5 主控制模块代码

        主控制模块是系统的核心,负责调用各模块函数,实现工作模式切换、LED 方向指示、OLED 信息显示等功能。

hal_entry.c 主函数实现

#include "hal_data.h"
#include "Maix_mic_array.h"
#include "chip_eight_choose_one.h"
#include "sk9822.h"
#include "OLED.h"// 全局变量声明
extern uint32_t mic_datbuff[3][BUFF_SIZE];
extern int32_t max_value;
extern uint8_t led_num;
extern uint8_t flag;
extern uint8_t flag1;void hal_entry(void)
{fsp_err_t err1;uint32_t j = 0;// 1. 初始化I2C主控制器(用于OLED通信)err1 = R_I2C_Master_Open(&g_i2c_master0_ctrl, &g_i2c_master0_cfg);assert(FSP_SUCCESS == err1);  // 确保初始化成功,失败则触发断言// 2. 初始化核心硬件模块MIC_Init();                    // 麦克风阵列初始化(含SSI、GPT配置)chip_eight_choose_one_init();  // 74HC4051D多路复用器初始化sk9822_init();                 // SK9822 LED阵列初始化OLED_Init();                   // SSD1306 OLED显示屏初始化// 3. 初始化通道选择与首次I2S数据采集channel_choose(0);  // 默认选择0通道(MIC_D0,对应0号、1号麦克风)R_SSI_Read(&g_i2s0_ctrl, mic_datbuff[0], 4 * BUFF_SIZE);  // 启动I2S接收,4*BUFF_SIZE对应32位数据长度// 4. 主循环:系统核心逻辑调度while (1){// 4.1 按键检测与工作模式切换(消抖处理)if (R_BSP_PinRead(BSP_IO_PORT_01_PIN_01) == BSP_IO_LEVEL_LOW){R_BSP_SoftwareDelay(20, BSP_DELAY_UNITS_MILLISECONDS);  // 20ms消抖,避免按键误触发while (R_BSP_PinRead(BSP_IO_PORT_01_PIN_01) == BSP_IO_LEVEL_LOW);  // 等待按键释放flag = !flag;  // 切换模式:1-工作模式(实时定位),0-待机模式(停止更新)// 模式切换提示:待机模式熄灭LED,工作模式重置显示if (flag == 0){sk9822_choose_led(led_num, 0, 0, 0, 0);  // 待机时熄灭当前LEDOLED_Clear();OLED_ShowString(16, 2, "Standby Mode", 8);  // 显示待机提示}else{OLED_Clear();OLED_ShowString(16, 2, "Working Mode", 8);   // 显示工作提示R_BSP_SoftwareDelay(1000, BSP_DELAY_UNITS_MILLISECONDS);  // 提示停留1秒}}// 4.2 工作模式下的声源更新(数据更新标志触发)if (flag1 == 1 && flag == 1){flag1 = 0;  // 清除更新标志,避免重复处理sk9822_voice_judge(max_value, led_num);  // 根据声强更新LED指示方向}// 4.3 OLED屏幕刷新(每0.8秒更新一次,主循环含40ms延时,j=20时累计0.8秒)if (j >= 20 && flag == 1){j = 0;  // 重置计数器// 有效声源判断(声强>40000,过滤微弱噪声)if (max_value > 40000){OLED_Clear();  // 清屏避免显示重叠// 第一行:显示声强最大的麦克风编号(led_num映射为1-6号麦克风)OLED_ShowChinese(0, 0, 0);    // 显示“麦”OLED_ShowChinese(16, 0, 1);   // 显示“克”OLED_ShowChinese(32, 0, 2);   // 显示“风”OLED_ShowChinese(48, 0, 3);   // 显示“号”OLED_ShowChinese(64, 0, 4);   // 显示“码”OLED_ShowChar(80, 0, ':', 8); // 显示“:”OLED_ShowNum(96, 0, (uint8_t)(led_num + 1) / 2 + 1, 1, 8);  // led_num/2映射为0-5,+1后为1-6号麦克风// 第二行:显示声源角度范围(每个麦克风对应60°,根据led_num映射)OLED_ShowChinese(0, 2, 5);    // 显示“角”OLED_ShowChinese(16, 2, 6);   // 显示“度”OLED_ShowChar(32, 2, ':', 8); // 显示“:”switch ((led_num + 1) / 2){case 0:OLED_ShowString(48, 2, "0-60°", 8);break;case 1:OLED_ShowString(48, 2, "60-120°", 8);break;case 2:OLED_ShowString(48, 2, "120-180°", 8);break;case 3:OLED_ShowString(48, 2, "180-240°", 8);break;case 4:OLED_ShowString(48, 2, "240-300°", 8);break;case 5:OLED_ShowString(48, 2, "300-360°", 8);break;default:OLED_ShowString(48, 2, "Unknown", 8);break;}// 第三行:显示最大声强值OLED_ShowChinese(0, 4, 7);    // 显示“声”OLED_ShowChinese(16, 4, 8);   // 显示“强”OLED_ShowChar(32, 4, ':', 8); // 显示“:”OLED_ShowNum(48, 4, max_value, 5, 8);  // 显示5位声强值OLED_ShowString(96, 4, "dB", 8);       // 显示单位}else{// 无有效声源时显示待机界面OLED_Clear();OLED_ShowString(32, 2, "Waiting for Sound", 8);}}// 4.4 主循环延时与计数器更新(40ms延时,控制屏幕刷新频率)R_BSP_SoftwareDelay(40, BSP_DELAY_UNITS_MILLISECONDS);j++;}
}// 错误处理函数:当FSP API调用失败时触发(可选实现,用于调试)
void fsp_err_t handle_error(fsp_err_t err, const char *func, uint32_t line)
{if (err != FSP_SUCCESS){// 此处可扩展:通过串口打印错误信息,或控制LED闪烁报警while (1);  // 错误时进入死循环,便于调试定位问题}
}

        代码说明

  1. 初始化阶段:依次完成 I2C(OLED 通信)、麦克风阵列(含 SSI/I2S、GPT 时钟)、多路复用器、LED、OLED 的初始化,确保硬件模块处于就绪状态。其中,R_SSI_Read启动首次 I2S 数据采集,为后续中断回调触发做准备。
  2. 按键处理:通过R_BSP_PinRead检测用户按键,结合 20ms 消抖逻辑避免误触发,切换工作 / 待机模式。待机模式下熄灭 LED 并显示提示,工作模式下重置显示界面。
  3. 声源更新:当flag1(I2S 中断回调设置的 data 更新标志)为 1 时,调用sk9822_voice_judge根据声强值控制 LED 点亮 —— 中等声强(40000-80000)亮绿色,高强度(>80000)亮蓝色,无有效声强时熄灭。
  4. OLED 显示:每 0.8 秒(主循环 40ms 延时 ×20 次)刷新一次屏幕。有效声源时显示麦克风编号、角度范围、声强值;无有效声源时显示 “等待声音” 提示,确保信息实时且不闪烁。
  5. 错误处理:通过assert和自定义handle_error函数确保初始化阶段无故障,主循环中通过死循环避免错误扩散,便于调试。

五、系统测试与结果分析

5.1 测试环境搭建

5.1.1 硬件连接验证

  1. 电源连接:使用万用表检测各模块供电电压 —— 开发板 5V 输出至麦克风阵列 VIN,3.3V 输出至 74HC4051D VCC 与 OLED VCC,确保无短路、虚接,所有 GND 共地。
  2. 信号连接:通过示波器检测 I2S 信号(MIC_CK、MIC_WS、MIC_D0)—— 上电后 MIC_CK 应输出 3.072MHz 时钟,MIC_WS 输出 7.8KHz 帧同步信号,确保信号稳定无杂波。
  3. 外设响应:初始化后,SK9822 LED 应执行 3 次呼吸灯效果,OLED 显示清屏后的待机界面,按键按下时模式切换提示正常,说明硬件连接与初始化无误。

5.1.2 测试工具与场景

  • 测试工具:智能手机(播放固定频率音频,作为可控声源)、分贝仪(测量声强,验证系统检测准确性)、串口助手(打印调试日志,监控变量变化)。
  • 测试场景
    1. 静态测试:固定声源位置(如距离系统 1 米,角度 0°),观察 LED 与 OLED 显示是否稳定。
    2. 动态测试:移动声源(顺时针 360° 旋转),观察 LED 是否跟随声源移动,OLED 角度更新是否实时。
    3. 噪声干扰测试:在环境噪声(如办公室背景音,约 50dB)下,测试系统是否能准确识别目标声源(>60dB)。

5.2 测试结果与分析

5.2.1 静态测试结果

声源角度理论麦克风编号实际 LED 编号OLED 显示角度定位误差声强检测值(dB)
1 号00-60°≤±5°65(手机中等音量)
60°2 号260-120°≤±8°63
180°4 号6180-240°≤±10°61
300°6 号10300-360°≤±7°62

        分析:在静态场景下,系统定位误差均≤±10°,满足设计指标(≤±15°);声强检测值与分贝仪测量值偏差≤3dB,说明声强计算算法准确,噪声滤波效果良好。

5.2.2 动态测试结果

  • 响应速度:声源移动速度为 0.5m/s 时,LED 跟随延迟≤0.2 秒,OLED 更新延迟≤0.8 秒(与屏幕刷新频率一致),无明显滞后。
  • 方向识别:声源顺时针旋转 360° 过程中,LED 依次点亮 0→2→4→6→8→10→0,无跳变或误识别;当声源位于两个麦克风中间(如 30°),LED 点亮 1 号(中间方向),符合方向修正逻辑。

5.2.3 噪声干扰测试

  • 抗干扰能力:环境噪声≤50dB 时,系统能准确识别≥60dB 的目标声源,无误触发;当噪声≥70dB 时,需将目标声源声强提升至≥80dB 才能稳定识别,说明系统抗干扰能力受环境噪声影响,可通过优化滤波算法(如自适应滤波)进一步提升。

5.3 常见问题与解决方案

问题现象可能原因解决方案
LED 不亮,OLED 无显示

1. 电源未接通;

2. I2C/SSI 初始化失败;

3. 引脚虚接

1. 检查电源适配器与杜邦线;

2. 通过串口打印初始化状态,排查R_I2C_Master_Open等 API 返回值;

3. 重新焊接 74HC4051D 引脚,用万用表检测通断

声源定位跳变

1. 麦克风阵列存在故障(如某麦克风损坏);

2. 滤波阈值过低,噪声误触发;3. I2S 数据传输错误

1. 替换麦克风阵列测试,或通过代码单独读取各麦克风数据排查;

2. 提高max_voice_value_find中噪声过滤阈值(如从 80000 调整为 100000);3. 用示波器检测 I2S 数据,确保SSI_BCK0_A时钟稳定

OLED 显示乱码

1. I2C 地址错误;

2. 显示缓存数据错误;

3. 字库未正确加载

1. 确认 SSD1306 地址为 0x78(而非 0x7A),修改OLED_I2C_ADDR

2. 检查OLED_WriteData函数数据传输长度,确保为 2 字节(命令 + 数据);

3. 验证oledfont.h中字库数组是否正确,重新导入字库文件

六、项目优化与拓展方向

6.1 现有系统优化点

  1. 算法优化
    • 滤波算法:当前采用最大值滤波,可替换为自适应滤波(如维纳滤波)或小波变换,进一步抑制环境噪声,提升低信噪比场景下的定位精度。
    • 方向计算:引入高分辨率谱估计(如 MVDR 算法),替代最大值比较法,实现多声源同时定位,满足多人对话等复杂场景需求。
  2. 硬件优化
    • 麦克风阵列:替换为更高灵敏度的 MEMS 麦克风(如 SGM3770),降低最小可检测声强,提升远距离定位能力。
    • 电源设计:增加 LDO 稳压模块(如 XC6206-3.3V),减少电源噪声对麦克风信号的干扰,尤其在工业环境中效果显著。
  3. 软件优化
    • 多线程调度:基于 FreeRTOS 操作系统,将数据采集、信号处理、显示控制拆分为独立任务,通过任务优先级调度提升系统实时性。
    • 低功耗设计:在待机模式下关闭 SSI、GPT 等模块时钟,降低 MCU 功耗,延长电池供电场景下的使用时间。

6.2 功能拓展方向

  1. 三维定位:增加垂直方向麦克风阵列(如 2 层 7 路麦克风),结合惯导传感器(如 MPU6050),计算声源的垂直角度(俯仰角),实现三维空间定位。
  2. 语音增强:引入波束成形技术(如延迟求和波束成形),根据声源方向动态调整麦克风阵列增益,增强目标声源信号,抑制其他方向噪声,适用于会议系统定向拾音。
  3. 无线传输:通过开发板 WiFi 模块(如 ESP8266)将定位结果与音频数据上传至上位机(如 PC 或手机 APP),实现远程监控与数据分析。
  4. 异常声报警:预设异常声阈值(如玻璃破碎声、尖叫声,声强 > 100dB),当系统检测到异常声时,通过 LED 闪烁、蜂鸣器报警,并向上位机发送报警信息,适用于安防监控场景。

总结·

        本项目开发的声源定位系统,从硬件选型、环境搭建到软件实现、测试优化,完整覆盖了嵌入式系统开发的全流程。

        未来若想进一步探索,还可以将人工智能算法与声源定位相结合(例如基于深度学习的声源分类与定位),提升系统在复杂场景下的适应性。

http://www.dtcms.com/a/592717.html

相关文章:

  • Vue 2 转 Vue 3, 差异不同点汇总, 快速上手vue3
  • 工业级环境传感器的网络通信与协议兼容性分析
  • 个人网站建设 免费下载一个公司备案两个网站
  • PR(1)11.10
  • 数据结构(19)
  • LWIP--以太网
  • 3分钟搞定,接口管理工具PostIn安装和配置
  • 【剑斩OFFER】算法的暴力美学——在排序数组中查找元素的第一个和最后一个位置
  • Agentic TASK01
  • 麒麟最新操作系统登录锁定配置
  • RLHF、DPO 算法
  • 网站排名优化课程网站建设公司华网天下官网
  • 营销型企业网站建设教案wordpress中调用文章内容
  • MySQL 错误 1046 (3D000) 是因为在执行 SQL 语句时 没有选择当前数据库
  • Jenkins Jobs 备份与恢复
  • HTTP和HTTPS工作原理、安全漏洞及防护措施全面解析
  • 百度怎样建设网站网站建设风险怎样规避
  • 使用Docker和Selenium构建自动化测试环境
  • 网站建设公司怎么写宣传语阿里云1m服务器可以搭建网站
  • 12.1 Qt 窗口与视口 详解
  • Flink原理与实战(java版)#第1章 Flink快速入门(第一节IDE词频统计)
  • 了解GPTs
  • Biotin-FAPI-4,在化学研究和生物体系实验中主要用途
  • uni-app vue2 在 iOS 退出应用后将 cookie 清空了
  • 有趣的网站网址大学院系网站建设
  • 常见的接口测试工具有哪些?(Postman、JMeter、RestAssured等)
  • NJet event框架性能百倍提升,Why and How
  • 解析请求体内容(如 JSON、表单数据、XML 等) 将原始数据转换为 Python 数据结构 使转换后的数据可在 request. ...
  • 网页素材及网站架构制作用asp做网站优势
  • 网站建设指引快速提高网站排名