Sunday, July 14, 2013

16x2 Character LCD interfacing with Xilinx CPLD



This time, I'll be writing on 16x2 Character LCD interfacing with Xilinx CPLD XC9572XL. One can use any other CPLD/FPGA, but you would have to use different constraints and wiring as per your device and hardware.


Hardware:


I'll be using the CPLD Breakout Board from Numato Lab, and program the CPLD using Chipmunk JTAG programmer. Although, if you have any other hardware, you would to connect & program accordingly. The LCD i'm using works at 5V, but the CPLD works at 3.3V. I managed to get it working, by giving 5V supply to LCD, 3.3V supply to CPLD and sending signals from CPLD at 3.3V, which the LCD read successfully. Since, I don't have to read from the LCD, only have to write, this method works.

CPLD Breakout Board for Xilinx XC9572XL


Chipmunk JTAG Programmer


Connect the hardware as follows (click to open larger image):



Few things to note in above connection:
  1. I have NOT used a variable resistor for the contrast, because in most of my LCDs I get the best contrast when the LCD Contrast pin (Pin 3: VEE) is grounded. Hence i've connected it to ground.
  2. The R/W pin of LCD (Pin 5) is connected to ground, since we are only writing to LCD, and not reading from it.
  3. The LCD is getting 5V supply from Chipmunk
  4. The CPLD is getting 3.3V supply from Chipmunk JTAG header.
  5. The JTAG pins are connected for programming. They can be removed after the CPLD is programmed. I prefer to keep them as is.
Only for CPLD Breakout Board & Chipmunk Users:
  1.  BE SURE to set the jumpers properly. In my case, I actually spent 3 days trying to figure out the problem in code/wiring/LCD. The problem was: The 'CLK' jumper on CPLD Breakout Board was not set. Hence, the CPLD was not getting any clock signal! <Facepalm>
  2. Pin labelled '1' of CPLD Breakout Board is also connected to Oscillator, for clock signal, so we can't use that pin, making the wiring slightly untidy. 

CODE:

The VHDL code for this was adapted from this article at PyroElectro.com.


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity lcd_orig is
    Port ( 
			  --Data Pins
			  LCD_DATA : out  STD_LOGIC_VECTOR (7 downto 0);
				
			  --Control Pins	
           LCD_ENABLE : out  STD_LOGIC;
           
           LCD_RS : out  STD_LOGIC;
			  
			  
           LED1 : out  STD_LOGIC;
           LED2 : out  STD_LOGIC;
			  
			  --Clock
           CLK : in  STD_LOGIC);
end lcd_orig;
 
ARCHITECTURE behavior of lcd_orig IS

type state_type is (	S0, S1, S2, S3, S4, S5, S6, S7, S8, S9, 
							S10, S11, S12, S13, S14, S15, S16, S17,
							S18, S19, S20, S21, S22, S23, S24, S25,
							S26, S27, IDLE);
signal current_state: state_type;
signal a : std_logic := '0';
signal b : std_logic := '0';



BEGIN

LED1 <= a;
LED2 <= b;

PROCESS(CLK)
VARIABLE cnt: INTEGER RANGE 0 TO 1750000;
BEGIN

if rising_edge(CLK) then

	--Count Clock Ticks
	IF(cnt = 1750000)THEN		
		cnt := 0;
		a <= '0';
		b <= '1';
	ELSE
		cnt := cnt  + 1;
	END IF;

	--Slowly Move Into Next States
	IF(cnt = 1500000)THEN		
	--Next State Logic
		a <='1';
		b <='0';
		case current_state is
		
-------------------Function Set-------------------
			 when S0 =>
				current_state <= S1;
				
				LCD_DATA		<= x"38";
				
				LCD_ENABLE	<= '0';
				
				LCD_RS		<= '0';

			 when S1 =>
				current_state <= S2;

				LCD_DATA		<= x"38";
				
				LCD_ENABLE	<= '1';
				
				LCD_RS		<= '0';
				
			 when S2 =>
				current_state <= S3;

				LCD_DATA		<= x"38";
				
				LCD_ENABLE	<= '0';
				
				LCD_RS		<= '0';

-------------------Reset Display-------------------				
			 when S3 =>
				current_state <= S4;

				LCD_DATA		<= "00000001";
				
				LCD_ENABLE	<= '0';
				
				LCD_RS		<= '0';
				
			 when S4 =>
				current_state <= S5;

				LCD_DATA		<= "00000001";
				
				LCD_ENABLE	<= '1';
				
				LCD_RS		<= '0';	
				
			 when S5 =>
				current_state <= S6;				
				
				LCD_DATA		<= "00000001";
				
				LCD_ENABLE	<= '0';
				
				LCD_RS		<= '0';

