Low res monochrome Hitachi LCD driver


Recommended Posts

A friend of mine found a junked Hitachi LCD and gave it to me to see if I could get it going. The LCD is an older type, monochrome low resolution. I couldn't find a datasheet for it or pinouts so I went old school and tracked down datasheets for the chips on board and did some reverse engineering of the schematic using a continuity meter. A very good datasheet resource is the "Hitachi LCD Controller Driver LSI Data Book" which is a compendium of a large number of Hitachi LCD controller chips.

According to the datasheet, the large chips on board are six HD61200 column shifters (IC3,4,5 and IC 6,7,8) and two HD61203 row shifters (IC 1,2). The unpopulated footprint is is a little weird with some missing pin traces (the one trace gaps on the short edge of the chip) and one less pin on one side of the chip compared to the other side. This footprint matched exactly the footprint of the HD61830 controller chip. The last unpopulated footprint of IC12 should be a SRAM chip for the IC11 controller.
The LCD has one 20 pin set of connections that just go to the unpopulated IC11 controller chip. It also has a 10 pin edge connector that connects to the outputs of the IC11 controller and the inputs of the row/column shifters. It appears the PCB was designed to work in both a smart mode (with controller) and a dumb mode (no controller) hence the two different connector options.
Because the controller chip was missing, I realised immediately that this LCD could not be driven in a static mode, where you just write data to the LCD internal memory and you can leave it alone to display that data. Instead this LCD would be more like a VGA display where you must constantly send data to it to refresh the picture.
The 10 pin connector pinout is:1   to pin 47 IC11 D1  -                         pin 42 (DR)  IC 3,4,5 (upper half of screen)2   to pin 10 IC11 FLM - pin 50 (DR)  IC 1,23   to pin  5 IC11 MB  - no connection4   to pin 11 IC11 CL1 - pin 52 (CL2) IC 1,2 and pin 37 (CL1) IC 3,4,5,6,7,85   to pin 46 IC11 CL2 -                         pin 40 (CL2) IC 3,4,5,6,7,86   to pin 48 IC11 D2  -                         pin 41 (DL)  IC       6,7,8 (lower half of screen)7   to pin 29 IC11 VCC (+5V supply)8   to pin 20 IC11 GND9   to TR1 Collector (LCD bias -10V  )10  to TR1 Base      (LCD bias enable, contrast?)


The internal chip to chip connections of the column shifters match those from the datasheet as below.


Essentially each column shifter can address 80 columns and three of them are chained together to form a 240 column shifter. There is one set for addressing the top half of the screen and another set for the bottom half of the screen. The two row shifters IC1 and IC2 then select one of the 64 rows to be addressed. Together these two chips can address 128 rows but because their inputs are connected together they address the entire screen (both top and bottom of screen simultaneously) so the entire screen is refreshed in 64 cycles.
In cycle 1, row 1 and row 65 are selected and a line is drawn through the top and bottom column shifters, in cycle two row 2 and row 66 is selected, etc, through to cycle 64 where row 64 and row 128 are selected. So the LCD has a resolution of 240x128 but can be throught of as two independent 240x64 areas stacked together.
Input D1 sends serial pixel data to the top half of the LCD (top column shifters) and D2 to the bottom. CL2 is the LCD master clock which can go up to 2.5Mhz according to the datasheet. CL1 is the signal that latches data from the shifter outputs to the display registers, MB pin is not connected to anything and FLM is the input to the row shifters. At the start of each frame a 1 is shifted into the row shifters through FLM then zeroes are shifted for the remaining 63 cycles. This way a single bit travels through the both top 64 and bottom 64 rows selecting one row at a time in each half of the screen.
Once I understood the basic function of the shifters I decided to hook it up the a Papilio One FPGA as the ideal driver hardware. Please note that the LCD panel runs off 5V whereas the FPGA outputs are 3.3V, however since the LCD does not output signals to the FPGA, it is quite safe to drive the 5V LCD inputs with 3.3V from the FPGA without using level translators.
I decided to display a picture on the LCD and I picked a dithered bilevel picure of Lena.
 I cropped it to 256x128 resolution so it can be easily stored and displayed to the LCD without performing additional math. If I had stored the picture in its more compact 240x128 native LCD resolution I'd have to convert the LCD vertical and horizontal coordinates to ROM addresses via some math such as address = v + h*240 whereas by storing the picture as two ROMs of 256x64 bytes the v counter can directly index ROM address lines 10..5, the top bits of the h counter can index ROM address lines 4..0 and the bottom three bits of the h counter index a bit within the currently addressed ROM byte. 
