Are there any others that are experiencing FFT computation problems with the Microphone example on the BOOSTXL-EDUMKII Educational BoosterPack for the MSP432P401R LaunchPad or know of a problem with the real FFT routines in the current CMSIS library?
The CMSIS real FFT routine used in the Microphone example in the BOOSTXL-EDUMKII Educational BoosterPack for the MSP432P401R LaunchPad does not calculate the FFT results correctly.
The routine that Texas Instruments has used to generate a frequency spectrum on the 128X128 LCD display is the "arm_rfft_q15()" routine from the current CMSIS libraray. This routine should take the 512 data points sampled from the microphone on this board and display the frequency spectrum of the waveform from zero to 2 KHz.
When tested using forced sine waves that are interger multiples of the sampling frequency, the "arm_rfft_q15()" correctly calculates the FFT. However, any frequency that is not an integer multiple of the sampling frequency does not gerenerate a correct FFT.
When the "arm_rfft_q15()" routine is changed to the use of its equivalent "q31" format, then the FFT is correctly calculated for any input waveform. Use of this version of the real FFT also results in correct output on the LCD display of the BoosterPack.
It is most likely that the problem with the "arm_rfft_q15()" routine on the MSP432P401R processor is due to an anomaly in the CMSIS real FFT routine and not the M4F processor in the MSP432P401R.
The main() code below shows the modifications made to the Texas Instruments Microphone FFT demo (in red) to change from the "q15" version of the real FFT routine to the "q31" version.
//**************************************************************************** // // main.c - MSP-EXP432P401R + Educational Boosterpack MkII - Microphone FFT // // CMSIS DSP Software Library is used to perform 512-point FFT on // the audio samples collected with MSP432's ADC14 from the Education // Boosterpack's onboard microhpone. The resulting frequency bin data // is displayed on the BoosterPack's 128x128 LCD. // //**************************************************************************** #include "msp.h" #include <driverlib.h> #include <grlib.h> #include "Crystalfontz128x128_ST7735.h" #include <stdio.h> #include <arm_math.h> #include "arm_const_structs.h" #define TEST_LENGTH_SAMPLES 512 #define SAMPLE_LENGTH 512 /* ------------------------------------------------------------------ * Global variables for FFT Bin Example * ------------------------------------------------------------------- */ uint16_t fftSize = SAMPLE_LENGTH; uint8_t ifftFlag = 0; uint8_t doBitReverse = 1; volatile arm_status status; /* Graphic library context */ Graphics_Context g_sContext; #define SMCLK_FREQUENCY 48000000 #define SAMPLE_FREQUENCY 8000 /* DMA Control Table */ #ifdef ewarm #pragma data_alignment=256 #else #pragma DATA_ALIGN(controlTable, 256) #endif uint8_t controlTable[256]; /* FFT data/processing buffers*/ float hann[SAMPLE_LENGTH]; int16_t data_array1[SAMPLE_LENGTH]; int16_t data_array2[SAMPLE_LENGTH]; int16_t data_input[SAMPLE_LENGTH*2]; int16_t data_output[SAMPLE_LENGTH]; // q31 variables to fix q15 FFT problem q31_t Buffer_In1_Real[SAMPLE_LENGTH]; q31_t Buffer_In1_Real_Copy[SAMPLE_LENGTH]; q31_t Buffer_In2_Real[SAMPLE_LENGTH]; q31_t Buffer_Out_Complex[SAMPLE_LENGTH * 2]; q31_t Buffer_Magnitude_Real[SAMPLE_LENGTH]; arm_rfft_instance_q31 Instance_Real; volatile int switch_data = 0; /* Timer_A PWM Configuration Parameter */ Timer_A_PWMConfig pwmConfig = { TIMER_A_CLOCKSOURCE_SMCLK, TIMER_A_CLOCKSOURCE_DIVIDER_1, (SMCLK_FREQUENCY/SAMPLE_FREQUENCY), TIMER_A_CAPTURECOMPARE_REGISTER_1, TIMER_A_OUTPUTMODE_SET_RESET, (SMCLK_FREQUENCY/SAMPLE_FREQUENCY)/2 }; void main(void) { /* Halting WDT and disabling master interrupts */ MAP_WDT_A_holdTimer(); MAP_Interrupt_disableMaster(); /* Initializes Clock System */ MAP_CS_setDCOCenteredFrequency(CS_DCO_FREQUENCY_48); MAP_CS_initClockSignal(CS_MCLK, CS_DCOCLK_SELECT, CS_CLOCK_DIVIDER_1 ); MAP_CS_initClockSignal(CS_HSMCLK, CS_DCOCLK_SELECT, CS_CLOCK_DIVIDER_1 ); MAP_CS_initClockSignal(CS_SMCLK, CS_DCOCLK_SELECT, CS_CLOCK_DIVIDER_1 ); MAP_CS_initClockSignal(CS_ACLK, CS_REFOCLK_SELECT, CS_CLOCK_DIVIDER_1); /* Initializes display */ Crystalfontz128x128_Init(); /* Set default screen orientation */ Crystalfontz128x128_SetOrientation(LCD_ORIENTATION_UP); /* Initializes graphics context */ Graphics_initContext(&g_sContext, &g_sCrystalfontz128x128); Graphics_setForegroundColor(&g_sContext, GRAPHICS_COLOR_RED); Graphics_setBackgroundColor(&g_sContext, GRAPHICS_COLOR_WHITE); GrContextFontSet(&g_sContext, &g_sFontFixed6x8); Graphics_clearDisplay(&g_sContext); Graphics_drawStringCentered(&g_sContext, "512-Point FFT", AUTO_STRING_LENGTH, 64, 6, OPAQUE_TEXT); Graphics_drawStringCentered(&g_sContext, "1kHz", AUTO_STRING_LENGTH, 64, 122, OPAQUE_TEXT); Graphics_drawStringCentered(&g_sContext, "0", AUTO_STRING_LENGTH, 6, 122, OPAQUE_TEXT); Graphics_drawStringCentered(&g_sContext, "2", AUTO_STRING_LENGTH, 122, 122, OPAQUE_TEXT); // Initialize Hann Window int n; for (n = 0; n < SAMPLE_LENGTH; n++) { hann[n] = 0.5 - 0.5 * cosf((2*PI*n)/(SAMPLE_LENGTH-1)); } // Configuring Timer_A to have a period of approximately 500ms and // an initial duty cycle of 10% of that (3200 ticks) Timer_A_generatePWM(TIMER_A0_MODULE, &pwmConfig); // Initializing ADC (MCLK/1/1) ADC14_enableModule(); ADC14_initModule(ADC_CLOCKSOURCE_MCLK, ADC_PREDIVIDER_1, ADC_DIVIDER_1, 0); ADC14_setSampleHoldTrigger(ADC_TRIGGER_SOURCE1, false); // Configuring GPIOs (4.3 A10) GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P4, GPIO_PIN3, GPIO_TERTIARY_MODULE_FUNCTION); // Configuring ADC Memory ADC14_configureSingleSampleMode(ADC_MEM0, true); ADC14_configureConversionMemory(ADC_MEM0, ADC_VREFPOS_AVCC_VREFNEG_VSS, ADC_INPUT_A10, false); // Set ADC result format to signed binary ADC14_setResultFormat(ADC_SIGNED_BINARY); // Configuring DMA module DMA_enableModule(); DMA_setControlBase(controlTable); DMA_disableChannelAttribute(DMA_CH7_ADC12C, UDMA_ATTR_ALTSELECT | UDMA_ATTR_USEBURST | UDMA_ATTR_HIGH_PRIORITY | UDMA_ATTR_REQMASK); // Setting Control Indexes. In this case we will set the source of the // DMA transfer to ADC14 Memory 0 // and the destination to the // destination data array. MAP_DMA_setChannelControl(UDMA_PRI_SELECT | DMA_CH7_ADC12C, UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_1); MAP_DMA_setChannelTransfer(UDMA_PRI_SELECT | DMA_CH7_ADC12C, UDMA_MODE_PINGPONG, (void*) (ADC14_BASE + OFS_ADC14MEM0), data_array1, SAMPLE_LENGTH); MAP_DMA_setChannelControl(UDMA_ALT_SELECT | DMA_CH7_ADC12C, UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_1); MAP_DMA_setChannelTransfer(UDMA_ALT_SELECT | DMA_CH7_ADC12C, UDMA_MODE_PINGPONG, (void*) (ADC14_BASE + OFS_ADC14MEM0), data_array2, SAMPLE_LENGTH); // Assigning/Enabling Interrupts MAP_DMA_assignInterrupt(DMA_INT1, 7); MAP_Interrupt_enableInterrupt(INT_DMA_INT1); MAP_DMA_assignChannel(DMA_CH7_ADC12C); MAP_DMA_clearInterruptFlag(7); MAP_Interrupt_enableMaster(); // Now that the DMA is primed and setup, enabling the channels. The ADC14 // hardware should take over and transfer/receive all bytes MAP_DMA_enableChannel(7); MAP_ADC14_enableConversion(); while(1) { MAP_PCM_gotoLPM0(); int i = 0; /* for (i=0; i<SAMPLE_LENGTH; i++) { // Generate a 1000 Hz sine wave with DC offset. // Sample rate is 8000 samples/second Buffer_In1_Real[i] = (q31_t)( 1000.0 * sinf( 6.283185308 * (float)i * 1000.0 / 8000.0 ) + 0X000 ); // Save a copy of Buffer_In1_Real[] since it will be manipulated by the FFT Buffer_In2_Real[i] = Buffer_In1_Real[i]; } */ // Copy TI's data arrays to the new FFT working buffers for (i = 0; i < SAMPLE_LENGTH; i++) { Buffer_In1_Real[i] = (q31_t)data_array1[i]; Buffer_In2_Real[i] = (q31_t)data_array2[i]; } //Compute real FFT using the completed data buffer if (switch_data & 1) { arm_rfft_instance_q31 Instance_Real; status = arm_rfft_init_q31(&Instance_Real, fftSize, ifftFlag, doBitReverse); arm_rfft_q31(&Instance_Real, Buffer_In1_Real, Buffer_Out_Complex); } else { arm_rfft_instance_q31 Instance_Real; status = arm_rfft_init_q31(&Instance_Real, fftSize, ifftFlag, doBitReverse); arm_rfft_q31(&Instance_Real, Buffer_In2_Real, Buffer_Out_Complex); } // The following ARM function calculates the real magnitudes of the complex // data output from the RFFT but does not work correctly. //arm_cmplx_mag_q31(Buffer_Out_Complex, Buffer_Magnitude_Real, (uint32_t)SAMPLE_LENGTH); // Calculate magnitude of FFT complex output for (i = 0; i < SAMPLE_LENGTH * 2; i+=2) { Buffer_Magnitude_Real[i/2] = (q31_t)(sqrtf((Buffer_Out_Complex[i] * Buffer_Out_Complex[i]) + (Buffer_Out_Complex[i+1] * Buffer_Out_Complex[i+1]))); } // Draw frequency bin graph for (i = 0; i < 128; i++) { // Clip the magnitude so that it doesn't go off the display int x = min (100, (int)(Buffer_Magnitude_Real[i] / 10)); Graphics_setForegroundColor(&g_sContext, GRAPHICS_COLOR_WHITE); Graphics_drawLineV(&g_sContext, i, 115-x, 15); Graphics_setForegroundColor(&g_sContext, GRAPHICS_COLOR_RED); Graphics_drawLineV(&g_sContext, i, 115, 115 - x); } } // end of while(1) } // end of main() /* Completion interrupt for ADC14 MEM0 */ void DMA_1_ISR(void) { /* Switch between primary and alternate bufferes with DMA's PingPong mode */ if (DMA_getChannelAttribute(7) & UDMA_ATTR_ALTSELECT) { DMA_setChannelControl(UDMA_PRI_SELECT | DMA_CH7_ADC12C, UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_1); DMA_setChannelTransfer(UDMA_PRI_SELECT | DMA_CH7_ADC12C, UDMA_MODE_PINGPONG, (void*) (ADC14_BASE + OFS_ADC14MEM0), data_array1, SAMPLE_LENGTH); switch_data = 1; } else { DMA_setChannelControl(UDMA_ALT_SELECT | DMA_CH7_ADC12C, UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_1); DMA_setChannelTransfer(UDMA_ALT_SELECT | DMA_CH7_ADC12C, UDMA_MODE_PINGPONG, (void*) (ADC14_BASE + OFS_ADC14MEM0), data_array2, SAMPLE_LENGTH); switch_data = 0; } }