こんにちは、ゆうかりです。
作業場の撤収やら引っ越しやら新規構築やらでバタバタしており、なかなか電子工作回りをやる余裕が無いんですけれども、まあ。
禁断症状?的な感じなので、前から気になってたネタを、やらかしてみようと思います。
それは・・・。
表題の通り、「コンピューター用テープドライブの不思議な動きを確かめてみる」です。
そもそもテープドライブって何よ?
ひと昔前はありふれていた、「テープドライブ」。
まあほら、音楽用のカセットテープとか、VHSビデオテープとかその辺です。
コンピューター用では、「LTO」とか「AIT」、「DDS」だの「QIC」だのありますね。
まあ、一部例外を除き、こんな構造をしています。
*出典:Wikipedia
ま、テープは左右の白「ハブ」に巻き取られる形で。
まあ、テープ巻き取り直径的な速度の違いはあるにせよ、左右のハブは同期して回ります。
じゃないとテープ切れちゃいます、からね。
今時、というか、ほぼすべてのテープドライブは、この「左右の巻き取りハブ」は、バラバラに動くことはありません。
が、しかし
9-track tape driveの不思議
だいぶ昔の、コンピュータ用テープドライブは、不思議な動きをします。
まあ、下の動画をどうぞ。
なんか、右と左のテープハブ、ばらばらに動いてるように見えませんか?。
実際、ある程度ばらばらに動いています。
オイラがご幼少の頃は、これが不思議だったのですが。
まあ、ある程度裏事情的なものが察せられる現在、なーんとなく理由が見えてきています。
じゃあなによ?って。
9-track tape driveの規格と構造
ま、これは、「9-track tape」という、けっこーかなり古い規格です。
なんせIC使われる前、レベルの。
ま、こんなのです。
*出典:Wikipedia
*詳しい解説は、Wikipediaへ!→ https://en.wikipedia.org/wiki/9-track_tape
なんであんな不思議な動きになるかというと、まあこれは今でいう「HDD」みたいな使い方をされていて。
ランダムアクセスをさせていたから、なのと。
ランダムアクセスに耐えられるように工夫が入っていたから、です。
IBMのSystem/360に使われていた、2401というテープドライブとテープの規格は、以下な感じです。
項目 | Model 1 | Model 2 | Model 3 |
---|---|---|---|
データレート (8bit bytes/sec) | 30KB/60KD | 60KB/120KD | 90KB/180KD |
テープ速度 (インチ/sec) | 37.5 | 75.0 | 112.5 |
巻き戻し時間(分) | 3.0 | 1.4 | 1.0 |
項目 | 内容 |
---|---|
テープ長さ | 最大2400フィート |
記録密度 | 800Bytes / インチ |
* 出典: https://www.ibm.com/ibm/history/exhibits/storage/storage_2401.html
モデル3だとして、テープ速度は112.5インチ/秒、約2.9m/秒です、時速約10km。
一般的なテープだと、例えばカセットテープは秒速4.8cm、VHSは標準で秒速33cm、音楽用オープンテープの民生用で一番早いのが秒速38cmです。
まあ、ざっとそのへんのテープの10倍くらいの速度で、コンピュータ用テープは、ぶんまわっていたのです。
しかもランダムアクセス!。
なお、1巻最大2400フィートで、記録密度はインチ当たり800バイトなので、1巻で約24MBですね、、、。
で、まあ、時速10kmなテープを止めたり逆回転とかをそのままやらかしたら、テープは一瞬で切れちゃいます。
なので、そんなトリックプレイに耐えられるように、テープ読み込み部に、「遊び」が設けられています。
テープドライブ下にある、||な部分が、テープ遊び用の空間です、「真空チャンバー」なるところ。
実際のテープ読み取り部は、テープハブ下側中央黒い長四角部なのですが、そこにテープが行く前に、テープは下の||部に垂れ下がります。
ここは真空引きされており、テープを「優しく」吸引することで、急速なテープの走行に対して、テープにテンションがかかりづらいようにしているのです。
んで、これが、上の動画にあった、左右のテープハブがバラバラに動いて見える原因を作っています。
左右の真空チャンバーに送り込まれたテープは、それぞれ一定までテープを送り、吸い込むため、一定時間は静止している状態です。
真空チャンバーに溜まったテープが多すぎたり、余裕が少なくなったりして初めて、左右のテープハブが動く構造になっているため、実際の読み込みと左右のテープハブが同期せず、バラバラに動くように見えるのです。
ちなみに実際の動作は、こんな感じ、テープロード/アンロード、真空チャンバーとテープの関係が分かるかと。
で、この非同期なハブ構造と楽しんでみようかと
制御とモーター系は、この組み合わせで。
arduino互換の「maker nano」と、I2Cなモータードライバ「DRV8830」、そして適当なDCモーターです。
まあ、アドレス違いでブレッドボード上に組むと、こんな感じになります。
適当に習作プログラムを組めば、まあ普通にモーター動作しましたよ、と。
SENSE端子をGNDに落とさないと、エラー吐きますが。
走行用データ用意
さて、ランダムアクセス用のテープ走行データをでっちあげないといけませぬ。
左右の走行を切り替えて、視覚的に楽しいのといえば、ファミコンのTASかなと。
つーわけで、以下ページから、fm2ファイルを取得。
https://tasvideos.org/1546M
fm2ファイルは、fceuxというファミコンエミュレータのムービーファイルです。
ざっと中身を確認するに、フレーム毎に「押すボタン」を記述する形式のようです。
fceuxのフレームレートは約60.001fpsらしいので、ま、大体18msごとに読み込むようにしてやればいいかなと。
で、このままだとArduinoに食わせられないので、「左右」のボタンOn/Offを2ビットで表現した上で、4つ束ねてバイト列にするスクリプトを記述。
gplay.py
f = open('aglar,hotarubi-gimmickbestending.fm2', 'r')
count = 15
bitarray = ''
while True:
data = f.readline()
if data == '':
break
if data[0] != '|':
continue
if data[3] == 'R':
bitarray += '01'
elif data[4] == 'L':
bitarray += '10'
else:
bitarray += '00'
if len(bitarray) == 8:
intdata = int(bitarray, 2)
hexdata = str(hex(intdata)[2:].zfill(2)) + ','
print ('0x' + hexdata, end='')
bitarray = ''
count += 1
if count % 16 == 15:
print ('')
print (' ', end='')
f.close()
上の出力をAVRのFlash領域に展開し、順次メモリに読み込みしつつモーター制御するプログラムを書いてみました。
tapesoukou.ino
#include <avr/pgmspace.h>
#include <Wire.h>
const int driveraddress1 = 0x64;
const int driveraddress2 = 0x63;
unsigned long timepre;
unsigned long timepost;
unsigned long timedelta;
int vcol1;
int vcol2;
int motorrun1 = 0;
int motorrun2 = 0;
const int motorspeed1 = 5;
const int motorspeed2 = 6;
byte bitleft;
byte bitright;
byte bitpoint;
byte movebasedata;
int movearraycount;
byte movedata;
byte movecount;
byte tapespeed;
byte tapespeedcount;
byte tapespeedlow = 2;
const int vcollength = 12;
const int vcolswitch = 3;
const byte vcolhubsize1 = 8;
const byte vcolhubsize2 = 3;
const PROGMEM byte movearray[] = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
-- 中略 --
0xaa,0x95,0x00,0x05,0x55,0x55,0x55,0x56,0xaa,0xa6,0xaa,0xaa,0xaa,0xaa,0x55,0x54,
0x01,0x55,0x55,0x55,0xaa,0xaa,0x6a,0xaa,0xaa,0xaa,0xa5,0x55,0x55,0x55,0x55,0x55,
0x55,0x55,
};
const int movearraylength = sizeof(movearray)/sizeof(movearray[0]);
void setup() {
Wire.begin();
vcol1 = 0;
vcol2 = 0;
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
runMotor(0,1);
runMotor(0,2);
delay(5000);
}
void loop() {
timepre = micros();
if (movecount == 0) {
movebasedata = pgm_read_byte(movearray + movearraycount);
movearraycount += 1;
if (movearraycount >= movearraylength) {
movearraycount -= 1;
delay (10000);
movebasedata = 0x00;
}
}
bitpoint = 7 - movecount;
bitleft = bitRead(movebasedata, bitpoint);
bitpoint -= 1;
bitright = bitRead(movebasedata, bitpoint);
movecount += 2;
if (movecount > 7) {
movecount = 0;
}
digitalWrite(4, LOW);
digitalWrite(5, LOW);
tapespeed = tapespeedlow;
if (tapespeedcount < 20){
tapespeed = tapespeedlow * 5;
}
if (bitleft == 1) {
tapespeedcount += 1;
vcol1 = vcol1 + tapespeed;
vcol2 = vcol2 + tapespeed;
digitalWrite(5, HIGH);
}
else if (bitright == 1) {
tapespeedcount += 1;
vcol1 = vcol1 - tapespeed;
vcol2 = vcol2 - tapespeed;
digitalWrite(4, HIGH);
}
else {
tapespeedcount = 0;
}
if (vcol1 > vcollength) {
motorrun1 = 1;
}
else if (vcol1 < -vcollength) {
motorrun1 = -1;
}
if ((vcol1 < vcolswitch) && (vcol1 > -vcolswitch)) {
motorrun1 = 0;
}
if (motorrun1 == 1){
runMotor(motorspeed1,1);
vcol1 -= vcolhubsize1;
}
if (motorrun1 == -1){
runMotor(-motorspeed1,1);
vcol1 += vcolhubsize1;
}
if (motorrun1 == 0){
brakeMotor(1);
}
if (vcol2 > vcollength) {
motorrun2 = 1;
}
else if (vcol2 < -vcollength) {
motorrun2 = -1;
}
if ((vcol2 < vcolswitch) && (vcol2 > -vcolswitch)) {
motorrun2 = 0;
}
if (motorrun2 == 1){
runMotor(motorspeed2,2);
vcol2 -= vcolhubsize2;
}
if (motorrun2 == -1){
runMotor(-motorspeed2,2);
vcol2 += vcolhubsize2;
}
if (motorrun1 == 0){
brakeMotor(2);
}
timepost = micros();
timedelta = timepost - timepre;
if (timedelta > 16660) {
timedelta = 16660;
}
timedelta = 16666 - timedelta;
delayMicroseconds(timedelta);
}
void runMotor(int inVector,byte devicenumber) {
int direction;
int voltage;
if (inVector > 0) {
direction = 0x01;
voltage = inVector;
} else if (inVector == 0) {
direction = 0x00;
voltage = 0;
} else {
direction = 0x02;
voltage = -inVector;
}
writeToDriver(direction, voltage, devicenumber);
}
void brakeMotor(byte devicenumber) {
writeToDriver(0x03, 0x00,devicenumber);
}
void writeToDriver(byte inDirection, byte inVoltage,byte devicenumber) {
int outData = (inVoltage & 0x3f) << 2 | (inDirection & 0x03);
if (devicenumber == 2) {
Wire.beginTransmission(driveraddress2);
}
else {
Wire.beginTransmission(driveraddress1);
}
Wire.write(0x00); // control
Wire.write(outData); //
Wire.endTransmission(true);
}
左右、一定時間まではサーチということでテープ速度は速めに、左右のハブはテープの偏りがあるって感じで送り出し量に差をつけています。
LEDは実際のテープ走行左右 = TASの左右ボタン入力を表しています。
実際に動かしてみた
ま、TASとの対比は、こんな感じで!。
なんとなく、左右の回転差や、非同期具合が再現できているような気もします。
作ってみて
なかなか不思議な、テープの動きが、ある程度再現出来て、楽しめましたとさ。
ちゃんと「テープドライブ」らしい、タンスみたいな箱に作りこみたいけれども、工作機械の稼働が出来ないため、今回はブレッドボードで満足としました、とさ。
いやあ、モーター系は初めて手を出してみたんだけれども、なかなか興味深い世界でした。
次はステッピングモーターでも遊んでみようかな・・・。
以上です。
投稿者の人気記事
-
eucaly
さんが
2022/10/29
に
編集
をしました。
(メッセージ: 初版)
-
eucaly
さんが
2022/10/29
に
編集
をしました。
-
eucaly
さんが
2022/10/29
に
編集
をしました。
ログインしてコメントを投稿する