Analog Microphone

本示例代码实现采集 AMIC(Analog Microphne) 数据,并经CODEC编码为PCM语音数据。

AMIC采集模拟语音数据,经CODEC编码,送到 I2S 接收FIFO,利用 DMA 将数据搬运到 UART

PC端通过串口助手接收UART传输的数据,使用音频解析软件解析数据,并播放录制的语音。

环境需求

该示例支持以下开发套件:

开发套件

Hardware Platforms

Board Name

RTL87x2G HDK

RTL87x2G EVB

更多信息请参考快速入门

硬件连线

EVB外接AMIC模块,连接MICBIAS和1.8V,GND和GND,P2_6和MIC_P,P2_7和MIC_N;

EVB外接FT232模块,连接P3_2和RX,P3_3和TX。

编译和下载

该示例的工程路径如下:

Project file: samples\peripheral\codec\amic\proj\rtl87x2g\mdk

Project file: samples\peripheral\codec\amic\proj\rtl87x2g\gcc

请按照以下步骤操作构建并运行该示例:

  1. 打开工程文件。

  2. 按照 快速入门编译APP Image 给出的步骤构建目标文件。

  3. 编译成功后,在路径 mdk\bingcc\bin 下会生成 app bin app_MP_xxx.bin 文件。

  4. 按照 快速入门MP Tool 给出的步骤将app bin烧录至EVB内。

  5. 按下复位按键,开始运行。

测试验证

准备阶段

  1. 启动 PuTTY 或 UartAssist 等PC终端,连接到使用的COM端口,并进行以下UART设置:

  • 波特率: 3000000

  • 8 数据位

  • 1 停止位

  • 无校验

  • 无硬件流控

  1. 启动音频解析软件。

测试阶段

  1. 开始录音到录音完成,采集到的语音数据,Codec编码为PCM数据后通过UART发送到PC终端。

  2. 从PC终端复制解码数据到新建HEX bin文档中。

  3. 使用音频解析软件查看解码后的语音波形,也可以播放语音。

代码介绍

该章节分为以下几个部分:

  1. 源码路径

  2. 初始化函数将在 初始化 章节介绍。

  3. 初始化后的功能实现将在 功能实现 章节介绍。

源码路径

  • 工程路径: sdk\samples\peripheral\codec\amic\proj

  • 源码路径: sdk\samples\peripheral\codec\amic\src

该工程的工程文件代码结构如下:

└── Project: amic
    └── secure_only_app
        └── Device                   includes startup code
            ├── startup_rtl.c
            └── system_rtl.c
        ├── CMSIS                    includes CMSIS header files
        ├── CMSE Library             Non-secure callable lib
        ├── Lib                      includes all binary symbol files that user application is built on
            └── rtl87x2g_io.lib
        ├── Peripheral               includes all peripheral drivers and module code used by the application
            ├── rtl_rcc.c
            ├── rtl_pinmux.c
            ├── rtl_uart.c
            ├── rtl_gdma.c
            ├── rtl_nvic.c
            ├── rtl_codec.c
            └── rtl_i2s.c
        └── APP                      includes the ble_peripheral user application implementation
            ├── main_ns.c
            └── io_codec.c

初始化

初始化流程包括了codec、uart、i2s 、gdma 模块的初始化,具体流程如下。


board_codec_init 包含了I2S和AMIC相关引脚的对PAD和PINMUX的设置。

  1. 配置PAD:

    1. AMIC相关引脚:SW模式、NOT PowerOn、无内部上拉。

    2. I2S相关引脚:PINMUX模式、PowerOn、无内部上拉。

  2. 配置PINMUX:分配引脚分别为BCLK_SPORT0、LRC_RX_SPORT0、SDI_CODEC_SLAVE、SDO_CODEC_SLAVE功能。

Pad_Config(AMIC_MSBC_CLK_PIN, PAD_SW_MODE, PAD_NOT_PWRON, PAD_PULL_NONE, PAD_OUT_DISABLE,
        PAD_OUT_HIGH);
Pad_Config(AMIC_MSBC_DAT_PIN, PAD_SW_MODE, PAD_NOT_PWRON, PAD_PULL_NONE, PAD_OUT_DISABLE,
        PAD_OUT_LOW);

Pad_Config(CODEC_BCLK_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_ENABLE,
        PAD_OUT_LOW);
Pad_Config(CODEC_LRCLK_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_ENABLE,
        PAD_OUT_LOW);
Pad_Config(CODEC_TX_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_ENABLE, PAD_OUT_LOW);
Pad_Config(CODEC_RX_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_ENABLE, PAD_OUT_LOW);

Pinmux_Config(CODEC_BCLK_PIN, BCLK_SPORT0);
Pinmux_Config(CODEC_LRCLK_PIN, LRC_RX_SPORT0);
Pinmux_Config(CODEC_TX_PIN,    SDI_CODEC_SLAVE);
Pinmux_Config(CODEC_RX_PIN,    SDO_CODEC_SLAVE);

board_uart_init 包含了UART相关引脚的PAD和PINMUX的设置。

  1. 配置PAD:设置引脚、PINMUX模式、PowerOn、内部上拉。

  2. 配置PINMUX:分配引脚为UART5_TX、UART5_RX功能。


driver_i2s_init 包含了对I2S外设的初始化。

  1. 使能PCC时钟。

  2. 设置I2S时钟源为40M,位时钟为16K。

  3. 设置为主设备模式,单声道输出,数据格式为I2S模式。

  4. 设置数据宽度和通道宽度。

RCC_PeriphClockCmd(APBPeriph_I2S0, APBPeriph_I2S0_CLOCK, ENABLE);

