logo elektroda
logo elektroda
X
logo elektroda
REKLAMA
REKLAMA
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

Jak generować czarno-biały sygnał wideo za pomocą mikrokontrolera?

defrag07 28 Sie 2010 15:06 6605 39
Najlepsze odpowiedzi LABEL_AI_GENERATED

Jak wygenerować stabilny czarno-biały sygnał wideo z mikrokontrolera, żeby wyświetlać na telewizorze znaki lub prostą grafikę?

Trzeba generować pełny sygnał composite video z poprawnymi impulsami synchronizacji poziomej i pionowej, a nie tylko sam „obraz” z pikseli; bez H-sync, V-sync, blankingu i właściwych czasów linia/ramka telewizor nie złapie stabilnego obrazu [#8450954][#8452900] Najprościej zacząć od gotowego przykładu albo projektu, np. „TV Terminal Atmega8”, „Generate Video Signal” czy dokumentu „AVR Video Generator with an AVR Mega163”, żeby zobaczyć poprawne przebiegi i timingi [#8448656][#8452825] Jeden z uczestników pokazał, że da się to zrobić także w C, jeśli steruje się timerem i w przerwaniu generuje kolejne linie; podał przykład z przerwaniem co ok. 65 µs, długością linii 64 µs i układem ramki z V-sync oraz blokami danych rysowanymi linia po linii [#8453115][#8458199] Do wyświetlania znaków trzeba więc najpierw zbudować stabilną ramkę, potem dopiero wstawiać dane pikseli z tablicy; przy większej rozdzielczości potrzebna będzie też większa pamięć, a dla lepszej kontroli czasów lepszy bywa assembler niż czyste C [#8452000][#8458504]
Podsumowanie AI na podstawie dyskusji. Może zawierać błędy.
REKLAMA
  • #31 8453320
    mirekk36
    Poziom 42  
    Posty: 9195
    Pomógł: 964
    Ocena: 2289
    ktrot --> w AVR GCC czyli pod WinAVR polecenie _delay_us(6) wykona się co do taktu zegara 6us. I wcale nie zostanie przyjęte jako float ;) ... a co za tym idzie nie zmienią się żadne timingi. Kod w asm wygenerowany do takiego polecenia _delay_us(6) to kilka linijek w asemblerze.
  • REKLAMA
  • #32 8453432
    defrag07
    Poziom 11  
    Posty: 65
    Pomógł: 1
    ktrot testowałem i u mnie niestety nie działa. Używam avr-gcc pod Linuksem.
  • REKLAMA
  • #33 8453614
    ktrot
    Poziom 20  
    Posty: 166
    Pomógł: 47
    Ocena: 3
    Cytat:
    w AVR GCC czyli pod WinAVR polecenie _delay_us(6) wykona się co do taktu zegara 6us. I wcale nie zostanie przyjęte jako float


    To nie do mnie. Wcześniej w tej dyskusji był zapis delay_us(18.5) i twierdziłem, że albo argument jest rzutowany do int albo delay_us() zadziała niepoprawnie - kolega tmf sprostował mnie, że argumentem delay_us jest w gcc double, więc to przyjąłem. Proponuję więc kolego Mirek36 uzgodnić z kolegą tmf jak to jest z funkcją delay_us() w gcc bo prawdę mówiąc w ogóle mnie to nie interesuje - nie używam gcc.

    ->defrag07 tak jak pisałem różne odbiorniki mają różną wrażliwość na odstępstwa od wzorcowego przebiegu. Nie liczyłem przebiegów czasowych przy generowaniu tych pasów - dobrałem je doświadczalnie - tak na pierwszy rzut oka u mnie linia trwa ponad 66us (zamiast 64) to daje 3% i widocznie taki przenośny cz.b. telewizor uważa to za wystarczającą dokładność co nie oznacza, że na innym zadziała. Jeszcze raz proponuję: zapoznaj się z timerami.
  • #34 8453627
    tymon_x
    Poziom 30  
    Posty: 1021
    Pomógł: 171
    Ocena: 15
    defrag07 napisał:
    ktrot testowałem i u mnie niestety nie działa. Używam avr-gcc pod Linuksem.

    Dobrze Ci szło, zobacz że on ściąga do 0V prawie co chwile, a to u Ciebie może zaburzać złapanie synchronizacji. 0V jest dla układów synchronizacyjnych, 0.3-1.0V dla sygnału video. Teraz wykorzystaj o to piękny schemat, przerzucisz go do C z wykorzystaniem tego co stworzyłeś:
    Jak generować czarno-biały sygnał wideo za pomocą mikrokontrolera?
    1. Nieskończona pętla while(), a w niej:
    2. 1-5 (linie) synchronizacja pionowa, gdzie pomarańczowe to 0V, a żółte 0.3V.
    3. Twój kod zapętlony 305 razy (parzyste linie)
    3. 311-317 Znowu V-sync
    4. Twój kod zapętlony 305 razy (nieparzyste linie, przeplot razem 610)
    5. 623-625 Znowu V-sync
    Gratulacje wygenerowałeś jedną klatkę. 25 klatek na sekundę.
    Gdzie jak widać rozmiar to 64us. Napisz wszystko w C. 20min robótki. Póżniej zacznie się kombinowanie z textem i innymi graficznymi bajerami. Efekt zamieść zdjęciem dla potomnych (;
  • REKLAMA
  • #35 8453756
    mirekk36
    Poziom 42  
    Posty: 9195
    Pomógł: 964
    Ocena: 2289
    ktrot napisał:
    Cytat:
    w AVR GCC czyli pod WinAVR polecenie _delay_us(6) wykona się co do taktu zegara 6us. I wcale nie zostanie przyjęte jako float


    To nie do mnie. Wcześniej w tej dyskusji był zapis delay_us(18.5) i twierdziłem, że albo argument jest rzutowany do int albo delay_us() zadziała niepoprawnie - kolega tmf sprostował mnie, że argumentem delay_us jest w gcc double, więc to przyjąłem. Proponuję więc kolego Mirek36 uzgodnić z kolegą tmf jak to jest z funkcją delay_us() w gcc bo prawdę mówiąc w ogóle mnie to nie interesuje - nie używam gcc.


    Dlatego wyjaśniam, że jeśli w AVR GCC wywoła się funkcję _delay_us() do której przekazywany parametr to stała dosłowna (nawet jeśli jest to wtedy liczba typu float 18.5) - to na poziomie kompilacji zostanie ona zastąpiona liczbą typu int bez znaku, która będzie po prostu licznikiem w krótkiej pętli asm odmierzającej ściśle dobraną ilość taktów zegara. I jeśli nie włączone są przerwania to będzie to bardzo dokładny czas - identycznie byś napisał taką pętlę opóźniającą w asm.
  • #36 8453830
    tmf
    VIP Zasłużony dla elektroda
    Posty: 14318
    Pomógł: 2090
    Ocena: 2206
    Jak tylko tak pro forma - owa "stała dosłowna" nazywa się fachowo literał :)
  • #37 8454860
    defrag07
    Poziom 11  
    Posty: 65
    Pomógł: 1
    tymon_x, wczoraj już coś w tym stylu wieczorem próbowałem, dzisiaj zrobiłem na nowo, i nie wiem, albo ja coś robię źle(prawie pewne), albo też z telewizorem coś nie tak. Jak będzie dalej tak jak jest, to trzeba będzie nowy procek bo mi cykli FLASHa braknie. Prościej by chyba było kupić jakiś wyświetlacz z noki 3310 i z nim się bawić.
    Tutaj to co napisałem, efekt podobny jak na poprzednim zdjęciu, tylko cyklicznie co jakiś czas obraz mrugnie i skoczy do góry.
  • REKLAMA
  • #38 8454933
    tymon_x
    Poziom 30  
    Posty: 1021
    Pomógł: 171
    Ocena: 15
    1. Kod masakra. W sensie skróć do prostych funkcji, short i long sync i zapętl. Mniej, krócej, łatwiej się edytuje.
    2. Łatwiej i przyjemniej się używa się czegoś takiego #define. Zamiast edytować co linijkę, dajesz na początek. Zmiana tam, powoduje zmianę w każdym wystąpieniu. Chodzi o wartości stałe.
    3. Nie czytasz co robi dana funkcja _delay_loop_2(); z biblioteki:
    Cytat:
    Delay loop using a 16-bit counter __count, so up to 65536 iterations are possible. (The value 65536 would have to be passed as 0.) The loop executes four CPU cycles per iteration, not including the overhead the compiler requires to setup the counter register pair.

    Thus, at a CPU speed of 1 MHz, delays of up to about 262.1 milliseconds can be achieved.

    Jak masz zegar 10MHz, czyli 100ns na cykl (0.1us), to przy wartości 37, tak naprawdę masz 37*4*0.1us=14.8us!!! A to na pewno już nie jest wymagane 4us dla H-sync.
    4. Dalsze podpowiedzi znajdziesz tu: PAL video timing specification
    5. Nie poddawaj się (;
  • #39 8458199
    ktrot
    Poziom 20  
    Posty: 166
    Pomógł: 47
    Ocena: 3
    Jak generować czarno-biały sygnał wideo za pomocą mikrokontrolera?
    Cytat:
    Dobrze Ci szło, zobacz że on ściąga do 0V prawie co chwile

    Nie ściągałem do zera tylko mi się kabelki przy cinchu zamieniły i miałem na 0 biały a synchronizację na 1V - przepraszam.

    Poprawiłem te kabelki i wygenerowałem ramkę obrazu, tak z ciekawości czy uda się to zrobić w czystym C. Ustawiłem przerwanie timera co ok 65us, trochę uprościłem synchronizację pionową (pominąlem m.in. impulsy wyrównawcze na końcu ramki). Zegar 16Mhz. Przerwanie CTC timer0. Kod przerwania:
    
    interrupt [TIM0_COMP] void timer0_comp_isr(void)
    {
    //synchronizacja H musi być pierwsza aby nie zmieniały jej kolejne ify 
    if (n>=6)
    {
    PORTD=0; delay_us(3);  //hsync
    PORTD=1; 
    }
    
    //======== V sync =============
    if ((n>=1) && (n<=2))
    {
     PORTD=0; delay_us(28);
     PORTD=1; delay_us(1); 
     PORTD=0; delay_us(28);
     PORTD=1; 
    }
    
    if (n==3)
    {
     PORTD=0; delay_us(28);
     PORTD=1; delay_us(1); 
     PORTD=0; delay_us(1); 
     PORTD=1; delay_us(28);
    }
    
    if ((n>=4) && (n<=5))
    {
     PORTD=0; delay_us(1);
     PORTD=1; delay_us(28); 
     PORTD=0; delay_us(1);
     PORTD=1;
    } //====== koniec V sync
    
    //trochę przerwy od góry
    if ((n>=6) && (n<=75))
     delay_us(8);
    
    //pierwszy blok 
    if ((n>=76) && (n<=130))
    {
      delay_us(10);
      PORTD=2; delay_us(10); //szary 
      PORTD=1; delay_us(6);  //czarny 
      PORTD=3; delay_us(4);  //bialy
      PORTD=2; delay_us(1);  //szary
      PORTD=3; delay_us(4);  //bialy
      PORTD=2; delay_us(2); //szary
      PORTD=1;  //czarny do końca linii
    } 
    //drugi blok
    if ((n>=131) && (n<=240))
    {
      delay_us(9);
      PORTD=3; delay_us(7); //biały 
      PORTD=1; delay_us(1);  //czarny 
      PORTD=2; delay_us(14);  //szary
      PORTD=1; delay_us(5);  //czarny
      PORTD=3; delay_us(1);  //bialy
      PORTD=1;  //czarny do końca linii
    }
    
    //a dalej czarne do dołu
    if (n>=241) delay_us(8);
     
    if (n++==313) n=1;
    }
    

    Tam gdzie są fragmenty 'pierwszy blok' i 'drugi blok' wypadało by zrobić jeden blok i wyświetlać dane z tablicy. Jeżeli pominiemy odcień szary to dla atmega32 (2k RAM) można zrobić grafikę 160x100 - niewiele ale dla większej rozdzielczości potrzebna jest w pierwszym rzędzie większa pamięć. Ustawienia timera w main():
    
    DDRD=00b00000011;
    TIMSK=0x02;
    OCR0=0x81;
    TCCR0=0x0a;
    #asm("sei");
    

    Wprawdzie nie testowałem ale nie polecam tego kodu dla mniejszego kwarcu niż 16MHz.
    Na koniec: kod oczywiście mało praktyczny, te wszystkie delay_us() to marnowanie czasu procesora - można by w tym czasie rysować na ekranie, wymagało by to jednak przeprojektowania kodu i prawdopodobnie użycia drugiego timera.
  • #40 8458504
    tymon_x
    Poziom 30  
    Posty: 1021
    Pomógł: 171
    Ocena: 15
    ktrot napisał:
    ...można by w tym czasie rysować na ekranie, wymagało by to jednak przeprojektowania kodu i prawdopodobnie użycia drugiego timera

    Przeprojektowania to może nie, ale dorzucenie drugiego timer'a tak. Z tego co widzę generujesz obraz bez przeplotu na 50Hz. Czyli 50 klatek/s, wykorzysta się efekt "bezwładności oka" i zejdzie do 25 klatek/s. Do rysowania i textu nada się idealnie. Chodzi o coś takiego:
    |klatka 0|*|operacje|*|klatka 1|*|dalsze operacje...|*...
    Da to 20ms między klatkami. Podbije się zegarem do 20MHz, da nam 400k cykli na obsługę czego dusza zapragnie.
    Albo zejdzie się do absolutnego minimum 15 kl/s z naprzemiennym rysowaniem linii parzystych/nieparzystych, żeby zapewnić czas na ściąganie danych z zewnętrznego SRAM do bufora. Resztę poświęcić na obsługę rysowania i uzupełnienia danych do SRAM'u. Można też wykorzystać powielenie piksela.

Podsumowanie tematu

LABEL_AI_GENERATED
W dyskusji poruszono temat generowania czarno-białego sygnału wideo za pomocą mikrokontrolera, szczególnie w kontekście wyświetlania obrazu na telewizorze. Użytkownik zadał pytanie dotyczące podstawowych zasad generowania sygnału wideo, w tym synchronizacji poziomej (H-sync) i pionowej (V-sync). Odpowiedzi wskazują na konieczność zachowania odpowiednich timingów dla impulsów synchronizacyjnych oraz na znaczenie użycia asemblera dla precyzyjnego generowania sygnału. Uczestnicy dyskusji podzielili się przykładami kodu, wskazówkami dotyczącymi użycia timerów oraz literaturą, która może pomóc w zrozumieniu tematu. Wskazano również na możliwość użycia mikrokontrolerów AVR, takich jak Atmega8, do generowania sygnału wideo.
Podsumowanie AI na podstawie dyskusji. Może zawierać błędy.
REKLAMA