Raspberry Pi A+のGPIOを用いた3.5インチフロッピーのリード/ライト実験

FDDの34ピン信号をRaspberry PiのGPIOに接続してフロッピーを読み書きしてみたので、作業メモを残しておきます。USB-FDDが使えない、古い倍密度フォーマットのための環境を用意したくてやってみたのですが、2HDにも一応使えるようです。テストは主に2HDとPC-6601用1Dで行いました。

Raspberry Pi A+とFDD
Raspberry Pi A+のGPIOにFDDを接続


目次


概要

Raspbian上で動きます。カーネル4.4.44+でテストしました。専用のデバイスドライバ内で割り込みを停止して、データリード信号がONになるタイミングをポーリングで待ち、その時間の一覧をファイルに書き出す、というのが基本的な仕組みです。読み出し終了後、別のプログラムでD88形式に変換します。書き込みはその逆です。ディスクイメージを丸ごと吸い出したり流し込んだりする形になります。原理としては、以前RXマイコンを使ってやってみた実験と同じことをやっています。

吸い出したイメージは、セクタ内データだけでなく、GAP/SYNC/アドレスマーク/セクタID/CRC/書き足し境界に発生した中途半端な信号、などをすべて含んだものになります。また、データビットの他にクロックビットも含みます。D88への変換時には、ビット間の時間差を求めて2μs単位 (倍密度の場合) もしくは1μs単位 (高密度の場合) に四捨五入し、SYNC部でデータビットかクロックビットかを判定して解析を行います。SYNCによる周波数調整は行っていません。


FDDの接続

Y-E DATA製YD-702D-6639Dを使ってテストしました。普通の (新しめの) 34ピン2モード/3モードFDDであれば、大きな違いは無いのではないかと思います。また、以下の信号線の他に、FDDの電源 (5V) を接続する必要があります。電源は1Aあれば大抵のドライブで足りそうな感じです。

信号線の接続
GPIO番号 GPIO設定 RPIピン番号 FDDピン番号 信号名 FDDから見たIN/OUT 機能 (ON=LOW)
- - - 2 MODE SELECT IN 2HDモード時に回転数を360rpmにする (使用せず)
4 IN 7 8 INDEX OUT トラックの開始位置信号
17 OUT 11 12 DRIVE SELECT IN ドライブ使用時ONにする
18 OUT 12 16 MOTOR ON IN ディスクを回転させるモーターをON
27 OUT 13 18 DIRECTION SELECT IN シーク方向を決める。ON時内側
22 OUT 15 20 STEP IN 1ステップ分シークする
23 OUT 16 22 WRITE DATA IN データ書き込み信号
24 OUT 18 24 WRITE GATE IN データ書き込み時ONにする。OFF時リード・シーク可
25 IN 22 26 TRACK 00 OUT ヘッドが最外周にあればON
- - - 28 WRITE PROTECT OUT ライトプロテクトがかかったディスクならON (使用せず)
8 IN 24 30 READ DATA OUT データ読み取り信号
7 OUT 26 32 SIDE ONE SELECT IN OFF時サイド0、ON時サイド1選択
- - - 34 DISK CHANGE OUT 電源投入後もしくはディスクが抜かれるとON (使用せず)
GND GND 39など奇数ピン GND GND GND

一応、UARTとI2CとSPIを避けて、26ピン内に収めるようにしてみました。面倒なので自分は信号線すべて直結していますが、Raspberry PiのGPIOが3.3Vなのに対し、FDDのI/Oは一般に5Vなので、おすすめしません。適宜レベル変換などするべきでしょう。


GPIO入力 (FDD出力) 側

FDDの出力は、複数台つないだ時の電圧の衝突を避けるため、大抵オープンドレインになっています。なので内部プルアップしていなければ直結しても大丈夫かなと思ったのですが、中には5Vを直接出すドライブもあるようです。手元にあるものだと、TEAC FD-05HG-4668-Uが5V直接出力でした (3ステートバッファを使っているようです)。34ピン式でなくFFCケーブルを使うタイプで、コネクタとピンアサインを変換して接続したのですが、直結だと動きませんでした。と言うかGPIO動作がしばらく変になったりしたので、何らかのダメージを受けたかもしれません。34ピン式ドライブなら大体大丈夫なのかな?

オープンドレインの場合、READ DATA信号については、500〜1kΩ程度の抵抗で3.3Vにプルアップし、なるべく短いケーブルで接続した方が良さそうです。倍密度ディスクではあまり気にしなくても大丈夫なようで、手元のセットアップでは外部プルアップなしでも読めましたが、2HDだと駄目でした。GPIOの内部抵抗 (約50kΩ) によるプルアップのみだと、信号が鈍って隣り合うビットが分離できなくなるケースがあるようです。


GPIO出力 (FDD入力) 側

手元のFDDでは1kΩの抵抗で5Vにプルアップしてあるようです。直結すると3.3Vになりましたが、降下分の電流はどこに流れてるんでしょう。GPIO出力のシンク電流はデフォルトでは最大8mAということですが、こういう接続を想定した値なのかはよくわかりません。またドライブによってプルアップ抵抗値はまちまちのようです。幸い今のところは壊れていないようですが……。

また、GPIOが入力設定の状態で直結すると5Vがそのままかかります。Raspbianでは初期状態が入力設定なので、直結するなら出力に設定した後でFDDの電源を入れるようにした方がいいでしょう。後述の読み書き手順の場合は、スクリプトを走らせた後で電源を入れることになります。繰り返しますが、直結はおすすめしません。


