Läsa gamla remsor med Digital PC04 och ett Arduino kort

Berätta om dina pågående projekt.
MattisLind
Inlägg: 775
Blev medlem: 27 maj 2011, 20:27:12
Ort: Älvsjö
Kontakt:

Läsa gamla remsor med Digital PC04 och ett Arduino kort

Inlägg av MattisLind »

Här är ett cross-over projekt: Vintage datorperiferienhet med Arduinokort.

Varför vill man läsa gamla pappersremsor? Det kom en fråga från Rhode Island Computer Museum http://www.ricomputermuseum.org (via Pontus) om vi (dvs min far och jag) på Dalby Datormuseum (http://www.datormuseum.se) råkade ha några pappersremsor till PDP-9 eftersom man höll på med att restaurera sin gamla PDP-9.

Faderen letade och kom upp med en hög remsor. Hur gör man då på säkrast möjliga sätt för att flytta dessa ett kvarts varv runt jorden, med tanke på att det kanske är de enda kvarvarande remsorna som finns? Naturligtvis kopierar man dem och mailar en fil till jänkarna.

Fast hur kopierar man dem till en fil på ett bra sätt? Remsorna är av veckad typ så den FACIT standalone läsare som fanns att tillgå var inte så lämplig. Annars är dessa FACIT läsare från Åtvidaberg nog det bästa i pappersremseläsarväg. HP använde dessa.

Det blev till att plocka fram en gammal Digital Equipment PC04 läsare ur förråden.

Bild

Denna sak från tidigt 70-tal läser veckade remsor med 300 tecken per sekund. Det är bara en hake. Man måste ha något som styr den. Den är stendum. Det fanns ett antal alternativ att tillgå. T ex att försöka få igång vår PDP-9 maskin. Inte så lockande. Den har aldrig fungerat särskilt bra. En PDP8/a skulle nog vara betydligt enklare att koppla den till och på så sätt läsa ut remsorna. Fast frakta den till Stockholm dessutom kändes krångligt och då var det dessutom mycket intressantare att prova att läsa med ett Arduino kort.

Sagt och gjort Arduino blev det!

Bild

Till saken hör alltså att PC04 (läsardelen) som sagt är stendum. Den består en stegmotor, en rad forotransistorer förstärkare för foto transistor (G918) och drivare för stegmotor (M040)

Bild


Kondensatorerna är ganska stora i denna enhet så jag använde en varia för att sakta ta mig upp till 115 VAC som var märkspänningen. Inga problem uppstod. Det gamla linjära kragget i den levererade spänningar väl inom toleranserna 40 år efter födelsedagen. Observera att i bilden ovan så är ett M040 och G918 kortet urtaget för fotografering. Alla drivkort för stansen är också urplockade. Stansen är för övrigt grunkan till vänster. Den blå stegmotorn är den som matar pappersremsan vid läsning.


Bild

G918. Jag var tvungen att trimma den stora trimpoten som styr tröskel referensen till förstärkarna eftersom en bit betedde sig lite illa.

Bild

M040. Rejäla drivtransistorer till stegmotorn.

All logik för att styra stegmotor etc sitter på kortet som sitter i datorn (i PDP8/a eller /e), på M840 som består av ett fyrtiotal TTL kretsar på ett quad kort. För PDP-8/I och PDP-8/L sitter ett kort som gör motsvarande i läsaren. Jag är osäker på hur PDP-11 interfacade till denna läsare. Fast det finns det säkert någon annan som vet.

Mer info om PC04:
https://dl.dropboxusercontent.com/u/969 ... 4-PC05.pdf

Nåväl. Jag knackade i hop denna lilla snutt Arduino-c-kod:

Kod: Markera allt

