当前文档版本为 NDK-v1.1.0,您可以访问当前页面的 开发中 版本以获取最近可能的更新。

BLE HID MIC

1 功能概述

本例程演示基于PAN1070芯片的BLE HID语音传输功能,通过MIC传感器采集语音信号,经ADC采样、ADPCM编码后,通过BLE协议传输至接收端(如Google TV盒子),实现语音控制功能。

主要特性

  • 支持8kHz/16kHz采样率,16位量化

  • IMA ADPCM编码,压缩比4:1

  • 遵循Google Voice over BLE spec 1.0协议

  • 支持BLE HID服务,蓝牙广播名称为”Chromecast Remote”

  • 按键触发语音传输(KEY1启动,再次按下停止)

应用场景

  • 智能电视遥控器语音控制

  • BLE语音采集与传输

2 环境要求

硬件环境

  • 开发板: PAN107x EVB(QFN32模块 + 底板)

  • MIC传感器模块(二选一):

    1. 高感度麦克风传感器模块(淘宝型号)

    2. 声音传感器模块LM386(推荐,放大200倍)

  • 调试工具: UART串口(波特率921600,8n1)

硬件连接

PAN1070底板

MIC传感器模块

说明

5V或3.3V

VCC

电源正极

GND

GND

电源负极

P07

AO

ADC模拟量输入

软件环境

  • IDE: Keil MDK

  • SDK: PAN1070 BLE Nimble协议栈

  • 测试设备: Google TV Streamer 4K电视盒子

注意事项

  1. MIC传感器模块AO脚输出偏置电压需调节至1.65V(无声音输入时)

  2. Google TV盒子需要连接支持访问谷歌服务器的网络(或安装VPN软件)

3 编译和烧录

工程位置

<SDK_ROOT>\nimble\pan10xx_samples\solutions\ble_hid_mic\keil_107x

编译步骤

  1. 使用Keil MDK打开工程文件 ble_hid_mic.uvprojx

  2. 选择目标设备为PAN107x

  3. 点击”Build”或”Rebuild”编译工程

  4. 编译成功后,在输出窗口查看Flash和RAM使用情况

烧录步骤

  1. 连接PAN107x EVB开发板到PC

  2. 使用Keil的Flash下载功能或外部烧录工具

  3. 烧录生成的.hex.bin文件

  4. 复位开发板,观察串口日志输出

配置选项

app_ble.h或相关配置文件中可调整:

/* Select ADC sample rate */
#define ADC_SAMPLE_RATE   8000    // 8kHz
// #define ADC_SAMPLE_RATE   16000  // 16kHz (recommended)

/* ADC data buffer length */
#define ADC_DATA_LEN      160     // 20ms@8kHz or 10ms@16kHz

4 关键模块说明

4.1 app_ble.c

核心功能实现:

  • BLE初始化: 配置广播参数、GATT服务、连接参数

  • ADC DMA配置: 使用PWM触发ADC采样,DMA传输数据

  • 语音数据传输: 实现ble_voice_data_notify()发送编码后的语音数据

  • BLE数据交互协议: 实现Google Voice over BLE协议的命令处理

关键函数

void app_ble_init(void);           // BLE stack initialization
void adc_pwm_dma_init(void);      // ADC PWM DMA initialization
void adc_dma_start(void);         // Start ADC DMA transfer
void adc_dma_stop(void);          // Stop ADC DMA transfer
void ble_voice_data_notify(void);  // Send voice data notification

4.2 hog.c

HID over GATT实现:

  • 按键GPIO中断配置(P06, P12)

  • 按键事件处理(音量键按下/释放)

  • 触发语音传输启动/停止

关键函数

void hog_init(void);              // HOG initialization
void key_vol_up(void);           // Volume+ key handler (start voice)
void key_vol_down(void);         // Volume- key handler (stop voice)
void key_process(void);          // Key event processing main logic

4.3 adpcm.c

IMA ADPCM编码/解码算法实现:

  • adpcm_encoder(): ADPCM编码(16bit PCM → 4bit ADPCM)

  • adpcm_decoder(): ADPCM解码(4bit ADPCM → 16bit PCM)

  • adpcm_encoder_custom(): 自定义版本编码器(适配左移4bit的ADC数据)