デバイスドライバのビルド

まずカーネルソースをダウンロードします。ダウンロードできたソースは4.4.44+、カーネルのバージョンが4.4.38+だったので (2017.1.27現在)、rpi-updateでカーネルのアップデートも行いました。そういうもんなの?

# cd /usr/src
# git clone --depth 1 https://github.com/raspberrypi/linux.git
# git clone --depth 1 https://github.com/raspberrypi/firmware.git
# cp -p firmware/extra/Module.symvers linux/
# uname -r
4.4.38+
# rpi-update
# uname -r
4.4.44+
# cd linux/
# modprobe configs
# zcat /proc/config.gz > .config
# make modules_prepare

この後、Makefileドライバのソース (2017.3.10バージョン) を置いたディレクトリでmakeすると、ドライバがビルドできます。

ドライバは最低限の機能しか持っていません。insmod gpiofd.koとした後、デバイスファイル /dev/gpiofd に対してデータの読み書き (read/write) がかかると、フロッピーに対して1トラック分のアクセスを行います。GPIO端子の設定、モーターのON/OFF、シーク、サイド選択といった動作はドライバでは関知しておらず、ユーザーランドで行う前提です。アクセスはdd bs=524288 count=1のように十分大きなバッファサイズを指定してトラックごと各1回で行います。後述の読み書き手順では、スクリプト側でこれらの操作を行っています。データ形式は、INDEX信号を起点としたREAD DATA信号の時間をns単位に直して4バイトデータとして記録し、最後に一周したINDEX信号の時間も記録します。また、2HD書き込み時には書き込み補償 (write precompensation) を行っています。

時間計測は、ARMタイマーをフリーランニングモードで使っています。Raspberry Pi A+だとコアクロック250MHzなので、4nsごとに+1するカウンターとして使えます。最初は「じゃあ4nsの時間精度で読み書きできるんだ!」と思ったのですが、残念ながらそううまくは行かないようです。GPIOのアクセスには一回あたり数十nsかかるので、それより良い精度は出せません。バスの調停の問題らしいので、ユーザーからはどうしようもないような気が。ちなみにコアクロック周波数は設定やRaspberry Piのモデルによって異なるため、1μsごとに+1するシステムタイマーを使い、トラックアクセスの度に測定するようにしています。


読み書きの手順

Wiring Piとcpufrequtilsが必要です。

# apt-get install wiringpi cpufrequtils

読み出し

  1. Raspberry PiとFDDを接続します。
  2. ドライバファイルgpiofd.koのあるディレクトリに移動します。
  3. 読み書きスクリプトを実行します。2HDならば
    ./fd.sh r 2hd
    とします。sudoの権限が必要です。
  4. 直結の場合、初回時は「recalibrate」と表示されてからFDDの電源を入れます。
  5. うまく読めれば、c##h#.datという名前の100〜400KB程度のファイルがたくさん作成されます。#の部分はシリンダ番号とヘッド番号です。 シリンダ番号=81まで読みます (オーバートラック)。
  6. 同じディレクトリで、D88への変換プログラム (ソース 2017.7.26バージョン) を実行すると、D88ファイルが作成されます。 書式は
    to_d88 <type> [-r rpm] [outfile]
    です。typeは1d/1dd/2d/2dd/2hdのいずれか、rpmのデフォルトは300、outfileのデフォルトはfd.d88です。試していませんが、-r 360とすればPC-98用なんかのデータを変換できるかもしれません。 メッセージが大量に出るので、変換エラーがないかチェックするなり/dev/nullに放り込むなりしましょう。

書き込み

  1. ドライバファイルgpiofd.koのあるディレクトリに移動します。
  2. D88からの変換プログラム (ソース) を実行します。
  3. 書式は
    from_d88 [-r rpm] [infile]
    です。rpmのデフォルトは300、infileのデフォルトはfd.d88です。試していませんが、-r 360とすればPC-98用なんかのデータを変換できるかもしれません。また、回転数を少しだけ遅くすれば、標準より多くのデータを詰め込むことも可能です。
  4. c##h#.datという名前のファイルがたくさん作成されます。#の部分はシリンダ番号とヘッド番号です。メッセージが大量に出ますが、普通は気にしなくて構いません。
  5. Raspberry PiとFDDを接続します。
  6. 同じディレクトリで、読み書きスクリプトを実行します。2HDならば
    ./fd.sh w 2hd
    とします。sudoの権限が必要です。
  7. 直結の場合、初回時は「recalibrate」と表示されてからFDDの電源を入れます。
  8. ライトプロテクトスイッチが開いていても教えてくれません。FDDは書き込み保護を行ってくれるはずですが、プログラム側は気付かずに書き込みシーケンスを一通り行います。時間が無駄になるので注意しましょう。

アンフォーマット

  1. Raspberry PiとFDDを接続します。
  2. アンフォーマット用スクリプトを実行します。2HDならば
    ./unformat.sh 2hd
    とします。sudoの権限が必要です。
  3. 直結の場合、初回時は「recalibrate」と表示されてからFDDの電源を入れます。
  4. ライトプロテクトスイッチが開いていても教えてくれません。FDDは書き込み保護を行ってくれるはずですが、プログラム側は気付かずに書き込みシーケンスを一通り行います。時間が無駄になるので注意しましょう。

いろいろメモ


参考文献


トップ > 雑多なもの > Raspberry Pi A+のGPIOを用いた3.5インチフロッピーのリード/ライト実験