mirror of
https://github.com/antonblanchard/microwatt.git
synced 2026-03-06 11:03:25 +00:00
arty a7: Add an interface for a TFT LCD touchscreen
This adds an interface for an Arduino-compatible LCD touchscreen. The screen module plugs directly on to the Arduino/chipKit shield connector on the Arty A7. Unfortunately, the slightly strange way the resistive touchscreen is brought out (connected to the D0, D1, RS and CS pins) combined with the 200 ohm protection resisters on the Arty board mean that some hardware hacks to the module are necessary. I rewired mine so that D0 and D1 are on the A4 and A5 pins and the reset is where D0 was (shield I/O 8). This interface is suitable for boards with a HX8347 driver chip. The timing may not be quite suitable for other driver chips. The interface is a byte which can be read and written at 0xc8050000, containing an index register, and a 1-8 byte data register at 0xc8050008. Reading at offsets 1 to 7 from those addresses yields the same value as at offset 0. Writing 64 bits to the data register writes the bytes at offset 1, 0, 3, 2, 5, 4, 7, 6 in that order to the driver chip. This allows pixel data to be transferred using 64-bit writes, ending up in the frame buffer in the expected order (for 16-bit pixels, the driver chip expects MS byte then LS byte). 32-bit writes do 1, 0, 3, 2, and 16-bit writes do 1, 0. The touchscreen support so far is a 1-byte register containing bits to set RS, D0, D1 and CS high or low or make them tri-state. There is nothing to do analog conversions of the signal levels at this stage. Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
This commit is contained in:
214
fpga/arty-lcd-ts.vhdl
Normal file
214
fpga/arty-lcd-ts.vhdl
Normal file
@@ -0,0 +1,214 @@
|
||||
library ieee;
|
||||
use ieee.std_logic_1164.all;
|
||||
use ieee.numeric_std.all;
|
||||
|
||||
library work;
|
||||
use work.wishbone_types.all;
|
||||
|
||||
-- Interface for LCD/touchscreen connected to Arduino-compatible socket on Arty A7
|
||||
entity lcd_touchscreen is
|
||||
port (
|
||||
clk : in std_ulogic;
|
||||
rst : in std_ulogic;
|
||||
wb_in : in wb_io_master_out;
|
||||
wb_out : out wb_io_slave_out;
|
||||
wb_sel : in std_ulogic;
|
||||
tp : out std_ulogic;
|
||||
|
||||
lcd_din : in std_ulogic_vector(7 downto 0);
|
||||
lcd_dout : out std_ulogic_vector(7 downto 0);
|
||||
lcd_doe : out std_ulogic;
|
||||
lcd_doe0 : out std_ulogic;
|
||||
lcd_doe1 : out std_ulogic;
|
||||
lcd_rd : out std_ulogic; -- note active low
|
||||
lcd_wr : out std_ulogic; -- note active low
|
||||
lcd_rs : out std_ulogic;
|
||||
lcd_rsoe : out std_ulogic;
|
||||
lcd_cs : out std_ulogic; -- note active low
|
||||
lcd_csoe : out std_ulogic;
|
||||
lcd_rst : out std_ulogic -- note active low
|
||||
);
|
||||
end entity lcd_touchscreen;
|
||||
|
||||
architecture rtl of lcd_touchscreen is
|
||||
|
||||
type state_t is (idle, prep1, prep2, writing, wr_pause, reading, rd_recovery);
|
||||
|
||||
signal state : state_t;
|
||||
signal delay : unsigned(5 downto 0);
|
||||
signal ack : std_ulogic;
|
||||
signal idle1 : std_ulogic;
|
||||
signal idle2 : std_ulogic;
|
||||
|
||||
signal rs : std_ulogic;
|
||||
signal rsoe : std_ulogic;
|
||||
signal cs : std_ulogic;
|
||||
signal csoe : std_ulogic;
|
||||
signal d0 : std_ulogic;
|
||||
signal doe0 : std_ulogic;
|
||||
signal doe1 : std_ulogic;
|
||||
signal d1 : std_ulogic;
|
||||
signal tsctrl : std_ulogic;
|
||||
|
||||
signal wr_data : std_ulogic_vector(31 downto 0);
|
||||
signal rd_data : std_ulogic_vector(7 downto 0);
|
||||
signal wr_sel : std_ulogic_vector(3 downto 0);
|
||||
signal req_wr : std_ulogic;
|
||||
|
||||
-- Assume touchscreen is connected as follows:
|
||||
-- X+ -> A5 (D1), X- -> A3 (CS), Y+ -> A2 (RS), Y- -> A4 (D0)
|
||||
|
||||
begin
|
||||
-- for now; should make sure it is at least 10us wide
|
||||
lcd_rst <= not rst;
|
||||
|
||||
wb_out.dat <= rd_data & rd_data & rd_data & rd_data;
|
||||
wb_out.ack <= ack;
|
||||
wb_out.stall <= '0' when state = idle else '1';
|
||||
|
||||
lcd_doe0 <= doe0;
|
||||
lcd_doe1 <= doe1;
|
||||
lcd_rs <= rs;
|
||||
lcd_rsoe <= rsoe;
|
||||
lcd_cs <= cs;
|
||||
lcd_csoe <= csoe;
|
||||
|
||||
tp <= tsctrl;
|
||||
|
||||
process (clk)
|
||||
begin
|
||||
if rising_edge(clk) then
|
||||
ack <= '0';
|
||||
idle2 <= idle1;
|
||||
if rst = '1' then
|
||||
state <= idle;
|
||||
delay <= to_unsigned(0, 6);
|
||||
rd_data <= (others => '0');
|
||||
lcd_rd <= '1';
|
||||
lcd_wr <= '1';
|
||||
cs <= '1';
|
||||
csoe <= '1';
|
||||
rs <= '0';
|
||||
rsoe <= '1';
|
||||
lcd_doe <= '0';
|
||||
doe0 <= '0';
|
||||
doe1 <= '0';
|
||||
d0 <= '0';
|
||||
d1 <= '0';
|
||||
idle1 <= '0';
|
||||
idle2 <= '0';
|
||||
tsctrl <= '0';
|
||||
elsif delay /= "000000" then
|
||||
delay <= delay - 1;
|
||||
else
|
||||
case state is
|
||||
when idle =>
|
||||
req_wr <= wb_in.we;
|
||||
wr_data <= wb_in.dat;
|
||||
wr_sel <= wb_in.sel;
|
||||
if idle2 = '1' then
|
||||
-- delay this one cycle after entering idle
|
||||
lcd_doe <= '0';
|
||||
doe0 <= '0';
|
||||
doe1 <= '0';
|
||||
end if;
|
||||
idle1 <= '0';
|
||||
if wb_in.cyc = '1' and wb_in.stb = '1' and wb_sel = '1' then
|
||||
if wb_in.sel = "0000" then
|
||||
ack <= '1';
|
||||
else
|
||||
if wb_in.we = '1' or wb_in.adr(2) = '1' then
|
||||
ack <= '1';
|
||||
end if;
|
||||
if wb_in.adr(2) = '0' then
|
||||
tsctrl <= '0';
|
||||
csoe <= '1';
|
||||
cs <= '0'; -- active low
|
||||
rsoe <= '1';
|
||||
rs <= wb_in.adr(1);
|
||||
doe0 <= '0';
|
||||
doe1 <= '0';
|
||||
state <= prep1;
|
||||
else
|
||||
tsctrl <= '1';
|
||||
idle2 <= '0';
|
||||
rd_data <= rsoe & rs & doe0 & d0 & doe1 & d1 & csoe & cs;
|
||||
if wb_in.we = '1' and wb_in.sel(0) = '1' then
|
||||
rsoe <= wb_in.dat(7);
|
||||
rs <= wb_in.dat(6);
|
||||
doe0 <= wb_in.dat(5);
|
||||
d0 <= wb_in.dat(4);
|
||||
lcd_dout(0) <= wb_in.dat(4);
|
||||
doe1 <= wb_in.dat(3);
|
||||
d1 <= wb_in.dat(2);
|
||||
lcd_dout(1) <= wb_in.dat(2);
|
||||
csoe <= wb_in.dat(1);
|
||||
cs <= wb_in.dat(0);
|
||||
end if;
|
||||
end if;
|
||||
end if;
|
||||
else
|
||||
if tsctrl = '0' then
|
||||
cs <= '1';
|
||||
end if;
|
||||
end if;
|
||||
when prep1 =>
|
||||
lcd_doe <= req_wr;
|
||||
doe0 <= req_wr;
|
||||
doe1 <= req_wr;
|
||||
if req_wr = '1' then
|
||||
if wr_sel(1 downto 0) /= "00" then
|
||||
if wr_sel(1) = '1' then
|
||||
lcd_dout <= wr_data(15 downto 8);
|
||||
wr_sel(1) <= '0';
|
||||
else
|
||||
lcd_dout <= wr_data(7 downto 0);
|
||||
wr_sel(0) <= '0';
|
||||
end if;
|
||||
else
|
||||
if wr_sel(3) = '1' then
|
||||
lcd_dout <= wr_data(31 downto 24);
|
||||
wr_sel(3) <= '0';
|
||||
else
|
||||
lcd_dout <= wr_data(23 downto 16);
|
||||
wr_sel(2) <= '0';
|
||||
end if;
|
||||
end if;
|
||||
end if;
|
||||
state <= prep2;
|
||||
when prep2 =>
|
||||
if req_wr = '1' then
|
||||
lcd_wr <= '0'; -- active low
|
||||
state <= writing;
|
||||
delay <= to_unsigned(1, 6);
|
||||
else
|
||||
lcd_rd <= '0';
|
||||
state <= reading;
|
||||
delay <= to_unsigned(35, 6);
|
||||
end if;
|
||||
when writing =>
|
||||
-- last cycle of writing state
|
||||
lcd_wr <= '1';
|
||||
if wr_sel = "0000" then
|
||||
state <= idle;
|
||||
idle1 <= '1';
|
||||
else
|
||||
state <= wr_pause;
|
||||
end if;
|
||||
when wr_pause =>
|
||||
state <= prep1;
|
||||
when reading =>
|
||||
-- last cycle of reading state
|
||||
lcd_rd <= '1';
|
||||
rd_data <= lcd_din;
|
||||
ack <= '1';
|
||||
state <= rd_recovery;
|
||||
delay <= to_unsigned(6, 6);
|
||||
when rd_recovery =>
|
||||
state <= idle;
|
||||
end case;
|
||||
end if;
|
||||
end if;
|
||||
end process;
|
||||
|
||||
end architecture;
|
||||
@@ -166,6 +166,26 @@ set_property IOB true [get_cells -hierarchical -filter {NAME =~*.litesdcard2/sdp
|
||||
set_property -dict { PACKAGE_PIN D2 IOSTANDARD LVCMOS33 PULLUP TRUE } [get_ports { i2c_rtc_d }];
|
||||
set_property -dict { PACKAGE_PIN H2 IOSTANDARD LVCMOS33 PULLUP TRUE } [get_ports { i2c_rtc_c }];
|
||||
|
||||
################################################################################
|
||||
# TFT LCD shield (arduino-compatible)
|
||||
# hacked to swap the LCD_RST and LCD_D0 lines, and put LCD_D1 on A5
|
||||
################################################################################
|
||||
|
||||
set_property -dict { PACKAGE_PIN N15 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { lcd_rst }];
|
||||
#set_property -dict { PACKAGE_PIN M16 IOSTANDARD LVCMOS33 } [get_ports { lcd_tp }];
|
||||
set_property -dict { PACKAGE_PIN P14 IOSTANDARD LVCMOS33 } [get_ports { lcd_d[2] }];
|
||||
set_property -dict { PACKAGE_PIN T11 IOSTANDARD LVCMOS33 } [get_ports { lcd_d[3] }];
|
||||
set_property -dict { PACKAGE_PIN R12 IOSTANDARD LVCMOS33 } [get_ports { lcd_d[4] }];
|
||||
set_property -dict { PACKAGE_PIN T14 IOSTANDARD LVCMOS33 } [get_ports { lcd_d[5] }];
|
||||
set_property -dict { PACKAGE_PIN T15 IOSTANDARD LVCMOS33 } [get_ports { lcd_d[6] }];
|
||||
set_property -dict { PACKAGE_PIN T16 IOSTANDARD LVCMOS33 } [get_ports { lcd_d[7] }];
|
||||
set_property -dict { PACKAGE_PIN F5 IOSTANDARD LVCMOS33 PULLUP TRUE } [get_ports { lcd_rd }]; # A0
|
||||
set_property -dict { PACKAGE_PIN D8 IOSTANDARD LVCMOS33 PULLUP TRUE } [get_ports { lcd_wr }]; # A1
|
||||
set_property -dict { PACKAGE_PIN C7 IOSTANDARD LVCMOS33 } [get_ports { lcd_rs }]; # A2
|
||||
set_property -dict { PACKAGE_PIN E7 IOSTANDARD LVCMOS33 } [get_ports { lcd_cs }]; # A3
|
||||
set_property -dict { PACKAGE_PIN D7 IOSTANDARD LVCMOS33 } [get_ports { lcd_d[0] }]; # A4
|
||||
set_property -dict { PACKAGE_PIN D5 IOSTANDARD LVCMOS33 } [get_ports { lcd_d[1] }]; # A5
|
||||
|
||||
################################################################################
|
||||
# Arduino/chipKIT shield connector
|
||||
################################################################################
|
||||
|
||||
@@ -30,6 +30,7 @@ entity toplevel is
|
||||
HAS_UART1 : boolean := true;
|
||||
USE_LITESDCARD : boolean := false;
|
||||
HAS_GPIO : boolean := true;
|
||||
USE_LCD : boolean := true;
|
||||
NGPIO : natural := 32
|
||||
);
|
||||
port(
|
||||
@@ -140,6 +141,14 @@ entity toplevel is
|
||||
i2c_rtc_d : inout std_ulogic;
|
||||
i2c_rtc_c : inout std_ulogic;
|
||||
|
||||
-- LCD display interface
|
||||
lcd_d : inout std_ulogic_vector(7 downto 0);
|
||||
lcd_rs : out std_ulogic;
|
||||
lcd_cs : out std_ulogic;
|
||||
lcd_rd : out std_ulogic;
|
||||
lcd_wr : out std_ulogic;
|
||||
lcd_rst : out std_ulogic;
|
||||
|
||||
-- DRAM wires
|
||||
ddram_a : out std_ulogic_vector(13 downto 0);
|
||||
ddram_ba : out std_ulogic_vector(2 downto 0);
|
||||
@@ -185,6 +194,7 @@ architecture behaviour of toplevel is
|
||||
signal wb_ext_is_dram_init : std_ulogic;
|
||||
signal wb_ext_is_eth : std_ulogic;
|
||||
signal wb_ext_is_sdcard : std_ulogic;
|
||||
signal wb_ext_is_lcd : std_ulogic;
|
||||
|
||||
-- DRAM main data wishbone connection
|
||||
signal wb_dram_in : wishbone_master_out;
|
||||
@@ -213,6 +223,9 @@ architecture behaviour of toplevel is
|
||||
-- for conversion from non-pipelined wishbone to pipelined
|
||||
signal wb_sddma_stb_sent : std_ulogic;
|
||||
|
||||
-- LCD touchscreen connection
|
||||
signal wb_lcd_out : wb_io_slave_out := wb_io_slave_out_init;
|
||||
|
||||
-- Status LED
|
||||
signal led_b_pwm : std_ulogic_vector(3 downto 0) := (others => '0');
|
||||
signal led_r_pwm : std_ulogic_vector(3 downto 0) := (others => '0');
|
||||
@@ -289,6 +302,7 @@ begin
|
||||
HAS_UART1 => HAS_UART1,
|
||||
HAS_SD_CARD => USE_LITESDCARD,
|
||||
HAS_SD_CARD2 => USE_LITESDCARD,
|
||||
HAS_LCD => USE_LCD,
|
||||
HAS_GPIO => HAS_GPIO,
|
||||
NGPIO => NGPIO
|
||||
)
|
||||
@@ -336,6 +350,7 @@ begin
|
||||
wb_ext_is_dram_init => wb_ext_is_dram_init,
|
||||
wb_ext_is_eth => wb_ext_is_eth,
|
||||
wb_ext_is_sdcard => wb_ext_is_sdcard,
|
||||
wb_ext_is_lcd => wb_ext_is_lcd,
|
||||
|
||||
-- DMA wishbone
|
||||
wishbone_dma_in => wb_sddma_in,
|
||||
@@ -828,10 +843,54 @@ begin
|
||||
disk_activity <= sdc0_activity or sdc1_activity;
|
||||
end generate;
|
||||
|
||||
-- LCD touchscreen on arduino-compatible pins
|
||||
has_lcd : if USE_LCD generate
|
||||
signal lcd_dout : std_ulogic_vector(7 downto 0);
|
||||
signal lcd_doe : std_ulogic;
|
||||
signal lcd_doe0 : std_ulogic;
|
||||
signal lcd_doe1 : std_ulogic;
|
||||
signal lcd_rso : std_ulogic;
|
||||
signal lcd_rsoe : std_ulogic;
|
||||
signal lcd_cso : std_ulogic;
|
||||
signal lcd_csoe : std_ulogic;
|
||||
signal tp : std_ulogic;
|
||||
begin
|
||||
lcd0 : entity work.lcd_touchscreen
|
||||
port map (
|
||||
clk => system_clk,
|
||||
rst => soc_rst,
|
||||
wb_in => wb_ext_io_in,
|
||||
wb_out => wb_lcd_out,
|
||||
wb_sel => wb_ext_is_lcd,
|
||||
tp => tp,
|
||||
|
||||
lcd_din => lcd_d,
|
||||
lcd_dout => lcd_dout,
|
||||
lcd_doe => lcd_doe,
|
||||
lcd_doe0 => lcd_doe0,
|
||||
lcd_doe1 => lcd_doe1,
|
||||
lcd_rd => lcd_rd,
|
||||
lcd_wr => lcd_wr,
|
||||
lcd_rs => lcd_rso,
|
||||
lcd_rsoe => lcd_rsoe,
|
||||
lcd_cs => lcd_cso,
|
||||
lcd_csoe => lcd_csoe,
|
||||
lcd_rst => lcd_rst
|
||||
);
|
||||
-- lcd_d(0), lcd_d(1), lcd_rs, lcd_cs are used for the touchscreen
|
||||
-- interface and hence have individual output enables.
|
||||
lcd_d(0) <= lcd_dout(0) when lcd_doe0 = '1' else 'Z';
|
||||
lcd_d(1) <= lcd_dout(1) when lcd_doe1 = '1' else 'Z';
|
||||
lcd_d(7 downto 2) <= lcd_dout(7 downto 2) when lcd_doe = '1' else (others => 'Z');
|
||||
lcd_rs <= lcd_rso when lcd_rsoe = '1' else 'Z';
|
||||
lcd_cs <= lcd_cso when lcd_csoe = '1' else 'Z';
|
||||
end generate;
|
||||
|
||||
-- Mux WB response on the IO bus
|
||||
wb_ext_io_out <= wb_eth_out when wb_ext_is_eth = '1' else
|
||||
wb_sdcard_out when wb_ext_is_sdcard = '1' and wb_ext_io_in.adr(13) = '0' else
|
||||
wb_sdcard2_out when wb_ext_is_sdcard = '1' and wb_ext_io_in.adr(13) = '1' else
|
||||
wb_lcd_out when wb_ext_is_lcd = '1' else
|
||||
wb_dram_ctrl_out;
|
||||
|
||||
status_led_colour : process(all)
|
||||
|
||||
@@ -139,6 +139,10 @@ filesets:
|
||||
uart16550:
|
||||
depend : [":microwatt:uart16550"]
|
||||
|
||||
lcdts:
|
||||
files:
|
||||
- fpga/arty-lcd-ts.vhdl : {file_type : vhdlSource-2008}
|
||||
|
||||
targets:
|
||||
nexys_a7:
|
||||
default_tool: vivado
|
||||
@@ -315,7 +319,7 @@ targets:
|
||||
|
||||
arty_a7-100-nodram:
|
||||
default_tool: vivado
|
||||
filesets: [core, arty_a7, soc, fpga, debug_xilinx, uart16550, xilinx_specific, litesdcard]
|
||||
filesets: [core, arty_a7, soc, fpga, debug_xilinx, uart16550, xilinx_specific, litesdcard, lcdts]
|
||||
parameters :
|
||||
- memory_size
|
||||
- ram_init_file
|
||||
@@ -336,7 +340,7 @@ targets:
|
||||
|
||||
arty_a7-100:
|
||||
default_tool: vivado
|
||||
filesets: [core, arty_a7, soc, fpga, debug_xilinx, litedram, liteeth, uart16550, xilinx_specific, litesdcard]
|
||||
filesets: [core, arty_a7, soc, fpga, debug_xilinx, litedram, liteeth, uart16550, xilinx_specific, litesdcard, lcdts]
|
||||
parameters:
|
||||
- cpus
|
||||
- memory_size
|
||||
@@ -344,6 +348,7 @@ targets:
|
||||
- use_litedram=true
|
||||
- use_liteeth=true
|
||||
- use_litesdcard
|
||||
- use_lcd
|
||||
- disable_flatten_core
|
||||
- no_bram
|
||||
- spi_flash_offset=4194304
|
||||
@@ -570,6 +575,12 @@ parameters:
|
||||
paramtype : generic
|
||||
default : false
|
||||
|
||||
use_lcd:
|
||||
datatype : bool
|
||||
description : Use LCD touchscreen interface
|
||||
paramtype : generic
|
||||
default : false
|
||||
|
||||
uart_is_16550:
|
||||
datatype : bool
|
||||
description : Use 16550-compatible UART from OpenCores
|
||||
|
||||
8
soc.vhdl
8
soc.vhdl
@@ -34,6 +34,7 @@ use work.wishbone_types.all;
|
||||
-- 0xc8020000: LiteEth CSRs (*)
|
||||
-- 0xc8030000: LiteEth MMIO (*)
|
||||
-- 0xc8040000: LiteSDCard CSRs
|
||||
-- 0xc8050000: LCD touchscreen interface
|
||||
|
||||
-- (*) LiteEth must be a single aligned 32KB block as the CSRs and MMIOs
|
||||
-- are actually decoded as a single wishbone which LiteEth will
|
||||
@@ -95,6 +96,7 @@ entity soc is
|
||||
DCACHE_TLB_NUM_WAYS : natural := 2;
|
||||
HAS_SD_CARD : boolean := false;
|
||||
HAS_SD_CARD2 : boolean := false;
|
||||
HAS_LCD : boolean := false;
|
||||
HAS_GPIO : boolean := false;
|
||||
NGPIO : natural := 32
|
||||
);
|
||||
@@ -116,6 +118,7 @@ entity soc is
|
||||
wb_ext_is_dram_init : out std_ulogic;
|
||||
wb_ext_is_eth : out std_ulogic;
|
||||
wb_ext_is_sdcard : out std_ulogic;
|
||||
wb_ext_is_lcd : out std_ulogic;
|
||||
|
||||
-- external DMA wishbone with 32-bit data/address
|
||||
wishbone_dma_in : out wb_io_slave_out := wb_io_slave_out_init;
|
||||
@@ -686,6 +689,7 @@ begin
|
||||
wb_ext_is_dram_csr <= '0';
|
||||
wb_ext_is_eth <= '0';
|
||||
wb_ext_is_sdcard <= '0';
|
||||
wb_ext_is_lcd <= '0';
|
||||
end if;
|
||||
if do_cyc = '1' then
|
||||
-- Decode I/O address
|
||||
@@ -715,6 +719,10 @@ begin
|
||||
slave_io := SLAVE_IO_EXTERNAL;
|
||||
io_cycle_external <= '1';
|
||||
wb_ext_is_sdcard <= '1';
|
||||
elsif std_match(match, x"--05-") and HAS_LCD then
|
||||
slave_io := SLAVE_IO_EXTERNAL;
|
||||
io_cycle_external <= '1';
|
||||
wb_ext_is_lcd <= '1';
|
||||
else
|
||||
io_cycle_none <= '1';
|
||||
end if;
|
||||
|
||||
Reference in New Issue
Block a user