An electronic typewriter transformed into a serial terminal

[todo FOTO]


In this document I describe the project in which I modified an electronic typewrites to that it will function as a serial printer or a serial terminal. [todo WIDEO]


There was the typewriter SHARP PA-3000H in our house. In the year 2013 I had the first idea to do something with it. At that time I was a student of electronics in the Wrocław University of Technology.

My thought was: "Why should it only print what is typed on the keyboard? It would be much cooler if a computer could tell it what to print." And then: "The typewriter always obeys the keyboard. What if I cheat and make a device that pretends to be the keyboard?"

So I decided to make a device that does exactly this. Instead of the real keyboard I would make a device that simulates key presses.

I used the MSP430F1232 microcontroller connected to the computer through the RS-232 interface and some logic gates connected to the typewriter in place of the keyboard.

(the page in the typewriter was not printed by the typewriter.)

It could print the characters that are present on the keyboard and additionally the Polish characters ĄĆĘŁŃÓŚŹŻąćęłńóśźż. The Polish characters where encoded as in 8859-2 or CP1250 (there is no collision).

I showed it at university, got a grade for it, and then I left it unfinished, for later. Until finally in 2022 I decided to finish this project and do it properly.

Goals and requirements

This time I wanted more than just print some characters from the computer. My requirements were:

Keyboard operation

The typewriter has an M50747-2A8SP microcontroller which is connected to the keyboard matrix as shown below.

The assignment of what is a row and what is a column on a keyboard matrix is arbitrary. I decided to call the P0 (CN5) outut pins "rows" and the P2 (CN6) input pins "columns". Also the numbering of the rows and columns is my arbitrary choice. The key numbers are in base 8.

The typewriter is scanning the keyboard in a sequence shown below.

Keyboard interface circuit

This is the circuit to interface between the typewriter and its keyboard.

This circuit requires some explanation.

I have unsoldered the connectors CN5 and CN6 from the typewriter. Instead I added horizontal "goldpin" connectors to which I connect my board. The original connectors are installed on my board so that the keyboard connects directly to it. My device consists of a main controller ATmega1284 and some 74HC gates.

The P0 port of the M50747 is its keyboard output. Originally, the "row" signals would go through diodes to CN5 and then to the keyboard. I have removed the diodes and replaced them with wire. Instead I installed the diodes on my board. This allows to connect the signals not only to the keyboard but also to other things. And so the signal is connected to the port A of the ATmega1284 so that it can know which row is active at each time.

"After" the keyboard the "column" signals would go to CN6 and from there to port P2 of the M50747. There they are also connected to pulldown resistors. I replicated the pulldown on my board and connected the signals to a 74HC244 instead. This allows to connect or disconnect the signals to CN6, thus enabling or disabling the typewriter's keyboard. The "column" signals are also connected to port C of the ATmega1284 so that it can know on which column a key press is detected.

There are 54 keys on the keyboard. But there is no need to add 54 transistor switches to simulate every possible key press. With the exception of 2 special keys only 1 key at a time is used for typing. The 8 "row" signals go into the 74HC151 multiplexer. From there the signal for only 1 row goes out, selected by 3 address bits controlled by pins B3, B4, B5. It goes into the 74HC238 demultiplexer from which it goes to 1 of the 8 column signals, selected by 3 address bits controlled by pins B0, B1, B2. The enable inputs of the 74HC151 and 74HC238, connected to pin D5, allow the signal to go through or not, thus simulating pressing or releasing the key.

There are 2 special keys which can change the meaning of other keys, SHIFT and CODE. These are routed separately through AND (7408) and OR (7432) gates, controlled by pins B6 and B5. Together with the multiplexing this simulates the entire keyboard.

As with the real keyboard, also the simulated keyboard is not connected directly to CN6 but through the 74HC244. This allows to enable or disable the simulated keyboard.

The +5V power supply is taken from pin 1 of CN5. The ground is taken from unpopulated jumpers JP1, JP2. The (caps lock) LED is connected through the 7708 AND gate so that it will turn on if either the P10 pin on the M50747 or D5 on the ATmega1284 is in the low state.

