LinkedIn YouTube Facebook
Szukaj

Wstecz
Artykuły

Analizator widma z FFT na STM32 z Cortex-M4

Widmo częstotliwościowe sygnału audio z mikrofonu

Ostatni etap projektu to wyświetlenie widma sygnału jaki otrzymujemy z mikrofonu. Do tego celu przygotowano dwie funkcje DrawSpectrum_Prepare(), która rysuje tylko raz stałe elementy wykresu oraz DrawSpectrum_Update(), która rysuje samo widmo lub go usuwa w zależności od przekazanego jako argument koloru.

void DrawSpectrum_Prepare(uint16_t xpos, uint8_t ypos){
  int i;

  g_SPECTRUM_Xpos = xpos;
  g_SPECTRUM_Ypos = ypos;

  // Rysowanie obramowania
  LCD_SetForegroundColor(White);
  x_LCD_DrawRectangle(xpos + 10, ypos + 10,
                      SPECTRUM_NR_SAMPLES/2 + 2, SPECTRUM_HEIGHT+2);

  // Wypełnienie tła
  x_LCD_DrawFilledRectangle(xpos + 11, ypos + 11,
                            SPECTRUM_NR_SAMPLES/2, SPECTRUM_HEIGHT, SPECTRUM_BG_COLOR);

  // Rysowanie podziałek osi poziomej
  LCD_SetFont(&Font8x8);
  LCD_SetTextColor(Grey);
  for(i=0; i<=16; ++i){
    sprintf(text,"%.2f",i*16*SPECTRUM_FREQ_S_kHz/SPECTRUM_NR_SAMPLES);

    x_LCD_DrawLine(xpos + 10 + i*16, ypos + 10 + SPECTRUM_HEIGHT+2, 5, Vertical);
    x_LCD_DrawText(xpos + 10 + i*16 + 4, ypos + 10 + SPECTRUM_HEIGHT+2 + 10,
                   text, LeftJustify, RightOrientation);
  }
  x_LCD_DrawText(xpos + 10 + i*16 + 4, ypos + 10 + SPECTRUM_HEIGHT+2 + 10,
                 SPECTRUM_X_LABEL, LeftJustify, RightOrientation);

  // Rysowanie podziałek osi pionowej
  for(i=0; i<=6; ++i){
    x_LCD_DrawLine(xpos + 10 - 5, ypos + 10 + SPECTRUM_HEIGHT+2 - 1 - i*25,
                  5, Horizontal);
  }
}

void DrawSpectrum_Update(uint16_t color){
  int i;

  // Rysowanie szpilek
  LCD_SetForegroundColor(color);
  for(i=0; i<256; ++i){
      x_LCD_DrawLine_2(g_SPECTRUM_Xpos + 11 + i,
                       g_SPECTRUM_Ypos + 160,
                       g_SPECTRUM_Xpos + 11 + i,
                       g_SPECTRUM_Ypos + (uint16_t)(160 - buffer_output_mag[i+1]));
  }
}

Poniżej przedstawiono kod który wykorzystuje wszystkie podane dotąd informacje. Komentarza mogą wymagać dwa miejsca. Ponieważ w funkcje obsługi przerwań od odbioru danych od I2S maksymalny rozmiar paczki zapisanej w buforze PCM_Output_Buffer wynosi w danej sytuacji 32 elementy to trzeba zebrać ich 16 aby otrzymać tablicę 512 wartości rzeczywistych przekazywanych do funkcji wyznaczającej transformatę. Po drugie zamiast skalować widmo wykorzystując maksymalną jej wartość jest ono skalowane z wartością stałą wyznaczoną doświadczalnie tak aby wyświetlanie odbywało się poprawnie.

// Rysowanie stałych elementów wykresu; cały obiekt rozpoczyna się w punkcie x=21, y=20
DrawSpectrum_Prepare(21,20);

// Włączenie odbioru danych przez układ I2S
I2S_Cmd(SPI2, ENABLE);

while(1){
  // Jeśli wciśnięto przycisk to zakończ wyświetlanie widma
  if(STM_EVAL_PBGetState(BUTTON_USER) == SET){
    while(STM_EVAL_PBGetState(BUTTON_USER) == SET);
    break;
  }

  // Jeśli nowa paczka danych w postaci sygnału PCM jest gotowa
  if(Data_Status){
    // Przekopiuj dane paczki do bufora
    for(i=0; i<(OUT_FREQ/1000); i++){ buffer_input[i+(OUT_FREQ/1000)*z] = (float32_t) PCM_Output_Buffer[i]; } ++z; if(z > 512/(OUT_FREQ/1000)){
      z = 0;

      // ************************************************************
      // Usunięcie poprzedniego widma
      DrawSpectrum_Update(Black);

      // ************************************************************
      // Wyznaczenie transformaty Fouriera
      arm_rfft_f32(&S, buffer_input, buffer_output);
      // Obliczenie modułów
      arm_cmplx_mag_f32(buffer_output, buffer_output_mag, 512);
      // Znalezienie składowej harmonicznej sygnału o największej amplitudzie
      arm_max_f32(&(buffer_output_mag[1]), 512, &maxvalue, &maxvalueindex);
      // Skalowanie wartości modułów
      for(i=0; i<512; ++i){
        buffer_output_mag[i+1] = 140*buffer_output_mag[i+1]/20000000;
      }

      // ************************************************************
      // Rysowanie nowego widma
      DrawSpectrum_Update(Green);
    }
    Data_Status = 0;
  }

  // Wyświetl częstotliwość składowej harmonicznej o największej amplitudzie
  sprintf(text,"F=%2.2f[kHz]   ",(maxvalueindex+1)*32.0/512);
  x_LCD_DrawText(100, 15, text, LeftJustify, NormalOrientation);

  // Czekaj jakiś czas, aby widmo było lepiej widoczne
  for(i=0; i<0x10000; ++i);
}

// Wyłącz odbiór danych
I2S_Cmd(SPI2, DISABLE);

Rys. 15. Struktura prezentowanego projektu

Rys. 15. Struktura prezentowanego projektu

 

Ostatecznie struktura projektu wygląda tak jak na rysunku 15, a na rysunkach 16 i 17 przedstawiono konfigurację w zakładce Compile oraz Link środowiska CooCox CoIDE.

 

Rys. 16. Konfiguracja w zakładce Compile

Rys. 16. Konfiguracja w zakładce Compile

Rys. 17. Konfiguracja w zakładce Link

Rys. 17. Konfiguracja w zakładce Link

 

W pliku system_stm32f4xx.c można znaleźć wartości użyte do konfiguracji sygnałów taktujących zarówno samego systemu jak również przekazywanego do peryferii I2S. Sprzętowa jednostka FPU nie została włączona gdyż biblioteka PDM nie jest przystosowana do jej obsługi (kompilator wyrzuca błędy).

 

Literatura:

[1] AN3997 – Audioplayback and recording using the STM32F4DISCOVERY

[2] AN3998 – PDM audio software decoding on STM32 microcontrollers

[3] MP45DT02 – MEMS audio sensor omnidirectional digital microphone

[4] T. Zieliński, Cyfrowe przetwarzanie sygnałów. Od teorii do zastosowań , WKŁ, W-wa 2009

Autor: Jan Szemiet