-------------------Display On-------------------				
			 when S6 =>
				current_state <= S7;					

				LCD_DATA		<= "00001110";
				
				LCD_ENABLE	<= '0';
				
				LCD_RS		<= '0';
				
			 when S7 =>
				current_state <= S8;
	
				LCD_DATA		<= "00001110";
				
				LCD_ENABLE	<= '1';
				
				LCD_RS		<= '0';
				
			 when S8 =>
				current_state <= S9;	

				LCD_DATA		<= "00001110";
				
				LCD_ENABLE	<= '0';
				
				LCD_RS		<= '1';
				
-------------------Write 'C'-------------------				
			 when S9 =>
				current_state <= S10;			

				LCD_DATA		<= x"43";
				
				LCD_ENABLE	<= '1';
				
				LCD_RS		<= '1';		
				
			 when S10 =>
				current_state <= S11;

				LCD_DATA		<= x"43";
				
				LCD_ENABLE	<= '0';
				
				LCD_RS		<= '1';		
				
-------------------Write 'P'-------------------				
			 when S11 =>
				current_state <= S12;			

				LCD_DATA		<= x"50";
				
				LCD_ENABLE	<= '1';
				
				LCD_RS		<= '1';		
				
			 when S12 =>
				current_state <= S13;

				LCD_DATA		<= x"50";
				
				LCD_ENABLE	<= '0';
				
				LCD_RS		<= '1';		
-------------------Write 'L'-------------------				
			 when S13 =>
				current_state <= S14;			

				LCD_DATA		<= x"4C";
				
				LCD_ENABLE	<= '1';
				
				LCD_RS		<= '1';		
				
			 when S14 =>
				current_state <= S15;

				LCD_DATA		<= x"4C";
				
				LCD_ENABLE	<= '0';
				
				LCD_RS		<= '1';		
-------------------Write 'D'-------------------				
			 when S15 =>
				current_state <= S16;			

				LCD_DATA		<= x"44";
				
				LCD_ENABLE	<= '1';
				
				LCD_RS		<= '1';		
				
			 when S16 =>
				current_state <= S17;

				LCD_DATA		<= x"44";
				
				LCD_ENABLE	<= '0';
				
				LCD_RS		<= '1';		
-------------------Write 's'-------------------				
			 when S17 =>
				current_state <= S18;			

				LCD_DATA		<= x"73";
				
				LCD_ENABLE	<= '1';
				
				LCD_RS		<= '1';		
				
			 when S18 =>
				current_state <= S19;

				LCD_DATA		<= x"73";
				
				LCD_ENABLE	<= '0';
				
				LCD_RS		<= '1';		
-------------------Write ' '-------------------				
			 when S19 =>
				current_state <= S20;			

				LCD_DATA		<= x"20";
				
				LCD_ENABLE	<= '1';
				
				LCD_RS		<= '1';		
				
			 when S20 =>
				current_state <= S21;

				LCD_DATA		<= x"20";
				
				LCD_ENABLE	<= '0';
				
				LCD_RS		<= '1';		
-------------------Write ':'-------------------				
			 when S21 =>
				current_state <= S22;			

				LCD_DATA		<= x"3A";
				
				LCD_ENABLE	<= '1';
				
				LCD_RS		<= '1';		
				
			 when S22 =>
				current_state <= S23;

				LCD_DATA		<= x"3A";
				
				LCD_ENABLE	<= '0';
				
				LCD_RS		<= '1';		
-------------------Write 'D'-------------------				
			 when S23 =>
				current_state <= S24;			

				LCD_DATA		<= x"44";
				
				LCD_ENABLE	<= '1';
				
				LCD_RS		<= '1';		
				
			 when S24 =>
				current_state <= S25;

				LCD_DATA		<= x"44";
				
				LCD_ENABLE	<= '0';
				
				LCD_RS		<= '1';
-------------------Write ' '-------------------				
			 when S25 =>
				current_state <= S26;			

				LCD_DATA		<= x"20";
				
				LCD_ENABLE	<= '1';
				
				LCD_RS		<= '1';		
				
			 when S26 =>
				current_state <= S27;

				LCD_DATA		<= x"20";
				
				LCD_ENABLE	<= '0';
				
				LCD_RS		<= '1';
				
			 when S27=>
				current_state <= IDLE;				
				
			 when IDLE	=>
				current_state <= IDLE;
				
		    when others =>
				current_state <= IDLE;		 
	
		end case;	

	END IF;