技术特点

  • 压缩比4:1(16bit → 4bit)

  • 算法复杂度低,PAN1070@32MHz耗时约1.3ms

  • 自适应量化步长,避免溢出或精度损失

5 技术实现详解

5.1 MIC电路分析

本例程测试了两种MIC传感器模块:

5.1.1 高感度麦克风传感器模块

高感度麦克风传感器模块

High-sensitivity Microphone Sensor Module

参数

  • +:外接电源正极(5V直流电供电)

  • G:外接电源负极(GND)

  • AO:模拟量输出

  • DO:数字量输出(声音超过阈值输出高电平)

  • 电位器:设定阈值(逆时针增加阈值,顺时针减小阈值)

适配PAN1070的修改

  1. PAN1070 ADC检测范围配置为0~VDD(3.3V)

  2. 调节模块电位器,使无声音时AO输出电压为1.65V

  3. 实际使用效果:底噪小,但声音采集距离较短

5.1.2 声音传感器模块LM386(推荐)

声音传感器模块LM386

LM386 Sound Sensor Module

参数

  • LM386:音频放大芯片(放大200倍)

  • VCC:外接电源正极(3.3V~5.3V)

  • GND:外接电源负极

  • AOUT:模拟量输出

  • DOUT:数字量输出

  • 电位器A:AO灵敏度调节

  • 电位器D:DO阈值调节

模块原理图

LM386模块原理图

LM386 Module Schematic

实际使用效果

  • 调节电位器A,AOUT输出电压维持1.85V不变

  • 底噪较大,但声音采集距离较远(约20cm)

注意:为避免ADC输入饱和,需调节电位器使无声音时AO输出约为VDD/2(1.65V)。

5.2 ADC采集配置

5.2.1 硬件配置

  • ADC通道:通道5,对应GPIO P07

  • 输入范围:高压档 0~VDD(0~3.3V)

  • 采样位数:12bit(软件中左移4bit,扩展为16bit以匹配音频处理系统)

  • 触发方式:PWM触发(减少CPU资源消耗)

  • 采样频率:8kHz 或 16kHz(通过ADC_SAMPLE_RATE宏配置)

5.2.2 软件实现

初始化流程adc_pwm_dma_init()函数):

/* 1. Enable clocks */
CLK_AHBPeriphClockCmd(CLK_AHBPeriph_DMAC, ENABLE);
CLK_APB1PeriphClockCmd(CLK_APB1Periph_ADC | 
                       CLK_APB1Periph_PWM0_EN | 
                       CLK_APB1Periph_PWM0_CH01, ENABLE);

/* 2. Initialize ADC */
ADC_Init(ADC, true);  // Use hardware calibration data
ADC_Open(ADC, ADC_CHEN_CHEN5_Msk);  // Enable channel 5
ADC_SelInputRange(ADC, ADC_INPUTRANGE_HIGH);  // 0~VDD
ADC_PowerOn(ADC);

/* 3. Configure PWM trigger */
PWM_ConfigOutputChannel(PWM, 0, ADC_SAMPLE_RATE, 50, OPERATION_EDGE_ALIGNED);
PWM_EnableOutput(PWM, BIT0);
PWM_Start(PWM, BIT0);

/* 4. Configure DMA */
DMAC_Init(DMA);
RxConfigTmp.TransferType = DMAC_TransferType_Per2Mem;
RxConfigTmp.PeripheralSrc = DMAC_Peripheral_ADC;
DMAC_StartChannel(DMA, gRxChNum, (void*)&(ADC->DAT), RcvDataBuf, ADC_DATA_LEN);

数据缓冲区

#define ADC_DATA_LEN    160  // 20ms@8kHz, 10ms@16kHz
uint16_t RcvDataBuf[ADC_DATA_LEN * 2];  // Double buffer
volatile uint8_t RcvIdx = 0;  // Buffer index

5.2.3 DMA传输完成中断

void DMA_IRQHandlerOverlay()
{
    if(DMAC_IntFlag(DMA, 0, DMAC_FLAG_INDEX_TFR)){
        DMAC_ClrIntFlag(DMA, 0, DMAC_FLAG_INDEX_TFR);
        RcvIdx = !RcvIdx;  // Switch buffer
        voice_data_notify_flag = 1;  // Notify application layer to process data
        adc_dma_start();  // Restart DMA transfer
    }
}

