Zde můžete vidět rozdíly mezi vybranou verzí a aktuální verzí dané stránky.
Obě strany předchozí revize Předchozí verze Následující verze | Předchozí verze | ||
2015:sstv-gen [2016/01/26 04:44] Tomáš Kret [Firmware] |
2015:sstv-gen [2016/01/26 18:04] (aktuální) Tomáš Kret [Záver] |
||
---|---|---|---|
Řádek 1: | Řádek 1: | ||
====== Generátor SSTV signálu ====== | ====== Generátor SSTV signálu ====== | ||
+ | * Tomáš Kret | ||
+ | * xkrett00@stud.feec.vutbr.cz | ||
===== Zadanie ===== | ===== Zadanie ===== | ||
Implementujte audio generátor pre signál SSTV (slow-scan television), napr. mód Martin M1 alebo Robot B&W. Obrazové data prečítajte z SD karty alebo USB flash disku, signál vygenerujte a odošlite pomocou DAC alebo zvukového kodeku. Overte príjem softwarom MMSSTV. | Implementujte audio generátor pre signál SSTV (slow-scan television), napr. mód Martin M1 alebo Robot B&W. Obrazové data prečítajte z SD karty alebo USB flash disku, signál vygenerujte a odošlite pomocou DAC alebo zvukového kodeku. Overte príjem softwarom MMSSTV. | ||
Řádek 34: | Řádek 36: | ||
- 3x I<sup>2</sup>c | - 3x I<sup>2</sup>c | ||
===== Firmware ===== | ===== Firmware ===== | ||
- | O generovanie zvuku sa stará audio kodek, do ktorého prúdi DMA stream, kde sa využíva headphone výstup. Harmonický signál je vytváraný pomocou funkcie //arm_sin_f32()// podporovanej floatovacou jednotkou. Aby sa dosiahla vyššia účinnosť prenosu, je mono signál prerozdelený pre ľavý a pravý kanál. | + | Vytváraný bol pomocou STM32CubeMX 4.12 v prostredí Em:Bitz 0.42 a pomocou ďalších podporných knižníc. O generovanie zvuku sa stará audio kodek, do ktorého prúdi DMA stream, kde sa využíva headphone výstup. Harmonický signál je vytváraný pomocou funkcie //arm_sin_f32()// podporovanej floatovacou jednotkou. Aby sa dosiahla vyššia účinnosť prenosu, je mono signál prerozdelený pre ľavý a pravý kanál. |
**Vývojový diagram:** | **Vývojový diagram:** | ||
Řádek 58: | Řádek 60: | ||
</code> | </code> | ||
+ | **Významné funkcie v súboru main.c** | ||
+ | Vo firmware sa používajú 3 druhy bufferov: | ||
+ | <code> | ||
+ | /* USER CODE BEGIN 0 */ | ||
+ | static int8_t pol2 = 0; | ||
+ | static int8_t pol1 = 0; | ||
+ | static int16_t wind[2*BUFFER_SIZE] = {0}; | ||
+ | static int16_t buffer[4*BUFFER_SIZE] = {0}; | ||
+ | static uint8_t RGB[4*BUFFER_SIZE] = {0}; | ||
+ | </code> | ||
+ | Buffer wind sa odosiela priamo do dma streamu, obsahuje ľavý a pravý kanál okopírovaného z mono signálu. Buffer buffer obsahuje vzorky harmonického signálu, ktorý je vzorkovaný 48000 Hz. RGB obsahuje hodnoty jednolivých pixelov načítaných z USB flash. BUFER_SIZE je stanovené na 4096, čo je hodnota 2<sup>n</sup>, vďaka čomu je možné realizovať kruhový buffer pomocou logického súčinu &(BUFFER_SIZE-1). | ||
+ | |||
+ | Úryvok časti main: | ||
+ | <code> | ||
+ | /* USER CODE BEGIN 2 */ | ||
+ | BSP_AUDIO_OUT_Init(OUTPUT_DEVICE_AUTO, 70, 48000); | ||
+ | |||
+ | /* USER CODE END 2 */ | ||
+ | |||
+ | /* Infinite loop */ | ||
+ | /* USER CODE BEGIN WHILE */ | ||
+ | while (1) | ||
+ | { | ||
+ | /* USER CODE END WHILE */ | ||
+ | MX_USB_HOST_Process(); | ||
+ | /* USER CODE BEGIN 3 */ | ||
+ | if( f_mount( &fatfs,"",0) != FR_OK ) | ||
+ | { | ||
+ | while(1); | ||
+ | } | ||
+ | if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == 1) | ||
+ | { | ||
+ | BSP_AUDIO_OUT_Play((uint16_t*)wind, BUFFER_SIZE*2); | ||
+ | BSP_LED_On(LED4); | ||
+ | sstv_send(); | ||
+ | } | ||
+ | HAL_Delay(50); | ||
+ | } | ||
+ | /* USER CODE END 3 */ | ||
+ | </code> | ||
+ | |||
+ | DMA prenos sa v počiatočných podmienkach začne pri prázdnych bufferoch, o naplnení vzorkami sa starajú až funkcie volané funkciou //sstv_send()//. Funkcia //f_mount()// priraďuje tabuľkový systém pre knižnicu FATFS. //MX_USB_HOST_Process()// obsaluhuje USB zbernicu pre enumeráciu, nastavuje stavový automat USB zbernice a pod. | ||
+ | |||
+ | Funkcia //sstv_send()//: | ||
+ | <code> | ||
+ | void sstv_send(void) { | ||
+ | int32_t ln = 0, uk = 0; | ||
+ | BSP_LED_Off(LED6); | ||
+ | sstv_init(); | ||
+ | if(f_open(&fp,name,FA_READ)!=FR_OK) | ||
+ | { | ||
+ | while(1); | ||
+ | }/*red data from flash*/ | ||
+ | for (int32_t i=0; i<256; i++){ | ||
+ | if (!(i&16)) { | ||
+ | f_lseek(&fp, i*960+ln); | ||
+ | if(f_read(&fp,RGB,4*BUFFER_SIZE,&ret)!=FR_OK) | ||
+ | { | ||
+ | while(1); | ||
+ | } | ||
+ | uk=0; | ||
+ | } | ||
+ | |||
+ | frgen(1200,4842); | ||
+ | ln = 0; | ||
+ | while (ln<960) { | ||
+ | frgen(1500+(RGB[uk])*3.137254902,458); | ||
+ | if (ln%960==320 || ln%960==640) frgen(1500,750); | ||
+ | uk++; | ||
+ | ln++; | ||
+ | } | ||
+ | } | ||
+ | f_close(&fp); | ||
+ | BSP_LED_On(LED6); | ||
+ | } | ||
+ | </code> | ||
+ | Vo funkcii //sstv_send()// sa rieši odoslanie VIS kódu pomocou funkcie //sstv_init()//, načítanie ďalších pixelov z USB (funkciou //f_read()//) posúvaním pointera na súbor uložený vo flash disku (funkciou //f_lseek()//), generovanie synchronizačných signálov: | ||
+ | -//frgen(1200,4842);// -začiatok nového riadku | ||
+ | -//if (ln%960==320 || ln%960==640) frgen(1500,750);// -oddeľovač farieb | ||
+ | -//frgen(1500+(RGB[uk])*3.137254902,458);// -modulovanie samotných pixelov | ||
+ | Keďže diskrétny signál s periódou vzorkovania 20.83 us neumožňoval nastaviť dobrú synchronizáciu, bolo potrebné upraviť pôvodné hodnoty synchronizačných signálov (miesto 572 ms na 750 ms a miesto 4,862 ms na 4,842 ms). | ||
+ | |||
+ | Funkcia generujúca Vis code pre Martin M1: | ||
+ | <code> | ||
+ | void sstv_init(void) { | ||
+ | frgen(1200,3000000); //3s test frequency 1200 Hz | ||
+ | frgen(1900,300000); | ||
+ | frgen(1200,10000); | ||
+ | frgen(1900,300000); | ||
+ | |||
+ | frgen(1200,30000); //start bit | ||
+ | frgen(1300,30000); | ||
+ | |||
+ | frgen(1300,30000); | ||
+ | frgen(1100,30000); | ||
+ | frgen(1100,30000); | ||
+ | |||
+ | frgen(1300,30000); | ||
+ | frgen(1100,30000); | ||
+ | frgen(1300,30000); | ||
+ | frgen(1100,30000); | ||
+ | frgen(1200,30000); //stop bit | ||
+ | |||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Funkcia generujúca harmonický signál vzorkovaný 48k: | ||
+ | <code> | ||
+ | void frgen(int32_t frq, int32_t tm) { | ||
+ | /*freq[Hz] tm[us]*/ | ||
+ | static int64_t ph = 0; | ||
+ | static int32_t ind1=0; | ||
+ | int32_t ind2=0; | ||
+ | int32_t tmd = 0; | ||
+ | float32_t tmdel = tmd*(20.833333333); | ||
+ | while((tmdel<=tm)) { | ||
+ | float32_t x=2.0*3.141592*(frq*ind2+ph)*0.000020833333333; | ||
+ | float32_t y=32767.0; | ||
+ | buffer[ind1] = (int16_t)(arm_sin_f32(x)*y); | ||
+ | ind1++; | ||
+ | ind2++; | ||
+ | tmd++; | ||
+ | if (ind1 >= BUFFER_SIZE*4) { | ||
+ | //ind1&=(BUFFER_SIZE*4-1); | ||
+ | ind1 = 0; | ||
+ | BSP_LED_On(LED5); | ||
+ | while(!pol2); | ||
+ | pol2=0; | ||
+ | BSP_LED_Off(LED5); | ||
+ | } | ||
+ | else if (ind1 == (BUFFER_SIZE*2)) { | ||
+ | BSP_LED_On(LED3); | ||
+ | while(!pol1); | ||
+ | BSP_LED_Off(LED3); | ||
+ | pol1=0; | ||
+ | } | ||
+ | tmdel = tmd*(20.833333333); | ||
+ | } | ||
+ | ph += frq*ind2; | ||
+ | } | ||
+ | </code> | ||
+ | Funkcia //frgen(int32_t frq, int32_t tm)// je základná funkcia využívaná generátorom. Generuje harmonický signál s nastaviteľnou frekvenciou a časom dĺžky trvania. Pre SSTV prenos je potrebné zabezpečiť plynulú zmenu kmitočtu - o to sa stará pričítavanie zvyškovej fázy do 64-bitovej premennej ph, ktorá je daná súčinom diskrétneho času s frekvenciou. Premenné typu float32_t alokujú miesto pre float unit a tým úrychľujú výpočet. Buffer pre výpočet vzoriek je rozdelený do dvoch častí pomocou premenných pol 1 a pol2. Kým prebieha výpočet v 1. častu, DMA prenos prebieha v druhej časti a naopak. Buffere sú kruhové. Premenná tmdel sleduje dĺžku generovaného signálu. | ||
+ | |||
+ | Obsluha prerušenia na koniec DMA prenosu: | ||
+ | <code> | ||
+ | void BSP_AUDIO_OUT_TransferComplete_CallBack(void) | ||
+ | { | ||
+ | static int32_t index = 0; | ||
+ | if(0) | ||
+ | { | ||
+ | BSP_AUDIO_OUT_Stop(CODEC_PDWN_HW); | ||
+ | index = 0; | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | static uint8_t ind = 0; | ||
+ | BSP_AUDIO_OUT_ChangeBuffer((uint16_t*)&wind[0], 2*BUFFER_SIZE); | ||
+ | |||
+ | index = index + BUFFER_SIZE; | ||
+ | index &= (BUFFER_SIZE*4-1); | ||
+ | if (index>=BUFFER_SIZE*2) { | ||
+ | pol2 = 1; | ||
+ | pol1=0; | ||
+ | BSP_LED_Off(LED5); | ||
+ | } | ||
+ | else { | ||
+ | pol1=1; | ||
+ | pol2=0; | ||
+ | BSP_LED_Off(LED3); | ||
+ | } | ||
+ | |||
+ | |||
+ | pref(index); | ||
+ | |||
+ | } | ||
+ | |||
+ | } | ||
+ | </code> | ||
+ | Wind je kruhový bufer, do ktorého sa nové hodnoty vkladajú až zavolaním funkcie //pref(int32_t indx)//: | ||
+ | <code> | ||
+ | void pref(int32_t indx) { | ||
+ | int ind = 0; | ||
+ | do { | ||
+ | wind[(ind*2)&(BUFFER_SIZE*2-1)] = buffer[(ind+indx)&(4*BUFFER_SIZE-1)]; | ||
+ | wind[(ind*2+1)&(BUFFER_SIZE*2-1)] = buffer[(ind+indx)&(4*BUFFER_SIZE-1)]; | ||
+ | if ((ind+indx)>(4*BUFFER_SIZE-1)) BSP_LED_Off(LED4); | ||
+ | else if ((ind+indx)<(2*BUFFER_SIZE-1)) BSP_LED_On(LED4); | ||
+ | ind++; | ||
+ | |||
+ | } while ((ind*2+1)<=(2*BUFFER_SIZE+buf)); | ||
+ | |||
+ | } | ||
+ | </code> | ||
+ | Funkcia //pref(int32_t indx)// slúži na duplikovanie vzoriek do oboch kanálov. Hodnoty bufferov sa načítavajú a zapisuju cyklicky, čím sa predpočítajú prvky do ďalšieho kroku. | ||
+ | |||
+ | Celý firmware pre EmBitz {{:2015:sstv-gen:sstv.zip|tu}}. | ||
+ | |||
+ | Súbory uložené na USB flash disku {{:2015:sstv-gen:matlab_generated_files.zip|tu}}. | ||
+ | |||
+ | ===== Overenie funkčnosti ===== | ||
+ | {{ :2015:sstv-gen:img_20160126_062859_1.jpg?300 |}} | ||
+ | Testovacie podmienky | ||
+ | {{ :2015:sstv-gen:test.png |}} | ||
+ | {{ :2015:sstv-gen:test_lines.jpg |}} | ||
+ | {{ :2015:sstv-gen:landscape.jpg |}} | ||
+ | {{ :2015:sstv-gen:sstv.jpg |}} | ||
+ | Výsledky prenosov (vpravo synchronizačné signály) a porovnanie s originálmi | ||
+ | **Videozáznam prenosu SSTV** | ||
+ | {{youtube>sZ8ZAE_yu98?medium}} | ||
+ | ===== Záver ===== | ||
+ | Na obrázkoch je vidieť, že dochádza k miernemu rozladeniu farieb ako aj synchronizácie, rozladenie môže byť spôsobené vlastnosťami mikrofónu. Synchronizácia by sa mohla zlepšiť pridaním multitaskingu, odstránením blokujúcich čakaní, lepšou synchronizáciou jednotlivých buferov, zvýšením vzorkovacieho kmitočtu a lepším zaokrúhľovaním času. Pre správne načítanie dát z usb po zapnutí treba približne 5 až 10 sekúnd čakať pred stlačením user tlačidla kvôli dokončeniu enumerácie, avšak nie je to nikde signálizované, čo je tiež potrebné vyriešiť. |