/*

PC04 paper tape reader program for Arduino

Mattis Lind 

Ardruino Uno PINs.

Pins 10,11,12,13 is reserved for the Ethernet shield
Pin 4 is used to control the SD card

To use serial we need to use pin 4 and 10 for stepper rather than 0 and 1.
  
  Arduino pin      Use                      Direction     PC04 Reader Connector
  ------------------------------------------------------------------------------
  4                 Stepper Motor Coil A(0)     Out               P
  10                Stepper Motor Coil A(1)     Out               R
  2                 Stepper Motor Coil B(0)     Out               S
  3                 Feed hole detector          In                N

  5                 Stepper power enable        Out               U
  6                 Feed switch                 In                V
  7                 Stepper Motor Coil B(1)     Out               T
  8                 Hole 1 detector             In                D
  9                 Hole 2 detector             In                E
 A0                 Hole 3 detector             In                F 
 A1                 Hole 4 detector             In                H
 A2                 Hole 5 detector             In                J
 A3                 Hole 6 detector             In                K
 A4                 Hole 7 detector             In                L
 A5                 Hole 8 detector             In                M
 
                    Ground                      GND               C
                    
 Reader 300 cps. I.e 300 steps per second. Timer driven, one interrupt each 1.667 milisecond
 Use timer 1 to control the stepper motor.
 
 There need to be a slow turn on / turn off logic as well. The M840 module start
 at a 5 ms clock time and then ramps down to a 1.67 ms clock time. We will do 
 similar when starting and stopping.
 
 The Power line is just to decrease the power to the motor when it is in a stopped
 state. Thus to let the motor become completely lose we need to switch all stepper
 signals to off. 

 Feed hole input generate an edge triggered interrupt. The ISR will the initiate a timer to 
 expire within 200 microseconds.
 
 The timer 2 200 microseconds timeout ISR will sample the eight holes data and put them into
 a buffer and signal a semafor to the mainloop that data is available.

 Mainloop waits for the semaphore to be active and then reformats the data into hexadecimal 
 and transmits it over serial line. 

*/
#include <avr/io.h>
#include <avr/interrupt.h>

#define STEPPER_A0      4
#define STEPPER_A1      10
#define STEPPER_B0      2
#define STEPPER_B1      7
#define STEPPER_POWER   5
#define FEEDSWITCH      6
#define FEEDHOLE        3
#define HOLE_1          8
#define HOLE_2          9
#define HOLE_3         A0
#define HOLE_4         A1
#define HOLE_5         A2
#define HOLE_6         A3
#define HOLE_7         A4
#define HOLE_8         A5
#define TEST_OUT       11
#define HOLE_1_SHIFT   0
#define HOLE_2_SHIFT   1
#define HOLE_3_SHIFT   2
#define HOLE_4_SHIFT   3
#define HOLE_5_SHIFT   4
#define HOLE_6_SHIFT   5
#define HOLE_7_SHIFT   6
#define HOLE_8_SHIFT   7
#define MAX_RAMP       100
#define RAMP_FACTOR    360

#define TIMER1_VALUE  38869   // preload timer1 65536-16MHz/1/600Hz
//#define TIMER1_VALUE  2

#define TIMER2_VALUE  206    // 256 - 16000000/64*200E-6
#define STEPPER_ON    1
void setup ()
{
  noInterrupts();           // disable all interrupts
  Serial.begin (115200);
  // Setup Stepper pins as output
  pinMode(STEPPER_A0, OUTPUT);
  pinMode(STEPPER_A1, OUTPUT);
  pinMode(STEPPER_B0, OUTPUT);
  pinMode(STEPPER_B1, OUTPUT);
  pinMode(STEPPER_POWER, OUTPUT);
  pinMode(TEST_OUT, OUTPUT);
  // initialize timer1 

  pinMode(FEEDHOLE, INPUT);
  digitalWrite(FEEDHOLE, HIGH);    // Enable pullup resistor
  /*
  EIMSK |= (1 << INT1);     // Enable external interrupt INT1
  EICRA |= (1 << ISC11);    // Trigger INT1 on rising edge
  EICRA |= (1 << ISC10);
  */
  attachInterrupt(1,extInt,RISING);
  
  TCCR1A = 0;
  TCCR1B = 0;
  TCCR2A = 0;
  TCCR2B = 0;

  TCNT1 = TIMER1_VALUE;            
  TCCR1B |= (1 << CS10);    // no prescaler 
  //TCCR1B |= (1 << CS11);
  //TCCR1B |= (1 << CS12);
  TIMSK1 |= (1 << TOIE1);   // enable timer overflow interrupt
  
  //TCCR2B |= (1 << CS10);    // clk / 64 prescaler
  //TCCR2B |= (1 << CS11);
  TCCR2B |= (1 << CS12);
  digitalWrite(STEPPER_POWER, ~STEPPER_ON);

  interrupts();             // enable all interrupts

}

