Digital Microphone

This example implements the function of DMIC (Digital Microphone) collecting digital voice and decoding it through a CODEC.

DMIC collects digital voice data, encodes it through a CODEC, and sends it to the I2S receive FIFO. The data is then transferred to the UART using DMA.

The PC receives the data transmitted via UART, uses audio analysis software (such as Audacity) to parse the data, and plays the recorded voice.

The DMIC CODEC process is shown as follows.

Here should be the CODEC DMIC image

CODEC DMIC Process

Requirements

The sample supports the following development kits:

Development Kits

Hardware Platforms

Board Name

RTL8752H HDK

RTL8752H EVB

For more requirements, please refer to Quick Start.

Wiring

The EVB connects to the DMIC module, with P3_2 (DMIC CLK) connected to CLK, and P3_3 (DMIC DAT) connected to DATA.

The EVB connects to the FT232 module, with P3_0 (UART TX) connected to the RX of the FT232, and P3_1 (UART RX) connected to the TX of the FT232.

Building and Downloading

This sample can be found in the SDK folder:

Project file: board\evb\io_sample\CODEC\DMIC\mdk

Project file: board\evb\io_sample\CODEC\DMIC\gcc

Please follow these steps to build and run the example:

  1. Open sample project file.

  2. To build the target, follow the steps listed on the Generating App Image in Quick Start.

  3. After a successful compilation, the app bin app_MP_xxx.bin will be generated in the directory mdk\bin or gcc\bin.

  4. To download app bin into EVB board, follow the steps listed on the MP Tool Download in Quick Start.

  5. Press reset button on EVB board and it will start running.

Experimental Verification

Preparation Phase

  1. Launch a PC terminal such as PuTTY or UartAssist, connect to the used COM port, and perform the following UART settings:

    • Baud Rate: 3000000

    • 8 Data Bits

    • 1 Stop Bit

    • No Parity

    • No Hardware Flow Control

  2. Launch the audio analysis software.

Testing Phase

  1. Start recording until the recording is complete. The collected voice data will be decoded and sent to the PC terminal through UART.

  2. Copy the decoded data from the PC terminal into a newly created ASCII bin document.

  3. Use the audio analysis software to view the decoded audio waveform, which can also be played back.

Code Overview

This chapter will be introduced according to the following several parts:

  1. Source Code Directory.

  2. Peripheral initialization will be introduced in chapter Initialization.

  3. Functional implementation after initialization will be introduced in chapter Function Implementation.

Source Code Directory

  • Project directory: sdk\board\evb\io_sample\CODEC\DMIC

  • Source code directory: sdk\src\sample\io_sample\CODEC\DMIC

Source files are currently categorized into several groups as below.

└── Project: codec_dmic
    └── secure_only_app
        └── include
            ├── app_define.h
            └── rom_uuid.h
        ├── cmsis                    includes CMSIS header files and startup files
            ├── overlay_mgr.c
            ├── system_rtl876x.c
            └── startup_rtl876x.s
        ├── lib                      includes all binary symbol files that user application is built on
            ├── rtl8752h_sdk.lib
            ├── gap_utils.lib
            ├── ROM.lib
            └── adc.lib
        ├── peripheral               includes all peripheral drivers and module code used by the application
            ├── rtl876x_rcc.c
            ├── rtl876x_pinmux.c
            ├── rtl876x_nvic.c
            ├── rtl876x_gdma.c
            ├── rtl876x_uart.c
            ├── rtl876x_codec.c
            └── rtl876x_i2s.c
        ├── profile
        └── app                      includes the ble_peripheral user application implementation
            └── main.c

Initialization

When the EVB reset, execute the main function and follow the steps below:

int main(void)
{
    extern uint32_t random_seed_value;
    srand(random_seed_value);
    __enable_irq();

    codec_demo();

    while (1)
    {
        ;

    }
}

In codec_demo, it includes the processes for PAD/PINMUX settings, I2S peripheral initialization, UART peripheral initialization, DMA peripheral initialization, and CODEC peripheral initialization.

void codec_demo(void)
{
    board_codec_init();
    board_uart_init();
    driver_i2s_init();
    driver_uart_init();
    driver_gdma_init();
    driver_codec_init();

    I2S_Cmd(I2S0, I2S_MODE_TX | I2S_MODE_RX, ENABLE);
    GDMA_Cmd(GDMA_Channel_DMIC_NUM, ENABLE);
}

board_codec_init is for setting up the PAD and PINMUX for DMIC-related pins, including the following steps:

  1. Configure PAD: set the pins, PINMUX mode, PowerOn, internal pull-none, and disable output.

  2. Configure PINMUX: assign the pins to the DMIC1_CLK and DMIC1_DAT functions respectively.