The 7408 and 7432 are on a separate small board because of a design mistake which I made. Instead of making a new board from the beginning again I fixed it by only making a small adapter to install the 2 chips into the socket dedicated for a single chip.

Serial interface circuit

This is the serial interface circuit

The SP3232 chip is used as the level converter.

Should the terminal be a DTE or DCE device? By definition, a terminal should be a DTE. But a computer is also a DTE and expects a DCE at the other end of the cable. So I added 2 connectors: a DB-23 for the DCE interface and a DE-9 for the DTE interface.

There are more control signals than the SP3232 can handle. On the DTE interface I completely ignored the RI and DCD signals. The DTR is always asserted, meaning the terminal is always "ready" and only the RTS is used for flow control. DSR and CTS are merged into 1 signal such that both must be asserted or disconnected to register as a single asserted signal. A very similar design is for the DCE interface.

Software design

In a most general overview the software is made of multiple single task engines connected by pipelines. Data is entering on one side, gets transformed while going through it and leaves on the other side.

Keyboard scanning

The row and column signals are sampled at about 6kHz. If

then the previous sampled column signal is checked.

Some debounce is performed through multiple iterations to check if in next repeats of the same row still the same columns are active. If the signal is stable for 30ms (3 full scans) and is correct (only a single column active, not counting the special keys CODE and SHIFT) an event is generated and sent further. After 700ms the event will be repeated every 120ms as long as the keyboard signal stays the same.

The key press event consists of a single byte which identifies the key pressed. Lowest 3 bits encode the column number, next 3 bits encode the row number, the highest bits encode the state of the special keys SHIFT and CODE.

Keyboard decoding

The key presses have to be converted to actual characters. This is the layout I have chosen:

On a key the top part represents SHIFT pressed and right part represents CODE pressed.

The main goal behind this choice was to keep all characters at their original positions and add new ones in the places that make the most sense. So \ | was added to Ü, [ { was added to Ö, and ] } was added to Ä. The characters ĄĆĘŁŃÓŚŻŹ were added to their "base" characters with 2 exceptions: Ź was added to Y (where on QWERTY keyboards is the place for Z), because Z was already occupied by Ż. uppercase Ś was added to D instead of S because of the keyboard matrix layout. Pressing CODE+SHIFT+S makes the machine also detect the K and the typewriter ignores it. Also other keys in columns 1 and 3 are unavailable for such combinations.

The key code (8 bit value) is used as an index in a keymap table which stores the values for each key. There is a separate keymap for each of the supported character encodings. For single byte encodings the decoded value is an 8 bit number which goes straight to the serial output queue. For UTF-8 the decoded value is a 16 bit number which gets converted to a sequence of 1 or more bytes in the UTF-8 encoding.

Some key combinations have special functions and are handled separately instead of being converted by the keymap. This is described in a later chapter related to the menu and settings.

The "ESC" key sends the code 0x1B (Esc). Pressing a normal key afterwards is the equivalent of pressing the key with ALT on some terminals. The "CTRL" key changes the code of the next pressed key by performing a logical AND with the value 0x1F on it. This is the equivalent of pressing the key with CTRL on some terminals.

If the buffer for keyboard events is full (there are many unhandled key presses) then the event is lost and sound is played on the typewriter's buzzer (requested by simulated key press).

Serial communication

As described in earlier chapter, the device has 2 RS-232 connectors. 1 DTE interface and 1 DCE interface. Because both are connected to the same inputs and outputs of the microcontroller it only makes sense to use one at a time.

The available baudrates are (in bps):

Lower rates like 50, 75, 110 are not avaliable simply because the counter values needed for generating them are too big to fit into the 12 bit register.

It's possible to have 7 or 8 data bits, 1 or 2 stop bits, wven, odd, or no parity. Of course i 7 data bits mode only ASCII characters are available.

Flow control is an interesting topic because there is a lot of misleading information available about it. Some articles even contradict themselves. Normally, it would be a good idea to check the text of the actual standard. But to access the TIA-232F standard it is required to pay $156 to get a printed copy or a "Secure PDF".

