cancel
Showing results for 
Search instead for 
Did you mean: 

Problems using PDM To PDM

Christian Pommer
Associate II

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;


_legacyfs_online_stmicro_images_0693W00000biUQeQAM.png

1 REPLY 1
Christian Pommer
Associate II

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;