void board_codec_init(void)
{
    Pad_Config(DMIC_MSBC_CLK_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_DISABLE,
            PAD_OUT_LOW);
    Pad_Config(DMIC_MSBC_DAT_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_DISABLE,
            PAD_OUT_LOW);

    Pinmux_Config(DMIC_MSBC_CLK_PIN, DMIC1_CLK);
    Pinmux_Config(DMIC_MSBC_DAT_PIN, DMIC1_DAT);
}

board_uart_init is the setup for PAD and PINMUX settings related to UART pins, including the following process:

  1. Configure PAD: Set the pins, PINMUX mode, PowerOn, and internal pull-up. The TX pin outputs high, and the RX pin is disabled.

  2. Configure PINMUX: Assign the pins to UART0_TX and UART0_RX respectively.

void board_uart_init(void)
{
    Pad_Config(UART_TX_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_ENABLE, PAD_OUT_HIGH);
    Pad_Config(UART_RX_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_LOW);

    Pinmux_Config(UART_TX_PIN, UART0_TX);
    Pinmux_Config(UART_RX_PIN, UART0_RX);
}

driver_i2s_init is the initialization of the I2S peripheral, which includes the following processes:

  1. Enable the RCC clock.

  2. Set the I2S clock source to 40MHz, and the sample rate to 16kHz.

  3. Set it to master mode with mono output.

void driver_i2s_init(void)
{
    RCC_PeriphClockCmd(APBPeriph_I2S0, APBPeriph_I2S0_CLOCK, ENABLE);

    I2S_InitTypeDef I2S_InitStruct;

    I2S_StructInit(&I2S_InitStruct);
    I2S_InitStruct.I2S_ClockSource      = I2S_CLK_40M;
    I2S_InitStruct.I2S_BClockMi         = 0x271;/* <!BCLK = 16K */
    I2S_InitStruct.I2S_BClockNi         = 0x10;
    I2S_InitStruct.I2S_DeviceMode       = I2S_DeviceMode_Master;
    I2S_InitStruct.I2S_ChannelType      = I2S_Channel_Mono;
    I2S_InitStruct.I2S_DataFormat       = I2S_Mode;
    I2S_Init(I2S0, &I2S_InitStruct);
}

driver_uart_init is the initialization of the UART peripheral, including the following process:

  1. Enable the RCC clock source.

  2. Set the UART baud rate to 3M.

  3. Enable the UART TX DMA function.

void driver_uart_init(void)
{
    /* Enable clock */
    RCC_PeriphClockCmd(APBPeriph_UART0, APBPeriph_UART0_CLOCK, ENABLE);

    /* UART init */
    UART_InitTypeDef UART_InitStruct;

    UART_StructInit(&UART_InitStruct);
    /* change to 3M baudrate */
    UART_InitStruct.UART_Div             = 1;
    UART_InitStruct.UART_Ovsr            = 8;
    UART_InitStruct.UART_OvsrAdj        = 0x492;
    UART_InitStruct.UART_TxWaterLevel    = 12;
    UART_InitStruct.UART_TxDmaEn         = ENABLE;
    UART_InitStruct.UART_DmaEn           = UART_DMA_ENABLE;
    UART_Init(UART0, &UART_InitStruct);
}

driver_gdma_init is the initialization for the DMA peripheral, including the following steps:

  1. Use DMA channel 0.

  2. The DMA transfer direction is from peripheral to peripheral.

  3. The source address is (&(I2S0->RX_DR)), and the destination address is (&(UART0->RB_THR)).

  4. Set the source data width to 32 bits and the destination data width to 8 bits.

  5. Set the number of data for single burst transfer at the source to 1, and the number of data for single burst transfer at the destination to 4.

  6. Enable the DMA channel 0 total transfer completion interrupt GDMA_INT_Transfer.

