1
\$\begingroup\$

EDIT: I have made the changes suggested with the &gate and the 555 timer to get the (E) enable pulse. I hooked up a debounced switch so I could walk through step by step. This highlighted some logic errors in my code. I also hooked up the R/W pin to the EPROM at a very high address so that I could create what is effectively a NOP. The section I changed is only the buildData function.

uint16_t buildData(uint16_t code)                                                                                                                                                                                                             
{                                                                                                                                                                                                                                             
  uint16_t displayOn=0x010C;    //                                                                                                                                                                                                            
  uint16_t initalize=0x0103;    // clear screen move cursor home                                                                                                                                                                              
  //uint16_t mvcrs=0x011D;      // move cursor right                                                                                                                                                                                          
  uint16_t nop=0xF000;  // Read from LCD (Effectively a NOP instruction)                                                                                                                                                                      
                                                                                                                                                                                                                                              
  switch (code)                                                                                                                                                                                                                               
    {                                                                                                                                                                                                                                         
    case 0x000  ...     0x0FF   : return displayOn;                                                                                                                                                                                         
    case 0x100  ...     0x1FF   : return initalize;                                                                                                                                                                          
    case 0x200  ...     0x2FF   : return digits[code % 10];                                                                                                                                                                                   
    case 0x300  ...     0x3FF   : return digits[(code/10) % 10];                                                                                                    
    case 0x400  ...     0x4FF   : return digits[(code/100) % 10];                                                                                                                                                                             
    default                     : return nop;                                                                                                                                                                                                 
    }                                                                                                                                                                                                                                         
}    

The enable pulses seem correct, and it correctly brings the RW pin high to get a NOP for the upper address ranges.

But I still have the problem that it isn't outputing numbers! Only solid blocks of pixels, of a block and 1/2 of pixels.

I've added the datasheet into the question if anyone needs to look at the commands. I think I might need to change the value for initialization, since I'm not sure you can do "return home" and "clear display" with the same write.

https://datasheetspdf.com/pdf-file/519148/CA/LCD-1602A/1

=====================

EDIT: I don't know the protocol? I have made changes to the setup originally described (but it still doesn't work) and I'm wondering if I should ask a new question, or continue with this one.

I'll continue with this until someone tells me otherwise.

I have added another 74HC107 in order to give myself more clock pulses. I am now using the original 555 timer output for the enable pin and three other pulses for pins A8-A10 on the EPROM. I believe the signals are as per the picture. enter image description here But I don't have an oscilloscope, so I cannot be sure, but after using 4 LEDs for configuration it seems to be correct.

I have modified the code slightly based on the feedback below. The only change is in the "buildData" function:

uint16_t buildData(uint16_t code)                                                                                                                                                                                                             
{                                                                                                                                                                                                                                             
  uint16_t displayOn=0x0F0C;    //                                                                                                                                                                                                            
  uint16_t initalize=0x0F03;    // clear screen move cursor home                                                                                                                                                                              
  //uint16_t mvcrs=0x0F1D;      // move cursor right                                                                                                                                                                                          
                                                                                                                                                                                                                                              
  switch (code)                                                                                                                                                                                                                               
    {                                                                                                                                                                                                                                         
    case 0      ...     255     : return digits[code % 10];                                                                                                                                                                                   
    case 256    ...     511     : return digits[(code/10) % 10];                                                                                                                                                                              
    case 512    ...     767     : return digits[(code/100) % 10];                                                                                                                                                                             
    case 768    ...     1279    : return initalize;                                                                                                                                                                                           
    default                     : return displayOn;                                                                                                                                                                                           
    }                                                                                                                                                                                                                                         
}     

It appears to be doing more now because a second line appears on the LCD of 1/2 blocks when the FS pin is high (writing data, not instruction)enter image description here

and then clears down when it gets an instruction (FS pin low).

enter image description here

I am encouraged because no one here has yet said I was crazy, and it couldn't be done. I seem to be close, but I'm not getting numbers only blocks.

Just to clarify the LCD pinout:

  • 1 (VSS) <> GND
  • 2 (VDD) <> 5V
  • 3 (VO) <> 5V (through variable resister)
  • 4 (RS) <> D8 Eprom (Data pin 8)
  • 5 (R/W) <> GND (I always write, never read)
  • 6 (E) <> 555 Timer output
  • 7 (DB0) <> D0 Eprom
  • 8 (DB1) <> D1 Eprom
  • 9 (DB2) <> D2 Eprom
  • 10 (DB3) <> D3 Eprom
  • 11 (DB4) <> D4 Eprom
  • 12 (DB5) <> D5 Eprom
  • 13 (DB6) <> D6 Eprom
  • 14 (DB7) <> D7 Eprom
  • 15 (A) <> 5v
  • 16 (K) <> GND

==== Original Question Below ========

