;**************************************************************************** ;* * ;* 40 x 25 TV Terminal / TV Controller * ;* * ;* Version 3.0 * ;* * ;* © by Benedikt * ;* * ;* Email: benedikt83 ät gmx.net * ;* * ;**************************************************************************** ;* * ;* Die Software darf frei kopiert und verändert werden, solange sie nicht * ;* ohne meine Erlaubnis für kommerzielle Zwecke eingesetzt wird. * ;* * ;**************************************************************************** ;* * ;* Diese Software erzeugt ein PAL ähnliches Videosignal, das von allen von * ;* mir getesteten TV Geräten aktzeptiert wird. * ;* Somit lassen sich beliebige Texte darstellen. Dank vollständigem ASCII * ;* Zeichensatz sind auch ASCII Grafiken möglich. * ;* * ;* Das besondere an dieser Software: Das Timing wird per Timer gesteuert, * ;* der AVR kann daher nebenbei problemlos andere Aufgaben erledigen, da * ;* die Bilderzeugung lediglich etwa 60% der CPU Leistung benötigt. * ;* Die Sync Signale werden vollständig per Timer im PWM Modus erzeugt, * ;* die Bildausgabe erfolgt im Interrupt mit automatischer Jitter- * ;* kompensation (Idee von Bernhard Schulz). * ;* Das Hauptprogramm darf also den Interrupt um bis zu 10 Takte blockieren.* ;* Im Hauptprogramm darf jede beliebige Software laufen, solange diese mit * ;* den freien Registern und dem wenigen SRAM auskommt. * ;* * ;* * ;* Fuse Bit High: 11001001 = 0XC9 (CKOPT enabled) * ;* Fuse Bit Low: 01101110 = 0x6E (externer Quarz, BOD enabled) * ;* * ;* IO Pins: * ;* * ;* PB1 muss mit PB3 verbunden werden * ;* PB2 Sync Ausgang: über 1kOhm an Ausgang * ;* PB3 Pixeldaten: über 330Ohm an Ausgang, Ausgang über 100Ohm an Masse* ;* PB4 DNU (muss frei gelassen werden) * ;* PB5 DNU (muss frei gelassen werden) * ;* PB6 XTAL 16MHz Quarz * ;* PB7 XTAL 16MHz Quarz * ;* * ;**************************************************************************** .include "m16def.inc" #define mega16 ;Im Interrupt genutzte und geänderte Register: .def regsave =r2 .def spian =r3 .def spiaus =r4 .def X_Cnt =r5 .def tempi =r16 .def LOOP =r17 .def Y_Cnt =r18 .def C_Cnt =r19 ;zusätzlich: X und Z ;Sonstige Register, für Hauptprogramm frei: .def temp =r20 .def temp2 =r21 .def RX =r22 ;zusätzlich: Y #ifdef mega16 ;PortB .equ DOUT =3 ;Pixeldaten \ .equ SS =4 ; } Verbinden .equ MOSI =5 ;Pixeldaten / .equ MISO =6 ;NC .equ SCK =7 ;NC ;PortD .equ Sync =4 ;Sync Ausgang (OCR1B) #else ;PortB .equ DOUT =1 ;Pixeldaten \ .equ SS =2 ; } Verbinden .equ MOSI =3 ;Pixeldaten / .equ MISO =4 ;NC .equ SCK =5 ;NC ;PortB .equ Sync =2 ;Sync Ausgang (OCR1B) #endif .equ Baudrate =250000 ;Bildgröße .equ XSize =40 ;Bildgröße in X Richtung in Nibbles .equ YSize =25 ;Bildgröße in Y Richtung pro Halbbild ;Horizontal .equ XStart =220 ;Anzahl an CPU Takten zwischen Beginn HSync und Bildbeginn (mindestens 30, und mindestens HSyncbreite) .equ SyncWidth =75 ;Anzahl an CPU Takten für HSync ;Vertikal .equ Height =8 ;Zeichenhöhe .equ Dummy =0 ;Zusätzliche Zwischenzeilen (wird momentan nicht unterstützt, muss 0 sein !) .equ BPSize =50 ;VSync Back Porch in Zeilen (Y Verschiebung) .equ VSize =4 ;VSync Impuls in Zeilen ;#define LCD ;Zeichen 128-135: Balkenanzeige #define HOR_FILL ;Lücken zwischen den Zeichen füllen ;Interne Konstanten .equ BildSize =YSize*Height ;Bildhöhe in Zeilen .equ BildStart =313-BildSize+YSize ;Bildhöhe in SyncZeilen+Zeichen (interner Zähler) .equ FPSize =(313-YSize*(Height+Dummy))-VSize-BPSize ;Frontporchbreite in Zeilen .equ FPStart =YSize ;Frontporch Start in internen Zählerzeilen .equ VSStart =FPStart+FPSize ;Vsyncstart in internen Zählerzeilen .equ BPStart =VSStart+VSize ;Backporchstart in internen Zählerzeilen .dseg DDRAM: .byte XSize*YSize ;DDRAM ;**************************************************************************** ; Interrupt Vektoren ;**************************************************************************** .cseg .org 0 rjmp Reset .org OC1Aaddr ;Bildbeginn in regsave, sreg cpi Y_Cnt, YSize ;Bildbereich oder schon VSync + Zubehör ? brsh handle_VSync mov tempi, ZH in ZL, TCNT1L ;Timerwert lesen in ZH, TCNT1H subi ZL, low(XStart-10-comp) ;und Korrekturwert berechnen sbci ZH, high(XStart-10-comp) ijmp ;Wartezeit korrigieren nop nop nop nop nop nop nop nop nop nop nop nop nop comp: mov ZH, tempi ldi tempi, XSize mov X_Cnt, tempi hloop: ;Loop für 1 Zeile ld ZL, X+ ;ASCII aus RAM lesen 2 lpm tempi, Z ;Byte aus CGROM lesen 3 out DDRB, spiaus ;SPI aus, Port an 1 out SPDR, tempi ;Byte ausgeben 1 out DDRB, spian ;SPI an, Port aus 1 #ifdef HOR_FILL sbrs tempi, 0 ;Füllpixel setzen 1 cbi PORTB, DOUT ; 1,5 sbrc tempi, 0 ; 1 sbi PORTB, DOUT ; 1,5 #else nop ; 1 nop ; 1 nop ; 1 nop ; 1 nop ; 1 #endif nop ; 1 nop ; 1 dec X_Cnt ; 1 brne hloop ; 2 ; =18, also 16 (für 1 Byte) + Pause dazwischen nop nop nop nop nop nop out DDRB, spiaus ;SPI abschalten cbi PORTB, DOUT ;Bild: schwarz dec C_Cnt ;Zähler für Zeichenhöhe erniedrigen breq skipcloop ;1 Textzeile fertig ausgegeben ? inc ZH ;Nein -> Bilddatenadresse für nächste Pixelzeile laden und subi XL, low(XSize) ;Textadresse wieder an Anfang der Textzeile setzen sbci XH, high(XSize) ; rjmp exit_int skipcloop: ;Alles für neue Textzeile einrichten ldi ZH, 4 ;1024Bytes CGROM Offset ldi C_Cnt, Height ;Zeichenhöhen Counter laden inc Y_Cnt ;Zeilencounter erhöhen rjmp exit_int handle_VSync: inc Y_Cnt ;Zeilenzähler erhöhen cpi Y_Cnt, VSStart ;Beginn VSync ? breq VSyncstart cpi Y_Cnt, BPStart ;Ende VSync ? breq VSyncende cpi Y_Cnt, BildStart ;Bildende ? brsh Bildbeginn rjmp exit_int VSyncstart: ldi tempi, (1<