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传感器模块(二选一):
高感度麦克风传感器模块(淘宝型号)
声音传感器模块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电视盒子
注意事项¶
MIC传感器模块AO脚输出偏置电压需调节至1.65V(无声音输入时)
Google TV盒子需要连接支持访问谷歌服务器的网络(或安装VPN软件)
3 编译和烧录¶
工程位置¶
<SDK_ROOT>\nimble\pan10xx_samples\solutions\ble_hid_mic\keil_107x
编译步骤¶
使用Keil MDK打开工程文件
ble_hid_mic.uvprojx选择目标设备为PAN107x
点击”Build”或”Rebuild”编译工程
编译成功后,在输出窗口查看Flash和RAM使用情况
烧录步骤¶
连接PAN107x EVB开发板到PC
使用Keil的Flash下载功能或外部烧录工具
烧录生成的
.hex或.bin文件复位开发板,观察串口日志输出
配置选项¶
在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.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.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 Data Flow Diagram¶
流程说明:
配对连接:遥控器广播”Chromecast Remote”,TV盒子发起连接
能力协商:TV发送
GET_CAPS,遥控器回复CAPS_RESPMTU/DLEN协商:优化单包传输长度
启动语音:用户按下按键,遥控器发送
AUDIO_START数据传输:遥控器通过
ATVV_CHAR_AUDIO持续发送编码后的语音数据停止语音:用户再次按下按键,遥控器发送
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 硬件准备¶
准备PAN1070 QFN32模块和底板
准备MIC传感器模块(推荐LM386模块)
准备Google TV Streamer 4K电视盒子
6.1.2 硬件连接¶
将MIC传感器模块与PAN1070底板连接:
VCC → 5V或3.3V
GND → GND
AO → P07
上电后调节电位器,使无声音时AO输出电压为1.65V
6.1.3 软件配置¶
Google TV盒子连接支持访问谷歌服务器的网络(或安装VPN软件)
完成初始设置,确保语音助手可用
6.2 测试步骤¶
6.2.1 蓝牙配对¶
将编译好的固件烧录到PAN1070开发板
复位开发板,观察串口日志输出
打开Google TV盒子的蓝牙设置界面
搜索并配对”Chromecast Remote”设备
配对成功后,串口日志显示”connection established”
6.2.2 语音测试¶
使用Google TV盒子自带的遥控器,按下语音按键,确认语音识别功能正常
按下PAN1070底板的KEY1按键,启动语音传输(串口日志显示”key_vol_up pressed”)
对MIC说话,观察Google TV盒子是否识别语音命令
再次按下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.1 相关文档链接¶
PAN1070开发套件使用手册 (PDF下载)
Google Voice over BLE spec 1.0 (PDF下载)
IMA ADPCM编码原理详解 (技术博客)
Nordic UART Service (NUS) 官方文档 (Nordic文档)