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.

CODEC DMIC Process
Requirements
The sample supports the following 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:
Open sample project file.
To build the target, follow the steps listed on the Generating App Image in Quick Start.
After a successful compilation, the app bin
app_MP_xxx.bin
will be generated in the directorymdk\bin
orgcc\bin
.To download app bin into EVB board, follow the steps listed on the MP Tool Download in Quick Start.
Press reset button on EVB board and it will start running.
Experimental Verification
Preparation Phase
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
Launch the audio analysis software.
Testing Phase
Start recording until the recording is complete. The collected voice data will be decoded and sent to the PC terminal through UART.
Copy the decoded data from the PC terminal into a newly created ASCII bin document.
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:
Peripheral initialization will be introduced in chapter Initialization.
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:
Configure PAD: set the pins, PINMUX mode, PowerOn, internal pull-none, and disable output.
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:
Configure PAD: Set the pins, PINMUX mode, PowerOn, and internal pull-up. The TX pin outputs high, and the RX pin is disabled.
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:
Enable the RCC clock.
Set the I2S clock source to 40MHz, and the sample rate to 16kHz.
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:
Enable the RCC clock source.
Set the UART baud rate to 3M.
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:
Use DMA channel 0.
The DMA transfer direction is from peripheral to peripheral.
The source address is
(&(I2S0->RX_DR))
, and the destination address is(&(UART0->RB_THR))
.Set the source data width to 32 bits and the destination data width to 8 bits.
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.
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:
Initialize the analog circuit related configuration.
Enable the RCC clock.
Select the microphone type as DMIC.
Set the DMIC clock frequency to 2500kHz.
Set the data latch mode of channel 0 digital microphone to rising edge latch.
Set the sampling frequency to 16kHz.
Set the data format to I2S format.
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
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); }
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 functionGDMA_Channel_DMIC_Handler
.Reset the source address and destination address.
Reset the DMA transfer data size.
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); }