The top half 256x64 of the 256x128 picture is stored into one 8x2K RAMB and the bottom 256x64 of the picture into another 8x2K RAMB. The reason for using two separate RAMBs is because the top and bottom of the image is shifted simultaneously so both RAMBs need to be accessed simultaneously. Another way of doing this would be to create a 16x2K memory and use 8 bits for the top and 8 bits for the bottom, or a 8x4K using 4 bits for top, 4 bits for bottom but any of these schemes would require the original image to be preprocessed and interlaced accordingly.
The final code to drive the LCD ended up being very simple and is shown below. One last thing to mention is that initially I got no picture at all. After of course simulating the design to make sure the signals produced are correct and then examining the final design running on the FPGA with an oscilloscope, I saw that while the inputs to the chips had valid signals, all the outputs of the row and column drivers were sitting at 5V instead of toggling. After refering to the datasheet again I realised that these drivers need a negative Vee voltage in the range -7v to -17v. This would normally be connected to the collector of transistor TR1 and the base of TR1 could adjust the amount of voltage let through the transistor forming a contrast adjustment. Since I didn't have a handy negative supply I simply soldered a 9V battery in reverse, with the positive to the system ground and the negative directly to the Vee (emitter of TR1). This then provided the necessary negative bias to allow the LCD to display a picture. The contrast is fairly low due to the 9V battery not supplying the ideal bias voltage, I guess -12V would have been better.
-- Driver for LCD type LMG6351PLYR, 240x128 resolution, monochromelibrary ieee;	use ieee.std_logic_1164.all;	use ieee.std_logic_unsigned.all;	use ieee.numeric_std.all;library unisim;	use unisim.vcomponents.all;entity lcd_top isport (	RST_I					: in  std_logic; -- active high reset	CLK_I					: in  std_logic; -- master clock	--	D1_O					: out std_logic; -- data to    top half of lcd column shifter	D2_O					: out std_logic; -- data to bottom half of lcd column shifter	CL1_O					: out std_logic; -- clock	CL2_O					: out std_logic; -- latch display data	FLM_O					: out std_logic; -- row drivers input	MB_O					: out std_logic  -- not used);end;architecture RTL of lcd_top is	signal rst      : std_logic := '0';	signal clk      : std_logic := '0';	signal cl1      : std_logic := '0';	signal cl2      : std_logic := '0';	signal d1       : std_logic := '0';	signal d2       : std_logic := '0';	signal flm      : std_logic := '0';	signal mb       : std_logic := '0';	signal bits_top : std_logic_vector( 7 downto 0) := (others=>'0');	signal bits_bot : std_logic_vector( 7 downto 0) := (others=>'0');	signal mainctr  : std_logic_vector( 3 downto 0) := (others=>'0');	signal hctr     : std_logic_vector( 7 downto 0) := (others=>'0');	signal vctr     : std_logic_vector( 5 downto 0) := (others=>'0');begin	-- input assignments	rst   <= RST_I;	clk   <= CLK_I;	-- output assignments	FLM_O <= flm;	CL1_O <= cl1 and (not cl2);	CL2_O <= cl2;	D1_O  <= d1;	D2_O  <= d2;	MB_O  <= mb;	-- divide main 32Mhz clock by 16 to get 2Mhz clock	clk_inst : process(clk)	begin		if rising_edge(clk) then			mainctr <= mainctr + 1;		end if;	end process;	-- 2Mhz clock (max 2.5Mhz per datasheet)	cl2 <= mainctr(3);	-- horizontal counter	hctr_inst : process(cl2, rst)	begin		if (rst = '1') then			hctr <= (others=>'0');		elsif rising_edge(cl2) then			if hctr = 239 then				hctr <= (others=>'0');			else				hctr <= hctr + 1;			end if;		end if;	end process;	-- vertical counter	vctr_inst : process(cl2, rst)	begin		if (rst = '1') then			vctr <= (others=>'0');		elsif rising_edge(cl2) then			if vctr = 64 then				vctr <= (others=>'0');			elsif hctr = 239 then				vctr <= vctr + 1;			else				vctr <= vctr;			end if;		end if;	end process;	-- generate CL1 (display data latch)	cl1_inst : process(cl2)	begin		if falling_edge(cl2) then			if (hctr = 239) then				cl1 <= '1';			else				cl1 <= '0';			end if;		end if;	end process;	-- generate FLM	flm_inst : process(cl2)	begin		if falling_edge(cl2) then			if (hctr = 239) and (vctr = 0) then				flm <= '1';			else				flm <= '0';			end if;		end if;	end process;	-- image data shifters	bit_inst : process(cl2)	begin		if rising_edge(cl2) then			case hctr(2 downto 0) is				when "000" => d1 <= bits_top(7); d2 <= bits_bot(7);				when "001" => d1 <= bits_top(6); d2 <= bits_bot(6);				when "010" => d1 <= bits_top(5); d2 <= bits_bot(5);				when "011" => d1 <= bits_top(4); d2 <= bits_bot(4);				when "100" => d1 <= bits_top(3); d2 <= bits_bot(3);				when "101" => d1 <= bits_top(2); d2 <= bits_bot(2);				when "110" => d1 <= bits_top(1); d2 <= bits_bot(1);				when "111" => d1 <= bits_top(0); d2 <= bits_bot(0);				when others => null;			end case;		end if;	end process;	-- top and bottom of screen image ROMs	ROM_TOP : entity work.ROM_TOP	port map (		CLK                => clk,		ENA                => '1',		ADDR (10 downto 5) => vctr,		ADDR ( 4 downto 0) => hctr(7 downto 3),		DATA               => bits_top	);	ROM_BOT : entity work.ROM_BOT	port map (		CLK                => clk,		ENA                => '1',		ADDR (10 downto 5) => vctr,		ADDR ( 4 downto 0) => hctr(7 downto 3),		DATA               => bits_bot	);end RTL;