I2S_InitStruct.I2S_ClockSource        = I2S_CLK_40M;
I2S_InitStruct.I2S_RxBClockMi         = 0x271;/* <!BCLK = 16K */
I2S_InitStruct.I2S_RxBClockNi         = 0x10;
I2S_InitStruct.I2S_DeviceMode         = I2S_DeviceMode_Master;
I2S_InitStruct.I2S_RxChannelType      = I2S_Channel_Mono;
I2S_InitStruct.I2S_RxDataFormat       = I2S_Mode;
I2S_InitStruct.I2S_RxChannelWidth     = I2S_Width_32Bits;
I2S_InitStruct.I2S_RxDataWidth        = I2S_Width_16Bits;

driver_uart_init 包含了对UART外设的初始化。

  1. 使能PCC时钟源。

  2. 设置波特率为3M。

  3. 使能UART TX DMA功能。

RCC_PeriphClockCmd(APBPeriph_UART5, APBPeriph_UART5_CLOCK, ENABLE);

UART_InitStruct.UART_Div             = 1;
UART_InitStruct.UART_Ovsr            = 8;
UART_InitStruct.UART_OvsrAdj         = 0x492;

UART_InitStruct.UART_TxDmaEn         = ENABLE;
UART_InitStruct.UART_DmaEn           = UART_DMA_ENABLE;

driver_gdma_init 包含了对GDMA外设的初始化。

  1. 使用GDMA通道0。

  2. GDMA的传输方向为外设到外设传输。

  3. 源端地址为 (&(I2S0->I2S_RX_FIFO_RD_ADDR)) ,目的端地址为 (&(UART5->UART_RBR_THR))

  4. 使能GDMA通道0总传输完成中断 GDMA_INT_Transfer

GDMA_InitStruct.GDMA_ChannelNum          = GDMA_Channel_AMIC_NUM;
GDMA_InitStruct.GDMA_DIR                 = GDMA_DIR_PeripheralToPeripheral;
GDMA_InitStruct.GDMA_BufferSize          = AUDIO_FRAME_SIZE / 4;
...
GDMA_InitStruct.GDMA_SourceAddr          = (uint32_t)(&(I2S0->I2S_RX_FIFO_RD_ADDR));
GDMA_InitStruct.GDMA_DestinationAddr     = (uint32_t)(&(UART5->UART_RBR_THR));
GDMA_InitStruct.GDMA_SourceHandshake     = GDMA_Handshake_I2S0_TDM0_RX;
GDMA_InitStruct.GDMA_DestHandshake       = GDMA_Handshake_UART5_TX;
GDMA_InitStruct.GDMA_ChannelPriority = 2;

GDMA_Init(GDMA_Channel_AMIC, &GDMA_InitStruct);
GDMA_INTConfig(GDMA_Channel_AMIC_NUM, GDMA_INT_Transfer, ENABLE);

driver_codec_init 包含了对CODEC外设的初始化。

  1. 初始化模拟电路相关配置。

  2. 使能PCC时钟。

  3. 麦克风类型选择AMIC。

  4. 设置MICBST模式为差分模式。

  5. 设置采样率为16kHz。

  6. 设置数据格式为I2S格式。

  7. 设置通道宽度为32位。

  8. 设置RX数据宽度为16位。

  9. 设置I2S通道顺序为左右。

CODEC_AnalogCircuitInit();
RCC_PeriphClockCmd(APBPeriph_CODEC, APBPeriph_CODEC_CLOCK, ENABLE);
...
CODEC_InitStruct.CODEC_Ch0MicType       = CODEC_CH_AMIC;
CODEC_InitStruct.CODEC_MicBstMode       = MICBST_Mode_Differential;
CODEC_InitStruct.CODEC_SampleRate0      = SAMPLE_RATE_16KHz;
CODEC_InitStruct.CODEC_I2SFormat        = CODEC_I2S_DataFormat_I2S;
CODEC_InitStruct.CODEC_I2SChannelLen    = I2S_CHANNELLEN_32;
CODEC_InitStruct.CODEC_I2SRxDataWidth   = CODEC_I2S_Rx_DataWidth_16Bits;
CODEC_InitStruct.CODEC_I2SChSequence    = CODEC_I2S_CH_L_R;

功能实现

  1. 使能I2S发送模式和接收模式;使能GDMA通道0。

I2S_Cmd(I2S0, I2S_MODE_TX | I2S_MODE_RX, ENABLE);
GDMA_Cmd(GDMA_Channel_AMIC_NUM, ENABLE);
  1. AMIC采集到模拟语音数据,经CODEC解码,送到I2S0接收FIFO,利用GDMA将数据搬运到UART5;当数据传输完成时,触发 GDMA_INT_Transfer 中断,进入中断处理函数 GDMA_Channel_AMIC_Handler

    1. 重新设置源端地址和目的端地址。

    2. 重新设置GDMA传输数据大小。

    3. 清除GDMA通道0 GDMA_INT_Transfer 中断挂起位,使能GDMA通道0。

GDMA_SetSourceAddress(GDMA_Channel_AMIC, (uint32_t)(&(I2S0->I2S_RX_FIFO_RD_ADDR)));
GDMA_SetDestinationAddress(GDMA_Channel_AMIC, (uint32_t)(&(UART5->UART_RBR_THR)));

GDMA_SetBufferSize(GDMA_Channel_AMIC, AUDIO_FRAME_SIZE / 4);

GDMA_ClearINTPendingBit(GDMA_Channel_AMIC_NUM, GDMA_INT_Transfer);
GDMA_Cmd(GDMA_Channel_AMIC_NUM, ENABLE);