Dx display

From Hamsterworks Wiki!

Jump to: navigation, search

This FPGA Project was completed in September 2012.

Dx display.png

This project lights one segment in each display, and turns all the LEDS on. The board is a TM1638 display board from Deal Extreme (http://dx.com/p/8x-digital-tube-8x-key-8x-double-color-led-module-81873). The only documentation I had available to me was http://code.google.com/p/tm1638-library/ - an Arduino Library in C++.

Tested on a Digilent Nexys2 at both 5V and 3.3V VCC, and the Digilent Basys2 at 3.3V, with transfers slightly under 1MHz.

After this project was seen on Hackaday Paul posted a link to a translated datasheet: https://docs.google.com/file/d/0B84N2SrJaybwZTgxYjM4ZmEtY2EyZi00YjVjLWIzOTctYTlhMjJkM2MxMTBl/edit?pli=1 Strangely enough the link only works for me in Internet Explorer and not Chrome :-/ It looks like the data rate is 1Mb/s - as I divide the clock by 64 for each half of the interface's clock signal I'm running at about 0.390MHz for a 50MHz clock.

Contents

Connections (as viewed from component side

Important Note The layout of the pins that is silk-screened on the board is as viewed from the back of the board. VCC is the pin soldered in the hole with the square outline.

Strobe1 controls this board, the other strobes pass through to the 'out' connector.

Gnd Vcc
Clk Data
Strobe1 Strobe2
Strobe3 Strobe4
Strobe5 Strobe6

Device summary

Only 4 commands are needed to use the display functions. I haven't worked on reading the buttons:

Commands

bits Description
010000000 Address mode, auto increment
010000100 Address mode, single address
1100aaaa dddddddd At address aaaa write dddddddd - multiple bytes of data can be transferred
1000abbb Display control - a = active, bbb = brightness

Register map

Address Value
0000 7seg 1
0001 LED 1, bit 0 = red, bit 1 = green
0010 7seg 2
0010 LED 2, bit 0 = red, bit 1 = green
0100 7seg 3
0011 LED 3, bit 0 = red, bit 1 = green
0110 7seg 4
0111 LED 4, bit 0 = red, bit 1 = green
1000 7seg 5
1001 LED 5, bit 0 = red, bit 1 = green
1010 7seg 6
1011 LED 6, bit 0 = red, bit 1 = green
1100 7seg 7
1101 LED 7, bit 0 = red, bit 1 = green
1110 7seg 8
1111 LED 8, bit 0 = red, bit 1 = green

design_top.vhd

----------------------------------------------------------------------------------
-- Company: 
-- Engineer: Mike Field <hamster@snap.net.nz>
-- 
-- Module Name:    design_top - Behavioral 
-- Description: Top level for testing the DX display
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity design_top is
    Port ( clk      : in  STD_LOGIC;
           d_clk    : out  STD_LOGIC;
           d_strobe : out  STD_LOGIC;
           d_data   : out  STD_LOGIC);
end design_top;

architecture Behavioral of design_top is

   COMPONENT dx_display
   PORT(
      clk     : IN std_logic;
      reset   : IN std_logic;
      adv     : IN std_logic;          
      byte    : OUT std_logic_vector(7 downto 0);
      endCmd  : OUT std_logic;
      newData : OUT std_logic
      );
   END COMPONENT;
   
   COMPONENT dx_display_xmit
   PORT(
      clk      : IN std_logic;
      reset    : IN std_logic;
      byte     : IN std_logic_vector(7 downto 0);
      endCmd   : IN std_logic;
      newData  : IN std_logic;
      d_strobe : OUT std_logic;
      d_clk    : OUT std_logic;
      d_data   : OUT std_logic;          
      adv      : OUT std_logic
      );
   END COMPONENT;

   signal byte       : std_logic_vector(7 downto 0);
   signal endCmd   : std_logic;
   signal newData  : std_logic;
   signal adv        : std_logic;
   signal reset    : std_logic;
   signal resetShift : std_logic_vector(15 downto 0) := (others =>'1');
   
begin
   reset <= resetShift(0);

   Inst_dx_display: dx_display PORT MAP(
      clk     => clk,
      reset    => reset,
      byte    => byte,
      endCmd  => endCmd,
      newData => newData,
      adv      => adv
   );

   Inst_dx_display_xmit: dx_display_xmit PORT MAP(
      clk       => clk,
      reset    => reset,
      byte       => byte,
      endCmd    => endCmd,
      newData    => newData,
      adv       => adv,
      d_strobe => d_strobe,
      d_clk    => d_clk,
      d_data    => d_data 
   );
	
reset_proc: process(clk)
   begin
      if rising_edge(clk) then
         resetShift <= '0' & resetShift(15 downto 1);
      end if;
   end process;
end Behavioral;

dx_display.vhd

----------------------------------------------------------------------------------
-- Engineer: Mike Field   <hamster@snap.net.nz>
-- 
-- Description:  Driver for the DealExteme display board, 
--  8 x 7 segs
--  8 x bi-colour LED
--  8 x buttons
--
-- Dependencies: None
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity dx_display is
    Port ( clk     : in  STD_LOGIC;
           reset   : in  STD_LOGIC;
           byte     : out STD_LOGIC_VECTOR(7 downto 0);
           endCmd  : out STD_LOGIC;
           newData : out STD_LOGIC;
           adv     : in  STD_LOGIC);
end dx_display;

architecture Behavioral of dx_display is
   signal counter : std_logic_vector(4 downto 0) := (others => '0');
   signal nextcounter : unsigned(4 downto 0);
      
begin
   nextcounter <= unsigned(counter) + 1;
   
data_proc: process(counter)
   begin
      case counter is
         when  "00000" => byte <= x"40"; endCmd <= '1'; newData <= '1';	-- Set address mode - auto inc
         when  "00001" => byte <= x"8C"; endCmd <= '1'; newData <= '1';	-- Turn display on, brightness 4 of 7
         when  "00010" => byte <= x"C0"; endCmd <= '0'; newData <= '1';	-- Write at the left display
			
         when  "00011" => byte <= x"01"; endCmd <= '0'; newData <= '1'; -- 7seg1 - Top center Segment
         when  "00100" => byte <= x"01"; endCmd <= '0'; newData <= '1'; -- LED1 red
         when  "00101" => byte <= x"02"; endCmd <= '0'; newData <= '1';	-- 7seg2 - Top right segment
         when  "00110" => byte <= x"02"; endCmd <= '0'; newData <= '1';	-- LED2 green
			
         when  "00111" => byte <= x"04"; endCmd <= '0'; newData <= '1';	-- 7seg3 - Bottom right segment
         when  "01000" => byte <= x"01"; endCmd <= '0'; newData <= '1';	-- LED3 red
         when  "01001" => byte <= x"08"; endCmd <= '0'; newData <= '1';	-- 7seg4 - Bottom center segment
         when  "01010" => byte <= x"02"; endCmd <= '0'; newData <= '1';	-- LED4 green
			
         when  "01011" => byte <= x"10"; endCmd <= '0'; newData <= '1'; -- 7seg5 - Bottom left segment
         when  "01100" => byte <= x"01"; endCmd <= '0'; newData <= '1'; -- LED5 red
         when  "01101" => byte <= x"20"; endCmd <= '0'; newData <= '1';	-- 7seg6 - Top left segment
         when  "01110" => byte <= x"02"; endCmd <= '0'; newData <= '1';	-- LED6 green
			
         when  "01111" => byte <= x"40"; endCmd <= '0'; newData <= '1';	-- 7seg6 - Top right segment
         when  "10000" => byte <= x"01"; endCmd <= '0'; newData <= '1';	-- LED7 red
         when  "10001" => byte <= x"80"; endCmd <= '0'; newData <= '1';	-- 7seg6 - Center segment
         when  "10010" => byte <= x"02"; endCmd <= '1'; newData <= '1';	-- LED8 green
			
         when  others => byte <= x"FF"; endCmd <= '1'; newData <= '0';  -- End of data / idle
      end case;
   end process;
   
clk_proc: process(clk)
   begin
      if rising_edge(clk) then
         if reset = '1' then 
            counter <= (others => '0');
         elsif adv = '1' and counter /= "11111" then
            counter <= std_logic_vector(nextcounter);
         end if;
      end if;
   end process;
end Behavioral;

dx_display_xmit.vhd

This needs a good re-write to make it more compact, but it works for now!

----------------------------------------------------------------------------------
-- Engineer: Mike Field <hamster@snap.net.nz>
-- 
-- Module Name:    dx_display_xmit - Behavioral 
-- Description:    Drive the serial bus for the display
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity dx_display_xmit is
    Port ( clk      : in  STD_LOGIC;
           reset    : in  STD_LOGIC;
           byte     : in  STD_LOGIC_VECTOR (7 downto 0);
           endCmd   : in  STD_LOGIC;
           newData  : in  STD_LOGIC;
           adv      : out STD_LOGIC;
           d_strobe : out STD_LOGIC;
           d_clk    : out STD_LOGIC;
           d_data   : out STD_LOGIC);
end dx_display_xmit;

architecture Behavioral of dx_display_xmit is
   signal thisEndCmd     : std_logic;
   signal thisByte       : std_logic_vector(7 downto 0)  := (others => '0');
   signal bitsLeftToSend : std_logic_vector(6 downto 0)  := (others => '0');
   signal state          : std_logic_vector(2 downto 0)  := (others => '0');
   signal divider        : std_logic_vector(63 downto 0) := x"1000000000000000";
begin
   
clk_proc: process(clk)
   begin
      if rising_edge(clk) then
         divider <= divider(0) & divider(63 downto 1);
         adv      <= '0';
         
         if reset = '1'  then
            thisByte   <= (others => '0');
            thisEndCmd <= endCmd;
            state      <= (others => '0');
            d_strobe <= '1';
            d_clk    <= '1';
            d_data   <= '1';
            divider  <= x"1000000000000000";
         elsif divider(0) = '1' then
            d_strobe <= '1';
            d_clk    <= '1';
            d_data   <= '1';
            case state is 
               when "000" =>      -- Idle, without an open command
                  if newData = '1' then
                     state    <= std_logic_vector(unsigned(state)+1);
                     thisByte <= byte;
                     thisEndCmd <= endCmd;
                     adv       <= '1';

                     d_strobe <= '0';
                     d_clk    <= '1';
                     d_data   <= '1';
                  else
                     d_strobe <= '1';
                     d_clk    <= '1';
                     d_data   <= '1';
                  end if;
                  bitsLeftToSend <= (others => '1');
                  
               when "001" =>   -- transfer a bot
                  state    <= std_logic_vector(unsigned(state)+1);
                  d_strobe <= '0';
                  d_clk    <= '0';
                  d_data   <= thisByte(0);
               when "010" =>
                  if bitsLeftToSend(0) = '1' then -- Still got a bit to send?
                     state    <= std_logic_vector(unsigned(state)-1);
                  elsif thisEndCmd = '1' then
                     state    <= "011";   -- close off command
                  else
                     state    <= "100";   -- keep command open
                  end if;
                  d_strobe <= '0';
                  d_clk    <= '1';
                  d_data   <= thisByte(0);
                  thisByte <= '1' & thisByte(7 downto 1);
                  bitsLeftToSend <= '0' & bitsLeftToSend(6 downto 1);
                  
               when "011" => --- ending the command by rasing d_strobe, the going back to idle state
                  state    <= "000";
                  d_strobe <= '1';
                  d_clk    <= '1';
                  d_data   <= thisByte(0);
                  
               when "100" =>      -- Waiting for data, withan open command
                  d_strobe <= '0';
                  d_clk    <= '1';
                  d_data   <= '1';
                  if newData = '1' then
                     state      <= "001"; -- start transfering bits
                     thisByte   <= byte;
                     thisEndCmd <= endCmd;
                     adv       <= '1';
                     bitsLeftToSend <= (others => '1');
                  end if;
               
               when others =>      -- performa a reset
                  thisByte <= (others => '0');
                  state    <= (others => '0');
                  d_strobe <= '1';
                  d_clk    <= '1';
                  d_data   <= '1';
            end case;
         end if;
      end if;
   end process;
end Behavioral;

nexys2.ucf

NET "clk" TNM_NET = clk;
TIMESPEC TS_clk = PERIOD "clk" 50 MHz HIGH 50%;

##############################################################################
# Nexys2 - 12 pin connectors - top row of JA - data, clk, strobe, N/C, GND, 5V
##############################################################################
NET "clk"      LOC = "B8"  | IOSTANDARD=LVTTL; # Bank = 0, Pin name = IP_L13P_0/GCLK8, Type = GCLK, Sch name = GCLK0

NET "d_data"   LOC = "L15" | IOSTANDARD=LVTTL; # Bank = 1, Pin name = IO_L09N_1/A11, Type = DUAL, Sch name = JA1
NET "d_clk"    LOC = "K12" | IOSTANDARD=LVTTL; # Bank = 1, Pin name = IO_L11N_1/A9/RHCLK1, Type = RHCLK/DUAL, Sch name = JA2
NET "d_strobe" LOC = "L17" | IOSTANDARD=LVTTL; # Bank = 1, Pin name = IO_L10N_1/VREF_1, Type = VREF, Sch name = JA3


basys2.ucf

NET "clk" TNM_NET = clk;
TIMESPEC TS_clk = PERIOD "clk" 50 MHz HIGH 50%;

########################################
# Pins for Basys2 Board - Pmod JA
########################################
NET "clk" LOC = "B8"; # Bank = 0, Signal name = MCLK
NET "clk" CLOCK_DEDICATED_ROUTE = FALSE;
NET "d_data"   LOC = "B2"  | DRIVE = 2; # | PULLUP ; # Bank = 1, Signal name = JA1
NET "d_clk"    LOC = "A3"  | DRIVE = 2; # | PULLUP ; # Bank = 1, Signal name = JA2
NET "d_strobe" LOC = "J3"  | DRIVE = 2; # | PULLUP ; # Bank = 1, Signal name = JA3

Comparing the software vs hardware design

It is quite interesting to compare the intent of both designs. One is from a software perspective, one is from the hardware perspective. The C++ code inspired by the HDL design is much cleaner, but doesn't show the intent of the code (e.g. what is a command, what is data).

The original C++

void TM16XX::sendData(byte address, byte data)
{
  sendCommand(0x44);
  digitalWrite(strobePin, LOW);
  send(0xC0 | address);
  send(data);
  digitalWrite(strobePin, HIGH);
}

void TM16XX::sendCommand(byte cmd)
{
  digitalWrite(strobePin, LOW);
  send(cmd);
  digitalWrite(strobePin, HIGH);
}

void TM16XX::send(byte data)
{
  for (int i = 0; i < 8; i++) {
    digitalWrite(clockPin, LOW);
    digitalWrite(dataPin, data & 1 ? HIGH : LOW);
    data >>= 1;
    digitalWrite(clockPin, HIGH);
  }
}

The intent of the HDL version

void TM16XX::sendData(byte address, byte data)
{
  send(0x44,          1);
  send(0xC0 | address,0);
  send(data,          1);
}

void TM16XX::send(byte data, byte endCmd)
{
  digitalWrite(strobePin, LOW);
  for (int i = 0; i < 8; i++) {
    digitalWrite(clockPin, LOW);
    digitalWrite(dataPin, data & 1 ? HIGH : LOW);
    data >>= 1;
    digitalWrite(clockPin, HIGH);
  }
  digitalWrite(strobePin, endCmd);
}


Personal tools