void driver_gdma_init(void)
{
    /* Enable GDMA clock */
    RCC_PeriphClockCmd(APBPeriph_GDMA, APBPeriph_GDMA_CLOCK, ENABLE);

    /* Initialize GDMA peripheral */
    GDMA_InitTypeDef GDMA_InitStruct;
    GDMA_StructInit(&GDMA_InitStruct);
    GDMA_InitStruct.GDMA_ChannelNum          = GDMA_Channel_DMIC_NUM;
    GDMA_InitStruct.GDMA_DIR                 = GDMA_DIR_PeripheralToPeripheral;
    GDMA_InitStruct.GDMA_BufferSize          = AUDIO_FRAME_SIZE / 4;
    GDMA_InitStruct.GDMA_SourceInc           = DMA_SourceInc_Fix;
    GDMA_InitStruct.GDMA_DestinationInc      = DMA_DestinationInc_Fix;
    GDMA_InitStruct.GDMA_SourceDataSize      = GDMA_DataSize_Word;
    GDMA_InitStruct.GDMA_DestinationDataSize = GDMA_DataSize_Byte;
    GDMA_InitStruct.GDMA_SourceMsize         = GDMA_Msize_1;
    GDMA_InitStruct.GDMA_DestinationMsize    = GDMA_Msize_4;
    GDMA_InitStruct.GDMA_SourceAddr          = (uint32_t)(&(I2S0->RX_DR));
    GDMA_InitStruct.GDMA_DestinationAddr     = (uint32_t)(&(UART0->RB_THR));
    GDMA_InitStruct.GDMA_SourceHandshake     = GDMA_Handshake_I2S0_RX;
    GDMA_InitStruct.GDMA_DestHandshake       = GDMA_Handshake_UART0_TX;
    GDMA_Init(GDMA_Channel_DMIC, &GDMA_InitStruct);
    GDMA_INTConfig(GDMA_Channel_DMIC_NUM, GDMA_INT_Transfer, ENABLE);

    NVIC_InitTypeDef NVIC_InitStruct;
    NVIC_InitStruct.NVIC_IRQChannel = GDMA_Channel_DMIC_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPriority = 1;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);
}

driver_codec_init is the initialization of the CODEC peripheral, which includes the following steps:

  1. Initialize the analog circuit related configuration.

  2. Enable the RCC clock.

  3. Select the microphone type as DMIC.

  4. Set the DMIC clock frequency to 2500kHz.

  5. Set the data latch mode of channel 0 digital microphone to rising edge latch.

  6. Set the sampling frequency to 16kHz.

  7. Set the data format to I2S format.

  8. Set the data width to 16 bits.

void driver_codec_init(void)
{
    /*switch to pwm mode*/
    SystemCall(3, 1);
    CODEC_AnalogCircuitInit();
    RCC_PeriphClockCmd(APBPeriph_CODEC, APBPeriph_CODEC_CLOCK, ENABLE);

    CODEC_InitTypeDef CODEC_InitStruct;

    CODEC_StructInit(&CODEC_InitStruct);
    CODEC_InitStruct.CODEC_Ch0MicType       = CODEC_CH0_DMIC;
    CODEC_InitStruct.CODEC_DmicClock        = DMIC_Clock_2500KHz;
    CODEC_InitStruct.CODEC_Ch0DmicDataLatch = DMIC_Ch0_Rising_Latch;
    CODEC_InitStruct.CODEC_SampleRate       = SAMPLE_RATE_16KHz;
    CODEC_InitStruct.CODEC_I2SFormat        = CODEC_I2S_DataFormat_I2S;
    CODEC_InitStruct.CODEC_I2SDataWidth     = CODEC_I2S_DataWidth_16Bits;
    CODEC_Init(CODEC, &CODEC_InitStruct);
    DBG_DIRECT("Use dmic test!");
}

Functional Implementation

  1. After initialization is complete, enable the I2S transmission mode and reception mode; enable DMA channel 0 transmission.

    void codec_demo(void)
    {
        ...
        I2S_Cmd(I2S0, I2S_MODE_TX | I2S_MODE_RX, ENABLE);
        GDMA_Cmd(GDMA_Channel_DMIC_NUM, ENABLE);
    }
    
  2. DMIC collects analog voice data, decodes it via CODEC, sends it to I2S0 receive FIFO, and uses DMA to transfer the data to UART; when the data transfer is complete, it triggers the GDMA_INT_Transfer interrupt and enters the interrupt handler function GDMA_Channel_DMIC_Handler.

    1. Reset the source address and destination address.

    2. Reset the DMA transfer data size.

    3. Clear the DMA channel 0 GDMA_INT_Transfer interrupt pending bit and enable DMA channel 0.

    void GDMA_Channel_DMIC_Handler(void)
    {
        GDMA_SetSourceAddress(GDMA_Channel_DMIC, (uint32_t)(&(I2S0->RX_DR)));
        GDMA_SetDestinationAddress(GDMA_Channel_DMIC, (uint32_t)(&(UART0->RB_THR)));
    
        GDMA_SetBufferSize(GDMA_Channel_DMIC, AUDIO_FRAME_SIZE / 4);
    
        GDMA_ClearINTPendingBit(GDMA_Channel_DMIC_NUM, GDMA_INT_Transfer);
        GDMA_Cmd(GDMA_Channel_DMIC_NUM, ENABLE);
    }