目录
- AIR32F103(一) 合宙AIR32F103CBT6开发板上手报告
- AIR32F103(二) Linux环境和LibOpenCM3项目模板
- AIR32F103(三) Linux环境基于标准外设库的项目模板
- AIR32F103(四) 27倍频216MHz,CoreMark跑分测试
- AIR32F103(五) FreeRTOSv202112核心库的集成和示例代码
- AIR32F103(六) ADC,I2S,DMA和ADPCM实现的录音播放功能
- AIR32F103(七) AIR32F103CBT6/CCT6启用96K内存
- AIR32F103(八) 集成Helix MP3解码库播放MP3
- AIR32F103(九) CAN总线的通信和ID过滤机制及实例
- AIR32F103(十) 在无系统环境和FreeRTOS环境集成LVGL
- AIR32F103(十一) 在AIR32F103上移植微雪墨水屏驱动
- AIR32F103(十二) 搭载 AIR32F103CBT6 的Bluepill核心板
关于
使用AIR32的ADC, I2S 和 DMA 实现简单的语音录音和播放功能, 以及使用 ADPCM 编码提升录音时长. 使用的MCU型号为 AIR32F103CCT6. 如果用CBT6, 对应的音频数据数组大小需要相应减小.
音频录音和播放
工作方式
加电后开始录音, 录音结束后循环播放
- 录音: 麦克风模块 -> ADC采样(12bit, 8K, 11K 或 16K) -> 存储在内存
- 播放: I2S -> I2S外设(MAX98357A / PT8211) -> 喇叭
对中间每个环节的说明
存储
首先是存储, MCU的内存有限, 如果不借助AT24C, MX25L这类外部存储, 只用内存存储的数据是有限的, AIR32F103CCT6 带 64K Byte内存, 如果按原始采样值存储, 录音时长为
- 16bit
- 8K: 128kbps, 约4秒
- 11K: 176kbps, 约3秒
- 16K: 256kbps, 约2秒
- 8bit
- 8K: 64kbps, 约8秒
- 11K: 88kbps, 约6秒
- 16K: 128kbps, 约4秒
采样
使用AIR32的ADC, 配合定时器实现精确的每秒8K, 11K和16K采样. AIR32的ADC分辨率和STM32F103一样都是固定的12bit(STM32F4之后才可以用寄存器调节分辨率)
- 如果使用ADC的中断, 可以向高位偏移做成16bit, 也可以去掉低位做成8bit
- 如果使用DMA, 因为AIR32不能像STM32那样, 在4字节地址上偏移一个字节取值, 所以只能按16bit(halfword)传值
音频采集设备如果直接用驻极体话筒, 采样的信号很弱(不是没有, 但是非常小), 需要加一个三极管做放大. 也可以买成品的 MAX9814 模块. 两者的效果区别不大, 但是在调试阶段, 建议用 MAX9814, 因为不用担心信号是否过饱和和失真问题, 在调通之后, 再换回低成本的驻极体话筒和三极管.
驻极体话筒放大的电路和元件参数可以参考这一篇 https://www.cnblogs.com/milton/p/15315783.html
播放
播放可以使用PWM转DAC, 也可以直接用I2S.
- 如果使用PWM, 因为PWM本身是方波, 会产生大量的谐振噪音, 只有将PWM频率设置到16KHz以上才能明显降低噪音(因为谐振频率超出人耳的听觉范围了), 用8KHz时的噪音非常明显.
- 因为AIR32F103全系列都支持I2S(数据手册上写只有RPT7才有, 实际上CBT6和CCT6也有), 所以直接用I2S输出是最简单的. 这时候需要一个能接收I2S输出并转为音频的模块.
I2S模块可以用 MAX98357A 模块, 自带I2S解码和放大可以直连喇叭, 也可以买PT8211/TM8211/GH8211, 0.3元一片非常便宜还是双声道, 缺点是不带功放, 如果直连喇叭得贴着耳朵才能听到, 可以再加一个LM386或者PAM8403做放大, 都非常便宜.
实现
硬件
- AIR32F103CCT6
- MAX9814
- PT8211
- 8欧小喇叭
接线
* AIR32F103 MAX98357A / PT8211
* PB13(SPI1_SCK/I2S_CK) -> BCLK, BCK
* PB15(SPI1_MOSI/I2S_SD) -> DIN
* PB12(SPI1_NSS/I2S_WS) -> LRC, WS
* GND -> GND
* VIN -> 3.3V
* + -> speaker
* - -> speaker
*
* AIR32F103 MAX9814
* PA2 -> Out
* 3.3V -> VDD
* GND -> GND
* GND -> A/R
* GAIN -> float:60dB, gnd:50dB, 3.3v:40dB
代码
完整的示例代码
- GitHub: https://github.com/IOsetting/air32f103-template/tree/master/Examples/NonFreeRTOS/I2S/Audio_Recorder
- Gitee: https://gitee.com/iosetting/air32f103-template/tree/master/Examples/NonFreeRTOS/I2S/Audio_Recorder
定义了全局变量
// 定义不同的AUDIO_FREQ值, 可以切换不同的采样频率, 8K, 11K, 16K, 越高的采样频率, 音质越好, 录音时长越短
#define AUDIO_FREQ 8000
//#define AUDIO_FREQ 11000
//#define AUDIO_FREQ 16000
// 定义存储的音频数据大小, CCT6用的是30000, CBT6 或 RPT6 可以相应的减小或增大
#define BUFF_SIZE 30000
// 音频数据数组, 同时用于DMA的接收地址
uint16_t dma_buf[BUFF_SIZE];
// I2S传输时, 用于记录传输的位置
uint32_t index;
// I2S传输时, 用于区分左右声道
__IO uint8_t lr = 0;
初始化GPIO, PA2是采样输入, PB12, PB13, PB15 用于I2S传输, PC13 是板载的LED, 用于指示录音开始和结束. 如果使用的不是Bluepill而是合宙的开发板, 可以修改为开发板对应的LED GPIO.
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// PA2 as analog input
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// PB12,PB13,PB15 as I2S AF output
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;