Frage: Kann man 5¼″-Laufwerke an USB anschließen? Oder gar 8″?
Fertigprodukte
Es gibt folgendes, was die Eigenentwicklung nicht lohnt:
USB-Diskettenlaufwerke 3½″
also ein USB-A-Stecker und ein Gerät, wo man eine Diskette reinstecken kann
USB-Diskettenlaufwerks-Emulatoren 3½″
also ein Gerät mit 34-poligem Pfostenstecker und USB-A-Buchse,
welches an Stelle eines Diskettenlaufwerks bspw. in Oszilloskope eingebaut wird
und am USB-Ende einen USB-Stick anzuschließen erlaubt
Für 5¼″ scheint es da nichts zu geben.
Bastelei
Einen urtümlichen Floppy-Controllerchip
wie WD1772 oder U8272 zu benutzen,
um die seriellen MFM-Daten zu generieren
oder auszulesen erscheint unsportlich.
Geht das nicht mit den heutigen Rechenleistungen von 8-Bit-Controllern mit USB auch ohne?
Hier ist MFM und der Sektoraufbau erklärt,
ich habe das mal in ein handliches CHM gepackt.
Diskettenlaufwerke liefern und erwarten negative Impulse pro Flusswechsel.
Über die Pulsbreite konnte ich keine Info finden;
wahrscheinlich so dass TTL-Schaltkreise sicher ansprechen,
also deutlich kürzer als 1 µs.
Als erstes wurde untersucht, ob der Diskettencontroller mit seiner
MFM-Schreib- und Lesefunktion überhaupt in Software realisierbar ist.
Dies ist für DD und HD locker möglich, und auch ED ist realisierbar.
Immerhin passt ein typischer Diskettensektor in den RAM des ATmega32U4.
Daher muss man die Daten nicht strömen lassen. Vorerst.
Andere Kodierungsformate wie
GCR
(Apple) lassen sich
im Gegensatz zu einem Hardware-Diskettencontroller
einfach per Software nachrüsten.
Um während des Sektorlesens den vorher gelesenen Sektor in 64-Byte-Stückelung
zum USB zu schaffen, ist eine entsprechende Routine erforderlich,
die maximal 16 Takte dauert.
Da CALL und RET viel zu langsam sind, nur mit Sprüngen.
Der USB-Teil des ATmega32U4 ist sichtlich nicht AVR-typisch
und die Dokumentation im Datenblatt unter aller Kanone:
Interrupts müssen „zu Fuß“ bestätigt werden, auch dann, wenn eine
Interruptbedienroutine aufgerufen wird.
Das ist allerdings bei einem Multiplex-Interrupt auch nicht anders möglich.
Die Interruptbestätigung erfolgt durch Löschen von Bits,
nicht wie sonst durch Setzen.
Damit kein Interrupt durch die Lappen geht, müssen alle anderen
Interruptbits gesetzt werden (undokumentiert, aber logische Konsequenz).
Also INT_FLAGS = INT_FLAGS | andere_interruptbits & ~mein_interruptbit;
Sämtliche Register, auch die zur Interruptbestätigung,
liegen im unhandlichen Speicheradressraum, nicht im I/O-Adressraum.
Alle endpoint-spezifischen Operationen, auch für EP0,
sind über „gemultiplexte“ Register realisiert.
So muss stets erst mal der richtige Endpoint ausgewählt werden.
Dies ist bisweilen lästig.
Die Endpoint-Puffer sind nicht wahlfrei adressierbar (d.h. nicht im Speicher-Adressraum),
sondern man muss die Daten immer aus einer FIFO lesen bzw. in eine FIFO schreiben.
Besser wäre es, wenn beide Möglichkeiten zur Verfügung ständen.
Ein USB-Suspend-Interrupt kommt nach 3 s Inaktivität auf dem USB,
aber nur, wenn es vorher eine Aktivität gab.
Den Fall eines „toten“ USB (etwa beim Anschluss an ein Ladegerät)
muss man extra ausprogrammieren.
Bisher alle Programmierer vergessen das (ist ja auch undokumentiert)
und der Anwender wundert sich dann über den leeren Laptop-Akku
beim Anstöpseln an einen suspendierten Laptop, etwa durch Wackelkontakt
Ein USB-Wakeup-Interrupt entpricht einem Pegelwechsel-Interrupt
auf einer der beiden Datenleitungen.
Das hätte man auch so mal schreiben können.
Damit ist logisch, dass dieser Interrupt praktisch immer aktiviert wird.
Keinerlei Kodebeispiele im Datenblatt.
Eine simple Schleife zum FIFO lesen und FIFO schreiben gehört da hinein.
Sowie eine Minimalversion der EP0-Behandlungsroutine. Ohne LUFA.
Wichtig ist es, beim Energiemanagement getrennt auf Busversorgung
und Selbstversorgung einzugehen.
Fehler im Datenblatt: Diagramm unterhalb „Figure 21-12“:
SUSPI wird nicht durch Software, sondern bei WAKEUPI (tatsächlich) durch
die Hardware gelöscht. Ist in Abschnitt 22.8 dokumentiert.
Genauso beim AT90USB162: „Figure 19-11“ sowie Abschnitt 20.8
Widerspruch im Datenblatt: SUSPI sollte (automatisch) durch WAKEUPI
gelöscht werden, um den PAD-Status im Power-Down zu halten (Bild 21-12)
und Remote-Wakeup zu ermöglichen (Abschnitt 22.10).
Das Kodebeispiel 6.8.1 zeigt genau das Gegenteil.
Auch in 21.13 steht: „Suspending the USB interface: Clear Suspend Bit“.
Was ist nun richtig?
Gehaltenes SUSPI führte bei mir zu gelegentlichen Fehlern
bei der USB-Enumerierung beim Ab- und Anstecken des USB-Kabels
bei einem selbstversorgten Mikrocontroller.
Für diesen Fall muss UBUS ausgewertet werden, dann klappt's.
Siehe auch folgender Punkt.
OTGPADE (Bit 4) in USBCON muss gesetzt sein, sonst funtioniert USB nicht,
auch wenn man UBUS gar nicht auswertet!
Nirgends erwähnt.
Bild 22.4 hat die Interruptquelle VBUSTI vergessen, im Gegensatz zum Folgetext.
Im Bild 21.9 ist diese Interruptquelle drin. Ziemlich wirr.
Der AT90USB162 hat keinen eingebauten Busspannungsdetektor,
dort macht man das zu Fuß mit einem normalen I/O-Port.
Dieses Diskettenlaufwerk verwendet das veraltete CBI-Protokoll
(CBI = Control-Bulk-Interrupt).
Das bedeutet, der SCSI-Steuerblock (6, 10 oder 12 Byte) wird über den Control-Endpoint 0
zum Gerät gesendet.
Die Antwort, Zusatzdaten oder Sektordaten flutschen über den jeweiligen Bulk-Endpoint
(hier i2 und o2), und am Ende gibt's eine 2-Byte-Quittung über den Interrupt-Endpoint
(hier i1), bestehend aus SCSI-Fehlerkode „Sense“ und dem „Additional Sense Code“ ASC.
Ein Eigenbau-Diskettenlaufwerk mit CBI-Protokoll erfordert (zumindest unter Windows 7)
eine eigene .INF-Datei zur Installation und ist damit nicht gerade benutzerfreundlich.
Im Falle des o.a. Funktionsmusters findet Windows die Vendor- und Product-ID
(0x3EE = Mitsumi, 0x6901 = USB-Diskettenlaufwerk) in einer vorinstallierten INF-Datei
und lädt usbstor.sys.
Man müsste daher bei Eigenbauten jene Vendor- und Product-ID wiederverwenden,
um keine INF-Datei mitliefern zu müssen. (Auf welchem Medium? Auf Diskette — das geht nicht!)
Stellt man die Firmware auf das von Microsoft empfohlene Bulk-Only-Protokoll um,
lädt Windows den Treiber usbstor.sys auf generischer Basis mit Hilfe der USB-Klasse.
Daher verwenden auch die im Internet herumgeisternden Kodebeispiele zum Thema Massenspeicher
allesamt das Bulk-Only-Protokoll.
Hier wird der SCSI-Steuerblock mit einem 15-Byte-Rahmen versehen und über den
gleichen Bulk-Out-Endpoint versendet wie auch die Schreibdaten.
Sowohl die Lesedaten als auch der SCSI-Status werden über den Bulk-In-Endpoint
in den PC eingelesen, wobei der Status aus einem 9-Byte-Block besteht.
Bei Fehlern wird der Bulk-In-Endpoint im Stall-Modus angehalten.
Anschlüsse
Als erstes ist mir, wie vielen anderen Leidgeplagten auch, die Micro-USB-Buchse davongeflogen.
Das Problem ist nicht die Buchse sondern die schlechte Qualität der (blauen) Leiterplatte:
Die Kupferfolie klebt zu schlecht für die hohe Belastung beim Einstecken und Ausziehen.
Ein Kabelschwänzchen angelötet, nun geht erst mal die Platine zu verwenden.
Den hier unnützen 5-V-Festspannungsregler und einige C's habe ich gleich mit heruntergelötet,
vielleicht brauche ich die später für ein anderes Projekt.
Die Diode am USB-Anschluss muss drauf bleiben, damit nicht das eingeschaltete Diskettenlaufwerk
über dortige Pullup-Widerstände den ausgeschalteten PC versucht zu speisen.
Das Laufwerk oder die Laufwerke werden selbstverständlich nicht vom USB gespeist.
Hierfür ist ein eigenes Netzteil (5 V und 12 V) notwendig.
Die alten Laufwerke sind viel zu stromhungrig,
als dass man die Leistung einem USB-Anschluss antun sollte.
Nur der Mikrocontroller wird vom PC gespeist.
Die Anschlüsse wurden so verteilt, dass:
die Lesedaten RD an einen der beiden Input-Captures anliegen, hier ICP1
die Schreibdaten WD aus einem der fünf Output-Compare-Ausgängen kommen können, hier OC0B.
Der 8-Bit-Zähler liegt im schnelleren I/O-Adressraum
die Leitungen auf der Platine sich wenig kreuzen müssen:
Siehe Board-Layout.
RxD- und TxD-Anschlüsse auf „unwichtigen“ Laufwerksauswahl-Anschlüssen liegen, zum Debuggen
Für die Lesedaten ist ein externer Pullup-Widerstand von bspw. 4,7 kΩ erforderlich.
Der im Chip integrierte ist mit seinen rund 40 kΩ zu groß für die schnellen Impulse.
12 I/Os werden minimal für 1 Laufwerk benötigt.
An das o.a. Board passen insgesamt vier Diskettenlaufwerke sowie 1 Bandlaufwerk
(Streamer, hier: QIC-80) dran.
Der ATmega32U4 hat eine unintuitive Portpinverteilung.
Dazu kommt das Chaos, was Arduino&Co angestiftet hat.
Hier noch mal nach Ports sortiert:
Wie man sieht, es überwiegen die Ausgänge
Port B
Port C
Port D
Port E
Port F
Bit
A#
R.
Anschluss
Bit
A#
R.
Anschluss
Bit
A#
R.
Anschluss
Bit
A#
R.
Anschluss
Bit
A#
R.
Anschluss
PB0
-
A
(LED D1)
PC0
-
PD0/OC0B
3
A
WD
PE0
-
PF0
kein Anschluss
PB1
15
A
Motor2
PC1
-
PD1
2
A
Motor3
PE1
-
PF1
kein Anschluss
PB2
16
E
TK0
PC2
-
PD2
RxD
A
DS3
PE2
kein Anschluss
PF2
-
PB3
14
A
STP
PC3
-
PD3
TxD
E
IDX
PE3
-
PF3
-
PB4
8
E
WP
PC4
-
PD4/ICP1
4
E
RD
PE4
-
PF4
A3
A
Motor0
PB5
9
E
DC
PC5
-
PD5
-
A
(LED D3)
PE5
-
PF5
A2
A
DS1
PB6
10
A
SS
PC6
5
A
DS2
PD6
kein Anschluss
PE6
7
A
WG
PF6
A1
A
DS0
PB7
kein Anschluss
PC7
kein Anschluss
PD7
6
A
DIR
PE7
-
PF7
A0
A
Motor1
Es ist fraglich, ob vier Laufwerke an einem Hosenträgerkabel
vernünftig (d.h. ohne Reflexionen am Kabelende) laufen.
500 kBit/s sind nämlich schon recht flott.
Alles „ohne Anschluss“ (also auf dem Board nicht herausgeführt)
muss zur Vermeidung von verbotenen Eingangszuständen
entweder als Ausgang oder als Eingang mit aktivem Pullup programmiert werden.
Da in Wirklichkeit nahezu alle Laufwerkssignale invertiert sind,
habe ich die Negationsstriche hier weggelassen.
Und so sieht in etwa ein Adapterboard aus.
Links werden die Laufwerke mit DS2 und DS3 angeschlossen, rechts die mit DS0 und DS1.
Jeweils mit einem „gedrehten“ Floppykabel.
Das Bandlaufwerk kann irgendwo zusätzlich angeschlossen werden.
Egal ob vor oder nach der Drehung.
Extra große Pads vereinfachen das Selbermachen.
Die Platinenabmessungen sind 46 × 64 mm².
USB-Features aus PC-Sicht
Die Firmware könnte oder kann:
sich bei fehlender Speisespannung für die Laufwerke vom USB zurückziehen
(durch Abschalten des Pull-Up-Widerstandes)
die Anwesenheit der Laufwerke detektieren („Floppy Drive Seek At Boot“),
da das aber Krach macht und nervt, erst mal nicht
Bis zu vier Laufwerke und 1 Bandlaufwerk melden
(für das Bandlaufwerk wäre jedoch ein Windows-Treiber zu schreiben)
Eine Art BIOS-Setup über ein HID-Interface anbieten,
die Daten landen dann im EEPROM des Mikrocontrollers.
Was die Firmware nicht kann:
Sich als Dateisystem ausgeben und selbständig alle möglichen
alten Diskettenformate erkennen (Mikrocontroller zu klein)
Kassettenrecorder und Datasetten ansteuern (off-topic)