Analizator widma z FFT na STM32 z Cortex-M4
Odbiór sygnału audio z mikrofonu
Kiedy mamy pewność, że wyznaczanie widma częstotliwościowego sygnału działa poprawnie to możemy iść dalej i zamiast przygotowanego wcześniej sygnału wykorzystać sygnał odbierany z mikrofonu. Przedtem jednak konfigurujemy wykorzystywane peryferia:
static void RCC_Configure(void){
/********/
/* AHB1 */
/********/
// Włączenie sygnału taktującego układ CRC jest wymagane przez bibliotekę PDM
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC |
RCC_AHB1Periph_CRC, ENABLE);
/********/
/* APB1 */
/********/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
// Włączenie sygnału taktującego dla układu I2S
RCC_PLLI2SCmd(ENABLE);
}
static void NVIC_Configure(void){
NVIC_InitTypeDef NVIC_InitStructure;
// Konfiguracja grupy priorytetów
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
// Konfiguracja przerwań od SPI2 (w tym również dla I2S2)
NVIC_InitStructure.NVIC_IRQChannel = SPI2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
}
static void GPIO_Configure(void){
GPIO_InitTypeDef GPIO_InitStructure;
// Konfiguracja linii PB10 podłączonej do CLK układu MP45DT02 – funkcja I2S2_CLK
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// Konfiguracja linii PC3 podłączonej do DOUT układu MP45DT02 - funkcja I2S2_DATA
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_SPI2); // Podłączenie PB10 do SPI2
GPIO_PinAFConfig(GPIOC, GPIO_PinSource3, GPIO_AF_SPI2); // Podłączenie PC3 do SPI2
}
static void I2S_Configure(void){
I2S_InitTypeDef I2S_InitStructure;
SPI_I2S_DeInit(SPI2);
I2S_InitStructure.I2S_AudioFreq = OUT_FREQ*2;
I2S_InitStructure.I2S_Standard = I2S_Standard_LSB;
I2S_InitStructure.I2S_DataFormat = I2S_DataFormat_16b;
I2S_InitStructure.I2S_CPOL = I2S_CPOL_High;
I2S_InitStructure.I2S_Mode = I2S_Mode_MasterRx;
I2S_InitStructure.I2S_MCLKOutput = I2S_MCLKOutput_Disable;
I2S_Init(SPI2, &I2S_InitStructure);
// Włączenie przerwań od zapełnienia bufora odbiorczego I2S2
SPI_I2S_ITConfig(SPI2, SPI_I2S_IT_RXNE, ENABLE);
}
Po skonfigurowaniu peryferii można wykonać inicjalizację biblioteki PDM:
Filter.Fs = OUT_FREQ; // Częstotliwość próbkowania Filter.HP_HZ = 10; // Częstotliwość odcięcia filtru górnoprzepustowego Filter.LP_HZ = 16000; // Częstotliwość odcięcia filtru dolnoprzepustowego Filter.In_MicChannels = 1; // Liczba kanałów wejściowych Filter.Out_MicChannels = 1; // Liczba kanałów wyjściowych PDM_Filter_Init(&Filter); // Inicjalizacja biblioteki PDM
W pliku main.h zdefiniowane są stałe wykorzystywane w programie:
#define DECIMATION_FACTOR 64 #define OUT_FREQ 32000 #define PDM_Input_Buffer_SIZE ((OUT_FREQ/1000)*DECIMATION_FACTOR/8) #define PCM_Output_Buffer_SIZE (OUT_FREQ/1000) #define SPECTRUM_BG_COLOR Black #define SPECTRUM_FREQ_S_kHz 32.0 #define SPECTRUM_HEIGHT 150 #define SPECTRUM_NR_SAMPLES 512 #define SPECTRUM_X_LABEL "[kHz]"
Warto zwrócić uwagę na konfigurację układu I2S oraz biblioteki PDM pod względem wartości częstotliwości. Ponieważ biblioteka PDM oferuje cztery funkcje konwertujące sygnał z postaci PDM na PCM (współczynnik decymacji 64 lub 80, dane w kolejności MSB lub LSB; rysunek 14):
int32_t PDM_Filter_64_MSB(uint8_t* data, uint16_t* dataOut, uint16_t MicGain, PDMFilter_InitStruct * Filter); int32_t PDM_Filter_80_MSB(uint8_t* data, uint16_t* dataOut, uint16_t MicGain, PDMFilter_InitStruct * Filter); int32_t PDM_Filter_64_LSB(uint8_t* data, uint16_t* dataOut, uint16_t MicGain, PDMFilter_InitStruct * Filter); int32_t PDM_Filter_80_LSB(uint8_t* data, uint16_t* dataOut, uint16_t MicGain, PDMFilter_InitStruct * Filter);
to wymaganym jest aby częstotliwość na linii CLK układu MP45DT02 była równa:
FCLK = DecimatorFactor · FS
Tak więc, jeśli chcemy uzyskać częstotliwość próbkowania 32 kHz oraz wykorzystać współczynnik decymacji o wartości 64 to częstotliwość na linii CLK powinna wynosić 2048 kHz. Aby ustawić taką wartość w układzie I2S należy wiedzieć, że układ w trybie normalnej pracy odbiera po 16 bitów danych (najmniejsza możliwa wartość pola I2S_DataFormat) na przemian z kanału pierwszego oraz drugiego. A ponieważ w danej sytuacji będzie on cały czas odbierał dane z jednego źródła to na odbiór „próbki z dwóch kanałów” przeznaczone są 32 bity, czyli 2048 kHz / 32 = 64 kHz – taka też wartość jest ustawiana w polu I2S_AudioFreq.

Rys. 14. Proces obróbki sygnału przez bibliotekę PDM
Po włączeniu odbioru danych przez układ I2S komendą I2S_Cmd(SPI2, ENABLE) dane będą najpierw kopiowane do rejestru odbiorczego, a następnie w przerwaniu zostaną przeniesione do bufora PDM_Input_Buffer. Gdy bufor ten zostanie całkowicie zapełniony to wykona się funkcja PDM_Filter_64_LSB() konwertująca sygnał z postaci PDM do postaci PCM (plik stm32f4xx_it.c):
uint32_t InternalBufferSize = 0;
uint32_t Data_Status = 0;
void SPI2_IRQHandler(void){
extern PDMFilter_InitStruct Filter;
extern uint8_t PDM_Input_Buffer[];
extern uint16_t PCM_Output_Buffer[];
u16 volume;
u16 app;
// Sprawdź czy są dostępne nowe dane
if (SPI_GetITStatus(SPI2, SPI_I2S_IT_RXNE) != RESET){
// Odczytaj dane i zapisz do bufora – najpierw młodszy potem starszy bajt
app = SPI_I2S_ReceiveData(SPI2);
PDM_Input_Buffer[InternalBufferSize++] = (uint8_t)app;
PDM_Input_Buffer[InternalBufferSize++] = (uint8_t)HTONS(app);
// Sprawdź czy bufor jest pełny
if (InternalBufferSize >= PDM_Input_Buffer_SIZE){
InternalBufferSize = 0;
volume = 50;
PDM_Filter_64_LSB(PDM_Input_Buffer, PCM_Output_Buffer, volume, &Filter);
Data_Status = 1;
}
}
}

Technologie End of Life i bezpieczeństwo sieci – wyzwania Europy związane z tzw. długiem technologicznym
Najczęstsze błędy firm przy wyborze dostawcy energii i jak ich uniknąć
Fotorezystor, czyli czujnik światła dwojakiego działania. Przykład innowacji w automatyce i elektronice możliwej dzięki technologii fotooporników 