I'm attempting to translate binary numbers into digital numbers on an LCD screen. However, I'm not using a microcontroller, but trying to do it with some IC chips and an EPROM. The first chip is a simple 555 timer which gives me a pulse. This is fed into a 74HC107 which divides the signal into 3 parts, and I use it for a binary counter which is fed into the EPROM on address lines A8,A9,A10, with the A0-A7 being the binary number I want to convert. The image shows it without power, but it works electrically.

The output is based on the address, and it ties D8 (data out) into RS and will be high or low depending on if it is an instruction in the lower 8 bits or data. I have single stepped it through, and it appears to be sending the values I programmed into the EPROM (code below) correctly. I have checked that the hex file I am writing contains 16 bit values. But nothing appears on the screen. I've swapped all the components one after another and checked the signals coming out.

In theory, it should work, I've seen YT videos of people putting text to an LCD with just a series of switches, so I don't know why this method doesn't work. But it doesn't.

Any suggestions would be greatly appreciated.

enter image description here

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/i2c-dev.h>
#include <stdlib.h>

#define scRom1024 16384 

// Bit patterns for the digits 0..9
uint16_t  digits[] =
  { 0x30, //0
    0x31, //1
    0x32, //2
    0x33, //3
    0x34, //4
    0x35, //5
    0x36, //6
    0x37, //7
    0x38, //8
    0x39, //9
  };

uint16_t firstEpromFile[scRom1024]; 
uint16_t buildData(uint16_t code);

// Write binary to path
void WriteBinary(const char *path, const void *data, const unsigned int dataSize)
{
  FILE *fp = fopen(path, "wb");
  if (!fp)
    {
      fprintf(stderr, "fopen() failed for '%hn'\n", firstEpromFile);
    }
  fwrite(data, 2, dataSize, fp);
  fclose(fp);
  printf("%d bytes written to \"%s\"\n", dataSize, path);
}

int main ()
{
  printf ("Build LCD control EPROM Data\n");
  for (uint16_t addr=0;addr<scRom1024;addr++)
    {
      firstEpromFile[addr]=buildData(addr);
    }
  WriteBinary("eprom_lcd.bin",firstEpromFile,scRom1024);
  return 0;
}

uint16_t buildData(uint16_t code)
{
  uint16_t initalize=0x0F03;    // clear screen move cursor home
  uint16_t mvcrs=0x0F1D;    // move cursor right

  switch (code)
    {
    case 0  ...     255 : return digits[code % 10];
    case 256    ...     511     : return mvcrs;
    case 512    ...     767 : return digits[(code/10) % 10];
    case 768    ...     1023    : return mvcrs;
    case 1024   ...     1279    : return digits[(code/100) % 10];
    default         : return initalize;
    }                   
}