5.3 ADC数据处理

5.3.1 数据格式转换

ADC采集的12bit数据在软件中左移4bit,转换为16bit格式:

/* ADC raw data: 12bit (0~4095) */
/* After left shift 4bit: 16bit (0~65520) */
/* Center voltage 1.65V corresponds to: 2048 << 4 = 32768 */

说明:虽未增加实际分辨率,但可匹配16bit音频处理系统的输入格式要求,避免频繁位操作。

5.3.2 软件滤波

当前版本未实现软件滤波算法,后续增加:

  • 快速抑制突发噪声

  • 平滑噪声

  • 动态响应优化

5.4 ADPCM压缩算法

5.4.1 算法原理

本例程采用**IMA ADPCM(Adaptive Differential Pulse Code Modulation)**编码算法,与Google TV盒子端解码算法一致。

核心思想

  • 对音频信号的差值进行量化,而非直接量化信号本身

  • 根据信号变化动态调整量化步长(自适应)

  • 将16bit PCM数据压缩为4bit ADPCM数据(压缩比4:1)

5.4.2 编码流程

int adpcm_encoder(short *indata, unsigned char *outdata, int len)
{
    /* Step 1: Calculate difference between current sample value and predicted value */
    diff = val - valpred;
    
    /* Step 2: Quantize the difference based on step size (4bit) */
    delta = 0;
    if (udiff >= step) { delta = 4; udiff -= step; vpdiff += step; }
    step >>= 1;
    if (udiff >= step) { delta |= 2; udiff -= step; vpdiff += step; }
    step >>= 1;
    if (udiff >= step) { delta |= 1; vpdiff += step; }
    
    /* Step 3: Update predicted value */
    if (sign) valpred -= vpdiff;
    else valpred += vpdiff;
    
    /* Step 4: Update quantization step size and index */
    index += indexTable[delta];
    step = stepsizeTable[index];
    
    /* Step 5: Output 4bit ADPCM data */
    // Pack two 4bit data into one byte
}

5.4.3 技术特点

特性

说明

压缩效率

压缩比1:4(16bit→4bit),存储/传输成本大幅降低

实时性

算法复杂度低,适用于单片机、DSP等资源受限设备

自适应能力

量化步长动态调整,适应不同幅度信号变化

多声道支持

立体声需独立处理左右声道数据

CPU消耗

PAN1070@32MHz,耗时约1.3ms

5.4.4 数据流

MIC → ADC(12bit) → 左移4bit(16bit) → ADPCM编码(4bit) → BLE传输
  • 8kHz采样率:128 kbps → 32 kbps(压缩后)

  • 16kHz采样率:256 kbps → 64 kbps(压缩后)

5.5 BLE数据交互协议

本例程遵循**《Google Voice over BLE spec 1.0》**规范。

5.5.1 语音传输服务(GATT Service)

类型

简称

UUID

属性

ATV Voice Service

ATVV_SERVICE_UUID

AB5E0001-5A21-4F05-BC7D-AF01F617B664

Primary Service

Write Characteristic

ATVV_CHAR_TX

AB5E0002-5A21-4F05-BC7D-AF01F617B664

Write (WriteNoResponse)

Read Characteristic

ATVV_CHAR_RX

AB5E0003-5A21-4F05-BC7D-AF01F617B664

Notify

Control Characteristic

ATVV_CHAR_CTL

AB5E0004-5A21-4F05-BC7D-AF01F617B664

Notify

Audio Characteristic

ATVV_CHAR_AUDIO

-

Notify

特性说明

  • ATVV_CHAR_TX:Android TV设备写入控制信息或指令,采用WriteNoResponse模式

  • ATVV_CHAR_RX:通过Notification返回控制数据,首字节包含关键信息

  • ATVV_CHAR_CTL:控制通道,用于交互命令(GET_CAPS、CAPS_RESP、AUDIO_START、AUDIO_STOP等)

  • ATVV_CHAR_AUDIO:音频数据流通道,实时传输语音数据

5.5.2 语音传输命令

语音传输命令交互

BLE Voice Commands

主要用到以下四条命令:

GET_CAPS(主机→遥控器)

字段

长度

说明

version

2字节

ATV支持的最高spec版本(0x0100 = v1.0)

legacy_0x0003

2字节

兼容spec 0.4e的常量(0x0003)

supported assistant interaction models

1字节

支持的交互模型(0x00=On-request, 0x01=PTT, 0x03=HTT)

CAPS_RESP(遥控器→主机)

字段

长度

说明

version

2字节

遥控器实现的spec版本

codecs supported

1字节

支持的音频编解码器(0x01=ADPCM 8kHz, 0x02=ADPCM 16kHz)

assistant interaction model

1字节

使用的交互模型

audio frame size

2字节

期望的音频帧大小(默认20字节,推荐160字节)

extra configuration

1字节

是否启用DLE和MTU协商

remote fw data

任意

遥控器固件信息(可选)

AUDIO_START(遥控器→主机)

字段

长度

说明

reason

1字节

启动原因(0x00=MIC_OPEN, 0x01=PTT, 0x03=HTT)

codec used

1字节

使用的编解码器(0x01=ADPCM 8kHz, 0x02=ADPCM 16kHz)

stream id

1字节

音频流标识符

AUDIO_STOP(遥控器→主机)

字段

长度

说明

reason

1字节

停止原因(0x00=MIC_CLOSE, 0x02=HTT释放, 0x04=新的AUDIO_START, 等)

5.5.3 数据交互流程

BLE数据交互流程

BLE Data Flow Diagram

流程说明

  1. 配对连接:遥控器广播”Chromecast Remote”,TV盒子发起连接

  2. 能力协商:TV发送GET_CAPS,遥控器回复CAPS_RESP

  3. MTU/DLEN协商:优化单包传输长度

  4. 启动语音:用户按下按键,遥控器发送AUDIO_START

  5. 数据传输:遥控器通过ATVV_CHAR_AUDIO持续发送编码后的语音数据

  6. 停止语音:用户再次按下按键,遥控器发送AUDIO_STOP

5.5.4 语音数据流格式

  • 采样率:8kHz 或 16kHz

  • 量化位宽:16位(线性PCM编码)

  • 压缩算法:IMA ADPCM,压缩比4:1

  • 数据解析:每字节按高位(bits 4-7)→ 低位(bits 0-3)顺序解码

  • 传输通道ATVV_CHAR_AUDIO特征值

  • 默认帧长度:20字节

  • 推荐帧长度:160字节(配合16kHz采样率和20ms连接间隔)

6 演示说明

6.1 测试环境搭建

6.1.1 硬件准备

  1. 准备PAN1070 QFN32模块和底板

  2. 准备MIC传感器模块(推荐LM386模块)

  3. 准备Google TV Streamer 4K电视盒子

6.1.2 硬件连接

  1. 将MIC传感器模块与PAN1070底板连接:

    • VCC → 5V或3.3V

    • GND → GND

    • AO → P07

  2. 上电后调节电位器,使无声音时AO输出电压为1.65V

6.1.3 软件配置

  1. Google TV盒子连接支持访问谷歌服务器的网络(或安装VPN软件)

  2. 完成初始设置,确保语音助手可用

6.2 测试步骤

6.2.1 蓝牙配对

  1. 将编译好的固件烧录到PAN1070开发板

  2. 复位开发板,观察串口日志输出

  3. 打开Google TV盒子的蓝牙设置界面

  4. 搜索并配对”Chromecast Remote”设备

  5. 配对成功后,串口日志显示”connection established”

6.2.2 语音测试

  1. 使用Google TV盒子自带的遥控器,按下语音按键,确认语音识别功能正常

  2. 按下PAN1070底板的KEY1按键,启动语音传输(串口日志显示”key_vol_up pressed”)

  3. 对MIC说话,观察Google TV盒子是否识别语音命令

  4. 再次按下KEY1按键,停止语音传输(串口日志显示”key_vol_down pressed”)

6.2.3 预期结果

  • 串口日志输出正常,显示BLE连接状态和语音传输事件

  • Google TV盒子能够识别通过PAN1070传输的语音命令

  • 语音识别距离和准确度与自带遥控器接近(需优化)

6.3 串口日志示例

