News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu
avatar_elanstra

ACPCPE - Amstrad CPC Printer Emulator (DIY)

Started by elanstra, 12:10, 30 August 19

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

elanstra

Hi everybody, I would like to present to you my Epson-compatible dot matrix printer emulator for the Amstrad CPC.

It connects the Amstrad CPC's printer port to a PC acting as a virtual printer and generates files (currently txt, html and markdown) instead of printing paper.

It uses an Arduino-compatible  board (I used Teensy 2.0) to translate the parallel data coming out of the CPC's printer port into serial hexadecimal data, which is sent to a PC running Pyhton which processes the data and generates the files. If the data received contains ESC/P codes, it generates HTML or Markdown files. If the data is plain text, it generates TXT files.


It's great for printing BASIC programs and copy&paste to emulators using LIST#8 command.

You can find the details here: https://github.com/asmCcoder/ACPCPE


I hope it's of some use to somebody.

Gryzor


Patrick

Nice project.
But why using a NE555 just to debounce the push button. It could be done in software or with just a RC circuit.

norecess464

QuoteThis README was written on a real Amstrad CPC6128 using Protext and then printed as Markdown file on a Linux machine.
Wow, respect !! Good project.
My personal website: https://norecess.cpcscene.net
My current project is Sonic GX, a remake of Sonic the Hedgehog for the awesome Amstrad GX-4000 game console!

elanstra

Quote from: Patrick on 14:08, 30 August 19
Nice project.
But why using a NE555 just to debounce the push button. It could be done in software or with just a RC circuit.
They why was simply because I'm more familiar with the NE555 than with RC circuits  :-[ But you're absolutely right, I think it could be done by software on the Arduino.

I got a bit obsessed with debouncing the button after my first tests. The Arduino wasn't fast enough for the Amstrad CPC Printer port. Crazy, isn't it? The Printer port of the CPC is lacking the /Ack signal, so you have to read the parallel data at the speed of the /Strobe. I had to use ISR (Interrupt Service Routine) to be able read the pins (/Strobe) fast enough and then I did the same for the online/offline button. But after your comment, I thought a bit about it and actually this button doesn't need to be that fast because it's (should be) used when the CPC is not sending data.
Lets see if I have some free time soon and I can make a version 2 of the project. Thanks for the advice!

pelrun

#5
Quote from: elanstra on 09:23, 31 August 19
The Arduino wasn't fast enough for the Amstrad CPC Printer port. Crazy, isn't it?

There's a couple of reasons for it. While the CPC may not be sending characters particularly fast, there's only a very small window after Strobe is pulsed that the data is still valid. And entering an interrupt has a minimum delay, which is roughly 1ms. If the CPC changes the value too quickly then you can't capture it in a naive manner and would need some extra hardware support such as an external 8-way latch.

Also, GPIO's are arranged into groups of 8, and each is read as a whole byte. But since you're manually reading each bit individually you're really reading each pin many times but discarding the results, which makes the whole process take much longer in the critical period.

You currently have the data lines split across ports B and D, so instead you can just read each port once and then do the bit manipulation to extract the actual data byte afterwards, which will make it much easier to keep up with the CPC (partly because you eliminate 6 unnecessary port reads entirely, and partly because you move all the calculations out of the critical section.)

I rewrote the arduino side quickly, code is below. It adds software debouncing in the main loop (rather than using interrupts) and changed the serial writes to send the raw bytes instead of unnecessarily converting to hex ascii, although that also requires updating the python code. Also the select/deselect codes are 17 and 19 decimal, not hex! :D

