Kommen die Daten mit Bit 0 oder Bit 7 zuerst? Ich gehe mal davon aus, dass MSBfirst richtig ist.
Ausgangspunkt ist ein ATmega32U4 mit 16-MHz-Quarz,
verbaut in Arduino Leonardo
und Nachfolgern,
etwa dem sehr preiswerten Pro Micro
.
FĂĽr die zeitkritischen Routinen kommt nur Assembler in Frage.
Zur Kombination mit gcc wird der
Gnu-Assembler-Dialekt gas verwendet,
nicht avrasm.
FĂĽr das SPI eines AVR, ich sage mal ATmega32U4 wie auf Arduino Leonardo, ist es kein Problem, den seriellen Bitstrom mit 1 MBit/s zu generieren. Die Firmware mĂĽsste:
Die Zeit für jedes L und H beträgt 1 µs für jedes der Symbole bei Aufzeichnung mit 500 kBit/s = HD. Bei 16 MHz Systemtakt hat man pro Bit 32 Takte Zeit. Bei geringerer Datenrate entsprechend mehr. Daher die Idee, das Ganze mit dem PWM-Generator im Non-PWM-Modus generieren zu lassen. Das erspart es, CPU-Takte exakt abzählen zu müssen und verschafft eine zeitliche Pufferzone von 16 CPU-Takten, die für andere Aufgaben — außer zu warten — genutzt werden kann. Etwa dazu, die USB-OUT-Pipe zu bedienen, um weitere Sektordaten vom USB-Hostcontroller (PC) entgegennehmen zu können. Der Non-PWM-Modus ist erforderlich, damit der OCR-Wert sofort und nicht erst beim Zählerüberlauf durchgreift. Der nachfolgende Quelltext-Schnipsel erwartet folgende Randbedingungen:
// Als Makro, Unterprogrammaufrufe sind beim AVR lahm und fressen 9 Takte .macro timed_togglests OCR1AL,r0 ;in Zukunft toggeln lassen 9 : sbic TIFR1,OCF1Arjmp 9b ;warten bis es getoggelt hatsbi TIFR1,OCF1A ;Interrupt löschen .endm // Vorbereiteten Datenblock mit Datenadressmarke (vorn) und CRC (hinten) ausgeben // R22 = Datenrate 0=ED, 1=HD, 2=DD, 3=SD // R24 = Anzahl der Taktsynchronbits (Nullen), typisch 12×8 = 48 // R27:R26 = Länge (typisch 515) = Anzahl Datenbytes // R31:R30 = Adresse im RAM (Zeiger Z) // 3 A1-Synchronbytes werden automatisch ausgegeben // Verwendete Register: R0,R1=0,R22,R24,Parameter-Register outBlockMFM :sts OCR1AH,r1 ;High-Schattenregister sicherheitshalber löschen // Addierwert aus Datenrate errechnen ldi r25, 4 inc r22 0 : lsl r25dec r22 brne 0b mov r1,r25 // Zähler initialisieren und starten ; ldi r25,0b00000001 ; sts TCCR1B,r25 ;Zähler starten (falls erforderlich) lds r0,TCNT1L sts OCR1AL,r0 ;„ferne Zukunft“ initialisieren ldi r25, 0b01000000 sts TCCR1A,r25 ;„Toggle OC0A“ aktivieren // hier sicherheitshalber Pin auf High stellen - am besten weglassen // und in der Mikrocontroller-Initialisierungroutine 1× erledigen ldi r25, 0b10000001 sbis PINB, 5 sts TCCR1B,r25 ;Force Output Compare = Flipflopzustand kippen // Hier Schreibfreigabe aktivieren cbi PORTE, 6 // Taktsynchronbits 0 :rcall 6f dec r25 brne 0b // 3 Rahmensynchronbytes (A1) ldi r25, 3 add r0,r1 0 : rcall6f ;1 fertigadd r0,r1 ;0 fertig add r0,r1 rcall 6f ;1 fertigadd r0,r1 ;0 fertig rcall 6f ;0 fertigadd r0,r1 add r0,r1 ;spezielle 0 fertig rcall 6f ;0 fertigadd r0,r1 rcall 6f ;1 fertigdec r24 brne 0b clt ;„vorheriges Bit“ löschen 2 :ld r24,Z+ ;Byte aus RAM laden ldi r25, 8 ;Bitzähler3 :sbrc r24, 7 rjmp 7f brts 1f // 0-Bit: L-H ausgeben timed_toggle ;L ausgeben lassen add r0,r1 timed_toggle ;H ausgeben lassen rjmp 0f // 0-Bit: H-H ausgeben (= nichts tun als 2 Schritte warten) 1 :add r0,r1 0 : add r0,r1clt rjmp 8f // 1-Bit: H-L ausgeben: Wie L-H ausgeben aber vorher addieren 7 :set ;„Vorheriges Bit“ setzen add r0,r1 ;später timed_toggle ;L ausgeben lassen add r0,r1 timed_toggle ;H ausgeben lassen 8 : lsl r24dec r25 brne 3b sbiw r26, 1 brne 2b // Hier Schreibfreigabe wegnehmen sbi PORTE, 6 clr r1 sts TCCR1A,r1 ;nicht mehr toggeln ; sts TCCR1B,r1 ;Zähler stoppen (falls erforderlich) ret // Als Unterprogramm, wenn Zeit vorhanden ist // (sollte sogar für HD, nicht aber für ED gehen; ist aber schwer einzuschätzen) 6 :timed_toggle add r0,r1 timed_toggle add r0,r1 ret
Nur mit Schreiben kann man eine Diskette nur formatieren, nicht beschreiben! Denn zum Sektorschreiben muss der beim Formatieren aufgebrachte Sektor-Header gelesen werden und dann fix auf Schreiben umgeschaltet werden.