EDIT FOR COMMENTS: I attempted to draw a schematic with Kicad, but the learning curve is too high to knock something out. So I have tried to overlay info from the datasheets and take close up photos to show the wiring. I think I would need a couple of months to figure out KiCad. :(

I grounded the RW pin, because I'm never going to read from the lcd, only write to it.

enter image description here enter image description here enter image description here

\$\endgroup\$
14
  • 1
    \$\begingroup\$ @rick getting things like this to work takes many hours. If it doesn't work, you have to check everything, power, timing, logic levels. Get a cheap logic analyzer \$\endgroup\$
    – Voltage Spike
    Feb 20 at 22:34
  • 1
    \$\begingroup\$ You said E is always enabled. That can't work. The E pin must have a enable pulse to write data in. That is why you should provide the schematics, because it is unclear how RS, RW and E are connected just by looking at the picture. To me it looks like RS is grounded, RW is connected to supply, and E ia controlled by EEPROM - which would not work either. \$\endgroup\$
    – Justme
    Feb 21 at 7:44
  • 2
    \$\begingroup\$ Where are your decoupling capacitors? \$\endgroup\$
    – winny
    Feb 25 at 13:46
  • 2
    \$\begingroup\$ @RickDearman You do need them, especially as you're on a breadboard. You could start with a small one (100 nF is fine) at each IC's power pin, and a larger (say 100 uF) at the main power input. They provide clean power to your ICs, and there's lots of clocks and signals going around your board to cause problems. It might not fix it, but it will certainly remove one very common source of problems. \$\endgroup\$
    – awjlogan
    Feb 25 at 14:19
  • 1
    \$\begingroup\$ If you do not have a scope, you must slow it to timing be an observative. Do the clock pulses manualy instead of 555. Or use 555 with higher cap to longer the cycles. \$\endgroup\$ Feb 26 at 21:31
1
\$\begingroup\$

At least the LCD module init is missing a command to actually turn the display on. A simple clear screen will not turn it on. And the clear screen command takes quite long to execute, somewhere between 1.5 to 2 milliseconds.

There might be other problems as well.

The move right command may not be needed either, as the display will power up to defaults that the cursor moves right after writing a charater.

Edit: In the comments you said the Enable pin E is wired directly to supply. This will not work. There must be a high going pulse, as the falling edge of the pulse ends a bus operation like a command write or data write. The module just sees one infinitely long bus write that never ends, so it will not reveive any commands or data.

\$\endgroup\$
3
  • \$\begingroup\$ I will check into turning on the display. I didn't see that in the datasheet, so i will go back and check. \$\endgroup\$ Feb 21 at 2:10
  • \$\begingroup\$ I have added a 0x0F0C (set display on) to the upper memory and it does now show both lines on the LCD, but it is only showing nothing, or if I max out the contrast it will show blocks. I tried adding the E directly to the clock pulse, but after a little thought I worked out that it wouldn't be enabled during the lower 255 addresses, which is when I'm trying to send data. I don't know how I could do continuous clock pulses with this configuration to send an enable pulse for each address. This was why I wired it to supply. \$\endgroup\$ Feb 21 at 13:55
  • \$\begingroup\$ I have figured out a way to pulse the E pin. I'm just going to set the D16 bit to high for every address, so for every change it will pulse the signal. (That is my theory anyway) \$\endgroup\$ Feb 21 at 17:54
1
\$\begingroup\$

After modifications to the E pin and using the R/W to create a NOP, it still didn't work, however after reviewing a number of other questions related to LCD screens and looking at the header files for arduinio I discovered the initialization sequence wasn't correct. I modified the code using the init sequence examples in the datasheet. While it still isn't working correctly, it does now show some numbers rather than blobs.

uint16_t buildData(uint16_t code)                                                                                                                                                                                                             
{                                                                                                                                                                                                                                             
  uint16_t displayOn=0x010C;    //                                                                                                                                                                                                            
  uint16_t initalize=0x0130;    // 8-bit & 1 line & font 5x8                                                                                                                                                                                  
  uint16_t clearDisplay=0x0101;         // clear screen move cursor home                                                                                                                                                                      
  uint16_t nop=0xF000;  // Read from LCD (Effectively a NOP instruction)                                                                                                                                                                      
  uint16_t setFunction=0x0134;  
  uint16_t entryModeSet=0x0104;  
                                                                                                                                                                                                                                              
  switch (code)                                                                                                                                                                                                                               
    {                                                                                                                                                                                                                                         
    case 0x000  ...     0x0FF   : return displayOn; //                                                                                                                                                                                        
    case 0x100  ...     0x1FF   : return initalize; //                                                                                                                                                                                        
    case 0x200  ...     0x2FF   : return nop;                                                                                                                                                                                                 
    case 0x300  ...     0x3FF   : return setFunction;                                                                                                                                                                                         
    case 0x400  ...     0x4FF   : return nop;                                                                                                                                                                                                 
    case 0x500  ...     0x5FF   : return entryModeSet; //                                                                                                                                                                                     
    case 0x600  ...     0x6FF   : return nop;                                                                                                                                                                                                 
    case 0x700  ...     0x7FF   : return clearDisplay; // return mvcrs;                                                                                                                                                                       
    case 0x800  ...     0x8FF   : return digits[code % 10];                                                                                                                                                                                   
    case 0x900  ...     0x9FF   : return digits[(code/100) % 10];                                                                                                                                                                             
    default                     : return nop;                                                                                                                                                                                                 
    }                                                                                                                                                                                                                                         
}   

I think any issues I'm having now are input/software related.

The issue was threefold. Not using the RW pin, no/bad pulse length on the E pin, and initialisation sequence.

\$\endgroup\$
0
\$\begingroup\$

For controlling Enable I would use a clock from 555 with little time delay to let the data to be stabilized after new byte. You can change this delay to less (to about 1us), tune according your 555 frequency. I don't know what clock edge you get a new data from EEPROM, this proposed connection should work for rising edge. enter image description here

\$\endgroup\$
3
  • \$\begingroup\$ I have added an AND gate, but it doesn't appear to have made any notable difference. \$\endgroup\$ Feb 23 at 23:51
  • \$\begingroup\$ It is probably because you have to generate E after every 555 clock change (high and low also). \$\endgroup\$ Feb 24 at 9:41
  • \$\begingroup\$ And this circuit generate E only at 555 high pulse. So, from LCD perspective only every second command is received. \$\endgroup\$ Feb 24 at 9:44
0
\$\begingroup\$

You must probably achieve something like this, shorten the E pulses from both sides:

enter image description here

\$\endgroup\$
0
\$\begingroup\$

Since the 74HC107 is negative edge triggered, if you want to use a 555 signal for E you must negate it and cut beginings according to picture. Then you will get a safety time-intervals for command changes.

enter image description here

\$\endgroup\$
2
  • \$\begingroup\$ With two 74hc107s i have 4 pulses. So I suppose I should use the the first Q1 for E, then the remaining for a8-a10. But if I understand you are saying I need an inverter in order to make sure the data is set before the E is triggered? \$\endgroup\$ Feb 27 at 1:08
  • \$\begingroup\$ My idea was to use 3xJK, feed the first one with 555 and the outputs from JKs connect to A8-A10. Then also take the 555 signal, invert it and short this pulse with the AND and RC I posted before. This signal connect to E. \$\endgroup\$ Feb 27 at 1:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.