(edit: fixed minor bug in debounce, it's not like I tested this or anything...)


#define PRN_STRB  5   // /Strobe
#define ONOFF_BTN 6   // Online/Offline signal (through push-buttons + 555 Timer IC)

#define PRN_D0    0   // Data 0
#define PRN_D1    1   // Data 1
#define PRN_D2    2   // Data 2
#define PRN_D3    3   // Data 3
#define PRN_D4    4   // Data 4
#define PRN_D5    7   // Data 5
#define PRN_D6    8   // Data 6

#define PRN_BUSY  9   // Busy = HIGH / Ready = LOW
#define ONOFF_LED 10  // Indicator of "printer" Online/Offline

#define COM_SPEED 115200 // Speed of the COM port between Arduino and PC
#define DEBOUNCE_LIMIT 10 // button input must be stable for this many reads to register

/////////////////////////////////////////////////////////////////////////
byte data = 0;      // Variable for converting parallel data from D0..D6 as a single byte
bool wasOnline = false; // Variable for storing Online/Offline push-button state
byte debounce_count = 0;

/////////////////////////////////////////////////////////////////////////
void setup() {
  pinMode(PRN_BUSY, OUTPUT);
  digitalWrite(PRN_BUSY, 1); // Tell CPC that we're busy setting up everything
 
  pinMode(ONOFF_LED, OUTPUT);
  digitalWrite(ONOFF_LED, LOW);
 
  pinMode(PRN_D0, INPUT);
  pinMode(PRN_D1, INPUT);
  pinMode(PRN_D2, INPUT);
  pinMode(PRN_D3, INPUT);
  pinMode(PRN_D4, INPUT);
  pinMode(PRN_D5, INPUT);
  pinMode(PRN_D6, INPUT);

  pinMode(PRN_STRB, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(PRN_STRB), readCPCbyte, LOW);

  digitalWrite(11, HIGH);   // turn the Teensy's internal LED on

  Serial.begin(COM_SPEED);
  Serial.write(19); // 19 = Deselect printer
}

void readCPCbyte(){
  // This function is called when /Strobe is Low (detected by Arduino interrupt)

  // read the ports as fast as possible
  // PINx returns the current input of port x
  byte port_b = PINB;
  byte port_d = PIND;

  // below just needs to happen before the next Strobe

  // reassemble data byte
  // (0) D3 D2 B7 B3 B2 B1 B0
  data = ((port_d & 0b00001100) << 3) | ((port_b & 0b1000000) >> 3) | (port_b & 0b00001111);

  // print byte to the Serial port
  Serial.write(buf);
}

void loop()
{
  // debounce the online button and toggle busy status when pressed
  if (!digitalRead(ONOFF_BTN))
  {
    if (debounce_count < DEBOUNCE_LIMIT)
    {
      debounce_count++;
    }
    else
    {
      if (wasOnline)
      {
        wasOnline = false;
        Serial.write(19); // 19 = Deselect printer
        digitalWrite(PRN_BUSY, HIGH); // Printer is Offline
      }
      else
      {
        wasOnline = true;
        Serial.write(17); // 17 = Select printer
        digitalWrite(PRN_BUSY, LOW); // Printer is Online, tell the CPC that we're ready for data
      }
      digitalWrite(ONOFF_LED, wasOnline);
    }
  }
  else
  {
    debounce_count = 0;
  }

}

elanstra

Quote from: Patrick on 14:08, 30 August 19
Nice project.
But why using a NE555 just to debounce the push button. It could be done in software or with just a RC circuit.
You asked for it (or maybe not  :laugh: ) and you have it: ACPCPE v2.0 with debounce by software and no more NE555. I even took the time to make a nicer connector for future improvements.
https://github.com/asmCcoder/ACPCPE


Quote from: pelrun on 14:17, 31 August 19I rewrote the arduino side quickly, code is below. It adds software debouncing in the main loop (rather than using interrupts) and changed the serial writes to send the raw bytes instead of unnecessarily converting to hex ascii, although that also requires updating the python code. Also the select/deselect codes are 17 and 19 decimal, not hex! :D
Man, you were quick!  ;D Unfortunately I did already changes to debounce by software and remove the NE555 before I saw your post. I'm going to have a look at your code. Maybe for a newer version, but will have to be on another weekend.

Neurox66


Congratulations, interesting project.
It would be interesting to add the possibility to save data on SD without connecting a PC  :)
A project similar to RetroPrinter by Rwap Software but cheap  :D



CPC 464+ with C4CPC and Gotek HxC USB Drive - 
CPC 6128+ with Nova, FlashGordon,AmsDap, SymbiFace III -
CPC 6128 with M4 ... and other Amstrad computers

elanstra

Quote from: Neurox66 on 08:03, 01 September 19
Congratulations, interesting project.
It would be interesting to add the possibility to save data on SD without connecting a PC  :)
A project similar to RetroPrinter by Rwap Software but cheap  :D
AFAIK, RetroPrinter uses a Raspberry Pi to generate the files, so technically speaking it uses a PC too  :D

genesis8

Can I use the photo on the github page on my amstrad news site ?
____________
Amstrad news site at Genesis8 Amstrad Page

elanstra

Quote from: genesis8 on 17:55, 01 September 19
Can I use the photo on the github page on my amstrad news site ?
Sure thing! I always release under MIT License so feel free to use whatever you need/want.

destroyer

Hello, I just discovered this project and I think it's great!!!... congratulations!!!. I know I'm 3 years late :-[ ... but I would like to replicate it... is there a document with the pinout of connections of all the components?

Powered by SMFPacks Menu Editor Mod