Individální projekty MPOA

Mikroprocesory s architekturou ARM

Uživatelské nástroje

Nástroje pro tento web


2015:audio-visual-out

USB audio vizualizér

Zadání

Pomocí vývojové desky 32F429IDISCOVERY vytvořte USB audio zařízení, které bude realizovat zvukový výstup z PC pomocí integrovaného DA převodníku mikrokontroléru. Doplňte vizualizaci ve frekvenční oblasti na displeji vývojového kitu.

Použitý hardware

Projekt je vytvořen na vývojové desce STM32F429I-Discovery, která obsahuje mikrokontrolér STM32F429ZIT6 (ARM Cortex-M4), dále 2.4'' QVGA (320x240pix) TFT LCD a 2x 12-bitový DA převodník. Toto jsou nejdůležitější části desky pro tento projekt. Podle zadání by měla být deska nakonfigurována jako USB Audio Device, tedy po připojení k PC by se měla hlásit jako výstupní zařízení.

Displej používá SPI, která pomocí DMA přesouvá potřebná data mezi pamětí F429i a řadičem ILI9341.

Software

Potřebná kostra projektu byla vytvořena pomocí STM32CubeMX, kde bylo inicializováno LCD a dále v USB_OTG_HS nastaveno USB Audio Device Class. Vytvořený projekt byl pak dále programován v prostředí Em:Blocks 2.3.

Pro obsluhu LCD jsou do projektu přidány knihovny BSP (Board Support Package):
STM32Cube_FW_F4_V1.9.0\Drivers\BSP\STM32F429I-Discovery
STM32Cube_FW_F4_V1.9.0\Drivers\BSP\Components
a složky Fonts a Log z STM32Cube_FW_F4_V1.9.0\Utilities

LCD

Knihovna stm32f429i_discovery_lcd.h umožňuje obsluhu LCD pomocí funkcí jako vykreslení pixelu, řádku, znaku, vypsání stringu nebo vykreslení obejktů, jako kruh či čtverec. Nejdříve však musí být LCD inicializováno pomocí příkazů

BSP_LCD_Init();
BSP_LCD_LayerDefaultInit(0, (uint32_t) LCD_FRAME_BUFFER);
BSP_LCD_SetLayerVisible(0, ENABLE);

Řadič displeje podporuje dvě vrstvy pro zobrazování, indexované 0 a 1. V projektu je zatím použita pouze jedna vrstva.

BSP_LCD_SelectLayer(0);
BSP_LCD_Clear(LCD_COLOR_BLACK);
BSP_LCD_DisplayOn();
BSP_LCD_SetTextColor(LCD_COLOR_GREEN);

USB Audio Device

Inicializace zařízení byla automaticky generována programem STM32CubeMX a vložena do souboru usb_device.c, který obsahuje kód:

/* USB Device Core handle declaration */
USBD_HandleTypeDef hUsbDeviceHS;
/* init function */				
void MX_USB_DEVICE_Init(void)
{
  /* Init Device Library,Add Supported Class and Start the library*/
  USBD_Init(&hUsbDeviceHS, &HS_Desc, DEVICE_HS);
 
  USBD_RegisterClass(&hUsbDeviceHS, &USBD_AUDIO);
 
  USBD_AUDIO_RegisterInterface(&hUsbDeviceHS, &USBD_AUDIO_fops_HS);
 
  USBD_Start(&hUsbDeviceHS);
}

Tato funkce je volána při inicializaci všech zařízení (mimo jiné i LCD pomocí MX_LTDC_Init() ) v hlavním kódu main.c.

Veškerá obsluha audio device by měla být vytvořena v dalším vygenerovaném souboru usbd_audio_if.c, který obsahuje funkce volané při určitých jevech, jako například připojení zařízení k PC:

static int8_t AUDIO_Init_HS(uint32_t  AudioFreq, uint32_t Volume, uint32_t options)
{
  hUsbDevice_1 = &hUsbDeviceHS;
  /* USER CODE*/
  return (USBD_OK);
}

Důležitější je však funkce pro zapnutí a provozování přenosu audio dat, která je nejdříve spuštěna při volbě desky jako výstupního zařízení v host PC (s parametrem cmd = AUDIO_CMD_START). Poté by měla být periodicky volána s parametrem cmd = AUDIO_CMD_PLAY vždy, když je naplněn buffer pbuf. Bohužel při práci na projektu se sice zařízení objevilo jako výstupní v PC, při jeho aktivaci jako výstup už však nedocházelo k volnání funkce AUDIO_AudioCmd_HS s parametrem cmd = AUDIO_CMD_PLAY, a proto nebylo možné přijímat data a dále je zpracovat.

static int8_t AUDIO_AudioCmd_HS (uint8_t* pbuf, uint32_t size, uint8_t cmd)
{
  switch(cmd)
  {
    case AUDIO_CMD_START:
    break;
 
    case AUDIO_CMD_PLAY:
    // Funkce pro výpočet FFT a zobrazení na LCD
    break;
  }
  return (USBD_OK);
}

V případě funkčnosti přenosu dat by tak byl do oblasti vymezené komentářem vložena funkce pro výpočet hodnot audio spektra a následně zobrazovací funkce na LCD.

Pro demonstraci vytvořené funkce pro vykreslení audio spektra byl nahrán krátký úsek wav souboru do paměti FLASH jako

const float32_t audio_sample[].

Výpočet FFT

Pro výpočet rychlé Fourierovy transformace je zapotřebí includování matematické knihovny arm_math.h a přidání libarm_cortexM4lf_math.a do projektu.