volatile int data;
volatile int data_flag;
volatile int overrun;
// 200 micro second timeout interrupt 
ISR(TIMER2_OVF_vect) {
  digitalWrite(TEST_OUT,0);
  TIMSK2 &= ~(1 << TOIE2);  // Single shot disable interrupt from timer 2
  EIMSK |= (1 << INT1);     // Re-enable external interrupt INT1
  data = digitalRead(HOLE_1);
  data |= (digitalRead(HOLE_2) << HOLE_2_SHIFT);
  data |= (digitalRead(HOLE_3) << HOLE_8_SHIFT);
  data |= (digitalRead(HOLE_4) << HOLE_7_SHIFT);
  data |= (digitalRead(HOLE_5) << HOLE_6_SHIFT);
  data |= (digitalRead(HOLE_6) << HOLE_5_SHIFT);
  data |= (digitalRead(HOLE_7) << HOLE_4_SHIFT);
  data |= (digitalRead(HOLE_8) << HOLE_3_SHIFT);
  if (data_flag) {
    // data_flag was not cleared by main loop. Overrun detected set overrun flag!
    overrun = 1;
  } 
  else {
    data_flag = 1;
  }
}

volatile int reader_run = 0;
volatile int rampup=MAX_RAMP;


// Edge triggered feed hole interrupt routine
void extInt () {
  data_flag = 0;
  digitalWrite(TEST_OUT,1);
  if (reader_run) { 
    TCNT2 = TIMER2_VALUE;      // Set Timer 2 to the 200 micro second timeout
    TIMSK2 |= (1 << TOIE2);    // now enable timer 2 interrupt
    EIMSK &= ~(1 << INT1);     // Disable external interrupt INT1 untill timeout has occured
                            // filtering out spurious interrupts
    
  }
}

int state;

// Stepper motor interrupt routine. 300 Hz
ISR (TIMER1_OVF_vect) {
   if (!digitalRead(FEEDSWITCH)||reader_run) {
     digitalWrite(STEPPER_POWER, STEPPER_ON);
     if (rampup>0) rampup--;
     TCNT1 = TIMER1_VALUE-rampup*RAMP_FACTOR;            // preload timer
     //TCNT1 = TIMER1_VALUE;  
     switch (state) {
       case 0:
         digitalWrite(STEPPER_A1, 0);
         digitalWrite(STEPPER_B0, 0);
         digitalWrite(STEPPER_B1, 1);
         digitalWrite(STEPPER_A0, 1);      
         state = 1;
         break;
       case 1:
         digitalWrite(STEPPER_B1, 0);       
         digitalWrite(STEPPER_A1, 0);
         digitalWrite(STEPPER_A0, 1);
         digitalWrite(STEPPER_B0, 1); 
         state = 2;
         break;
       case 2:
         digitalWrite(STEPPER_B1, 0);
         digitalWrite(STEPPER_A0, 0);
         digitalWrite(STEPPER_A1, 1);
         digitalWrite(STEPPER_B0, 1);       
         state = 3;
         break;
       case 3:
         digitalWrite(STEPPER_A0, 0);
         digitalWrite(STEPPER_B0, 0);
         digitalWrite(STEPPER_B1, 1);
         digitalWrite(STEPPER_A1, 1);
         state = 0;
         break;         
     }
   }
   else {
     digitalWrite(STEPPER_POWER, !STEPPER_ON);
     rampup=MAX_RAMP;
     digitalWrite(STEPPER_A0, 1);
     digitalWrite(STEPPER_B0, 1);
     digitalWrite(STEPPER_B1, 1);
     digitalWrite(STEPPER_A1, 1);
     state=0;
     
   }
}