end if;
END PROCESS;

END behavior;


The User Constraints File code:



# 1  -- VSS ie GND
# 2  -- VCC ie +5V
# 3  -- VEE ie Contrast, GND
# 4  -- RS   --> Pin 40
# 5  -- R/W  
# 6  -- E    --> Pin 43
# 7  -- D0   --> Pin 44
# 8  -- D1   --> Pin 2
# 9  -- D2   --> Pin 3
# 10 -- D3   --> Pin 5
# 11 -- D4   --> Pin 6
# 12 -- D5   --> Pin 7
# 13 -- D6   --> Pin 8
# 14 -- D7   --> Pin 12
# 15 -- LED+
# 16 -- LED-

NET "LCD_RS"       LOC = "P40"  ;
NET "LCD_ENABLE"   LOC = "P43"  ;

NET "LCD_DATA[0]"  LOC = "P44" ;
NET "LCD_DATA[1]"  LOC = "P2" ;
NET "LCD_DATA[2]"  LOC = "P3" ;
NET "LCD_DATA[3]"  LOC = "P5" ;
NET "LCD_DATA[4]"  LOC = "P6" ;
NET "LCD_DATA[5]"  LOC = "P7" ;
NET "LCD_DATA[6]"  LOC = "P8" ;
NET "LCD_DATA[7]"  LOC = "P12" ;


NET "CLK"  LOC = "P1";

NET "LED1"  LOC = "P39"  ;
NET "LED2"  LOC = "P38"  ;

Final Result:





10 comments:

  1. sir, can u explain how u have downloaded the vhdl program into cpld using chipmunk jtag

    ReplyDelete
  2. There are 3 main steps:
    [1.] Setup Perl environment for using the Chipmunk Program code: I installed Strawberry Perl on Windows, and then installed Win32::SerialPort module for it from CPAN.
    [2.] Create the .SVF file for the project using Xilinx: Follow here: http://dangerousprototypes.com/docs/Export_%28X%29SVF_from_Xilinx_ISE_Webpack
    [3.] Setup the connections for the JTAG according to the connection diagram in my above article (If using another CPLD board, modify the connections accordingly) and then program it by using the command "perl XC9572XL.pl COMx file.svf", where "XC9572XL.pl" is the perl file provided by numato (here: http://svn.numato.cc/filedetails.php?repname=numatocc&path=%2Fjtag%2Fchipmunk%2Ftrunk%2Fapp%2Fscripts%2FXC9572XL%2FXC9572XL.pl) and "file.svf" is the svf file for your project, created in step [2]

    Wish u success!
    I'll be writing a detailed blogpost for this in few days!

    ReplyDelete
    Replies
    1. sir, thank u for the reply.......i had completed the step 1 and 2....bt the chipmunk jtag is not getting detected in my laptop so there is no com port visible for implementing the command "perl XC9572XL.pl COMx file.svf"............simply executing this command results in an msg "culd not open port specified at line 60 of XC9572XL.pl"......plz help me in solving this issue

      Delete
    2. Try re-installing the device drivers and ensure that you don't have the Firmware Upgrade (FWUP) Jumper set on the Chipmunk.

      Delete
    3. sir, FWUP jumper is not set ....... tried re installing usb drivers ... also tried with another chipmunk jtag.... uploaded bootloader pgm and chipmunk.hex pgm given along with jtag using pickit3 .......bt all showing same result device is not at all detecting ... is there any specific driver for chipmunk.....

      Delete
  3. Hello!
    I've been planning on doing something like this as one of my first CPLD projects.... this is an inspiration! Mind if I ask, how much of the CPLD did this code use up? That is one thing I hope to get a good feel for some day.
    Thanks,

    - Steve

    ReplyDelete
    Replies
    1. Hello Steve!
      Its a pretty inefficient implementation as it is meant for quick demo. It can be greatly improved.
      Here is the Xilinx CPLD Fitter Report's Resources Summary:
      Macrocells Used: 60/72 (84%)
      Pterms Used: 195/360 (55%)
      Registers Used: 59/72 (82%)
      Pins Used: 13/34 (39%)
      Function Block Inputs Used: 166/216 (77%)

      -Rohit

      Delete
  4. A gigantic moonlike of recommendation, keep moving on. miele w3037 review

    ReplyDelete
  5. Hello!
    Why your code don't have rw?
    have a nice day

    ReplyDelete
  6. Nice post. Here you can find a verilog example. https://kenosys.in/blog/2015/09/25/16x2-lcd-interface-with-fpga/

    ReplyDelete