Nejprve musí být vzorky signálu rozděleny do menšího řetězce o velikosti FFT. Poté je provedeno komplexní FFT nad tímto oknem a z výsledku jsou poté pomocí funkce arm_cmplx_mag_f32 vybrány reálné vzorky spektra. Nakonec je spuštěna funkce pro vykreslení získaného spektra.

#define FFT_SIZE (512)                       // Velikost FFT
//...
static float32_t audio_buffer[2*FFT_SIZE];   // vstup i výstup cfft má dvojnásobnou velikost,
                                             // jedna cast reálne a druha cast komplexni vzorky
static float32_t MAG_of_fft[FFT_SIZE];       // reálne vzorky FFT
//...
static uint16_t audio_sample_index = 0;
if(audio_sample_index < (audio_sample_size % (2*FFT_SIZE))){
  for(uint16_t j = 0; j < 2*FFT_SIZE; j++){
    audio_buffer[j] = audio_sample[j + audio_sample_index*FFT_SIZE*2];
  }
  arm_cfft_instance_f32  S;
  arm_cfft_f32(&S, audio_buffer, 0, 0);
  arm_cmplx_mag_f32(audio_buffer, MAG_of_fft, FFT_SIZE);
  MAG_of_fft[0] = 0;
  DrawSpectrum(MAG_of_fft);      // funkce pro vykreslení spektra (viz text nize)
  audio_sample_index++;
  if((audio_sample_index+1)*2*FFT_SIZE > audio_sample_size){
    audio_sample_index = 0;      // cykleni zobrazováni vzorku
  }
}

Vykreslení spektra na LCD

Funkce DrawSpectrum má jediný vstupní parametr a to ukazatel na začátek pole s vzorky audio spektra. Při prvním vykreslení dojde k vymazání obsahu displeje a pak je postupně v cyklu vykreslen jeden sloupec (o šířce 1px) pro každý vzorek. Výška sloupce je pak dána hodnotou vzorku. V případě dalšího kreslení téhož sloupce v následujícím cyklu je porovnána aktuální hodnota s tou předchozí a na základě nerovnosti je pak dokreslen rozdíl; tedy černý sloupec tam, kde je aktuální hodnota menší a zelený při vyšší aktuální hodnotě. Díky tomu není nutné při každém novém vykreslování vymazávat obsah displeje a spektrum pak v čase nemrká.

#define lcd_border (10)                      // okraje pri vykreslovani spektra
#define fft_display_offset (200)             // nastaveni odstupu od spodni strany displeje
#define y_res (1)                            // nasobek pro zvetseni
//...
static float32_t fft_signal_temp[FFT_SIZE/2];
//...
void DrawSpectrum(float32_t* fft_signal){
  if (first_spectrum_draw == true){
    BSP_LCD_Clear(LCD_COLOR_BLACK);
    BSP_LCD_SetTextColor(LCD_COLOR_GREEN);
    for (uint16_t y = 0; y < FFT_SIZE/2; y++){
      BSP_LCD_SetTextColor(LCD_COLOR_GREEN);
      BSP_LCD_DrawLine(lcd_border, (y + lcd_border), (lcd_border + fft_display_offset + (fft_signal[y]*y_res)), (y + lcd_border));
      fft_signal_temp[y] = fft_signal[y]*y_res;
    }
    first_spectrum_draw = false;
  }
  else{
    for (uint16_t y = 0; y < FFT_SIZE/2; y++){
      if ((fft_signal[y]*y_res) > fft_signal_temp[y]){
        BSP_LCD_SetTextColor(LCD_COLOR_GREEN);
        BSP_LCD_DrawLine((lcd_border + fft_display_offset + fft_signal_temp[y]), (y + lcd_border), (lcd_border + fft_display_offset + (fft_signal[y]*y_res)), (y + lcd_border));
        fft_signal_temp[y] = fft_signal[y]*y_res;
      }
      else if ((fft_signal[y]*y_res) < fft_signal_temp[y]){
        BSP_LCD_SetTextColor(LCD_COLOR_BLACK);
        BSP_LCD_DrawLine((lcd_border + fft_display_offset + fft_signal_temp[y]), (y + lcd_border), (lcd_border + fft_display_offset + (fft_signal[y]*y_res)), (y + lcd_border));
        fft_signal_temp[y] = fft_signal[y]*y_res;
      }
    }
  }
}

Praktická ukázka

Audio je do videa přidáno dodatečně, proto je zvuk trošku nesynchronizovaný.

Závěr

Zadání projektu bylo splněno pouze částečně. Zařízení se sice hlásí jako zvukové audio device, funkce pro přenos však nevrací data v daném bufferu. Velice hodně času jsem bohužel strávil nad zprovozňováním samotného device, kde má ST poněkud zvláštní dokumentaci v podobě mnoha examplů a kvant knihoven, ve které se jako stále ještě nováček v poli ARM-procesorů mám někdy problém vyznat.

Ale nechci se nějak vymlouvat, času na projekt bylo dost (sice byl redukován prací na semestrální práci, zkouškami a taky svátky) a i já jsem do toho svůj čas vložil. Bohužel když se vám stane věc, jako například že se vám ve správci zařízení vůbec nehlásí deska jako Audio device a po několika dnech koumání, hledání v examplech a STčkovských PDFkách zjistíte, že stačí prostě prohodit oba USB konektory, tak to trošku naštve a taková komplikace pak hodně zdrží.

Protože se mi aspoň paralelně podařilo vytvořit funkci pro vykreslování FFT, pro demonstraci jsem nechal vykreslit několikavteřinový audio vzorek. Vykreslené spektrum má po převedení FFT stále amplitudové hodnoty a zatím jsem nepoužil převedení na dB pomocí 10*logf(fft_signal[y]).

2015/audio-visual-out.txt · Poslední úprava: 2016/01/17 23:12 autor: Daniel Kresta