void loop () {
  int ch;
  if (Serial.available()) {
    ch = Serial.read();
    if (ch == 'R') { // If pressing R the reader will start
       reader_run = 1;
    }
    if (ch == 'S') { // Pressing S will cause it to stop.
       reader_run = 0;
    }
    
  }
  if(data_flag) {
    Serial.write(data);
    data_flag = 0;
  }
  if (overrun) {    // If overrun occurs it will stop reading and print error message.
    overrun=0;
    reader_run = 0;
    Serial.println("ERROR: OVERRUN OCCURED");
  }
  delay(1);
}
Och denna snutt C-kod på värddatorn:

Kod: Markera allt

#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>

int main (int argc, char *argv[])
{
  int serfd, filefd, ret, reading=50;
  unsigned char data;
  char * cmd;
  char b[1];  // read expects an array, so we give it a 1-byte array
  int i=0;
  int timeout;
  int preamble=1;
  struct termios toptions;

  if (argc!=3) {
    fprintf(stderr, "wrong number of arguments");
    exit(1);
  }
  fprintf (stderr, "serieport: %s fil: %s\n", argv[1], argv[2]); 
  serfd = open(argv[1], O_RDWR | O_NONBLOCK);
    
  if (serfd == -1)  {
    perror("Failed to open serial port ");
    exit(1);
  }
    
  if (tcgetattr(serfd, &toptions) < 0) {
    perror("Couldn't  get terminal attributes");
    exit(1);
  }
  cfsetispeed(&toptions, B115200);
  cfsetospeed(&toptions, B115200);

  // 8N1
  toptions.c_cflag &= ~CSIZE;
  toptions.c_cflag |= CS8;
  toptions.c_cflag &= ~PARENB;
  toptions.c_cflag &= ~CSTOPB;
  // no flow control
  toptions.c_cflag &= ~CRTSCTS;

  toptions.c_cflag |= CREAD | CLOCAL;            // turn on READ & ignore ctrl lines
  toptions.c_iflag &= ~(IXON | IXOFF | IXANY);   // turn off s/w flow ctrl

  toptions.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);   //  raw mode
  toptions.c_oflag &= ~OPOST; // raw mode

  toptions.c_cc[VMIN]  = 0;
  toptions.c_cc[VTIME] = 0;
    
  tcsetattr(serfd, TCSANOW, &toptions);
  if( tcsetattr(serfd, TCSAFLUSH, &toptions) < 0) {
    perror("Couldn't set terminal attributes");
    exit(1);
  }
  sleep(4);
  tcflush(serfd, TCIOFLUSH);
  sleep(4);
  fprintf (stderr, "Opened serial port OK\n");
  filefd = open (argv[2], O_RDWR | O_CREAT | O_TRUNC, 0666);
  if (filefd==-1) {
    fprintf (stderr, "Failed to open destination file: %s\n", argv[2] );
    exit(0);
  }
  fprintf (stderr, "Opened file OK\n");
  cmd = "R";
  ret = write(serfd,cmd , 1);
  if (ret!=1) {
    fprintf(stderr, "Failed to write start reader command\n");
    exit(1);
  }
  timeout=50;
  fprintf (stderr, "Wrote start reader command command\n");
  do { 
    int n = read(serfd, b, 1);  // read a char at a time
    if( n==-1) {
      fprintf(stderr, "Failed to read one byte\n");
      exit(1);
    }
    if( n==0 ) {
      usleep( 1 * 1000 );  // wait 1 msec try again
      timeout--;
      continue;
    }
    timeout=50;
    ret = write (filefd, b, 1);
    if (ret !=1) {
      fprintf (stderr, "Failed to write one byte to file\n");
      exit(0);
    }
    if (b[0] == 0) {
      reading--;
    }
    else {
      preamble=0;
      reading=50;
    }

  } while( (reading>0||preamble) && timeout>0 );

  cmd = "S";
  ret = write(serfd,cmd , 1);
  if (ret!=1) {
    fprintf(stderr, "Failed to write stop reader command\n");
    exit(1);
  }
  fprintf (stderr, "Wrote stop reader command\n");
  close(serfd);
  close(filefd);
}
Efter lite debuggande fungerade koden fint och resultatet blev en trevlig binärfil som gick att skicka utan risk.

