2023-04-20 03:36 AM - edited 2023-11-20 07:36 AM
Dear fellow Devs,
I got a problem using the PDM to PCM library. While it seems to be working fine for a single conversion. There seems to be some kind of gap and noise between the results.
The picture below shows a 440Hz sine wave.
The class i wrote looks like that:
class IM69D130_t {
private:
IM69D130_settings_t IM69D130_settings;
static volatile bool Complete;
static volatile bool HalfComplete;
static volatile bool Spi_Overflow;
void TransformAndTransferToRam();
bool CaptureComplete = false;
static uint16_t Dataarray[DataArraySize];
int32_t Datalength = 32;
static PDM_Filter_Handler_t PDMFilter;
APS6404L_t* SRAM;
SPI_HandleTypeDef* MicSPI;
uint32_t currentaddress = 0;
static PDM_Filter_Config_t PDM_FilterConfig;
uint32_t TrueFreq = 48000000;
public:
IM69D130_t(APS6404L_t* SRamIn, SPI_HandleTypeDef* MicSPI);
void ReStartCapture();
int32_t SetSamplesToCapture(uint32_t Samples16Bit);
static void HalfCompleteCallback();
static void CpltCallback();
uint32_t GetCurrentPCMData(uint8_t* DataOut);
void Pause_Capture();
void UnPauseCapture();
uint32_t SetPDM(uint8_t Gain, float HighPassTab, uint32_t Samplelength,uint16_t DecimationFactor);
virtual ~IM69D130_t();
uint32_t CaptureData(uint32_t CaptureTimeMs);
void SetTrueFreq(uint32_t Freq);
};
The initialization of the filter looks like that:
uint32_t IM69D130_t::SetPDM(uint8_t Gain=24, float HighPassTab=0, uint32_t Samplelength=256,uint16_t PDMDecimationFactor=PDM_FILTER_DEC_FACTOR_128)
// This function sets the parameters for the PDM filter of the IM69D130 sensor
{
__HAL_RCC_CRC_CLK_ENABLE();
CRC->CR = CRC_CR_RESET;
uint16_t PDMDecimationNumber = 1;
switch (PDMDecimationFactor) {
case PDM_FILTER_DEC_FACTOR_16:
PDMDecimationNumber = 16;
break;
case PDM_FILTER_DEC_FACTOR_24:
PDMDecimationNumber = 24;
break;
case PDM_FILTER_DEC_FACTOR_32:
PDMDecimationNumber = 32;
break;
case PDM_FILTER_DEC_FACTOR_48:
PDMDecimationNumber = 48;
break;
case PDM_FILTER_DEC_FACTOR_64:
PDMDecimationNumber = 64;
break;
case PDM_FILTER_DEC_FACTOR_80:
PDMDecimationNumber = 80;
break;
case PDM_FILTER_DEC_FACTOR_128:
PDMDecimationNumber = 128;
break;
default:
PDMDecimationNumber = 128;
break;
}
IM69D130_settings.Gain = Gain;
IM69D130_settings.FilterDecFactor = PDMDecimationNumber;
IM69D130_settings.HIGHPassTab = HighPassTab;
IM69D130_settings.SampleLength = DataArraySize/2;
// Set the length of the sample
Datalength = Samplelength;
// Initialize the PDM filter configuration data structure
// Set the PDM filter parameters
PDMFilter.bit_order = PDM_FILTER_BIT_ORDER_MSB; // Specify least significant bit first
PDMFilter.endianness = PDM_FILTER_ENDIANNESS_LE; // Specify big-endian data
PDMFilter.high_pass_tap = HighPassTab*(2147483648-1); // Set the high-pass filter tap
PDMFilter.out_ptr_channels = 1; // Set the number of output channels to 1
PDMFilter.in_ptr_channels = 1; // Set the number of input channels to 1
// Set the PDM filter configuration parameters
PDM_FilterConfig.output_samples_number = (DataArraySize)*16/(PDMDecimationNumber*2); // Set the number of output samples (16 bits)
PDM_FilterConfig.mic_gain = 10; // Set the microphone gain
PDM_FilterConfig.decimation_factor = PDMDecimationFactor; // Set the decimation factor
// Initialize the PDM filter with the specified configuration
PDM_Filter_Init(&PDMFilter);
// Set the PDM filter configuration
return (PDM_Filter_setConfig(&PDMFilter, &PDM_FilterConfig));
}
And the capture part like that:
uint32_t IM69D130_t::CaptureData(uint32_t CaptureTimeMs = 1000)
{
//Set Base Data
currentaddress = 0;
CaptureComplete = false;
//Calculate current Sample Rate
uint32_t SPIFreq = TrueFreq/(SpiPrescalerToDivider(MicSPI->Init.BaudRatePrescaler));
float Samplerate = SPIFreq/(IM69D130_settings.FilterDecFactor*2);
uint32_t SamplesToCapture = CaptureTimeMs*Samplerate*1.0/1000;
int16_t PcmDataBuffer[PDM_FilterConfig.output_samples_number]; //temporary variable for pcm data converted from pdm data from data-array
HAL_SPI_Receive_DMA(MicSPI,(uint8_t*) Dataarray, DataArraySize*2);
if (SRamSizeByte<SamplesToCapture*2)
return 0;
while(currentaddress < SamplesToCapture*2){
if (Complete||HalfComplete)
{
uint8_t DATAPOS = Complete * Datalength/2; // set array position
if (PDM_Filter((uint8_t*) &Dataarray[DATAPOS], PcmDataBuffer, &PDMFilter) //run pdm to pcm filter on data in data-array
while(SRAM->QspiBusy()){} //WAIT FOR QSPI READY!
SRAM->SendToQspiRamDMA((uint8_t*) PcmDataBuffer, PDM_FilterConfig.output_samples_number*2, currentaddress);// write pcm-data to external RAM
currentaddress += PDM_FilterConfig.output_samples_number*2; //increment external RAM-address for pcm-data (2 Byte per sample)
if (Spi_Overflow==true)
Spi_Overflow = false; //to be able to set a breakpoint here (not triggert)
Complete = false; // reset flags
HalfComplete = false;
}
}
while(SRAM->QspiBusy()){} //Wait for last save
HAL_SPI_DMAStop(MicSPI);
return SamplesToCapture;
}
The callbacks only set the flags Complete and HalfComplete on their respective calls and checks or Overflow on slow capture.
void IM69D130_t::HalfCompleteCallback()
{
if (Complete)
Spi_Overflow = true;
HalfComplete = true;
}
void IM69D130_t::CpltCallback()
{
if (HalfComplete)
Spi_Overflow = true;
Complete=true;
}
p.s. I use a circular ring buffer and here are the SPI Settings:
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES_RXONLY;
hspi1.Init.DataSize = SPI_DATASIZE_16BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64;
hspi1.Init.FirstBit = SPI_FIRSTBIT_LSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 7;
hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
2023-04-20 08:45 AM
I found the error. Its working perfectly now.
This line was wrong:
uint8_t DATAPOS = Complete * Datalength/2;
Correct is
uint16_t DATAPOS = Complete * DataArraySize/2;