Tuesday, October 30, 2018

Let's Amplify 3.3V TTL Signals to 5V for Character LCD


Hi there. In my previous post, I have told that the events, I experienced, were loosely connected but it was partially true. I needed the desktop computer, I have prepared in the previous blog, for its parallel port. Unfortunately, it's impossible to find a computer with parallel port nowadays. If you need a parallel port, you have to buy those PCI cards, manufactured specifically for this purpose. USB to parallel port adapter could not provide standard parallel port interface. What I mean with "standard parallel port interface" is 0x378 or 0x3BC I/O port of course. The parallel port was needed for a LCD circuit. I will mention a bit of everything in this blog. By the way, my favorite port is 0x3BC for parallel interface however my BIOS does not support IO port selection for parallel interface. So, I had to use 0x378.



Before using the parallel port, I have checked on BIOS that it is enabled and working in a appropriate mode. In fact, I don't have a specific need as a port mode. So, appropriate mode means even "Output only" if the port is enabled.

I have installed CentOS to this machine and checked the dmesg output for parallel port:
[root@xxxxxxxx ~]# dmesg | grep -i parport
parport_pc 00:08: reported by Plug and Play ACPI
parport0: PC-style at 0x378, irq 7 [PCSPP]


The port mode is between the square brackets. My port works as a standard parallel port (SPP). For bidirectional mode, it should be [PCSPP, Tristate]; for Enhanced Parallel Port (EPP), should be [PCSPP, Tristate, EPP] and lastly, for Extended Capabilities Port (ECP), it should be [PCSPP, Tristate] with auxiliary I/O port in parentheses. SPP works unidirectional, and as far as I can remember, with 50KB/s. Bidirectional mode has the same speed and it works in both directions as the name suggested. EPP or ECP works from 500KB/s to 2MB/s however these are out of the scope of this post. Therefore I don't want to go deeper into the details.

