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_toggle sts OCR1AL,r0 ;in Zukunft toggeln lassen 9: sbic TIFR1,OCF1A rjmp 9b ;warten bis es getoggelt hat sbi 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 r25 dec 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: rcall 6f ;1 fertig add r0,r1 ;0 fertig add r0,r1 rcall 6f ;1 fertig add r0,r1 ;0 fertig rcall 6f ;0 fertig add r0,r1 add r0,r1 ;spezielle 0 fertig rcall 6f ;0 fertig add r0,r1 rcall 6f ;1 fertig dec r24 brne 0b clt ;„vorheriges Bit“ löschen 2: ld r24,Z+ ;Byte aus RAM laden ldi r25,8 ;Bitzähler 3: 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,r1 clt 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 r24 dec 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.