#include "pdm2pcm_glo.h" /****************************************************** * Macros ******************************************************/ /* SAI PDM input definitions */ /* SAI input peripheral configuration defines */ #define MIC_IN_SAI_PDMx SAI4_Block_A #define MIC_IN_SAI_PDMx_CLK_ENABLE() __HAL_RCC_SAI4_CLK_ENABLE() #define MIC_IN_SAI_PDMx_CLK_DISABLE() __HAL_RCC_SAI4_CLK_DISABLE() #define MIC_IN_SAI_PDMx_CLK_IN_ENABLE() __HAL_RCC_GPIOE_CLK_ENABLE() #define MIC_IN_SAI_PDMx_CLK_IN_PIN GPIO_PIN_2 #define MIC_IN_SAI_PDMx_CLK_IN_PORT GPIOE #define MIC_IN_SAI_PDMx_CLK_AF GPIO_AF10_SAI4 #define MIC_IN_SAI_PDMx_DATA_IN_ENABLE() __HAL_RCC_GPIOE_CLK_ENABLE() #define MIC_IN_SAI_PDMx_DATA_IN_PIN GPIO_PIN_6 #define MIC_IN_SAI_PDMx_DATA_IN_PORT GPIOE #define MIC_IN_SAI_PDMx_DATA_AF GPIO_AF9_SAI4 #define MIC_IN_SAI_PDMx_IRQHandler SAI4_IRQHandler #define MIC_IN_SAI_PDMx_IRQ SAI4_IRQn #define MIC_IN_SAI_IRQ_PREPRIO ((uint32_t)0x0E) /* SAI PDM DMA Stream definitions */ #define MIC_IN_SAI_PDMx_DMAx_CLK_ENABLE() __HAL_RCC_BDMA_CLK_ENABLE() #define MIC_IN_SAI_PDMx_DMAx_STREAM BDMA_Channel1 #define MIC_IN_SAI_PDMx_DMAx_REQUEST BDMA_REQUEST_SAI4_A #define MIC_IN_SAI_PDMx_DMAx_IRQ BDMA_Channel1_IRQn #define MIC_IN_SAI_PDMx_DMAx_PERIPH_DATA_SIZE DMA_PDATAALIGN_HALFWORD #define MIC_IN_SAI_PDMx_DMAx_MEM_DATA_SIZE DMA_MDATAALIGN_HALFWORD /* Private define ------------------------------------------------------------*/ #define AUDIO_FREQUENCY SAI_AUDIO_FREQUENCY_16K #define AUDIO_CHANNEL_NUMBER 2U #define AUDIO_BUFFER_SIZE 256U #define AUDIO_PCM_CHUNK_SIZE 32U /* Private macro -------------------------------------------------------------*/ /* Audio PDM FS frequency = 128KHz = 8 * 16KHz = 8 * FS_Freq */ #define AUDIO_PDM_GET_FS_FREQUENCY(FS) (FS * 8) /* Private typedef -----------------------------------------------------------*/ typedef enum { BUFFER_OFFSET_NONE = 0, BUFFER_OFFSET_HALF, BUFFER_OFFSET_FULL, BUFFER_OFFSET_ERROR, } BUFFER_StateTypeDef; /* SAI input Handle */ SAI_HandleTypeDef hSaiMic; DMA_HandleTypeDef hSaiMicDma; /* Buffer containing the PDM samples */ #if defined ( __CC_ARM ) /* !< ARM Compiler */ /* Buffer location should aligned to cache line size (32 bytes) */ ALIGN_32BYTES (uint16_t audioPdmBuf[AUDIO_BUFFER_SIZE]) __attribute__((section(".RAM_D3"))); #elif defined ( __ICCARM__ ) /* !< ICCARM Compiler */ #pragma location=0x38000000 /* Buffer location should aligned to cache line size (32 bytes) */ ALIGN_32BYTES (uint16_t audioPdmBuf[AUDIO_BUFFER_SIZE]); #elif defined ( __GNUC__ ) /* !< GNU Compiler */ /* Buffer location should aligned to cache line size (32 bytes) */ ALIGN_32BYTES (uint16_t audioPdmBuf[AUDIO_BUFFER_SIZE]) __attribute__((section(".RAM_D3"))); #endif ALIGN_32BYTES (uint16_t audioPcmBuf[AUDIO_BUFFER_SIZE]); /* PDM Filters params */ PDM_Filter_Handler_t PDM_FilterHandler[2]; PDM_Filter_Config_t PDM_FilterConfig[2]; /* Pointer to PCM data */ __IO uint32_t pcmPtr; /* Buffer status variable */ __IO BUFFER_StateTypeDef bufferStatus = BUFFER_OFFSET_NONE; static void SystemClock_Config(void) { RCC_ClkInitTypeDef RCC_ClkInitStruct; RCC_OscInitTypeDef RCC_OscInitStruct; HAL_StatusTypeDef ret = HAL_OK; /*!< Supply configuration update enable */ HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY); /* The voltage scaling allows optimizing the power consumption when the device is clocked below the maximum system frequency, to update the voltage scaling value regarding system frequency refer to product datasheet. */ __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {} /* Enable HSE Oscillator and activate PLL with HSE as source */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_BYPASS; RCC_OscInitStruct.HSIState = RCC_HSI_OFF; RCC_OscInitStruct.CSIState = RCC_CSI_OFF; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLM = 4; RCC_OscInitStruct.PLL.PLLN = 400; RCC_OscInitStruct.PLL.PLLFRACN = 0; RCC_OscInitStruct.PLL.PLLP = 2; RCC_OscInitStruct.PLL.PLLR = 2; RCC_OscInitStruct.PLL.PLLQ = 4; RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE; RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_1; ret = HAL_RCC_OscConfig(&RCC_OscInitStruct); if(ret != HAL_OK) { Error_Handler(); } /* Select PLL as system clock source and configure bus clocks dividers */ RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \ RCC_CLOCKTYPE_PCLK2 | RCC_CLOCKTYPE_D3PCLK1); RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2; RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4); if(ret != HAL_OK) { Error_Handler(); } /*activate CSI clock mondatory for I/O Compensation Cell*/ __HAL_RCC_CSI_ENABLE() ; /* Enable SYSCFG clock mondatory for I/O Compensation Cell */ __HAL_RCC_SYSCFG_CLK_ENABLE() ; /* Enables the I/O Compensation Cell */ HAL_EnableCompensationCell(); } static void AUDIO_IN_PDMToPCM_Init(uint32_t AudioFreq, uint32_t ChannelNumber) { uint32_t index = 0; /* Enable CRC peripheral to unlock the PDM library */ __HAL_RCC_CRC_CLK_ENABLE(); for(index = 0; index < ChannelNumber; index++) { /* Init PDM filters */ PDM_FilterHandler[index].bit_order = PDM_FILTER_BIT_ORDER_MSB;//PDM_FILTER_BIT_ORDER_LSB;//PDM_FILTER_BIT_ORDER_MSB; PDM_FilterHandler[index].endianness = PDM_FILTER_ENDIANNESS_LE; PDM_FilterHandler[index].high_pass_tap = 2122358088;//2104533974;//2122358088; PDM_FilterHandler[index].out_ptr_channels = ChannelNumber; PDM_FilterHandler[index].in_ptr_channels = ChannelNumber; PDM_Filter_Init((PDM_Filter_Handler_t *)(&PDM_FilterHandler[index])); /* Configure PDM filters */ PDM_FilterConfig[index].output_samples_number = AudioFreq/1000; PDM_FilterConfig[index].mic_gain = 24; PDM_FilterConfig[index].decimation_factor = PDM_FILTER_DEC_FACTOR_64; PDM_Filter_setConfig((PDM_Filter_Handler_t *)&PDM_FilterHandler[index], &PDM_FilterConfig[index]); } } static void AUDIO_IN_PDMToPCM(uint16_t *PDMBuf, uint16_t *PCMBuf, uint32_t ChannelNumber) { uint32_t index = 0; /* Invalidate Data Cache to get the updated content of the SRAM*/ SCB_InvalidateDCache_by_Addr((uint32_t *)PDMBuf, AUDIO_BUFFER_SIZE); for(index = 0; index < ChannelNumber; index++) { PDM_Filter(&((uint8_t*)(PDMBuf))[index], (uint16_t*)&(PCMBuf[index]), &PDM_FilterHandler[index]); } /* Clean Data Cache to update the content of the SRAM */ SCB_CleanDCache_by_Addr((uint32_t*)PCMBuf, AUDIO_PCM_CHUNK_SIZE*2); } void MIC_SAI_Init(void) { GPIO_InitTypeDef GPIO_Init; RCC_PeriphCLKInitTypeDef RCC_PeriphCLKInitStruct; /* Configure PLLSAI4A prescalers */ /* SAI4A: PLL2P = 8MHz/4*128/125 = 2.048MHz */ RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SAI4A; RCC_PeriphCLKInitStruct.Sai4AClockSelection = RCC_SAI4ACLKSOURCE_PLL2; RCC_PeriphCLKInitStruct.PLL2.PLL2P = 125; // 107--2.4MHz, 125--2.048MHz RCC_PeriphCLKInitStruct.PLL2.PLL2Q = 2; RCC_PeriphCLKInitStruct.PLL2.PLL2R = 2; RCC_PeriphCLKInitStruct.PLL2.PLL2N = 256;//128; RCC_PeriphCLKInitStruct.PLL2.PLL2FRACN = 6144.0; RCC_PeriphCLKInitStruct.PLL2.PLL2M = 4; RCC_PeriphCLKInitStruct.PLL2.PLL2RGE = RCC_PLL2VCIRANGE_1; RCC_PeriphCLKInitStruct.PLL2.PLL2VCOSEL = RCC_PLL2VCOWIDE; if(HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct) != HAL_OK) { Error_Handler(); } //--------------------------------------------------------------------------- /* SAI MSP Init */ //--------------------------------------------------------------------------- /* Enable SAI clock */ MIC_IN_SAI_PDMx_CLK_ENABLE(); MIC_IN_SAI_PDMx_CLK_IN_ENABLE(); MIC_IN_SAI_PDMx_DATA_IN_ENABLE(); GPIO_Init.Pin = MIC_IN_SAI_PDMx_CLK_IN_PIN; GPIO_Init.Mode = GPIO_MODE_AF_PP; GPIO_Init.Pull = GPIO_NOPULL; GPIO_Init.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_Init.Alternate = MIC_IN_SAI_PDMx_CLK_AF; HAL_GPIO_Init(MIC_IN_SAI_PDMx_CLK_IN_PORT, &GPIO_Init); GPIO_Init.Pull = GPIO_PULLUP; GPIO_Init.Speed = GPIO_SPEED_FREQ_MEDIUM; GPIO_Init.Pin = MIC_IN_SAI_PDMx_DATA_IN_PIN; GPIO_Init.Alternate = MIC_IN_SAI_PDMx_DATA_AF; HAL_GPIO_Init(MIC_IN_SAI_PDMx_DATA_IN_PORT, &GPIO_Init); //MIC_IN_SAI_PDMx_FS_SCK_ENABLE(); /* CODEC_SAI pins configuration: FS, SCK, MCK and SD pins GPIO_Init.Pin = MIC_IN_SAI_PDMx_FS_PIN | MIC_IN_SAI_PDMx_SCK_PIN; GPIO_Init.Mode = GPIO_MODE_AF_PP; GPIO_Init.Pull = GPIO_NOPULL; GPIO_Init.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_Init.Alternate = MIC_IN_SAI_PDMx_FS_SCK_AF; HAL_GPIO_Init(MIC_IN_SAI_PDMx_FS_SCK_GPIO_PORT, &GPIO_Init); */ /* Enable the DMA clock */ MIC_IN_SAI_PDMx_DMAx_CLK_ENABLE(); /* Configure the hdma_sai_rx handle parameters */ hSaiMicDma.Init.Request = MIC_IN_SAI_PDMx_DMAx_REQUEST; hSaiMicDma.Init.Direction = DMA_PERIPH_TO_MEMORY; hSaiMicDma.Init.PeriphInc = DMA_PINC_DISABLE; hSaiMicDma.Init.MemInc = DMA_MINC_ENABLE; hSaiMicDma.Init.PeriphDataAlignment = MIC_IN_SAI_PDMx_DMAx_PERIPH_DATA_SIZE; hSaiMicDma.Init.MemDataAlignment = MIC_IN_SAI_PDMx_DMAx_MEM_DATA_SIZE; hSaiMicDma.Init.Mode = DMA_CIRCULAR; hSaiMicDma.Init.Priority = DMA_PRIORITY_HIGH; hSaiMicDma.Init.FIFOMode = DMA_FIFOMODE_DISABLE; hSaiMicDma.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; hSaiMicDma.Init.MemBurst = DMA_MBURST_SINGLE; hSaiMicDma.Init.PeriphBurst = DMA_MBURST_SINGLE; hSaiMicDma.Instance = MIC_IN_SAI_PDMx_DMAx_STREAM; /* Associate the DMA handle */ __HAL_LINKDMA(&hSaiMic, hdmarx, hSaiMicDma); /* Deinitialize the Stream for new transfer */ HAL_DMA_DeInit(&hSaiMicDma); /* Configure the DMA Stream */ HAL_DMA_Init(&hSaiMicDma); /* SAI DMA IRQ Channel configuration */ HAL_NVIC_SetPriority(MIC_IN_SAI_PDMx_DMAx_IRQ, MIC_IN_SAI_IRQ_PREPRIO, 0); HAL_NVIC_EnableIRQ(MIC_IN_SAI_PDMx_DMAx_IRQ); //--------------------------------------------------------------------------- /* SAI PDM Input init */ __HAL_SAI_RESET_HANDLE_STATE(&hSaiMic); hSaiMic.Instance = MIC_IN_SAI_PDMx; hSaiMic.Init.AudioMode = SAI_MODEMASTER_RX; hSaiMic.Init.Synchro = SAI_ASYNCHRONOUS; hSaiMic.Init.SynchroExt = SAI_SYNCEXT_DISABLE; hSaiMic.Init.OutputDrive = SAI_OUTPUTDRIVE_DISABLE; hSaiMic.Init.NoDivider = SAI_MASTERDIVIDER_DISABLE; hSaiMic.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_HF;//SAI_FIFOTHRESHOLD_EMPTY;//SAI_FIFOTHRESHOLD_HF; hSaiMic.Init.AudioFrequency = AUDIO_PDM_GET_FS_FREQUENCY(AUDIO_FREQUENCY); hSaiMic.Init.Mckdiv = 0; hSaiMic.Init.MckOverSampling = SAI_MCK_OVERSAMPLING_DISABLE; hSaiMic.Init.MonoStereoMode = SAI_STEREOMODE;//SAI_MONOMODE;//SAI_STEREOMODE; hSaiMic.Init.CompandingMode = SAI_NOCOMPANDING; hSaiMic.Init.TriState = SAI_OUTPUT_NOTRELEASED; hSaiMic.Init.PdmInit.Activation = ENABLE; hSaiMic.Init.PdmInit.MicPairsNbr = 1; hSaiMic.Init.PdmInit.ClockEnable = SAI_PDM_CLOCK1_ENABLE; hSaiMic.Init.Protocol = SAI_FREE_PROTOCOL; hSaiMic.Init.DataSize = SAI_DATASIZE_16; hSaiMic.Init.FirstBit = SAI_FIRSTBIT_MSB;//SAI_FIRSTBIT_LSB; hSaiMic.Init.ClockStrobing = SAI_CLOCKSTROBING_RISINGEDGE;//SAI_CLOCKSTROBING_FALLINGEDGE;//SAI_CLOCKSTROBING_RISINGEDGE; hSaiMic.FrameInit.FrameLength = 16; hSaiMic.FrameInit.ActiveFrameLength = 1; hSaiMic.FrameInit.FSDefinition = SAI_FS_STARTFRAME; hSaiMic.FrameInit.FSPolarity = SAI_FS_ACTIVE_HIGH; hSaiMic.FrameInit.FSOffset = SAI_FS_FIRSTBIT; hSaiMic.SlotInit.FirstBitOffset = 0; hSaiMic.SlotInit.SlotSize = SAI_SLOTSIZE_DATASIZE; hSaiMic.SlotInit.SlotNumber = 1; hSaiMic.SlotInit.SlotActive = 0x0000FFFF;//SAI_SLOTACTIVE_0; HAL_SAI_DeInit(&hSaiMic); if(HAL_OK != HAL_SAI_Init(&hSaiMic)) { Error_Handler(); } __HAL_SAI_ENABLE(&hSaiMic); AUDIO_IN_PDMToPCM_Init(AUDIO_FREQUENCY, AUDIO_CHANNEL_NUMBER); bufferStatus = BUFFER_OFFSET_NONE; } void MIC_Receive_Start(void) { HAL_SAI_Receive_DMA(&hSaiMic, (uint8_t*)audioPdmBuf, AUDIO_BUFFER_SIZE); } void MIC_Data_Process(void) { uint16_t i; /* Wait Rx half transfer event */ if (bufferStatus == BUFFER_OFFSET_HALF) { /* Convert the first half of PDM data to PCM */ AUDIO_IN_PDMToPCM((uint16_t*)&audioPdmBuf[0], &audioPcmBuf[pcmPtr], AUDIO_CHANNEL_NUMBER); /* Update pcmPtr */ pcmPtr += AUDIO_PCM_CHUNK_SIZE; /* Initialize Rx buffer status */ bufferStatus &= BUFFER_OFFSET_NONE; } else if (bufferStatus == BUFFER_OFFSET_FULL) { /* Wait Rx transfer complete event */ //while(bufferStatus != BUFFER_OFFSET_FULL); /* Convert the second half of PDM data to PCM */ AUDIO_IN_PDMToPCM((uint16_t*)&audioPdmBuf[AUDIO_BUFFER_SIZE/2], &audioPcmBuf[pcmPtr], AUDIO_CHANNEL_NUMBER); /* Update pcmPtr */ pcmPtr += AUDIO_PCM_CHUNK_SIZE; /* Initialize Rx buffer status */ bufferStatus &= BUFFER_OFFSET_NONE; if(pcmPtr >= AUDIO_BUFFER_SIZE) { pcmPtr = 0; qst_util_serial_send((uint8_t *)audioPcmBuf, 2*AUDIO_BUFFER_SIZE); } } } void HAL_SAI_RxCpltCallback(SAI_HandleTypeDef *hsai) { bufferStatus = BUFFER_OFFSET_FULL; } void HAL_SAI_RxHalfCpltCallback(SAI_HandleTypeDef *hsai) { bufferStatus = BUFFER_OFFSET_HALF; }