;************************************************************************************* ;* DRAM Test * ;************************************************************************************* ;* * ;* Einfache Routinen zum Beschreiben und auslesen von DRAMs. Der Refresh läuft * ;* im Hauptptogramm. * ;* * ;* Hier wird ein 1M x 16 DRAM verwendet, da dieser weit verbreitet ist. * ;* Mit geringen Veränderungen auch mit anderen DRAMs verwendbar (z.B. 256k x 16) * ;* D0-7 sind mit D8-D15 verbunden um Pins zu sparen und sind an PORTD * ;* angeschlossen. Ebenso A0-A7. A8 und A9 bekommen einen eigenen Pin an PORTC, * ;* ebenso WE, OE, UCAS, LCAS und RAS an PORTB. 2MByte an 15 Pins ist doch ganz OK. * ;* Optimiert für 8-16MHz, bei langsameren Taktraten den Refresh Timer anpassen * ;* * ;************************************************************************************* .include "m8def.inc" .def null=r0 .def eins=r1 .def voll=r2 .def temp=r16 .def AdresseLow=r17 .def AdresseMid=r18 .def AdresseHigh=r19 .def Byte=r20 .def Refresh=r21 .def Bits=r22 ;Port B .equ OE=5 .equ UCAS=4 .equ LCAS=3 .equ WE=2 .equ RAS=0 ;PortC .equ A8=4 .equ A9=5 ;Bits .equ Refr=0 .org 0000 rjmp reset .org OVF0addr ;Timer 0 reti .org ADCCaddr rjmp adcready Reset: ldi temp, 255 ;PORTB Steuerleitungen = Ausgang out ddrb, temp ldi temp, 32+16 out ddrc, temp cbi PORTC, A8 cbi PORTC, A9 ;Initialisierung der Steuerleitungen sbi PORTB, RAS sbi PORTB, UCAS sbi PORTB, LCAS sbi PORTB, OE sbi PORTB, WE ser temp ;PORTD Adress/Daten = Ausgang (und Eingang) out ddrd, temp ldi r16, low(RAMEND) out SPL, r16 ; setup stack pointer ldi r16, high(RAMEND) out SPH, r16 ; setup stack pointer clr null ldi temp, 1 mov eins, temp ser temp mov voll, temp ldi temp, 224 out admux, temp ldi temp, 6+128+64+32+8 ;ADC aktivieren: 2,56V VRef, ADC0, fClk/64 out ADCSR, temp ldi temp, 128+64+1 ;Fast PWM als DAC out tccr1a, temp ldi temp, 1+8 out tccr1b, temp ldi temp, 5 ;Refresh Timer erzeugt alle 22ms out TCCR0, temp ;einen Interrupt und startet den ;Burst Refresh. out TCNT0, null ;Da 1024 Zeilen refreshed werden, ;benötigt ein Refresh 4x 256 Takte, ldi temp, 1 ;also 4x22ms=88ms, etwas über den 64ms out TIMSK, temp ;aus dem Datenblatt, funktioniert aber und spart CPU Zeit ;Insgesamt sollte man aber unter 100ms bleiben. clr AdresseLow ;24bit Adresszähler (21bit benötigt für 2MByte) clr AdresseMid clr AdresseHigh clr Refresh ;Refreshzähler sei Refresh: sbr Bits, 1 ;Refresh Bit setzen: Daran erkennt ein Interrupt ob gerade ein Refresh aktiv ist. cbi PORTB, LCAS ;Init CBR Refresh Main: cbi PORTB, RAS ;toogle RAS add Refresh, eins sbi PORTB, RAS brcc Main sbi PORTB, LCAS ;End CBR Refresh ldi temp, 128 ;Set Idle Mode out mcucr, temp nop sleep nop rjmp Refresh ReadLowByte: out PORTD, AdresseLow ;Low Adressbyte ausgeben cbi PORTC, A8 ;A8 Ausgeben, ist beim AVR etwas kompliziert, da kein direkter Bit -> IO Transfer möglich ist sbrc AdresseHigh, 0 sbi PORTC, A8 cbi PORTC, A9 ;A9 Ausgeben, ist beim AVR etwas kompliziert, da kein direkter Bit -> IO Transfer möglich ist sbrc AdresseHigh, 1 sbi PORTC, A9 nop cbi PORTB, RAS ;Zeilen Adresse im DRAM Speichern out PORTD, AdresseMid ;Mid Adressbyte ausgeben cbi PORTC, A8 ;A8 Ausgeben, ist beim AVR etwas kompliziert, da kein direkter Bit -> IO Transfer möglich ist sbrc AdresseHigh, 2 sbi PORTC, A8 cbi PORTC, A9 ;A9 Ausgeben, ist beim AVR etwas kompliziert, da kein direkter Bit -> IO Transfer möglich ist sbrc AdresseHigh, 3 sbi PORTC, A9 nop cbi PORTB, LCAS ;Spalten Adresse im DRAM Speichern out ddrd, null ;IO auf Eingang schalten cbi PORTB, OE ;OE aktivieren nop nop in Byte, pind ;Byte lesen sbi PORTB, OE ;OE deaktivieren out ddrd, voll ;IO auf Ausgang schalten sbi PORTB, LCAS ;CAS deaktivieren sbi PORTB, RAS ;RAS deaktivieren ret ReadHighByte: out PORTD, AdresseLow ;Low Adressbyte ausgeben cbi PORTC, A8 ;A8 Ausgeben, ist beim AVR etwas kompliziert, da kein direkter Bit -> IO Transfer möglich ist sbrc AdresseHigh, 0 sbi PORTC, A8 cbi PORTC, A9 ;A9 Ausgeben, ist beim AVR etwas kompliziert, da kein direkter Bit -> IO Transfer möglich ist sbrc AdresseHigh, 1 sbi PORTC, A9 nop cbi PORTB, RAS ;Zeilen Adresse im DRAM Speichern out PORTD, AdresseMid ;Mid Adressbyte ausgeben cbi PORTC, A8 ;A8 Ausgeben, ist beim AVR etwas kompliziert, da kein direkter Bit -> IO Transfer möglich ist sbrc AdresseHigh, 2 sbi PORTC, A8 cbi PORTC, A9 ;A9 Ausgeben, ist beim AVR etwas kompliziert, da kein direkter Bit -> IO Transfer möglich ist sbrc AdresseHigh, 3 sbi PORTC, A9 nop cbi PORTB, UCAS ;Spalten Adresse im DRAM Speichern out ddrd, null ;IO auf Eingang schalten cbi PORTB, OE ;OE aktivieren nop nop in Byte, pind ;Byte lesen sbi PORTB, OE ;OE deaktivieren out ddrd, voll ;IO auf Ausgang schalten sbi PORTB, UCAS ;CAS deaktivieren sbi PORTB, RAS ;RAS deaktivieren ret WriteLowByte: out PORTD, AdresseLow ;Low Adressbyte ausgeben cbi PORTC, A8 ;A8 Ausgeben, ist beim AVR etwas kompliziert, da kein direkter Bit -> IO Transfer möglich ist sbrc AdresseHigh, 0 sbi PORTC, A8 cbi PORTC, A9 ;A9 Ausgeben, ist beim AVR etwas kompliziert, da kein direkter Bit -> IO Transfer möglich ist sbrc AdresseHigh, 1 sbi PORTC, A9 nop cbi PORTB, RAS ;Zeilen Adresse im DRAM Speichern out PORTD, AdresseMid ;Mid Adressbyte ausgeben cbi PORTC, A8 ;A8 Ausgeben, ist beim AVR etwas kompliziert, da kein direkter Bit -> IO Transfer möglich ist sbrc AdresseHigh, 2 sbi PORTC, A8 cbi PORTC, A9 ;A9 Ausgeben, ist beim AVR etwas kompliziert, da kein direkter Bit -> IO Transfer möglich ist sbrc AdresseHigh, 3 sbi PORTC, A9 nop cbi PORTB, LCAS ;Spalten Adresse im DRAM Speichern out PORTD, Byte ;Byte ausgeben nop cbi PORTB, WE ;Eearly Write aktivieren nop sbi PORTB, WE ;Write deaktivieren sbi PORTB, LCAS ;CAS deaktivieren sbi PORTB, RAS ;RAS deaktivieren ret WriteHighByte: out PORTD, AdresseLow ;Low Adressbyte ausgeben cbi PORTC, A8 ;A8 Ausgeben, ist beim AVR etwas kompliziert, da kein direkter Bit -> IO Transfer möglich ist sbrc AdresseHigh, 0 sbi PORTC, A8 cbi PORTC, A9 ;A9 Ausgeben, ist beim AVR etwas kompliziert, da kein direkter Bit -> IO Transfer möglich ist sbrc AdresseHigh, 1 sbi PORTC, A9 nop cbi PORTB, RAS ;Zeilen Adresse im DRAM Speichern out PORTD, AdresseMid ;Mid Adressbyte ausgeben cbi PORTC, A8 ;A8 Ausgeben, ist beim AVR etwas kompliziert, da kein direkter Bit -> IO Transfer möglich ist sbrc AdresseHigh, 2 sbi PORTC, A8 cbi PORTC, A9 ;A9 Ausgeben, ist beim AVR etwas kompliziert, da kein direkter Bit -> IO Transfer möglich ist sbrc AdresseHigh, 3 sbi PORTC, A9 nop cbi PORTB, UCAS ;Spalten Adresse im DRAM Speichern out PORTD, Byte ;Byte ausgeben nop cbi PORTB, WE ;Eearly Write aktivieren nop sbi PORTB, WE ;Write deaktivieren sbi PORTB, UCAS ;CAS deaktivieren sbi PORTB, RAS ;RAS deaktivieren ret adcready: sbi PORTB, RAS ;Disable Refresh sbi PORTB, LCAS sbrs AdresseHigh, 4 ;Da 16bit Speicher, rcall ReadLowByte ;entscheidet das MSBit sbrc AdresseHigh, 4 ;ob Low oder Highbyte rcall ReadHighByte out ocr1al, Byte ;gespeicherten Wert ausgeben in Byte, adch ;neuen Wert einlesen sbrs AdresseHigh, 4 ;Eigentlich wäre ein Read-modify-write rcall WriteLowByte ;hier besser, aber das ganze ist ja nur ein Test sbrc AdresseHigh, 4 rcall WriteHighByte add AdresseLow,eins ;24 bit Adresszähler hochzählen adc AdresseMid,null adc AdresseHigh,null sbrc Bits, Refr ;War ein Refresh aktiv, als der Interrupt kam ? cbi PORTB, LCAS ;Wenn ja, dann alten Zustand widerherstellen reti