Kod: Markera allt

mattiss-mac-pro:~ mattis_lind$ hexdump test.bin
0000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
0000160 00 00 00 00 00 b8 9b 82 b8 80 84 b8 80 82 bd 80
0000170 80 b8 ad 84 b8 8f 82 b8 bf 82 b8 81 a4 8d bf bd
0000180 89 bf b4 85 bf be bc 89 80 b1 bf b0 9d bf bd 85
0000190 bf bd 89 bf b4 85 bf bf 9d bf bd 85 bf bd 89 bf
00001a0 b4 9d bf bd 85 bf bd 89 bf b4 87 bf be a5 bf be
00001b0 9d bf bd 85 bf bd a5 bf bf b1 bf a6 bc 82 80 bc
00001c0 80 a0 b1 bf 98 85 bf bf a5 bf bf b1 bf ba bc 80
00001d0 a0 80 80 80 b8 81 81 b1 bf b5 b8 81 8a b8 81 a4
00001e0 b3 bf b4 b8 81 81 b1 bf ba b3 bf be b1 bf d0 00
00001f0 80 80 80 bf bf bd aa 80 83 80 80 80 a4 80 80 b2
0000200 80 80 00 80 80 90 bf bf b8 b5 94 ad 80 80 90 b8
0000210 83 84 b8 83 81 bc 88 80 bc 80 a0 b8 80 a2 b8 9b
0000220 a4 b2 80 90 00 80 82 80 bf bf a7 a3 bd a1 bd 80
Filen är ett diagnostikprogram för att testa en bandstation av typen TC02 / TU55. Den är i BIN-format, därav att högsta biten nästan alltid är satt. PDP-9 är en 18 bitars maskin. Som jag förstår det läste man ut tre tecken från pappersremsan och slog ihop dem till ett 18 bitars ord. Vissa bitar använda bara för att markera blockindelningen.

Galet.. Javisst!
nifelheim
Den första
Inlägg: 2487
Blev medlem: 27 mars 2008, 22:31:16
Ort: stockholm

Re: Läsa gamla remsor med Digital PC04 och ett Arduino kort.

Inlägg av nifelheim »

Snyggt :tumupp:
blueint
Inlägg: 23238
Blev medlem: 4 juli 2006, 19:26:11
Kontakt:

Re: Läsa gamla remsor med Digital PC04 och ett Arduino kort.

Inlägg av blueint »

:tumupp:

Lite som ett äventyr att få iväg den där filen. Ett alternativ är att scanna remsorna. ;)
MattisLind
Inlägg: 775
Blev medlem: 27 maj 2011, 20:27:12
Ort: Älvsjö
Kontakt:

Re: Läsa gamla remsor med Digital PC04 och ett Arduino kort

Inlägg av MattisLind »

Tackar!

Här är ett litet skakigt filmklipp på läsaren in action så att säga...

http://youtu.be/-qRwt4Ec1Ns
Användarvisningsbild
säter
Inlägg: 35247
Blev medlem: 22 februari 2009, 21:16:35
Ort: Säter

Re: Läsa gamla remsor med Digital PC04 och ett Arduino kort

Inlägg av säter »

Är det något speciellt som gör att man inte kan ta en vanlig remsläsare med RS-232 utgång?

Veckade remsor? Eller hastigheten kanske?
Användarvisningsbild
JimmyAndersson
Inlägg: 26577
Blev medlem: 6 augusti 2005, 21:23:33
Ort: Oskarshamn (En bit utanför)
Kontakt:

Re: Läsa gamla remsor med Digital PC04 och ett Arduino kort

Inlägg av JimmyAndersson »

Kombinationen av korten i bild 2 såg väldigt udda ut. :lol:

Riktigt kul projekt! :tumupp: :tumupp: :tumupp:
blueint
Inlägg: 23238
Blev medlem: 4 juli 2006, 19:26:11
Kontakt:

Re: Läsa gamla remsor med Digital PC04 och ett Arduino kort

Inlägg av blueint »