Try to load HW calibration data.. DONE.
- Chip Info         : 0x41
- Chip CP Version   : 255
- Chip FT Version   : 7
- Chip MAC Address  : CC01000006D6
- Chip UID          : 060000465454455354
- Chip Flash UID    : 4250315A3538380B016DA44356035678
- Chip Flash Size   : 512 KB (Inc. 4KB Panchip Info Area)

- Current Flash Map :
  +-------------------------+ <- Addr: 0x00000
  |      App Partition      |
  |        (480 KB)         |
  +-------------------------+ <- Addr: 0x78000
  |    KVStore Partition    |
  |        ( 16 KB)         |
  +-------------------------+ <- Addr: 0x7C000
  |  User Custom Partition  |
  |        ( 12 KB)         |
  +-------------------------+ <- Addr: 0x7F000
  |    Panchip Info Area    |
  |     (4 KB, Hidden)      |
  +-------------------------+ <- End : 0x80000 (512 KB The RCL clock frequency (after calibrate) is 31995 Hz

[I] app started
[INF] ble stack init start..
[INF] Spark Controller Init Start
[INF] BLE Heap size:5220 B, remain:64 B
[INF] ble controller version: 0.9.0.0457ae09
[INF] Spark Controller Init OK
[KV Store] Initializing...
[KV Store] Partition: 0x00078000, Size: 16 KB
[KV Store] Init OK
========================================
[KV Store] Initialized Successfully
[KV Store] Partition: 0x00078000 - 0x0007C000 (16 KB)
[KV Store] Area Size: 8192 bytes (single area)
[KV Store] Current Usage: 64/8192 bytes (0%)
[KV Store] Remaining Space: 8128 bytes
[KV Store] Free Space Offset: 64 bytes
[KV Store] RAM Table: 0/32 entries
========================================
[INF] ble host init ok -> ble thread start..

[I] registered service 0x1800 with handle=1
[I]   registering characteristic 0x2a00 with def_handle=2 val_handle=3
[I]   registering characteristic 0x2a01 with def_handle=4 val_handle=5
[I] registered service 0x1801 with handle=6
[I]   registering characteristic 0x2a05 with def_handle=7 val_handle=8
[I]   registering characteristic 0x2b29 with def_handle=10 val_handle=11
[I]   registering characteristic 0x2b2a with def_handle=12 val_handle=13
[I] registered service 0x1812 with handle=14
[I]   registering characteristic 0x2a4a with def_handle=15 val_handle=16
[I]   registering characteristic 0x2a4b with def_handle=17 val_handle=18
[I]   registering characteristic 0x2a4d with def_handle=19 val_handle=20
[I]     registering descriptor 0x2908 with handle=22
[I]   registering characteristic 0x2a4d with def_handle=23 val_handle=24
[I]     registering descriptor 0x2908 with handle=26
[I]   registering characteristic 0x2a4d with def_handle=27 val_handle=28
[I]     registering descriptor 0x2908 with handle=30
[I]   registering characteristic 0x2a4c with def_handle=31 val_handle=32
[I] registered service 0x180f with handle=33
[I]   registering characteristic 0x2a19 with def_handle=34 val_handle=35
[I] registered service ab5e0001-5a21-4f05-bc7d-af01f617b664 with handle=37
[I]   registering characteristic ab5e0002-5a21-4f05-bc7d-af01f617b664 with def_handle=38 val_handle=39
[I]   registering characteristic ab5e0003-5a21-4f05-bc7d-af01f617b664 with def_handle=40 val_handle=41
[I]   registering characteristic ab5e0004-5a21-4f05-bc7d-af01f617b664 with def_handle=43 val_handle=44
[W] Device Id Address: 08 02 03 04 05 01 

6.4 测试效果对比

测试条件

  • 对比TV盒子自带遥控器和PAN1070 EVB DEMO板

  • 测试声源与MIC的不同距离

  • 使用相同的声源(手机录音播放)

测试结果

设备

语音识别成功距离

TV盒子自带遥控器

80~90 cm

PAN1070 EVB DEMO板

约20 cm

7 附录

7.2 购买链接

8 RAM/Flash资源使用情况

PAN107x(QFN32,512KB Flash,32KB RAM)

Flash Size:  147.37 KB
RAM Size:    38.70 KB