210 lines
6.5 KiB
C
210 lines
6.5 KiB
C
/*
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2022 Matthew McGowan for Blues Inc.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
|
|
|
|
#ifndef _MEMS_AUDIO_LL_STM32L4_H_
|
|
#define _MEMS_AUDIO_LL_STM32L4_H_
|
|
|
|
#include <stdbool.h>
|
|
#include <assert.h>
|
|
#include <stm32l4xx_hal.h>
|
|
#include "OpenPDMFilter.h"
|
|
#include "MEMS_Audio.h"
|
|
#include "MEMS_Audio_ll.h"
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/**
|
|
* @brief The SAI PDM interface captures 8 bits from the PDM signal.
|
|
*/
|
|
typedef uint8_t pdm_sample_t;
|
|
|
|
/**
|
|
* @brief The PDM sample frequency in kHz. (Bit samples per millisecond.)
|
|
*/
|
|
#define PDM_IN_FREQUENCY_KHZ 1024
|
|
|
|
#define PDM_IN_FREQUENCY (PDM_IN_FREQUENCY_KHZ * 1000)
|
|
|
|
/**
|
|
* @brief The number of channels of audio captured
|
|
*/
|
|
#define PDM_IN_CHANNELS 1
|
|
|
|
/**
|
|
* @brief The decimation of the PDM bit stream to produce PCM samples at the desired output rate.
|
|
*/
|
|
#define PDM_IN_DECIMATION_FACTOR 64
|
|
|
|
/**
|
|
* @brief The number of pdm samples captured per millisecond from the PDM interface.
|
|
*/
|
|
#define MEMS_AUDIO_PDM_SAMPLES_PER_MS ((PDM_IN_FREQUENCY_KHZ / (sizeof(pdm_sample_t) * 8)) * PDM_IN_CHANNELS)
|
|
|
|
/**
|
|
* @brief The size of the buffer used to hold PDM samples prior to conversion to PCM.
|
|
* Each half of the buffer generates an interrupt.
|
|
*/
|
|
#define MEMS_AUDIO_PDM_BUFFER_LENGTH (MEMS_AUDIO_PDM_SAMPLES_PER_MS * MEMS_AUDIO_MS_BUFFER * 2)
|
|
|
|
/**
|
|
* @brief The length of the PCM buffer required to hold converted samples.
|
|
*/
|
|
#define MEMS_AUDIO_PCM_BUFFER_LENGTH (PCM_OUT_SAMPLING_FREQUENCY * MEMS_AUDIO_MS_BUFFER / 1000)
|
|
|
|
|
|
/**
|
|
* Presently the internal PDM parameters and output PCM parameters are fixed for the values given here.
|
|
*/
|
|
|
|
|
|
/**
|
|
* @brief 128 point decimation did not work with the OpenPDMFilter and just produced PCM output
|
|
* approaching a square wave.
|
|
*/
|
|
_Static_assert(PDM_IN_DECIMATION_FACTOR == 64 || PDM_IN_DECIMATION_FACTOR == 128, "A decomation factor of 64 or 128 is supported at present.");
|
|
|
|
|
|
/**
|
|
* @brief The PDM bitstream frequency divided by the decimation factor should be the same as the desired output PCM frequency.
|
|
*/
|
|
_Static_assert(PDM_IN_FREQUENCY / PDM_IN_DECIMATION_FACTOR == PCM_OUT_SAMPLING_FREQUENCY, "PDM output frequency should equal the input frequency divided by the decimation factor.");
|
|
|
|
//
|
|
// SAI PDM interface clock configuration
|
|
//
|
|
|
|
#define MEMS_AUDIO_MSI_FREQUENCY (48 * 1000 * 1000)
|
|
#define MEMS_AUDIO_CLOCK_PLLM (15)
|
|
#define MEMS_AUDIO_CLOCK_PLLN (16)
|
|
#define MEMS_AUDIO_CLOCK_PLLP (RCC_PLLP_DIV25)
|
|
|
|
/**
|
|
* @brief The SAI PDM clock should be twice the desired PDM bitstream frequency
|
|
*/
|
|
_Static_assert((MEMS_AUDIO_MSI_FREQUENCY / MEMS_AUDIO_CLOCK_PLLM * MEMS_AUDIO_CLOCK_PLLN / MEMS_AUDIO_CLOCK_PLLP) == (PDM_IN_FREQUENCY_KHZ * 1000 * 2), "PDM clock should be twice the PDM sample frequency.");
|
|
|
|
typedef struct MemsAudio_STM32L4SAIPDM_t MemsAudio_STM32L4SAIPDM;
|
|
|
|
/**
|
|
* @brief Callback informing that PDM samples are available for processing.
|
|
* @param audio The MemsAudio instance
|
|
* @return `false` to skip conversion of PDM to PCM. `true` to convert the PDM samples to PCM.
|
|
*/
|
|
typedef bool (*pdm_data_available_t)(MemsAudio_STM32L4SAIPDM *audio, pdm_sample_t *pdmSamples, size_t pdmLength);
|
|
|
|
/**
|
|
* @brief Implementation details for the STM32 SAI PDM implementation.
|
|
*
|
|
*/
|
|
/**
|
|
* @brief Audio capture from a MEMS microphone on the STM32L4 using the SAI PDM interface.
|
|
*/
|
|
typedef struct MemsAudio_STM32L4SAIPDM_t {
|
|
|
|
MemsAudio *audio;
|
|
|
|
/**
|
|
* @brief The last error that happened in a void function (e.g. HAL callback)
|
|
*/
|
|
mems_audio_err_t lastError;
|
|
|
|
/**
|
|
* @brief The buffer to store PDM audio samples
|
|
*/
|
|
pdm_sample_t *pdmBuffer;
|
|
|
|
/**
|
|
* @brief The length of the PDM buffer. Should be at least MEMS_AUDIO_PDM_BUFFER_LENGTH
|
|
*/
|
|
size_t pdmBufferLength;
|
|
|
|
/**
|
|
* @brief Optional callback for when PDM data is available.
|
|
*/
|
|
pdm_data_available_t pdm_data_available;
|
|
|
|
/**
|
|
* @brief A cound of the number of PDM clients in use.
|
|
*/
|
|
uint32_t SAI1_client;
|
|
|
|
/**
|
|
* @brief The SAI peripheral handle being used for SAI A subclock 1.
|
|
*/
|
|
SAI_HandleTypeDef hSAI_BlockA1;
|
|
|
|
/**
|
|
* @brief The DMA handle to transfer SAI data from the peripheral to memory.
|
|
*/
|
|
DMA_HandleTypeDef hdma_sai1_a;
|
|
|
|
/**
|
|
* @brief An instance of the PDM filter that performs decimation, and high and low pass filtering.
|
|
* Unlike the DFSDM peripheral, the SAI PDM interface doesn't perform these operations in hardware.
|
|
*/
|
|
TPDMFilter_InitStruct filter;
|
|
|
|
/**
|
|
* @brief The number of DMA transfers to ignore after starting recording.
|
|
*/
|
|
volatile uint16_t discard_dma;
|
|
|
|
} MemsAudio_STM32L4SAIPDM;
|
|
|
|
/**
|
|
* @brief Creates a MemsAudio instance that retrieves PDM samples from SAI A block 1 via the PDM interface,
|
|
* decimates and filters these in software to produce the PCM output stream.
|
|
*
|
|
* @param audio
|
|
* @param implementation
|
|
* @return meems_audio_error_t
|
|
*/
|
|
mems_audio_err_t mems_audio_init_stm32l4_sai_pdm(MemsAudio *audio, MemsAudio_STM32L4SAIPDM *implementation);
|
|
|
|
/**
|
|
* @brief Implementation-specific error codes.
|
|
*
|
|
*/
|
|
typedef enum mems_audio_err_stm32l4_t {
|
|
MEMS_AUDIO_ERROR_SAI_DMA_INIT = 1,
|
|
MEMS_AUDIO_ERROR_SAI_CLOCK = 2,
|
|
MEMS_AUDIO_ERROR_SAI_INIT = 3,
|
|
MEMS_AUDIO_ERROR_SAI_DEINIT = 4,
|
|
MEMS_AUDIO_ERROR_DMA_START = 5,
|
|
MEMS_AUDIO_ERROR_DMA_STOP = 6,
|
|
MEMS_AUDIO_ERROR_DMA_PAUSE = 7,
|
|
MEMS_AUDIO_ERROR_DMA_RESUME = 8
|
|
} mems_audio_err_stm32l4_t;
|
|
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
|
|
#endif // _MEMS_AUDIO_LL_STM32L4_H_
|