Fabrication industrielle
Internet des objets industriel | Matériaux industriels | Entretien et réparation d'équipement | Programmation industrielle |
home  MfgRobots >> Fabrication industrielle >  >> Manufacturing Technology >> Processus de fabrication

Lecteur de disquettes Arduino Amiga (V1)

Composants et fournitures

Résistance 1k ohm
× 1
Planche à pain (générique)
× 1
Câbles de raccordement (générique)
× 1
SparkFun Breadboard Alimentation 5V/3.3V
× 1
Arduino UNO
Ou Pro Mini
× 1

Applications et services en ligne

Arduino IDE

À propos de ce projet

  • Mon objectif : Pour créer un moyen simple, bon marché et open source de récupérer les données des disquettes Amiga DD à partir de Windows 10 et d'autres systèmes d'exploitation.
  • Ma solution : Un Arduino + une application Windows
  • Pourquoi : Pour préserver les données de ces disques pour l'avenir. De plus, un PC normal ne peut pas lire les disques Amiga en raison de la façon dont ils sont écrits.

Site Web du projet :http://amiga.robsmithdev.co.uk/

Il s'agit de la V1 du projet. La V2 contient une lecture et une écriture améliorées !

Expériences Amiga

Je dois ma carrière à l'Amiga, en particulier à l'A500+ que mes parents m'ont acheté pour Noël à l'âge de 10 ans. Au début, j'ai joué aux jeux, mais après un certain temps, j'ai commencé à me demander ce qu'il pouvait faire d'autre. J'ai joué avec le Deluxe Paint III et j'ai découvert Workbench.

L'Amiga 500 Plus :

Chaque mois, j'achetais le magazine populaire Amiga Format. Un mois avait une copie gratuite d'AMOS. J'ai entré le format Amiga Write A Game In AMOS compétition quand AMOS Professional a été mis sur un disque de couverture plus tard, et a été l'un des 12 (je pense) gagnants avec In The Pipe Line . Cependant, vous deviez vraiment les chasser pour des prix !

AMOS - Le créateur :

Contexte

Ensuite, je l'ai utilisé dans le cadre de mes projets GCSE et A-Level (grâce à Highspeed Pascal, qui était compatible avec Turbo Pascal sur PC)

De toute façon, c'était il y a longtemps, et j'ai des boîtes de disques, et un A500+ qui ne fonctionne plus, alors j'ai pensé à sauvegarder ces disques sur mon ordinateur, à la fois pour la préservation et la nostalgie.

Le site Web Amiga Forever a une excellente liste d'options qui incluent le matériel et l'abus de deux lecteurs de disquettes dans un PC - Malheureusement, aucune de ces options n'était une option avec le matériel moderne, et les contrôleurs KryoFlux/Catweasel sont trop chers. J'ai été vraiment surpris que la plupart d'entre eux soient des sources fermées.

Passionné d'électronique et ayant joué avec des appareils Atmel (AT89C4051 ) alors que j'étais à l'université, j'ai décidé de jeter un œil à l'Arduino (crédit à GreatScott pour l'inspiration montrant à quel point il est facile de commencer) Je me demandais si cela était possible.

J'ai donc recherché la lecture du lecteur de disquette Arduino code, et après avoir ignoré tous les projets qui ont abusé l'envie de jouer de la musique, je n'ai pas vraiment trouvé de solutions. J'ai trouvé quelques discussions dans quelques groupes suggérant que ce ne serait pas possible. J'ai trouvé un projet basé sur un FPGA qui était très intéressant à lire, mais pas la direction dans laquelle je voulais aller, donc la seule option était de construire une solution moi-même.

Recherche

Quand j'ai commencé ce projet, je n'avais aucune idée du fonctionnement du lecteur de disquette, et encore moins de la façon dont les données y étaient encodées. Les sites Web suivants ont été inestimables pour ma compréhension de ce qui se passe et de leur fonctionnement :

  • techtravels.org (et cette page)
  • La FAQ au format .ADF (Amiga Disk File) par Laurent Clévy
  • Amiga pour toujours
  • Wikipédia - Fichier disque Amiga
  • Tableau Amiga anglais
  • QEEWiki - Compteurs sur l'ATmega168/328
  • Brochage du lecteur de disquette
  • Liste des formats de disquettes