The RS-232 standard was designed for a specific use case: There are 2 devices (computer, terminal, etc.) which are supposed to be communicating with each other over some communication channel (like a phone line). This type of device is called DTE in the standard. The DTE devices are not connected to the communication channel directly but instead there are specialised devices for dealing with the communication. This type of device is called DCE. The RS-232 standard defines the interface between the DTE and the DCE. Just this, not more. Other usage of the interface leads to confusions.

As mentioned before, my terminal should be a DTE device. However for simplifying communication with a computer which is also a DTE and expects to be connected to a DTE, it is also possible to use my terminal as if was actually a DCE device.

There are 2 lines for exchnging data between the 2 devices: TxD (transmit data) and RxD (receive data). Ok, but which is which? If I have 2 devices do I connect TxD of one to RxD of the other? Or do I connect TxD to TxD and RxD to RxD? The standard says that the TxD output of the DTE is connected to the TxD input of the DCE. And the RxD input of the DTE to the RxD output of the DCE. So the DCE is sending data on RxD and receiving on TxD. This could seem counterintuitive but it actually makes sense. In the original context, TxD is for data sent to the DCE so that it could transmit to a remote device. And RxD is for data sent to the DTE, data which was received from the remote device. So in my terminal The data output is connected to TxD on the DTE interface and to RxD on the DCE interface.

There are 2 lines which indicate the status of the devices. DTR (data terminal ready) which indicates that the DTE is ready and DSR (data set ready, a confusing name) which indicates that The DCE is ready. The DTR output of the DTE should be connected to the DTR input of the DCE. And the DSR input of the DTE to the DSR output of the DCE. In my device, on the DTE interface I use DTR as output and on the DCE interface I use DSR. I put a constant positive voltage on this output which means that the terminal is always ready as soon as it's turned on. I use other signals for actual flow control. On the DTE interface I use DSR as input and ot the DCE interface I use DTR. I don't use this input directly, I merge it with the other input (RTS/CTS). More on that later.

There are 2 lines for controlling the data transfer direction. RTS (request to send) and CTS (clear to send). The RTS output of the DTE should be connected to the RTS input of the DCE. And the CTS input of the DTE to the CTS output of the DCE. On a half-duplex communication channel the communication can go only in one direction at a time. By default the DCE would be in receive mode. It will listen for data from the remote device and send it to the DTE. When the DTE wants to send data instead it asserts the RTS signal. Then the DCE switches from receiving to transmitting. And when it's ready to transmit the data it asserts the CTS signal. In this mode the DTE request transmitting or not. And the DCE can control when it's ok to transmit or not. This mode is sometimes called RTS/CTS but even more commonly this name is (not so correctly) used for something else.

For a full-duplex channel that the devices would like to use efficiently the RTS/CTS mode is not the best choice. So a different mode is used. The RTS signal is replaced by the RTR (ready to receive) signal. The RTR uses the same physical circuit that would otherwise be RTS. And the actual RTS is assumed to be always asserted, meaning that the DTE would always want the DCE to be ready to accept data from the DTE. DTE asserts the RTR signal when it is ready to accept data from the DCE. So it is the symmetric counterpart of the CTS signal. In most cases when "RTS/CTS" is selected as the used flow control, actually it means this mode which should more correctly be called "RTR/CTS".

In my terminal I use the RTS/RTR as output on the DTE interface and on the DCE interface the CTS signal. As input on the DTE interface I use CTS (merged with DSR) and on the DCE interface I use RTS/RTR (merged with DTR) The terminal will only treat the input as asserted if both actual inputs are asserted or not connected.

I made it configurable which flow control mode is used:

Independent from the 3 possible hardware flow control modes there is also the software flow contlrol available where the devices send Xon (0x11) to accept incoming data and Xoff (0x13) to reject. The software flow control can be used instead of the hardware flow control or together with it or not at all. If used together then data will be sent only if it is allowed by both.

There is a serial input buffer for the received data. Depending on the number of bytes in the buffer (with some hysteresis) the device will accept or reject incoming data. If the buffer is full (the flow control was ignored or not present) then any more incoming data is lost and a sound is played on the typewriter's buzzer (requested by simulated key press).

Keyboard simulating


to do to do