From d412e9e0179b22eeda19d09a38850bf35e055168 Mon Sep 17 00:00:00 2001 From: Ibrahim Abd Elkader Date: Fri, 15 Oct 2021 16:05:12 +0200 Subject: [PATCH] Add DFSDM support to audio module. (#1459) * Add DFSDM support to audio module. --- src/hal/cmsis/src/st/system_stm32fxxx.c | 65 +++-- src/hal/stm32/h7/Makefile | 2 + src/micropython | 2 +- src/omv/boards/BORMIO/omv_boardconfig.h | 43 ++- src/omv/boards/BORMIO/omv_boardconfig.mk | 2 +- src/omv/boards/PORTENTA/omv_boardconfig.h | 4 +- src/omv/ports/stm32/modules/py_audio.c | 320 ++++++++++++++++------ src/omv/ports/stm32/stm32fxxx_hal_msp.c | 32 +++ 8 files changed, 358 insertions(+), 112 deletions(-) diff --git a/src/hal/cmsis/src/st/system_stm32fxxx.c b/src/hal/cmsis/src/st/system_stm32fxxx.c index 838d89014..12ffbded8 100644 --- a/src/hal/cmsis/src/st/system_stm32fxxx.c +++ b/src/hal/cmsis/src/st/system_stm32fxxx.c @@ -304,33 +304,56 @@ void SystemClock_Config(void) #endif #if defined(MCU_SERIES_H7) - PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC|RCC_PERIPHCLK_FDCAN - |RCC_PERIPHCLK_SPI3|RCC_PERIPHCLK_SPI2 - |RCC_PERIPHCLK_SDMMC|RCC_PERIPHCLK_I2C2 - |RCC_PERIPHCLK_ADC|RCC_PERIPHCLK_USB - |RCC_PERIPHCLK_QSPI|RCC_PERIPHCLK_FMC - |RCC_PERIPHCLK_RNG|RCC_PERIPHCLK_SAI4A; - PeriphClkInitStruct.UsbClockSelection = OMV_OSC_USB_CLKSOURCE; - PeriphClkInitStruct.SdmmcClockSelection = RCC_SDMMCCLKSOURCE_PLL; - PeriphClkInitStruct.FdcanClockSelection = RCC_FDCANCLKSOURCE_PLL; - PeriphClkInitStruct.FmcClockSelection = RCC_FMCCLKSOURCE_PLL2; - PeriphClkInitStruct.QspiClockSelection = RCC_QSPICLKSOURCE_PLL2; - PeriphClkInitStruct.AdcClockSelection = OMV_OSC_ADC_CLKSOURCE; - PeriphClkInitStruct.Spi123ClockSelection = OMV_OSC_SPI123_CLKSOURCE; - #if defined(OMV_OSC_SPI45_CLKSOURCE) - PeriphClkInitStruct.Spi45ClockSelection = OMV_OSC_SPI45_CLKSOURCE; + PeriphClkInitStruct.PeriphClockSelection |= RCC_PERIPHCLK_RTC; + PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSI; + + PeriphClkInitStruct.PeriphClockSelection |= RCC_PERIPHCLK_RNG; + PeriphClkInitStruct.RngClockSelection = OMV_OSC_RNG_CLKSOURCE; + + PeriphClkInitStruct.PeriphClockSelection |= RCC_PERIPHCLK_USB; + PeriphClkInitStruct.UsbClockSelection = OMV_OSC_USB_CLKSOURCE; + + PeriphClkInitStruct.PeriphClockSelection |= RCC_PERIPHCLK_FMC; + PeriphClkInitStruct.FmcClockSelection = RCC_FMCCLKSOURCE_PLL2; + + PeriphClkInitStruct.PeriphClockSelection |= RCC_PERIPHCLK_ADC; + PeriphClkInitStruct.AdcClockSelection = OMV_OSC_ADC_CLKSOURCE; + + PeriphClkInitStruct.PeriphClockSelection |= RCC_PERIPHCLK_SDMMC; + PeriphClkInitStruct.SdmmcClockSelection = RCC_SDMMCCLKSOURCE_PLL; + + PeriphClkInitStruct.PeriphClockSelection |= RCC_PERIPHCLK_FDCAN; + PeriphClkInitStruct.FdcanClockSelection = RCC_FDCANCLKSOURCE_PLL; + + PeriphClkInitStruct.PeriphClockSelection |= RCC_PERIPHCLK_QSPI; + PeriphClkInitStruct.QspiClockSelection = RCC_QSPICLKSOURCE_PLL2; + + PeriphClkInitStruct.PeriphClockSelection |= RCC_PERIPHCLK_SPI123; + PeriphClkInitStruct.Spi123ClockSelection = OMV_OSC_SPI123_CLKSOURCE; + + PeriphClkInitStruct.PeriphClockSelection |= RCC_PERIPHCLK_I2C123; + PeriphClkInitStruct.I2c123ClockSelection = RCC_I2C123CLKSOURCE_D2PCLK1; + + PeriphClkInitStruct.PeriphClockSelection |= RCC_PERIPHCLK_SAI4A; + PeriphClkInitStruct.Sai4AClockSelection = RCC_SAI4ACLKSOURCE_PLL; + + #if defined(OMV_OSC_DFSDM1_CLKSOURCE) + PeriphClkInitStruct.PeriphClockSelection |= RCC_PERIPHCLK_DFSDM1; + PeriphClkInitStruct.Dfsdm1ClockSelection = OMV_OSC_DFSDM1_CLKSOURCE; + #endif + + PeriphClkInitStruct.PeriphClockSelection |= RCC_PERIPHCLK_SPI45; + #if defined(OMV_OSC_SPI45_CLKSOURCE) + PeriphClkInitStruct.Spi45ClockSelection = OMV_OSC_SPI45_CLKSOURCE; + #else + PeriphClkInitStruct.Spi45ClockSelection = RCC_SPI45CLKSOURCE_PLL2; #endif - PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSI; - PeriphClkInitStruct.I2c123ClockSelection = RCC_I2C123CLKSOURCE_D2PCLK1; - PeriphClkInitStruct.Spi45ClockSelection = RCC_SPI45CLKSOURCE_PLL2; - PeriphClkInitStruct.RngClockSelection = OMV_OSC_RNG_CLKSOURCE; - PeriphClkInitStruct.Sai4AClockSelection = RCC_SAI4ACLKSOURCE_PLL; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) { // Initialization Error __fatal_error("HAL_RCCEx_PeriphCLKConfig"); } - #endif + #endif // defined(MCU_SERIES_H7) #if defined(MCU_SERIES_F4) || defined(MCU_SERIES_F7) if (HAL_PWREx_EnableOverDrive() != HAL_OK) { diff --git a/src/hal/stm32/h7/Makefile b/src/hal/stm32/h7/Makefile index 92126708f..7f6adb23a 100644 --- a/src/hal/stm32/h7/Makefile +++ b/src/hal/stm32/h7/Makefile @@ -16,6 +16,8 @@ stm32h7xx_hal_cortex.c\ stm32h7xx_hal_crc.c\ stm32h7xx_hal_crc_ex.c\ stm32h7xx_hal_dcmi.c\ +stm32h7xx_hal_dfsdm.c\ +stm32h7xx_hal_dfsdm_ex.c\ stm32h7xx_hal_dma.c\ stm32h7xx_hal_dma_ex.c\ stm32h7xx_hal_dma2d.c\ diff --git a/src/micropython b/src/micropython index 6f1022cbc..d44b3ba36 160000 --- a/src/micropython +++ b/src/micropython @@ -1 +1 @@ -Subproject commit 6f1022cbc15df7fcb05572a28992069096caea08 +Subproject commit d44b3ba367d04d05212062ce536bd23c8a7a402b diff --git a/src/omv/boards/BORMIO/omv_boardconfig.h b/src/omv/boards/BORMIO/omv_boardconfig.h index 0a0cf36b3..1df9e3d42 100644 --- a/src/omv/boards/BORMIO/omv_boardconfig.h +++ b/src/omv/boards/BORMIO/omv_boardconfig.h @@ -126,7 +126,8 @@ #define OMV_OSC_RNG_CLKSOURCE RCC_RNGCLKSOURCE_HSI48 #define OMV_OSC_ADC_CLKSOURCE RCC_ADCCLKSOURCE_PLL3 #define OMV_OSC_SPI45_CLKSOURCE RCC_SPI45CLKSOURCE_PLL3 -#define OMV_OSC_SPI123_CLKSOURCE RCC_SPI123CLKSOURCE_PLL3 +#define OMV_OSC_SPI123_CLKSOURCE RCC_SPI123CLKSOURCE_PLL2 +#define OMV_OSC_DFSDM1_CLKSOURCE RCC_DFSDM1CLKSOURCE_D2PCLK1 // HSE/HSI/CSI State #define OMV_OSC_HSE_STATE (RCC_HSE_BYPASS) @@ -153,7 +154,7 @@ #define OMV_FB_SIZE (400K) // FB memory: header + VGA/GS image #define OMV_FB_ALLOC_SIZE (100K) // minimum fb alloc size #define OMV_STACK_SIZE (64K) -#define OMV_HEAP_SIZE (200K) +#define OMV_HEAP_SIZE (192K) #define OMV_LINE_BUF_SIZE (3 * 1024) // Image line buffer round(640 * 2BPP * 2 buffers). #define OMV_MSC_BUF_SIZE (2K) // USB MSC bot data @@ -170,9 +171,9 @@ #define OMV_ITCM_ORIGIN 0x00000000 #define OMV_ITCM_LENGTH 64K #define OMV_SRAM1_ORIGIN 0x30000000 -#define OMV_SRAM1_LENGTH 248K -#define OMV_SRAM2_ORIGIN 0x3003E000 // 8KB of SRAM1 -#define OMV_SRAM2_LENGTH 8K +#define OMV_SRAM1_LENGTH 240K +#define OMV_SRAM2_ORIGIN 0x3003C000 // 16KB of SRAM1 +#define OMV_SRAM2_LENGTH 16K #define OMV_SRAM3_ORIGIN 0x30040000 #define OMV_SRAM3_LENGTH 32K #define OMV_SRAM4_ORIGIN 0x38000000 @@ -190,9 +191,9 @@ // Domain 2 DMA buffers region. #define OMV_DMA_MEMORY_D2 SRAM2 -#define OMV_DMA_MEMORY_D2_SIZE (1*1024) // Reserved memory for DMA buffers +#define OMV_DMA_MEMORY_D2_SIZE (6*1024) // Reserved memory for DMA buffers #define OMV_DMA_REGION_D2_BASE (OMV_SRAM2_ORIGIN+(0*1024)) -#define OMV_DMA_REGION_D2_SIZE MPU_REGION_SIZE_8KB +#define OMV_DMA_REGION_D2_SIZE MPU_REGION_SIZE_16KB // Domain 3 DMA buffers region. //#define OMV_DMA_MEMORY_D3 SRAM4 @@ -317,6 +318,34 @@ #define SOFT_I2C_SPIN_DELAY 64 +// DFSDM1 +#define AUDIO_DFSDM (DFSDM1_Channel2) +#define AUDIO_DFSDM_CHANNEL (DFSDM_CHANNEL_2) +// DFSDM output clock is derived from the Aclk (set in SAI1SEL[2:0]) +// for SAI1 and DFSDM1, which is clocked from PLL1Q by default (50MHz). +#define AUDIO_DFSDM_FREQMHZ (50) +#define AUDIO_MAX_CHANNELS (1) // Maximum number of channels. + +#define AUDIO_DFSDM_CK_PORT (GPIOD) +#define AUDIO_DFSDM_CK_PIN (GPIO_PIN_10) +#define AUDIO_DFSDM_CK_AF (GPIO_AF3_DFSDM1) + +#define AUDIO_DFSDM_D1_PORT (GPIOE) +#define AUDIO_DFSDM_D1_PIN (GPIO_PIN_7) +#define AUDIO_DFSDM_D1_AF (GPIO_AF3_DFSDM1) + +#define AUDIO_DFSDM_FLT0 DFSDM1_Filter0 +#define AUDIO_DFSDM_FLT0_IRQ DFSDM1_FLT0_IRQn +#define AUDIO_DFSDM_FLT0_IRQHandler DFSDM1_FLT0_IRQHandler +#define AUDIO_DFSDM_FLT0_DMA_STREAM DMA1_Stream1 +#define AUDIO_DFSDM_FLT0_DMA_REQUEST DMA_REQUEST_DFSDM1_FLT0 +#define AUDIO_DFSDM_FLT0_DMA_IRQ DMA1_Stream1_IRQn +#define AUDIO_DFSDM_FLT0_DMA_IRQHandler DMA1_Stream1_IRQHandler + +#define AUDIO_DFSDM_CLK_ENABLE() __HAL_RCC_DFSDM1_CLK_ENABLE() +#define AUDIO_DFSDM_CLK_DISABLE() __HAL_RCC_DFSDM1_CLK_DISABLE() +#define AUDIO_DFSDM_DMA_CLK_ENABLE() __HAL_RCC_DMA1_CLK_ENABLE() + #define IMU_CHIP_LSM6DSOX (1) #define IMU_SPI (SPI5) #define IMU_SPI_AF (GPIO_AF5_SPI5) diff --git a/src/omv/boards/BORMIO/omv_boardconfig.mk b/src/omv/boards/BORMIO/omv_boardconfig.mk index f48905c68..56ec713f5 100755 --- a/src/omv/boards/BORMIO/omv_boardconfig.mk +++ b/src/omv/boards/BORMIO/omv_boardconfig.mk @@ -19,7 +19,7 @@ MICROPY_PY_WINC1500 = 0 MICROPY_PY_IMU = 1 MICROPY_PY_LWIP = 1 MICROPY_PY_NETWORK_CYW43 = 1 -MICROPY_PY_AUDIO = 0 +MICROPY_PY_AUDIO = 1 MICROPY_PY_MICRO_SPEECH = 0 MICROPY_PY_LCD = 1 MICROPY_PY_TV = 1 diff --git a/src/omv/boards/PORTENTA/omv_boardconfig.h b/src/omv/boards/PORTENTA/omv_boardconfig.h index ef21a8550..2dbc321c9 100644 --- a/src/omv/boards/PORTENTA/omv_boardconfig.h +++ b/src/omv/boards/PORTENTA/omv_boardconfig.h @@ -363,7 +363,7 @@ // SCKx frequency = SAI_KER_CK / MCKDIV / 2 #define AUDIO_SAI_MCKDIV (12) #define AUDIO_SAI_FREQKHZ (2048U) // 2048KHz -#define AUDIO_SAI_NBR_CHANNELS (2) // Default number of channels. +#define AUDIO_MAX_CHANNELS (2) // Maximum number of channels. #define AUDIO_SAI_CK_PORT (GPIOE) #define AUDIO_SAI_CK_PIN (GPIO_PIN_2) @@ -387,7 +387,7 @@ // #define AUDIO_SAI (SAI1_Block_A) // #define AUDIO_SAI_MCKDIV (12) // #define AUDIO_SAI_FREQKHZ (2048U) // 2048KHz -// #define AUDIO_SAI_NBR_CHANNELS (2) // Default number of channels. +// #define AUDIO_MAX_CHANNELS (2) // Maximum number of channels. // // #define AUDIO_SAI_CK_PORT (GPIOE) // #define AUDIO_SAI_CK_PIN (GPIO_PIN_2) diff --git a/src/omv/ports/stm32/modules/py_audio.c b/src/omv/ports/stm32/modules/py_audio.c index 0001afd5c..93585d9b8 100644 --- a/src/omv/ports/stm32/modules/py_audio.c +++ b/src/omv/ports/stm32/modules/py_audio.c @@ -28,54 +28,85 @@ #if MICROPY_PY_AUDIO -#define RAISE_OS_EXCEPTION(msg) mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT(msg)) - -static CRC_HandleTypeDef hcrc; -static SAI_HandleTypeDef hsai; -static DMA_HandleTypeDef hdma_sai_rx; +#if defined(AUDIO_SAI) +static CRC_HandleTypeDef hcrc; +static SAI_HandleTypeDef hsai; +static DMA_HandleTypeDef hdma_sai_rx; +static PDM_Filter_Config_t PDM_FilterConfig[AUDIO_MAX_CHANNELS]; +static PDM_Filter_Handler_t PDM_FilterHandler[AUDIO_MAX_CHANNELS]; +// NOTE: BDMA can only access D3 SRAM4 memory. +#define PDM_BUFFER_SIZE (16384) +uint8_t OMV_ATTR_SECTION(OMV_ATTR_ALIGNED(PDM_BUFFER[PDM_BUFFER_SIZE], 32), ".d3_dma_buffer"); +#elif defined(AUDIO_DFSDM) +static DFSDM_Channel_HandleTypeDef hdfsdm; +// NOTE: Only 1 filter is supported right now. +static DFSDM_Filter_HandleTypeDef hdfsdm_filter[AUDIO_MAX_CHANNELS]; +static DMA_HandleTypeDef hdma_filter[AUDIO_MAX_CHANNELS]; +// NOTE: placed in D2 memory. +#define PDM_BUFFER_SIZE (512) +int32_t OMV_ATTR_SECTION(OMV_ATTR_ALIGNED(PDM_BUFFER[PDM_BUFFER_SIZE], 32), ".d2_dma_buffer"); +#define SaturaLH(N, L, H) (((N)<(L))?(L):(((N)>(H))?(H):(N))) +#else +#error "No audio driver defined for this board" +#endif +static volatile uint32_t xfer_status = 0; static mp_obj_array_t *g_pcmbuf = NULL; static mp_obj_t g_audio_callback = mp_const_none; +static int g_channels = AUDIO_MAX_CHANNELS; -static int g_channels = AUDIO_SAI_NBR_CHANNELS; -static PDM_Filter_Handler_t PDM_FilterHandler[2]; -static PDM_Filter_Config_t PDM_FilterConfig[2]; - -#define DMA_XFER_NONE (0x00U) -#define DMA_XFER_HALF (0x01U) -#define DMA_XFER_FULL (0x04U) -static volatile uint32_t xfer_status = 0; - -#define PDM_BUFFER_SIZE (16384) -// BDMA can only access D3 SRAM4 memory. -uint8_t OMV_ATTR_SECTION(OMV_ATTR_ALIGNED(PDM_BUFFER[PDM_BUFFER_SIZE], 32), ".d3_dma_buffer"); +#define DMA_XFER_NONE (0x00U) +#define DMA_XFER_HALF (0x01U) +#define DMA_XFER_FULL (0x04U) +#define RAISE_OS_EXCEPTION(msg) mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT(msg)) // Pendsv dispatch callback. static void audio_pendsv_callback(void); +#if defined(AUDIO_SAI) void AUDIO_SAI_DMA_IRQHandler(void) { HAL_DMA_IRQHandler(hsai.hdmarx); } +#elif defined(AUDIO_DFSDM) +void AUDIO_DFSDM_FLT0_IRQHandler() +{ + HAL_DFSDM_IRQHandler(&hdfsdm_filter[0]); +} +void AUDIO_DFSDM_FLT0_DMA_IRQHandler() +{ + HAL_DMA_IRQHandler(&hdma_filter[0]); +} +#endif // defined(AUDIO_SAI) + +#if defined(AUDIO_SAI) void HAL_SAI_RxHalfCpltCallback(SAI_HandleTypeDef *hsai) +#elif defined(AUDIO_DFSDM) +void HAL_DFSDM_FilterRegConvHalfCpltCallback(DFSDM_Filter_HandleTypeDef *hdfsdm_filter) +#endif { xfer_status |= DMA_XFER_HALF; - SCB_InvalidateDCache_by_Addr((uint32_t *)(&PDM_BUFFER[0]), PDM_BUFFER_SIZE / 2); + SCB_InvalidateDCache_by_Addr((uint32_t *)(&PDM_BUFFER[0]), sizeof(PDM_BUFFER) / 2); if (g_audio_callback != mp_const_none) { pendsv_schedule_dispatch(PENDSV_DISPATCH_AUDIO, audio_pendsv_callback); } } +#if defined(AUDIO_SAI) void HAL_SAI_RxCpltCallback(SAI_HandleTypeDef *hsai) +#elif defined(AUDIO_DFSDM) +void HAL_DFSDM_FilterRegConvCpltCallback(DFSDM_Filter_HandleTypeDef *hdfsdm_filter) +#endif { xfer_status |= DMA_XFER_FULL; - SCB_InvalidateDCache_by_Addr((uint32_t *)(&PDM_BUFFER[PDM_BUFFER_SIZE / 2]), PDM_BUFFER_SIZE / 2); + SCB_InvalidateDCache_by_Addr((uint32_t *)(&PDM_BUFFER[PDM_BUFFER_SIZE / 2]), sizeof(PDM_BUFFER) / 2); if (g_audio_callback != mp_const_none) { pendsv_schedule_dispatch(PENDSV_DISPATCH_AUDIO, audio_pendsv_callback); } } +#if defined(AUDIO_SAI) static uint32_t get_decimation_factor(uint32_t decimation) { switch (decimation) { @@ -89,24 +120,28 @@ static uint32_t get_decimation_factor(uint32_t decimation) default: return 0; } } +#endif static mp_obj_t py_audio_init(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { // Read Args. - g_channels = py_helper_keyword_int(n_args, args, 0, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_channels), 2); + g_channels = py_helper_keyword_int(n_args, args, 0, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_channels), AUDIO_MAX_CHANNELS); uint32_t frequency = py_helper_keyword_int(n_args, args, 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_frequency), 16000); + #if defined(AUDIO_SAI) int gain_db = py_helper_keyword_int(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_gain_db), 24); float highpass = py_helper_keyword_float(n_args, args, 3, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_highpass), 0.9883f); + #endif // Sanity checks if (frequency < 16000 || frequency > 128000) { RAISE_OS_EXCEPTION("Invalid frequency!"); } - if (g_channels != 1 && g_channels != 2) { - RAISE_OS_EXCEPTION("Invalid number of channels! Expected 1 or 2."); + if (g_channels != 1 && g_channels > AUDIO_MAX_CHANNELS) { + RAISE_OS_EXCEPTION("Invalid number of channels!"); } + #if defined(AUDIO_SAI) uint32_t decimation_factor = AUDIO_SAI_FREQKHZ / (frequency / 1000); uint32_t decimation_factor_const = get_decimation_factor(decimation_factor); if (decimation_factor_const == 0) { @@ -213,6 +248,79 @@ static mp_obj_t py_audio_init(uint n_args, const mp_obj_t *args, mp_map_t *kw_ar PDM_FilterConfig[i].decimation_factor = decimation_factor_const; PDM_Filter_setConfig(&PDM_FilterHandler[i], &PDM_FilterConfig[i]); } + #elif defined(AUDIO_DFSDM) + hdfsdm.Instance = AUDIO_DFSDM; + hdfsdm.Init.OutputClock.Activation = ENABLE; + hdfsdm.Init.OutputClock.Selection = DFSDM_CHANNEL_OUTPUT_CLOCK_AUDIO; + hdfsdm.Init.OutputClock.Divider = AUDIO_DFSDM_FREQMHZ / 2; /* Divider = Aclk / 2MHz*/ + hdfsdm.Init.Input.Multiplexer = DFSDM_CHANNEL_EXTERNAL_INPUTS; + hdfsdm.Init.Input.DataPacking = DFSDM_CHANNEL_STANDARD_MODE; + hdfsdm.Init.Input.Pins = DFSDM_CHANNEL_SAME_CHANNEL_PINS; + hdfsdm.Init.SerialInterface.Type = DFSDM_CHANNEL_SPI_RISING; + hdfsdm.Init.SerialInterface.SpiClock = DFSDM_CHANNEL_SPI_CLOCK_INTERNAL; + hdfsdm.Init.Awd.FilterOrder = DFSDM_CHANNEL_FASTSINC_ORDER; + hdfsdm.Init.Awd.Oversampling = 125; /* 2MHz/125 = 16kHz */ + hdfsdm.Init.Offset = 0; + hdfsdm.Init.RightBitShift = 0x02; + + __HAL_DFSDM_CHANNEL_RESET_HANDLE_STATE(&hdfsdm); + if (HAL_DFSDM_ChannelInit(&hdfsdm) != HAL_OK) { + RAISE_OS_EXCEPTION("Failed to init DFSDM"); + } + + hdfsdm_filter[0].Instance = AUDIO_DFSDM_FLT0; + hdfsdm_filter[0].Init.RegularParam.Trigger = DFSDM_FILTER_SW_TRIGGER; + hdfsdm_filter[0].Init.RegularParam.FastMode = ENABLE; + hdfsdm_filter[0].Init.RegularParam.DmaMode = ENABLE; + hdfsdm_filter[0].Init.InjectedParam.Trigger = DFSDM_FILTER_SINC3_ORDER; + hdfsdm_filter[0].Init.InjectedParam.ScanMode = ENABLE; + hdfsdm_filter[0].Init.InjectedParam.DmaMode = ENABLE; + hdfsdm_filter[0].Init.InjectedParam.ExtTrigger = DFSDM_FILTER_EXT_TRIG_TIM1_TRGO; + hdfsdm_filter[0].Init.InjectedParam.ExtTriggerEdge = DFSDM_FILTER_EXT_TRIG_RISING_EDGE; + hdfsdm_filter[0].Init.FilterParam.SincOrder = DFSDM_FILTER_FASTSINC_ORDER; + hdfsdm_filter[0].Init.FilterParam.Oversampling = 125; /* 2MHz/125 = 16kHz */ + hdfsdm_filter[0].Init.FilterParam.IntOversampling = 1; + + __HAL_DFSDM_FILTER_RESET_HANDLE_STATE(&hdfsdm_filter[0]); + if (HAL_DFSDM_FilterInit(&hdfsdm_filter[0]) != HAL_OK || + HAL_DFSDM_FilterConfigRegChannel(&hdfsdm_filter[0], + AUDIO_DFSDM_CHANNEL, DFSDM_CONTINUOUS_CONV_ON) != HAL_OK) { + RAISE_OS_EXCEPTION("Failed to init DFSDM filter"); + return 0; + } + + // Enable the DMA clock + AUDIO_DFSDM_DMA_CLK_ENABLE(); + + // Configure the DFSDM Filter 0 DMA/IRQ + hdma_filter[0].Instance = AUDIO_DFSDM_FLT0_DMA_STREAM; + hdma_filter[0].Init.Request = AUDIO_DFSDM_FLT0_DMA_REQUEST; + hdma_filter[0].Init.Direction = DMA_PERIPH_TO_MEMORY; + hdma_filter[0].Init.PeriphInc = DMA_PINC_DISABLE; + hdma_filter[0].Init.MemInc = DMA_MINC_ENABLE; + hdma_filter[0].Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; + hdma_filter[0].Init.MemDataAlignment = DMA_MDATAALIGN_WORD; + hdma_filter[0].Init.Mode = DMA_CIRCULAR; + hdma_filter[0].Init.Priority = DMA_PRIORITY_HIGH; + + __HAL_LINKDMA(&hdfsdm_filter[0], hdmaInj, hdma_filter[0]); + __HAL_LINKDMA(&hdfsdm_filter[0], hdmaReg, hdma_filter[0]); + + // Initialize the DMA stream + HAL_DMA_DeInit(&hdma_filter[0]); + if (HAL_DMA_Init(&hdma_filter[0]) != HAL_OK) { + RAISE_OS_EXCEPTION("SAI DFSDM init failed!"); + } + + // Configure and enable DFSDM Filter 0 DMA IRQ. + NVIC_SetPriority(AUDIO_DFSDM_FLT0_DMA_IRQ, IRQ_PRI_DMA21); + HAL_NVIC_EnableIRQ(AUDIO_DFSDM_FLT0_DMA_IRQ); + + NVIC_SetPriority(AUDIO_DFSDM_FLT0_IRQ, IRQ_PRI_DMA21); + HAL_NVIC_EnableIRQ(AUDIO_DFSDM_FLT0_IRQ); + + uint32_t samples_per_channel = PDM_BUFFER_SIZE / 2; // Half a transfer + #endif // defined(AUDIO_SAI) // Allocate global PCM buffer. g_pcmbuf = mp_obj_new_bytearray_by_ref( @@ -221,9 +329,11 @@ static mp_obj_t py_audio_init(uint n_args, const mp_obj_t *args, mp_map_t *kw_ar return mp_const_none; } +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_audio_init_obj, 0, py_audio_init); void py_audio_deinit() { + #if defined(AUDIO_SAI) // Stop SAI DMA. if (hdma_sai_rx.Instance != NULL) { HAL_SAI_DMAStop(&hsai); @@ -241,12 +351,116 @@ void py_audio_deinit() HAL_DMA_DeInit(&hdma_sai_rx); hdma_sai_rx.Instance = NULL; } + #elif defined(AUDIO_DFSDM) + if (hdma_filter[0].Instance != NULL) { + HAL_DFSDM_FilterRegularStop_DMA(&hdfsdm_filter[0]); + } + + // Disable IRQs + HAL_NVIC_DisableIRQ(AUDIO_DFSDM_FLT0_DMA_IRQ); + HAL_NVIC_DisableIRQ(AUDIO_DFSDM_FLT0_IRQ); + + if (hdfsdm.Instance != NULL) { + HAL_DFSDM_ChannelDeInit(&hdfsdm); + hdfsdm.Instance = NULL; + } + + if (hdma_filter[0].Instance != NULL) { + HAL_DMA_DeInit(&hdma_filter[0]); + hdma_filter[0].Instance = NULL; + } + #endif g_channels = 0; g_pcmbuf = NULL; g_audio_callback = mp_const_none; } +static void audio_pendsv_callback(void) +{ + // Check for half transfer complete. + if ((xfer_status & DMA_XFER_HALF)) { + // Clear buffer state. + xfer_status &= ~(DMA_XFER_HALF); + + #if defined(AUDIO_SAI) + // Convert PDM samples to PCM. + for (int i=0; iitems)[i], &PDM_FilterHandler[i]); + } + #elif defined(AUDIO_DFSDM) + int16_t *pcmbuf = (int16_t*) g_pcmbuf->items; + for (int i=0; i> 8), -32768, 32767); + } + #endif + } else if ((xfer_status & DMA_XFER_FULL)) { // Check for transfer complete. + // Clear buffer state. + xfer_status &= ~(DMA_XFER_FULL); + + #if defined(AUDIO_SAI) + // Convert PDM samples to PCM. + for (int i=0; iitems)[i], &PDM_FilterHandler[i]); + } + #elif defined(AUDIO_DFSDM) + int16_t *pcmbuf = (int16_t*) g_pcmbuf->items; + for(int i = 0; i < PDM_BUFFER_SIZE / 2; i++) { + pcmbuf[i] = SaturaLH((PDM_BUFFER[PDM_BUFFER_SIZE / 2 + i] >> 8), -32768, 32767); + } + #endif + } + // Call user callback + mp_call_function_1(g_audio_callback, MP_OBJ_FROM_PTR(g_pcmbuf)); +} + +static mp_obj_t py_audio_start_streaming(mp_obj_t callback_obj) +{ + g_audio_callback = callback_obj; + + if (!mp_obj_is_callable(g_audio_callback)) { + g_audio_callback = mp_const_none; + RAISE_OS_EXCEPTION("Invalid callback object!"); + } + + // Clear DMA buffer status + xfer_status &= DMA_XFER_NONE; + + #if defined(AUDIO_SAI) + // Start DMA transfer + if (HAL_SAI_Receive_DMA(&hsai, (uint8_t*) PDM_BUFFER, PDM_BUFFER_SIZE / g_channels) != HAL_OK) { + g_audio_callback = mp_const_none; + RAISE_OS_EXCEPTION("SAI DMA transfer failed!"); + } + #elif defined(AUDIO_DFSDM) + // Start DMA transfer + if (HAL_DFSDM_FilterRegularStart_DMA(&hdfsdm_filter[0], PDM_BUFFER, PDM_BUFFER_SIZE) != HAL_OK) { + RAISE_OS_EXCEPTION("DFSDM DMA transfer failed!"); + } + #endif + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_audio_start_streaming_obj, py_audio_start_streaming); + +static mp_obj_t py_audio_stop_streaming() +{ + #if defined(AUDIO_SAI) + // Stop SAI DMA. + if (hdma_sai_rx.Instance != NULL) { + HAL_SAI_DMAStop(&hsai); + } + #elif defined(AUDIO_DFSDM) + if (hdma_filter[0].Instance != NULL) { + HAL_DFSDM_FilterRegularStop_DMA(&hdfsdm_filter[0]); + } + #endif + g_audio_callback = mp_const_none; + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(py_audio_stop_streaming_obj, py_audio_stop_streaming); + +#if defined(AUDIO_SAI) static mp_obj_t py_audio_read_pdm(mp_obj_t buf_in) { mp_buffer_info_t pdmbuf; @@ -299,71 +513,17 @@ static mp_obj_t py_audio_read_pdm(mp_obj_t buf_in) return mp_const_none; } - -static void audio_pendsv_callback(void) -{ - // Check for half transfer complete. - if ((xfer_status & DMA_XFER_HALF)) { - // Clear buffer state. - xfer_status &= ~(DMA_XFER_HALF); - - // Convert PDM samples to PCM. - for (int i=0; iitems)[i], &PDM_FilterHandler[i]); - } - } else if ((xfer_status & DMA_XFER_FULL)) { // Check for transfer complete. - // Clear buffer state. - xfer_status &= ~(DMA_XFER_FULL); - - // Convert PDM samples to PCM. - for (int i=0; iitems)[i], &PDM_FilterHandler[i]); - } - } - // Call user callback - mp_call_function_1(g_audio_callback, MP_OBJ_FROM_PTR(g_pcmbuf)); -} - -static mp_obj_t py_audio_start_streaming(mp_obj_t callback_obj) -{ - g_audio_callback = callback_obj; - - if (!mp_obj_is_callable(g_audio_callback)) { - g_audio_callback = mp_const_none; - RAISE_OS_EXCEPTION("Invalid callback object!"); - } - - // Clear DMA buffer status - xfer_status &= DMA_XFER_NONE; - - // Start DMA transfer - if (HAL_SAI_Receive_DMA(&hsai, (uint8_t*) PDM_BUFFER, PDM_BUFFER_SIZE / g_channels) != HAL_OK) { - g_audio_callback = mp_const_none; - RAISE_OS_EXCEPTION("SAI DMA transfer failed!"); - } - - return mp_const_none; -} - -static mp_obj_t py_audio_stop_streaming() -{ - // Stop SAI DMA. - HAL_SAI_DMAStop(&hsai); - g_audio_callback = mp_const_none; - return mp_const_none; -} - -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_audio_init_obj, 0, py_audio_init); STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_audio_read_pdm_obj, py_audio_read_pdm); -STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_audio_start_streaming_obj, py_audio_start_streaming); -STATIC MP_DEFINE_CONST_FUN_OBJ_0(py_audio_stop_streaming_obj, py_audio_stop_streaming); +#endif static const mp_rom_map_elem_t globals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_audio) }, { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&py_audio_init_obj) }, - { MP_ROM_QSTR(MP_QSTR_read_pdm), MP_ROM_PTR(&py_audio_read_pdm_obj) }, { MP_ROM_QSTR(MP_QSTR_start_streaming), MP_ROM_PTR(&py_audio_start_streaming_obj)}, { MP_ROM_QSTR(MP_QSTR_stop_streaming), MP_ROM_PTR(&py_audio_stop_streaming_obj) }, + #if defined(AUDIO_SAI) + { MP_ROM_QSTR(MP_QSTR_read_pdm), MP_ROM_PTR(&py_audio_read_pdm_obj) }, + #endif }; STATIC MP_DEFINE_CONST_DICT(globals_dict, globals_dict_table); diff --git a/src/omv/ports/stm32/stm32fxxx_hal_msp.c b/src/omv/ports/stm32/stm32fxxx_hal_msp.c index 24db17ef3..97bbd8498 100644 --- a/src/omv/ports/stm32/stm32fxxx_hal_msp.c +++ b/src/omv/ports/stm32/stm32fxxx_hal_msp.c @@ -512,6 +512,38 @@ void HAL_SAI_MspDeInit(SAI_HandleTypeDef* hsai) HAL_GPIO_DeInit(AUDIO_SAI_D1_PORT, AUDIO_SAI_D1_PIN); } } +#elif defined(AUDIO_DFSDM) +void HAL_DFSDM_ChannelMspInit(DFSDM_Channel_HandleTypeDef *hdfsdm) +{ + GPIO_InitTypeDef GPIO_InitStruct; + if (hdfsdm->Instance == AUDIO_DFSDM) { + AUDIO_DFSDM_CLK_ENABLE(); + + GPIO_InitStruct.Pin = AUDIO_DFSDM_CK_PIN; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStruct.Alternate = AUDIO_DFSDM_CK_AF; + HAL_GPIO_Init(AUDIO_DFSDM_CK_PORT, &GPIO_InitStruct); + + GPIO_InitStruct.Pin = AUDIO_DFSDM_D1_PIN; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; + GPIO_InitStruct.Alternate = AUDIO_DFSDM_D1_AF; + HAL_GPIO_Init(AUDIO_DFSDM_D1_PORT, &GPIO_InitStruct); + } +} + +void HAL_DFSDM_ChannelMspDeInit(DFSDM_Channel_HandleTypeDef *hdfsdm) +{ + if (hdfsdm->Instance == AUDIO_DFSDM) { + AUDIO_DFSDM_CLK_DISABLE(); + + HAL_GPIO_DeInit(AUDIO_DFSDM_CK_PORT, AUDIO_DFSDM_CK_PIN); + HAL_GPIO_DeInit(AUDIO_DFSDM_D1_PORT, AUDIO_DFSDM_D1_PIN); + } +} #endif void HAL_CRC_MspInit(CRC_HandleTypeDef* hcrc)