Hypothèses

Sur la base des recherches, je savais maintenant théoriquement comment les données étaient écrites sur le disque et comment le disque tournait.

J'ai commencé à travailler quelques chiffres. En fonction de la vitesse à laquelle le disque double densité tournait (300 tr/min) et de la manière dont les données sont stockées (80 pistes, 11 secteurs par piste et 512 octets par secteur, encodés à l'aide de MFM), pour lire les données avec précision, j'avais besoin de pouvoir échantillonner les données à 500Khz ; c'est assez rapide si l'on considère que l'Arduino ne fonctionne qu'à 16Mhz.

Dans les tentatives qui suivent, je ne parle que du côté Arduino. Passez au décodage.

Tentative 1 :

J'avais d'abord besoin de rassembler le matériel et l'interface avec le lecteur de disquette. Le lecteur de disquette que j'ai pris d'un vieux PC au travail, et j'ai saisi son câble IDE en même temps.

Ci-dessous une photo de libéré lecteur de disquette d'un ancien PC :

En étudiant le brochage du lecteur, j'ai réalisé que je n'avais besoin que de quelques fils, et après avoir regardé le lecteur, j'ai réalisé qu'il n'utilisait pas non plus l'entrée 12v.

La rotation de l'entraînement a été obtenue en sélectionnant l'entraînement et en activant le moteur. Bouger la tête était simple. Vous définissez le /DIR broche haute ou basse, puis pulsé le /STEP épingler. Vous pouvez savoir si la tête a atteint la piste 0 (la première piste) en surveillant le /TRK00 épingler.

J'étais curieux au sujet du /INDEX épingler. Cela pulse une fois à chaque rotation. Comme l'Amiga ne l'utilise pas pour trouver le début de la piste, je n'en avais pas besoin et je pouvais l'ignorer. Après cela, il ne reste plus qu'à choisir le côté du disque à lire (/SIDE1 ) et en connectant /RDATA .

Avec l'exigence de débit de données élevé, ma première pensée a été de trouver un moyen de rendre cela moins problématique en essayant de réduire les exigences sur ce débit.

Le plan était d'utiliser deux registres à décalage 8 bits (SN74HC594N ) pour réduire la fréquence d'échantillonnage requise d'un facteur 8. J'ai utilisé ce que Ebay a appelé Pro Mini atmega328 Board 5V 16M Arduino Compatible Nano (donc je ne sais pas ce que c'est officiellement, mais cela fonctionne sur l'Uno !) pour mettre en mémoire tampon ce parallèle données et l'envoyer au PC à l'aide de son interface série/USART. Je savais que cela devait fonctionner à une vitesse supérieure à 500 Kbauds (avec toute la surcharge série impliquée également).

sn74hc594.pdf