Tänk om det funnits en Arduino på den tiden..
MattisLind
Inlägg: 775
Blev medlem: 27 maj 2011, 20:27:12
Ort: Älvsjö
Kontakt:

Re: Läsa gamla remsor med Digital PC04 och ett Arduino kort

Inlägg av MattisLind »

Är det något speciellt som gör att man inte kan ta en vanlig remsläsare med RS-232 utgång?

Veckade remsor? Eller hastigheten kanske?
Främst är det veckningen som ställer till det. Det är jobbigt att få enorma högar med remsor i oordning. Vi har några FACIT läsare (riktigt fina läsare) som har både serieport och parallell port vad jag minns. Jag testade en gammal Addmaster 608 men den hade heller ingen hanteringen för veckade remsor. Dessutom så fungerade den dåligt vid hastigheter över 11 tecken per sekund.
Användarvisningsbild
Lennart Aspenryd
Tidigare Lasp
Inlägg: 12607
Blev medlem: 1 juli 2011, 19:09:09
Ort: Helsingborg

Re: Läsa gamla remsor med Digital PC04 och ett Arduino kort

Inlägg av Lennart Aspenryd »

Eftersom det mer eller mindre är en engångshändelse har ju läshastigheten mindre betydelse.
Det får ta den tid det tar!

Men kul att läsa om!

Stod det vart detta museum ligger?
Användarvisningsbild
pbgp
Inlägg: 1450
Blev medlem: 11 november 2010, 09:09:22
Ort: Uppsala

Re: Läsa gamla remsor med Digital PC04 och ett Arduino kort

Inlägg av pbgp »

Snygg lösning :)

Kul att det blev något (det är ju jag som är Pontus). Och jag hoppas RCIM får nytta av ditt jobb.

@Lasp: Dalby datormuseum ligger i Dalby, det finns en karta på länken i första inlägget. RCIM ligger staterna :)

Edit: det var inte så tydligt som jag trodde, här är en karta: https://sites.google.com/site/dalbydatormuseum/contact
Användarvisningsbild
anders_bzn
Inlägg: 5773
Blev medlem: 17 december 2008, 19:22:18
Ort: Kävlinge
Kontakt:

Re: Läsa gamla remsor med Digital PC04 och ett Arduino kort

Inlägg av anders_bzn »

Kul! Jag har en PC05 som är motsvarande remsläsare/stans till PDP-11. Har för mig att det är mekaniskt identiskt med PC04 men att elektroniken skiljer radikalt. Jag funderade på att koppla min till PDP-8/L men insåg att det skulle bli svårt.

Man borde åka till Dalby och hälsa på, men det är ju en bit från Skåne. Killarna på RICM hjälpte jag med en del tips när de hade problem med minnet på sin PDP-8/I. Den är identisk (nära på) med 8/L när det kommer till minneskretsarna (och en del annat också). Man borde åka dit också, men det är för långt bort.

Jag har läst remsor till PC med ASR-33 teletype med mitt hembyggda strömslingeinterface. Orginalremsorna har jag dock inte använt eftersom alla fanns på bitsavers.org

En ide är ju att ladda upp remsorna på på bitsavers.org så finns de bevarade om någon annan skulle behöva dem. Bitsavers var en guldgruva när jag renoverade 8/L.
Användarvisningsbild
säter
Inlägg: 35247
Blev medlem: 22 februari 2009, 21:16:35
Ort: Säter

Re: Läsa gamla remsor med Digital PC04 och ett Arduino kort

Inlägg av säter »

PC04/PC05 är alltså inte en standalone-läsare?, utan avsedd för inbyggnad i datorn?

Jag måste kolla på min PDP-11 om den också hanterar veckade remsor. :)
Användarvisningsbild
anders_bzn
Inlägg: 5773
Blev medlem: 17 december 2008, 19:22:18
Ort: Kävlinge
Kontakt:

Re: Läsa gamla remsor med Digital PC04 och ett Arduino kort

Inlägg av anders_bzn »

Nej det är inte en standalone, den är gjort för att byggas in i samma rack som man bygger datorn i.
Skriv svar