This is the schematics, that I want to build:
Circuit schematics (originally from: http://www.aljaz.info/elektro/lcd/lcd-lpt.htm)
I did not follow the article in the link. I was using another source (printed book) and the schematic on the book was the same except the values of pull-up resistors. I am not really sure which source is original. Of course it is not surprising, everyone has built a very similar circuit because the project is very simple indeed.
After I built the circuit physically and run the code*, which is supposed to write something on the LCD. But there was no output on LCD at all. I have tried several things but had the same result each time. I have decreased character per second rate to one (rate of speed is measured in terms of characters per minute (cpm) for printers, historically). Anyway, as the last effort, I measured the voltage on the port and found the issue. The port was working on 3.3V. Actually, to make sure, I had to check on the datasheet of LCD that 3.3V is in the unstable region of TTL signal, but who cares. I did not do that.
(*): Please find this code near the bottom of the post.



This result is not surprisingly, considering that last time, I played with the parallel port on a Celeron 366 machine. I used the code snipped below for testing:

#include<stdio.h>
#include<sys/io.h>

#define BASE 0x378

int main(int argc, char* argv[])        {
    if(ioperm(BASE, 1, 1))      {
        fprintf(stderr, "Access denied to %x\n", BASE);
        return 1;
    }
    outb(1, BASE);
    return 0;
}

https://commons.wikimedia.org/wiki/File:IEEE_1284_36pin_plughead.jpgThis code sets the least significant signal and quits. The least significant bit (or signal) corresponds to the second pin on both the DB25 parallel port and the Centronics port. BTW, the term "Centronics port" actually means the IEEE 1284 bidirectional parallel port standard which is developed by the company Centronics. I have meant the 36-pin connector not the standard. This connector can also be seen on my picture above along my multimeter.

The real name of this connector (pictured) is Micro Ribbon Connector with 36 pins and is used for printers. I may keep calling it Centronics port because of my habit.


If the value 255 is sent to the I/O port, instead of 1, to avoid confusing the pins +3.3V can be measured from all data pins [2..9].

74HC244 Connection diagram
As I searched on the internet, especially CNC people are complaining about this voltage difference. In 6502.org forum, it was mentioned that by using small valued pull-up resistors (between the signal and Vcc) like 4.7K or even 1K the voltage can be increased to 5V [ here ]. But this did not sound safe to me. It is dependent to the impedance of the circuit and parallel port itself. Instead of that, I thought amplifying the input using a 8-bit tri-state buffer circuit. I used 74HC244 for it. According to its datasheet, minimum high level input voltage is slightly lower than 3.3V in normal operating conditions. This means 3.3V from the parallel port will work as a high level TTL signal on the input of IC and I could get 5V signal on the output.

I have built a very simple circuit, whose schematics is given above. Since I was not using the enable pin of IC, I have connected it to the ground. I have connected a 100nF bypass capacitor. Since I operated the LCD in 8 bit mode, I needed 10 bits including RS and E pins. Therefore, I have used two 74HC244s (maybe RS and E pins are not very susceptible to TTL voltage levels like data ping and +3.3V would work fine on them which is unlikely but worth to try anyway). I got +5V from the hard disk connector and GND from the chassis of parallel port. I made the first experiment with a single pin and the result was positive.



The twisted pair cable in the picture above carries +5V. This is how I threat old CAT5 cables. I was too lazy to put the 100nF bypass capacitor on the schematic on the breadboard. The input and output voltages can be seen in the upper and lower part of the picture above, respectively. Now I can connect all the pins to LCD through the integrated circuits.





The final circuit is in the picture above. My original plan was fixing the contrast with a resistor and not using the background lightning to keep the circuit simple. However, I experienced a problem during building the circuit because I have incorrectly connected on of the pins (noticed later). I have connected all these parts to troubleshoot whether I could not see the characters or the data is still not consistent. And I did not unplugged them after I found the problem. The code is below:

#include<stdio.h>
#include<sys/io.h>

#define BASE 0x378    // Base port address
#define CTRL 0x37A    // Control port address
#define DELAY 3000    // for busy waiting

void lcdKomut(unsigned char veri)    {
    outb(veri, BASE);
    outb(8   , CTRL);    // RS = 0; E = 1
    usleep(DELAY);
    outb(1   , CTRL);    // RS = 1; E = 0
    usleep(DELAY);
}

void lcdVeri(unsigned char veri)    {
    outb(veri, BASE);
    outb(0, CTRL);    // RS = 1, E = 1
    usleep(DELAY);
    outb(1, CTRL);    // RS = 1, E = 0
    usleep(DELAY);
}


int main(int argc, char* argv[])    {
    if(ioperm(BASE, 3, 1))    {
        fprintf(stderr, "Access denied to %x\n", BASE);
        return 1;
    }

    lcdKomut(0x38);    // 8 bit, 2 lines, 5x7 px
    lcdKomut(0x01);    // clear the screen
    //lcdKomut(0x80);    // linefeed
    lcdKomut(0x0F);    // enable screen, cursor blink

    lcdVeri('D'); lcdVeri('e'); lcdVeri('n');
    lcdVeri('e'); lcdVeri('m'); lcdVeri('e');
    lcdKomut(0xC0);    // ikinci satir
    lcdVeri('1'); lcdVeri('2'); lcdVeri('3');
 
    outb(0, BASE);
    return 0;
}


Control register of the parallel port is the base (data) port+2. Therefore, it would be better to define CTRL as "#define CTRL BASE+2". DELAY parameter is the time required for the LCD screen to process the commands in microseconds. As I decreased this parameter to 300µs, I had no issue with this code but I had issues with different codes. I found this value of 3000 by trial and error where I had no issue with any code, I tried. If I would be using the R/W pin, I could read the busy flag of LCD on bit D7. There is no upper limit for DELAY parameter.

lcdKomut() is a function for sending command and lcdVeri() is a function for sending data. I wrote the pin values during the execution of these functions in the code comments. It is more important to have E = 0 then the value of RS pin. If E pin is not reset immediately, the data that comes after the command was sent twice, which I could not understand. I am noting this issue to discuss it detailed in a further post.


In the main() function, I sent the fundamental commands to LCD. I commented out 0x80 command because 0x01 contains 0x80 implicitly AFAIK. I sent the characters to write on the screen using lcdVeri() function. The cursor goes further by itself as long as a character is received. 0xC0 command sets the cursor to beginning of second line. If there would be more than two lines on LCD, different commands should be used.

I am now finishing the post because it is about the TTL voltage values. I will publish an addendum about the pins, about the issue I had at the end and other LCD commands.