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]
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;
}
#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;
}
This 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 |
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;
}
#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.
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.