Après avoir abandonné la bibliothèque série Arduino standard, j'ai été très heureux de découvrir que je pouvais configurer l'USART sur l'Arduino à une vitesse de 2M baud, et avec l'une de ces cartes de dérivation F2DI (eBay l'appelait Carte de dérivation de base pour FTDI FT232RL USB vers CI série pour Arduino - voir ci-dessous) Je pouvais facilement envoyer et recevoir des données à ce débit (62,5 Khz), mais je devais le faire avec précision.

Atmel-42735-8-bit-AVR-Microcontrôleur-ATmega328-328P_Datasheet.pdf

La carte de dérivation FTDI qui s'adapte parfaitement à l'interface de la carte Arduino :

Tout d'abord, j'ai utilisé l'Arduino pour configurer sur les registres à décalage 8 bits un seul des 8 bits cadencés au maximum. L'autre a reçu un flux directement du lecteur de disquette (fournissant ainsi une conversion série/parallèle).

Ce qui suit est une image folle de la maquette sur laquelle j'ai construit cela à l'époque :

J'ai utilisé l'un des temporisateurs Arduinos pour générer un signal de 500Khz sur l'une de ses broches de sortie et comme le matériel le gère, c'est très précis ! - Eh bien, mon multimètre l'a mesuré exactement à 500 kHz de toute façon.

Le code a fonctionné, j'ai enregistré 8 bits complets de données à 62,5 kHz, laissant le processeur Arduino à peine utilisé. Cependant, je ne recevais rien de significatif. À ce stade, j'ai réalisé que je devais examiner de plus près les données réelles sortant du lecteur de disquette. J'ai donc acheté un vieil oscilloscope bon marché sur eBay (oscilloscope Gould OS300 20Mhz) pour vérifier ce qui se passait.

En attendant l'arrivée de l'oscilloscope, j'ai décidé d'essayer autre chose.

Un fragment de code utilisé pour lire les données des registres à décalage :

void readTrackData() { octet op; for (int a=0; a<5632; a++) { // Nous attendrons le marqueur de début "byte" while (digitalRead(PIN_BYTE_READ_SIGNAL)==LOW) {}; // Lit l'octet op=0; if (digitalRead(DATA_LOWER_NIBBLE_PB0)==HIGH) op|=1 ; if (digitalRead(DATA_LOWER_NIBBLE_PB1)==HIGH) op|=2; if (digitalRead(DATA_LOWER_NIBBLE_PB2)==HIGH) op|=4 ; if (digitalRead(DATA_LOWER_NIBBLE_PB3)==HIGH) op|=8; if (digitalRead(DATA_UPPER_NIBBLE_A0)==HIGH) op|=16; if (digitalRead(DATA_UPPER_NIBBLE_A1)==HIGH) op|=32 ; if (digitalRead(DATA_UPPER_NIBBLE_A2)==HIGH) op|=64 ; if (digitalRead(DATA_UPPER_NIBBLE_A3)==HIGH) op|=128; writeByteVersUART(op); // Attendre que le haut retombe pendant que (digitalRead(PIN_BYTE_READ_SIGNAL)==HIGH) {} ; }} 

Tentative 2 :

J'ai décidé que les registres à décalage, alors qu'une bonne idée n'aidait probablement pas. J'ai pu lire facilement 8 bits en une seule fois, mais il m'est venu à l'esprit que je ne pouvais pas être sûr que tous les bits étaient correctement synchronisés en premier lieu. En lisant la documentation, il a suggéré que les données étaient davantage des impulsions courtes plutôt que des hauts et des bas.

J'ai supprimé les registres à décalage et je me suis demandé ce qui se passerait si j'essayais de rechercher une impulsion du lecteur dans une interruption (ISR) en utilisant le signal 500Khz précédemment configuré. J'ai reconfiguré l'Arduino pour générer l'ISR, et après avoir surmonté les problèmes des bibliothèques Arduino (en utilisant l'ISR que je voulais), je suis passé à Timer 2.

J'ai écrit un court ISR qui décalerait un seul octet global à gauche d'un bit, puis si la broche connectée à la ligne de données du lecteur de disquette était LOW (les impulsions sont faibles) je ferais OU un 1 dessus. Toutes les 8 fois que j'ai fait cela, j'ai écrit l'octet complété à l'USART.

Cela ne s'est pas passé comme prévu ! L'Arduino a commencé à se comporter de manière très erratique et étrange. J'ai vite réalisé que l'ISR prenait plus de temps à s'exécuter que le temps entre les appels. Je pourrais recevoir une impulsion toutes les 2µSec et en fonction de la vitesse de l'Arduino, et faire l'hypothèse folle que chaque instruction C traduit en 1 cycle de code machine d'horloge, J'ai réalisé que je pouvais avoir au plus 32 instructions. Malheureusement, la plupart seraient plus d'une instruction, et après avoir recherché sur Google, j'ai réalisé que les frais généraux liés au démarrage d'un ISR étaient de toute façon énormes ; sans compter que les fonctions digitalRead sont très lentes.

J'ai abandonné le digitalRead fonction en faveur de l'accès direct aux broches du port ! Cela n'a toujours pas aidé et n'a pas été assez rapide. N'étant pas prêt à abandonner, j'ai abandonné cette approche et j'ai décidé de passer à autre chose et d'essayer autre chose.

À ce stade, l'oscilloscope que j'ai acheté est arrivé et il a fonctionné ! Un vieil oscilloscope croustillant qui était probablement plus vieux que moi ! Mais a quand même fait le travail parfaitement. (Si vous ne savez pas ce qu'est un oscilloscope, consultez EEVblog # 926 - Introduction à l'oscilloscope, et si vous aimez l'électronique, je vous suggère d'en regarder quelques autres et de parcourir le site Web EEVBlog.

Mon vieil oscilloscope croustillant nouvellement acheté (Gould OS300 20Mhz) :

Après avoir connecté le signal 500Khz à un canal et la sortie du lecteur de disquette à un autre, il était évident que quelque chose n'allait pas. Le signal 500Khz était une onde carrée parfaite en l'utilisant comme déclencheur, les données de la disquette étaient partout. Je pouvais voir les impulsions, mais c'était plus flou. De même, si je déclenchais à partir du signal du lecteur de disquette, le signal carré du signal 500Khz était partout et n'était pas synchronisé avec lui.

Photos des traces sur l'oscilloscope déclenchant des deux canaux. Vous ne pouvez pas tout à fait le voir, mais sur la chaîne pas déclenché est des milliers de lignes fantomatiques faibles :

Individuellement, je pouvais mesurer les impulsions des deux signaux à 500 kHz, ce qui n'avait pas de sens, comme s'ils fonctionnaient tous les deux à la même vitesse mais ne se déclencheraient pas afin que vous puissiez voir les deux signaux correctement, alors quelque chose ne va pas.

Après avoir beaucoup joué avec les niveaux de déclenchement, j'ai réussi à comprendre ce qui se passait. Mon signal était un 500Khz parfait, mais en regardant le signal du lecteur de disquette, eh bien, ils étaient correctement espacés, mais pas tout le temps. Entre les groupes d'impulsions, il y avait une dérive d'erreur, ainsi que des lacunes dans les données qui désynchronisaient totalement le signal.

En se souvenant des recherches précédentes, le lecteur était censé tourner à 300 tr/min, mais il se peut qu'il ne soit pas exactement à 300 tr/min, et le lecteur qui a écrit les données pourrait également ne pas être à exactement 300 tr/min. Ensuite, il y a l'espacement entre les secteurs et les écarts sectoriels. De toute évidence, il y avait un problème de synchronisation et la synchronisation du signal 500 Khz sur le lecteur de disquette au début d'une lecture n'allait pas fonctionner.

J'ai également découvert que l'impulsion du lecteur de disquette était extrêmement courte, bien que vous puissiez modifier cela en changeant la résistance de rappel, et si le timing n'était pas exactement le bon, l'Arduino pourrait manquer une impulsion ensemble.

Quand j'étais à l'université (Université de Leicester), j'ai suivi un module appelé système embarqué. Nous avons étudié les microcontrôleurs Atmel 8051. L'un des projets consistait à compter les impulsions d'une station météorologique simulée (encodeur rotatif). À l'époque, j'ai échantillonné la broche à intervalles réguliers, mais ce n'était pas très précis.

Le chargé de cours du module, Prof Pont suggéré que j'aurais dû utiliser le compteur matériel fonctionnalités de l'appareil (je ne savais même pas qu'il en avait un à l'époque.)

J'ai vérifié la fiche technique de l'ATMega328 et bien sûr, chacun des trois temporisateurs pourrait être configuré pour compter les impulsions déclenchées à partir d'une entrée externe. Cela signifiait que la vitesse n'était plus un problème. Tout ce que j'avais besoin de savoir, c'était si une impulsion s'était produite dans une fenêtre de temps de 2 µs.

Tentative 3 :

J'ai ajusté le croquis Arduino pour réinitialiser la minuterie 500khz lorsque la première impulsion a été détectée et chaque fois que la minuterie 500khz a débordé, j'ai vérifié la valeur du compteur pour voir si une impulsion avait été détectée. J'ai ensuite effectué la même séquence de décalage de bits et tous les 8 bits, j'ai écrit un octet sur l'USART.

Les données arrivaient et j'ai commencé à les analyser sur le PC. Dans les données, j'ai commencé à voir ce qui ressemblait à des données valides. Le mot de synchronisation impair apparaîtrait, ou des groupes de séquences 0xAAAA, mais rien de fiable. Je savais que j'étais sur quelque chose, mais quelque chose manquait encore.

Tentative 4 :

J'ai réalisé que pendant la lecture des données, les données du lecteur étaient probablement en désynchronisation/phase avec mon signal de 500 kHz. Je l'ai confirmé en lisant seulement 20 octets à chaque fois que j'ai commencé à lire.

En lisant sur la façon de gérer ce problème de synchronisation, je suis tombé sur l'expression boucle à verrouillage de phase ou PLL. En termes très simples, pour ce que nous faisons, la boucle à verrouillage de phase ajusterait dynamiquement la fréquence d'horloge (les 500 kHz) pour compenser la dérive de fréquence et la variance du signal.

La résolution de la minuterie n'était pas assez élevée pour la faire varier suffisamment (par exemple, 444 kHz, 470 kHz, 500 kHz, 533 kHz, 571 kHz, etc.) et pour l'exécuter correctement, j'aurais probablement besoin du code pour qu'il s'exécute beaucoup plus rapidement.

Les temporisateurs Arduino fonctionnent en comptant jusqu'à un nombre prédéfini (dans ce cas 16 pour 500khz ) puis ils définissent un registre de débordement et recommencent à partir de 0. La valeur réelle du compteur peut être lue et écrite à tout moment.

J'ai ajusté le croquis pour attendre en boucle jusqu'à ce que la minuterie déborde, et quand elle a débordé, j'ai vérifié une impulsion comme avant. La différence cette fois était que quand une impulsion a été détectée à l'intérieur de la boucle, j'ai remis la valeur du compteur de minuterie à une phase prédéfinie position, resynchronisant efficacement la minuterie à chaque impulsion.

J'ai choisi la valeur que j'ai écrite dans le compteur de la minuterie de telle sorte qu'elle déborde à 1 µs de l'impulsion de détection (à mi-chemin) de sorte que la prochaine fois que la minuterie a débordé, l'impulsion aurait été espacée de 2 µs.

Cela a fonctionné ! Je lisais maintenant des données presque parfaites sur le disque. J'avais encore beaucoup d'erreurs de somme de contrôle, ce qui était ennuyeux. J'ai résolu la plupart de ces problèmes en relisant en permanence la même piste sur le lecteur jusqu'à ce que j'aie les 11 secteurs avec des en-têtes et des sommes de contrôle valides.

J'étais curieux à ce stade, alors j'ai rebranché le tout à l'oscilloscope pour voir ce qui se passait maintenant, et comme je l'ai deviné, je pouvais maintenant voir les deux traces car elles restaient toutes les deux synchronisées l'une avec l'autre :

J'aimerais que cela soit un peu plus clair, si quelqu'un veut me faire don d'un bel oscilloscope numérique haut de gamme (par exemple l'un d'entre eux Keysight !) Je l'apprécierais vraiment !

Tentative 5 :

Je me demandais si je pouvais améliorer cela. En regardant le code, en particulier la boucle de lecture interne (voir ci-dessous), j'avais une boucle while en attente du débordement, puis un if interne à la recherche d'une impulsion avec laquelle se synchroniser.

Un fragment de code utilisé pour lire les données et les synchroniser :

register bool done =false;// Attente d'un débordement de 500 khz pendant (!(TIFR2&_BV(TOV2))) { // front descendant détecté en attendant l'impulsion de 500 khz. if ((TCNT0) &&(!done)) { // impulsion détectée, réinitialise le compteur de la minuterie pour qu'il se synchronise avec l'impulsion TCNT2=phase ; // Attendez que le pouls redevienne haut pendant (!(PIN_RAW_FLOPPYDATA_PORT &PIN_RAW_FLOPPYDATA_MASK)) {} ; fait =vrai ; }}// Réinitialiser le flag de débordementTIFR2|=_BV(TOV2) ; // Avons-nous détecté une impulsion du lecteur ?if (TCNT0) { DataOutputByte|=1; TCNT0=0;} 

J'ai réalisé que selon l'instruction qui était exécutée dans les boucles ci-dessus, le temps entre la détection d'impulsion et l'écriture de TCNT2=phase ; pourrait changer en fonction du temps nécessaire à l'exécution de quelques instructions.

Réalisant que cela peut provoquer des erreurs/gigue dans les données et aussi avec la boucle ci-dessus, il est possible que je manque l'impulsion du lecteur (manquant donc un bit de resynchronisation), j'ai décidé de prendre l'astuce de l'un de mes précédents tentatives, l'ISR (interruption).

J'ai câblé l'impulsion de données à une deuxième broche sur l'Arduino. Les données étaient maintenant connectées au déclencheur COUNTER0 et maintenant également à la broche INT0. INT0 est l'une des priorités d'interruption les plus élevées, il devrait donc minimiser les délais entre le déclenchement et l'appel de l'ISR, et comme c'est la seule interruption qui m'intéresse, toutes les autres sont désactivées.

Tout l'interruption nécessaire pour faire était d'exécuter le code de resynchronisation ci-dessus, cela a changé le code pour ressembler à ceci :

// Attendez le débordement de 500khz tandis que (!(TIFR2&_BV(TOV2))) {} // Réinitialisez l'indicateur de débordementTIFR2|=_BV(TOV2) ; // Avons-nous détecté une impulsion du lecteur ?if (TCNT0) { DataOutputByte|=1; TCNT0=0;} 

L'ISR ressemblait à ceci :(notez que je n'ai pas utilisé attachInterrupt car cela ajoute également une surcharge à l'appel).

octet volatile targetPhase;ISR (INT0_vect) { TCNT2=targetPhase;} 

La compilation a produit beaucoup trop de code pour une exécution assez rapide. En fait le démontage de ce qui précède a produit :

push r1push r0in r0, 0x3f; 63push r0eor r1, r1push r24 lds r24, 0x0102; 0x800102 m 0x00B2, r24; 0x8000b2 pop r24pop r0out 0x3f, r0; 63pop r0pop r1reti 

En analysant le code, j'ai réalisé qu'il n'y avait que quelques instructions dont j'avais réellement besoin. Notant que le compilateur garderait une trace de tous les registres que j'ai écrasés, j'ai modifié l'ISR comme suit :

 octet volatile targetPhase asm ("targetPhase");ISR (INT0_vect) { asm volatile("lds __tmp_reg__, targetPhase"); asm volatile("sts %0, __tmp_reg__" ::"M" (_SFR_MEM_ADDR(TCNT2)));} 

Qui a démonté, a produit les instructions suivantes :

push r1push r0in r0, 0x3f; 63push r0eor r1, r1lds r0, 0x0102; 0x800102 m 0x00B2, r0; 0x8000b2 pop r0out 0x3f, r0; 63pop r0pop r1reti 

Encore trop d'instructions. J'ai remarqué que le compilateur ajoutait beaucoup d'instructions supplémentaires, qui pour mon application n'avaient pas vraiment besoin d'être là. J'ai donc recherché le ISR() et est tombé sur un deuxième paramètre ISR_NAKED. L'ajout de cela empêcherait le compilateur d'ajouter un code spécial, mais je serais alors responsable de la maintenance des registres, de la pile et du retour correct de l'interruption. J'aurais également besoin de maintenir le registre SREG, mais comme aucune des commandes que je devais appeler ne l'a modifié, je n'avais pas à m'en soucier.

Cela a changé le code ISR pour devenir :

ISR (INT0_vect, ISR_NAKED) { asm volatile("push __tmp_reg__"); // Préserve le tmp_register asm volatile("lds __tmp_reg__, targetPhase"); // Copier la valeur de la phase dans le tmp_register asm volatile("sts %0, __tmp_reg__" ::"M" (_SFR_MEM_ADDR(TCNT2))); // Copiez le tmp_register dans l'emplacement mémoire où TCNT2 est asm volatile("pop __tmp_reg__"); // Restaure le tmp_register asm volatile("reti"); // Et quitter l'ISR} 

En lequel le compilateur a converti :

push r0lds r0, 0x0102 ; 0x800102 m 0x00B2, r0; 0x8000b2 pop r0reti 

Cinq consignes ! Parfait, ou du moins aussi rapide qu'il allait l'être, prenant théoriquement 0,3125 µs à s'exécuter ! Cela devrait maintenant signifier que la resynchronisation devrait se produire à des périodes cohérentes dans le temps après l'impulsion. Vous trouverez ci-dessous un chronogramme de ce qui se passe. Voici comment récupérer des données à partir d'un flux de données série qui n'a pas de signal d'horloge :

Cela a amélioré un peu les résultats. Ce n'est toujours pas parfait. Certains disques lisent parfaitement à chaque fois, d'autres prennent du temps et doivent réessayer sans cesse. Je ne sais pas si c'est parce que certains disques sont restés là depuis si longtemps que le magnétisme s'est dégradé à un niveau si bas que les amplificateurs de disques ne peuvent pas y faire face. Je me suis demandé si cela avait quelque chose à voir avec le lecteur de disquettes du PC, alors je l'ai connecté à un lecteur de disquettes Amiga externe que j'avais, mais les résultats étaient identiques.

Tentative 6 :

Je me demandais s'il y avait autre chose à faire. Peut-être que le signal du lecteur était plus bruyant que je ne le pensais. Après avoir lu de plus amples informations, j'ai découvert qu'une résistance de rappel de 1 KOhm était la norme, alimentée dans un déclencheur de Schmitt.

Après avoir installé un déclencheur Hex Schmitt SN74HCT14N et reconfiguré le croquis pour qu'il se déclenche sur les fronts montants au lieu des fronts descendants, j'ai essayé, mais cela n'a pas vraiment fait de différence notable. Je suppose que comme je cherchais une ou plusieurs impulsions à chaque fois, cela a probablement absorbé aucun bruit de toute façon. Alors on va s'en tenir à la méthode Tentative 5 !

sn74hct14.pdf

Ma solution de maquette finale ressemblait à ceci :

Notez que le câblage ci-dessus est légèrement différent de l'esquisse en direct. J'ai réorganisé certaines des broches Arduino pour faciliter le schéma de circuit.

Tentative 7 :

J'étais un peu insatisfait de certains des disques que je n'avais pas lus. Parfois, les disques ne s'installaient tout simplement pas correctement dans le lecteur de disquette. Je suppose que le ressort de l'obturateur n'aidait pas.

J'ai commencé à chercher à détecter s'il y avait des erreurs dans les données MFM reçues du disque.

À partir des règles de fonctionnement de l'encodage MFM, j'ai réalisé que quelques règles simples pouvaient être appliquées comme suit :

  • Il ne peut pas y avoir deux bits "1" côte à côte
  • Il ne peut pas y avoir plus de trois bits "0" côte à côte

Tout d'abord, lors du décodage des données MFM, j'ai regardé s'il y avait deux « 1 » d'affilée. S'ils l'étaient, j'ai supposé que les données s'étaient un peu estompées au fil du temps et j'ai ignoré le deuxième « 1 ».

Avec cette règle appliquée, il y a littéralement trois situations de 5 bits où des erreurs peuvent se produire. Ce serait un nouveau domaine dans lequel je pourrais chercher à améliorer les données.

La plupart du temps, j'ai été surpris qu'il n'y ait pas vraiment eu autant d'erreurs MFM détectées. Je suis un peu confus pourquoi certains disques ne seront pas lus si aucune erreur n'est trouvée.

C'est un domaine pour une enquête plus approfondie.

Décodage

Après avoir lu comment fonctionnait MFM, je n'étais pas tout à fait sûr de la façon dont il s'alignait correctement.

Au début, je pensais que le lecteur produisait des 1 et des 0 pour les bits d'activation et de désactivation. Ce n'était pas le cas. Le variateur délivre une impulsion pour chaque transition de phase, c'est-à-dire :à chaque fois que les données sont passées de 0 à 1, ou de 1 à 0.

Après avoir lu ceci, je me suis demandé si je devais reconvertir cela en 1 et en 0 en l'alimentant dans une bascule à bascule, ou lire les données, rechercher des secteurs, et si aucun n'a été trouvé, inverser les données et réessayer !

Il s'avère que ce n'est pas le cas et c'est beaucoup plus simple. Les impulsions sont en fait les données RAW MFM et peuvent être directement introduites dans les algorithmes de décodage. Maintenant que j'ai compris cela, j'ai commencé à écrire du code pour analyser un tampon du lecteur et rechercher le mot de synchronisation 0x4489. Étonnamment, je l'ai trouvé !

D'après les recherches que j'avais menées, j'ai réalisé que je devais réellement rechercher 0xAAAAAAAA44894489 (une note de la recherche a également suggéré qu'il y avait des bogues dans le premier code Amiga qui signifiaient que la séquence ci-dessus n'a pas été trouvée. J'ai donc cherché à la place 0x2AAAAAAA44894489 après avoir effectué un AND sur les données avec 0x7FFFFFFFFFFFFFF ).

Comme prévu, j'en ai trouvé jusqu'à 11 sur chaque piste correspondant au début réel des 11 secteurs Amiga. J'ai alors commencé à lire les octets qui ont suivi pour voir si je pouvais décoder les informations du secteur.

J'ai pris un extrait de code de l'une des références ci-dessus pour aider au décodage MFM. Inutile de réinventer la roue hein ?

Après avoir lu l'en-tête et les données, j'ai essayé de l'écrire sur le disque en tant que fichier ADF. Le format de fichier ADF standard est très simple. Il ne s'agit littéralement que des 512 octets de chaque secteur (des deux côtés du disque) écrits dans l'ordre. Après l'avoir écrit et essayé de l'ouvrir avec ADFOpus et obtenu des résultats mitigés, parfois il ouvrait le fichier, parfois il échouait. Il y avait manifestement des erreurs dans les données. J'ai commencé à regarder les champs de somme de contrôle dans l'en-tête, en rejetant les secteurs avec des sommes de contrôle invalides et en répétant la lecture jusqu'à ce que j'en ai 11 valides.

Pour certains disques, c'était tous les 11 lors de la première lecture, certains ont également pris plusieurs tentatives et différentes valeurs de phase.

Enfin, j'ai réussi à écrire des fichiers ADF valides. Certains disques prendraient des années, certains littéralement la vitesse à laquelle l'Amiga les aurait lus. N'ayant plus d'Amiga fonctionnel, je ne pouvais pas vraiment vérifier si ces disques lisaient correctement normalement, ils ont été stockés dans une boîte dans le grenier pendant des années et pourraient donc bien s'être dégradés.

Et ensuite ?

La suite est déjà arrivée - V2 est disponible ici et a amélioré le support de lecture et d'écriture !

Eh bien, tout d'abord, j'ai rendu l'ensemble du projet gratuit et open source sous la licence publique générale GNU V3. Si nous voulons avoir le moindre espoir de préserver l'Amiga, nous ne devrions pas nous arnaquer pour ce privilège, et en plus, je veux redonner à la meilleure plate-forme sur laquelle j'ai jamais travaillé. J'espère également que les gens développeront cela, iront plus loin et continueront à partager.

Je veux ensuite regarder d'autres formats. Les fichiers ADF sont bons, mais ils ne fonctionnent que pour les disques formatés AmigaDOS. Il existe de nombreux titres avec une protection contre la copie personnalisée et des formats de secteur non standard qui ne peuvent tout simplement pas être pris en charge par ce format.

Selon Wikipedia, il existe un autre format de fichier de disque, le format FDI. Un format universel bien documenté. L'avantage de ce format est qu'il essaie de stocker les données de la piste aussi près que possible de l'original, ce qui, espérons-le, résoudra les problèmes ci-dessus !

Je suis également tombé sur la Software Preservation Society, en particulier CAPS (anciennement la Classic Amiga Preservation Society ) et leur format IPF. Après un peu de lecture, j'ai été très déçu, tout est fermé et j'avais l'impression qu'ils utilisaient simplement ce format pour vendre leur matériel de lecture de disque.

Je me concentrerai donc sur les IDE ! format. Ma seule préoccupation ici concerne l'intégrité des données. Il n'y aura pas de sommes de contrôle contre lesquelles vérifier si la lecture était valide, mais j'ai quelques idées pour résoudre ce problème !

Et enfin, je chercherai également à ajouter une option de disque d'écriture (prenant peut-être en charge FDI ainsi que ADF), car cela ne devrait vraiment pas être si difficile à ajouter.

Code

Dépôt GitHub
Esquisse Arduino et code source Windowshttps://github.com/RobSmithDev/ArduinoFloppyDiskReader

Schémas


Processus de fabrication

  1. Animation
  2. Disquette
  3. Arduino Spybot
  4. FlickMote
  5. Téléviseur maison B-Gone
  6. horloge maîtresse
  7. Trouvez-moi
  8. Puissance Arduino
  9. Lecteur de disquettes Arduino Amiga (V1)