These scopes often appear on Ebay with broken or bad screens. And even if the screen is working it gives me a headache... So I decided to replace CRT with a color TFT screen. A document called 'TDS520B component service manual' can be downloaded from Tektronix which contains the schematics of the controller board. There are several different controller boards but the schematics are 99% the same.
The display system is basically a 640x480 VGA display. There is a catch though. There are two display planes. Which plane is active can be detected by the 5th display data bit (NDATA4). But that is not all... The scope uses different modes for normal and persistent display so the color palette needs to be changed depending on the mode. I didn't bother to see if there is a pin to determine the mode. It can be detected by keeping track which colors are used.
Now the problem is to translate all this into signals a TFT display understands. A TFT display generally needs a clock, hsync, vsync, data enable and color data. I looked into several programmable logic solutions and found the Xilinx XL9500 series are cheap and up to the job. I've attached a schematic as a PDF. The PCB I made need some rewiring so I didn't attach that. Below is the VHDL code:
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.numeric_std.all;
---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;
-- mode_color : in STD_LOGIC;
-- mode_invert : in STD_LOGIC;
entity lcdconv is
Port ( pclk : in STD_LOGIC;
vsync : in STD_LOGIC;
hsync : in STD_LOGIC;
ndata : in STD_LOGIC_VECTOR (4 downto 0);
pixclk : out STD_LOGIC;
r : out STD_LOGIC_VECTOR (2 downto 0);
g : out STD_LOGIC_VECTOR (2 downto 0);
b : out STD_LOGIC_VECTOR (2 downto 0);
hs : out STD_LOGIC;
vs : out STD_LOGIC;
enab : out STD_LOGIC);
end lcdconv;
architecture Behavioral of lcdconv is
type Tcolor_table is array(0 to 15) of integer;
--Standard color table (used for text plane)
--index 0=background
--index 1=CH1 V/div
--index 2=CH2 V/div
--index 3=CH3 V/div
--index 4=CH4 V/div
--index 5=Ref
--index 6=Math
--index 7=All text, menus, time/div, trigger level, square brackets screen position
--index 8=help background v1.1
--index 9=??
--index 10=help text v1.1
--index 11=graticule
--index 12=??
--index 13=Tek logo
--index 14=Red stripe on Tek logo
--index 15=??
--text plane colors
-- 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
constant color_red : Tcolor_table := (7, 6 ,0 ,0 ,6 ,6, 0, 1, 0, 7, 7, 5, 7, 0, 7, 0);
constant color_green : Tcolor_table := (7, 0 ,3 ,5 ,3 ,6, 6, 1, 5, 4, 7, 5, 0, 3, 3, 0);
constant color_blue : Tcolor_table := (7, 0 ,6 ,5 ,6 ,0, 0, 1, 5, 4, 7, 5, 0, 7, 0, 0);
--Signal color table (used for text plane)
--index 0=background
--index 1=CH1 non selected
--index 2=CH2 non selected
--index 3=CH3 non selected
--index 4=CH4 non selected
--index 5=Ref signal
--index 6=Math signal
--index 7=unused
--index 8=unused
--index 9=CH1 selected
--index 10=CH2 selected
--index 11=CH3 selected
--index 12=CH4 selected
--index 13=ref
--index 14=math
--index 15=collision
-- 1 2 3 4 r m 1 2 3 4 r m
-- 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
constant color_red_sig : Tcolor_table := (7, 6, 0 ,0 ,6 ,6, 0, 0, 4, 7, 2, 0, 7, 7, 0, 5);
constant color_green_sig : Tcolor_table := (7, 0 ,3 ,6 ,3 ,6, 6, 0, 4, 0, 4, 7, 4, 7, 7, 5);
constant color_blue_sig : Tcolor_table := (7, 0 ,6 ,6 ,6 ,0, 0, 7, 4, 0, 7, 7, 7, 0, 0, 5);
-- Persistence color table (a rainbow)
-- 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
constant color_red_pers : Tcolor_table := (7, 0 ,0 ,0 ,0 ,0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7);
constant color_green_pers : Tcolor_table := (7, 0 ,1 ,2 ,3 ,4, 5, 6, 7, 6, 5, 4, 3, 2, 1, 0);
constant color_blue_pers : Tcolor_table := (7, 7 ,6 ,5 ,4 ,3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0);
signal video_in_pos : std_logic_vector(4 downto 0); --video in positive
signal vsync_pos : std_logic;
signal hsync_pos : std_logic;
signal vsync_delayed : std_logic; --inverted vsync_pos delayed by 1 cycle
signal hsync_delayed : std_logic; --inverted hsync_pos delayed by 1 cycle
signal vsync_pulse : std_logic; --active for 1 cycle at the start of vsync
signal hsync_pulse : std_logic; --active for 1 cycle at the start of vsync
signal line_counter : std_logic_vector(8 downto 0);
signal pixel_counter : std_logic_vector(9 downto 0);
--counter used to detect whether the display is using 15 shades or 5 shades
signal shades_counter: std_logic_vector(3 downto 0);
signal use_15_shades : std_logic; --1 if in persistent mode
signal de_int : std_logic; --internal DE signal
begin
--signal assignments
video_in_pos <= ndata;
vsync_pos <= not vsync;
hsync_pos <= not hsync;
enab <= de_int;
pixclk <= pclk;
--planes_out <= use_15_shades;
main: process(pclk)
begin
if rising_edge(pclk) then
--edge detector delay
vsync_delayed <= not vsync_pos;
hsync_delayed <= not hsync_pos;
--egde detector
vsync_pulse <= vsync_pos and vsync_delayed;
hsync_pulse <= hsync_pos and hsync_delayed;
--line counter
if vsync_pulse='1' then
line_counter <= (others=> '0'); --reset
--decrement shades counter for each refresh
if shades_counter>0 then
shades_counter <= shades_counter-1;
end if;
else
if hsync_pulse='1' then
line_counter <= line_counter +1;
end if;
end if;
--pixel counter
if hsync_pulse='1' then
pixel_counter <= (others =>'0');
else
pixel_counter <= pixel_counter +1;
end if;
if (line_counter>=27) and (line_counter<(480+27)) then
if (pixel_counter>=(40+96)) and (pixel_counter <(640+40+96)) then
de_int <='1';
else
de_int <='0';
end if;
else
de_int <='0';
end if;
if (video_in_pos(4)='1') and (de_int='1') then
--The scope is drawing a pixel which is part of a signal trace.
--Now detect if it is using 15 shades or 5 shades
if (video_in_pos(3 downto 0)=8) or (video_in_pos(3 downto 0)=7) then
if shades_counter<15 then
shades_counter <= shades_counter+1; --count up if not at the limit
end if;
end if;
end if;
--make the signal which defines the number of shades to use
if shades_counter/=0 then
use_15_shades <='1';
else
use_15_shades <='0';
end if;
--Create video output. This looks a bit obfusticated with the
--loops but the loop is actually a way to use a signal (vector)
--as an array index.
if (video_in_pos(4)='1')then
--drawing signal plane
if use_15_shades='1' then
--persistent mode
for i in 0 to 15 loop
if i=video_in_pos(3 downto 0) then
r<=std_logic_vector(to_unsigned(color_red_pers(i),r'length));
g<=std_logic_vector(to_unsigned(color_green_pers(i),g'length));
b<=std_logic_vector(to_unsigned(color_blue_pers(i),b'length));
end if;
end loop;
else
--signal colors
for i in 0 to 15 loop
if i=video_in_pos(3 downto 0) then
r<=std_logic_vector(to_unsigned(color_red_sig(i),r'length));
g<=std_logic_vector(to_unsigned(color_green_sig(i),g'length));
b<=std_logic_vector(to_unsigned(color_blue_sig(i),b'length));
end if;
end loop;
end if;
else
--text plane
for i in 0 to 15 loop
if i=video_in_pos(3 downto 0) then
r<=std_logic_vector(to_unsigned(color_red(i),r'length));
g<=std_logic_vector(to_unsigned(color_green(i),g'length));
b<=std_logic_vector(to_unsigned(color_blue(i),b'length));
end if;
end loop;
end if;
--copy vsync and hsync
vs <= vsync;
hs <= hsync;
end if;
end process;
end Behavioral;
The mechanical bit is a tricky. The display needs to be aligned so its is in the plastic opening. I used a sheet of 1.5mm aluminium which holds the backlight inverter and the PCB with the PLD. I used 5mm standoffs between the aluminium plate and the display to get to about the right height. Fortunately CRT screens have large mechanical tolerances so the front bezel is designed to deal with that.
I've put some images on Imageshack:
http://imageshack.us/g/854/img0482xd.jpg/These are the display and inverter I've used:
http://www.ebay.com/itm/TOSHIBA-TFT-COLOR-LCD-DISPLAY-LTA065A041F-6-5-1-Stk-/310405207108http://www.ebay.com/itm/CCFL-Inverter-LXMG1617-05-41-Single-Lamp-3-5W-80KHz-10P-/310405208948The seller probably still has many of these displays and inverters. When I thought I'd bought the last one he relisted... The datasheets can be found on internet.