Link to comment
Share on other sites

I should mention, in case it's not already obvious, that since the "ROM"s are really just RAMs initialized with a static content, you could simply replace them with dual ported RAMs and then have some other logic write to the RAM space with dynamic content and it would automatically be displayed on the LCD.

Link to comment
Share on other sites

  • 2 months later...

I've just done something similar with an old Epson EG2401 display. It doesn't really have enough pixels for Lena, and a single dual-port block RAM suffices as the frame buffer. I made a buffer board to produce 5V logic for the LCD (using 74AHC541s) and used a MAX232 to generate the needed negative voltage.


I'd already done this with an AVR, and was able to get reasonably good grayscale by only setting lighter pixels for some fraction of updates. I might try that as my next step here. Avoiding flicker does require "overclocking" the display a good bit past what it was rated for, mine seems very tolerant of such abuse (I only started getting glitches when I gave it a 32 MHz pixel clock with the FPGA controller).



Source code: https://github.com/cjameshuff/vhdlstuff/blob/master/sed11x0_lcd.vhd



This post has been promoted to an article
Link to comment
Share on other sites

This is awesome! I meant to reply to this much earlier then now... I put this on the blog post list and then got pulled off in other directions before I could reply. But we just promoted your sweet hack to the showcase and posted it on the blog for others to enjoy.


I like your homemade buffer board, I love to see this type of stuff. :)


I'm actually just about to spend the rest of the afternoon working on the next revision of the Papilio I/O buffer board.


Thanks for posting your great project.



Link to comment
Share on other sites


This topic is now archived and is closed to further replies.