Difference between revisions of "DX TM1638 Display"
(25 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
+ | =Documents= |
||
− | I'm using the Hamster code from [http://hamsterworks.co.nz/mediawiki/index.php/Dx_display], and it works. Thanks Hamsterworks! |
||
+ | * [http://dl.dropboxusercontent.com/u/8663580/TM1638en.pdf Datasheet] |
||
+ | * [https://code.google.com/p/tm1638-library/ Google-code TM1638 library] |
||
+ | * [http://dx.com/p/8x-digital-tube-8x-key-8x-double-color-led-module-81873 DX order page, sku 81873] |
||
+ | =Hamsterworks code= |
||
− | Looks like the pinning of the DX display cable as described by Hamster has changed (or was wrong). The silkscreen on my DX display looks like this: |
||
+ | I'm using the [http://hamsterworks.co.nz/mediawiki/index.php/Dx_display VHDL code from Hamster] on my papilio board, and it works. Thanks Hamsterworks! |
||
+ | |||
+ | Looks like the pinning of the DX display cable as described by Hamster has changed (or was wrong). The silkscreen on my (ordered october 2011) DX display looks like this (this is the 'bottom view', the pins of the socket as seen from above are the mirror-image of this): |
||
{| border=1 |
{| border=1 |
||
− | | Gnd || Vcc |
||
|- |
|- |
||
+ | !pin |
||
− | | DIO || CLK |
||
+ | !func |
||
+ | !func |
||
+ | !pin |
||
|- |
|- |
||
− | | |
+ | | 2 || Gnd || Vcc || 1 |
|- |
|- |
||
− | | |
+ | | 4 || DIO || CLK || 3 |
|- |
|- |
||
− | | |
+ | | 6 || STB1 || STB0 || 5 |
+ | |- |
||
+ | | 8 || STB3 || STB2 || 7 |
||
+ | |- |
||
+ | | 10 || STB5 || STB4 || 9 |
||
|} |
|} |
||
So it looks like hamster's pinning has DIO/Data and CLK swapped, and the even Strobes were off by 2. |
So it looks like hamster's pinning has DIO/Data and CLK swapped, and the even Strobes were off by 2. |
||
Also, it's Strobe0 that controls this board (not Strobe1), the other strobes pass through to the 'out' connector. |
Also, it's Strobe0 that controls this board (not Strobe1), the other strobes pass through to the 'out' connector. |
||
− | I notice that after I power |
+ | I notice that after I power cycle (powered by USB cable) my papilio board (and the TM1638), I have to send the .bit file several times to the board. Strange. |
+ | * When setting d_clock to 32MHz/8/2, it always works the first time. |
||
+ | * Setting d_clock to 32MHz/4/2, it will not work at all |
||
I'm using this constraints.ucf file for my papilio board: |
I'm using this constraints.ucf file for my papilio board: |
||
Line 28: | Line 42: | ||
But other than that, Hamsters code works. |
But other than that, Hamsters code works. |
||
+ | |||
+ | === Commands === |
||
+ | {| border=1 |
||
+ | ! bits !! Description |
||
+ | |- |
||
+ | | 010000000 || setup TM1638 to write data to display register, auto increment |
||
+ | |- |
||
+ | | 010000010 || setup TM 1638 to read data from key scans, auto increment |
||
+ | |- |
||
+ | | 010000100 || setup TM 1638 to write data to display register, single address |
||
+ | |- |
||
+ | | 010000010 || setup TM 1638 to read data from key scans, single address |
||
+ | |- |
||
+ | | 1100aaaa dddddddd || At address aaaa write/read dddddddd - multiple bytes of data can be transferred |
||
+ | |- |
||
+ | | 1000abbb || Display control - a = active, bbb = brightness |
||
+ | |} |
||
+ | |||
+ | After sending <tt> 46</tt> the responce will be 4 bytes, key one is in bit zero (LSB) of the first byte, key two is in bit one of the second byte, etc. |
||
+ | key 5 is in bit 4 of the first byte, key 6 in bit 4 of the second byte, etc. |
||
+ | |||
+ | =VHDL code for numeric data display= |
||
+ | |||
+ | The code below can read the TM1638 key status, and show hex and decimal numbers on the display. |
||
+ | |||
+ | ==File <tt>design_top.vhdl</tt>== |
||
+ | <nowiki>--------------------------------------------------------------------------------- |
||
+ | -- Original by: Engineer: Mike Field <hamster@snap.net.nz> |
||
+ | -- Modifications by: Joost Witteveen |
||
+ | -- |
||
+ | -- |
||
+ | ---------------------------------------------------------------------------------- |
||
+ | library IEEE; |
||
+ | use IEEE.STD_LOGIC_1164.ALL; |
||
+ | use IEEE.NUMERIC_STD.ALL; |
||
+ | library work; |
||
+ | use work.types.all; |
||
+ | |||
+ | entity design_top is |
||
+ | Port ( clk : in STD_LOGIC; |
||
+ | d_clk : out STD_LOGIC; |
||
+ | d_strobe : out STD_LOGIC; |
||
+ | d_data : inout STD_LOGIC); |
||
+ | end design_top; |
||
+ | |||
+ | architecture Behavioral of design_top is |
||
+ | |||
+ | COMPONENT dx_display |
||
+ | Generic(clkDivider: integer); |
||
+ | PORT( |
||
+ | clk : IN std_logic; |
||
+ | d_strobe : OUT std_logic; |
||
+ | d_clk : OUT std_logic; |
||
+ | d_data : inOUT std_logic; |
||
+ | digits : in bytearray(0 to 7); |
||
+ | leds : in bytearray(0 to 7); |
||
+ | keybyte : out std_logic_vector(7 downto 0) |
||
+ | ); |
||
+ | END COMPONENT; |
||
+ | |||
+ | |||
+ | COMPONENT display7seg |
||
+ | PORT (digits : out bytearray; |
||
+ | data : in integer |
||
+ | ); |
||
+ | end COMPONENT; |
||
+ | signal Count : integer range 0 to 34*1000*1000 :=1000*1000-1; |
||
+ | signal digits: bytearray(0 to 7) := (others => x"00"); |
||
+ | signal leds: bytearray(0 to 7) := (others => x"00"); |
||
+ | signal data: integer range -2000000000 to 2000000000 :=0;--20000/3; |
||
+ | signal decdata: integer range -2000000000 to 2000000000 :=0;--20000/3; |
||
+ | signal tmp: std_logic_vector(31 downto 0); |
||
+ | signal keybyte: std_logic_vector(7 downto 0); |
||
+ | signal key_int:integer range 0 to 255; |
||
+ | begin |
||
+ | Inst_dx_display: dx_display |
||
+ | Generic Map(clkDivider => 16) |
||
+ | PORT MAP( |
||
+ | clk => clk, |
||
+ | d_strobe => d_strobe, |
||
+ | d_clk => d_clk, |
||
+ | d_data => d_data, |
||
+ | digits => digits, |
||
+ | leds => leds, |
||
+ | keybyte => keybyte |
||
+ | ); |
||
+ | |||
+ | Inst_display7seg:display7seg |
||
+ | PORT MAP( |
||
+ | digits => digits(0 to 1), |
||
+ | data => key_int); |
||
+ | |||
+ | Inst_display7segDec:display7seg |
||
+ | PORT MAP( |
||
+ | digits => digits(2 to 7), |
||
+ | data => decdata); |
||
+ | |||
+ | reset_proc: process(clk) |
||
+ | begin |
||
+ | if rising_edge(clk) then |
||
+ | if Count > 0 then |
||
+ | Count <=Count-1; |
||
+ | else |
||
+ | Count<=200*1000*1; |
||
+ | data<=data+1; |
||
+ | --for i in 0 to 3 loop |
||
+ | -- tmp(i*8 + 7 downto i*8) <= keydata(i); |
||
+ | --end loop; |
||
+ | --decdata<=decdata+1; |
||
+ | --tmp1<=std_logic_vector(to_unsigned(data,8)); |
||
+ | --tmp(31 downto 24) <= keydata(0); |
||
+ | --tmp(23 downto 16) <= keydata(1); |
||
+ | --tmp(15 downto 8) <= keydata(2); |
||
+ | --tmp(7 downto 0) <= keydata(3); |
||
+ | --tmp(23 downto 0)<=std_logic_vector(to_unsigned(data+1,24)); |
||
+ | --tmp(7 downto 0) <= keybyte; |
||
+ | --tmp(15 downto 8) <= keybyte; |
||
+ | --tmp(23 downto 16) <= keybyte; |
||
+ | --tmp(31 downto 24) <= keybyte; |
||
+ | --data<=to_integer(unsigned(tmp)); |
||
+ | decdata<=to_integer(to_bcd(bitarray(std_logic_vector(to_unsigned(data,20))))); |
||
+ | key_int <= to_integer((unsigned(keybyte))); |
||
+ | for i in 0 to 7 loop |
||
+ | leds(i)(0)<=keybyte(i); |
||
+ | leds(i)(1)<='0'; |
||
+ | end loop; |
||
+ | end if; |
||
+ | end if; |
||
+ | end process; |
||
+ | end Behavioral; |
||
+ | </nowiki> |
||
+ | |||
+ | ==File <tt>dx_display.vhdl</tt>== |
||
+ | <nowiki>---------------------------------------------------------------------------------- |
||
+ | -- Original by: Engineer: Mike Field <hamster@snap.net.nz> |
||
+ | -- Modifications by: Joost Witteveen |
||
+ | -- |
||
+ | -- Description: Driver for the DealExteme display board, TM1638 |
||
+ | -- 8 x 7 segs |
||
+ | -- 8 x bi-colour LED |
||
+ | -- 8 x buttons |
||
+ | ---------------------------------------------------------------------------------- |
||
+ | library IEEE; |
||
+ | use IEEE.STD_LOGIC_1164.ALL; |
||
+ | use IEEE.NUMERIC_STD.ALL; |
||
+ | library work; |
||
+ | use work.types.all; |
||
+ | |||
+ | entity dx_display is |
||
+ | Generic(clkDivider: integer); |
||
+ | PORT( |
||
+ | clk : IN std_logic; |
||
+ | d_strobe: OUT std_logic; |
||
+ | d_clk : OUT std_logic; |
||
+ | d_data : inOUT std_logic; |
||
+ | digits : in bytearray(0 to 7); |
||
+ | leds : in bytearray(0 to 7); |
||
+ | keybyte : out std_logic_vector(7 downto 0) |
||
+ | ); |
||
+ | end dx_display; |
||
+ | architecture Behavioral of dx_display is |
||
+ | signal counter : integer range 0 to 31 :=0;--std_logic_vector(5 downto 0) := (others => '0'); |
||
+ | signal counterPreClk:std_logic:='0'; |
||
+ | signal counterClk: std_logic:='0'; |
||
+ | signal nextcounter : integer range 0 to 31; --unsigned(counter'range); |
||
+ | signal byte : std_logic_vector(7 downto 0); |
||
+ | |||
+ | signal endCmd : std_logic; |
||
+ | signal newData : std_logic; |
||
+ | signal send : std_logic; |
||
+ | signal adv : std_logic; |
||
+ | signal reset : std_logic := '1'; |
||
+ | signal resetCount : integer range 0 to 32*1000*1000 :=1*100-1; |
||
+ | signal keydataone : std_logic_vector(7 downto 0); |
||
+ | constant headersize: integer := 8; |
||
+ | COMPONENT dx_display_xmit |
||
+ | Generic(clkDivider: integer); |
||
+ | Port ( |
||
+ | clk : in STD_LOGIC; |
||
+ | reset : in STD_LOGIC; |
||
+ | byte : in STD_LOGIC_VECTOR (7 downto 0); |
||
+ | keydata : inout STD_LOGIC_VECTOR (7 downto 0); |
||
+ | endCmd : in STD_LOGIC; |
||
+ | newData : in STD_LOGIC; |
||
+ | send : in STD_LOGIC; |
||
+ | adv : out STD_LOGIC; |
||
+ | d_strobe : out STD_LOGIC; |
||
+ | d_clk : out STD_LOGIC; |
||
+ | d_data : inout STD_LOGIC); |
||
+ | end component; |
||
+ | |||
+ | begin |
||
+ | --nextcounter <= unsigned(counter) + 1; |
||
+ | nextcounter <= counter +1; |
||
+ | Inst_dx_display_xmit: dx_display_xmit |
||
+ | Generic Map(clkDivider => clkDivider) |
||
+ | PORT MAP( |
||
+ | clk => clk, |
||
+ | reset => reset, |
||
+ | byte => byte, |
||
+ | keydata => keydataone, |
||
+ | endCmd => endCmd, |
||
+ | newData => newData, |
||
+ | send => send, |
||
+ | adv => adv, |
||
+ | d_strobe => d_strobe, |
||
+ | d_clk => d_clk, |
||
+ | d_data => d_data); |
||
+ | |||
+ | data_proc: process(counterClk) |
||
+ | begin |
||
+ | if counterClk'event and counterClk='1' then |
||
+ | send<= '1'; |
||
+ | newData<='1'; |
||
+ | case counter is |
||
+ | --when "000000" => byte <= x"00"; endCmd <= '1'; |
||
+ | when 0 => byte <= x"46"; endCmd <= '0'; |
||
+ | when 1 => byte <= x"00"; endCmd <= '0'; send<='0'; |
||
+ | when 2 => byte <= x"00"; endCmd <= '0'; send<='0'; |
||
+ | keybyte(0)<=keydataone(0); |
||
+ | keybyte(4)<=keydataone(4); |
||
+ | when 3 => byte <= x"00"; endCmd <= '0'; send<='0'; |
||
+ | keybyte(1)<=keydataone(0); |
||
+ | keybyte(5)<=keydataone(4); |
||
+ | when 4 => byte <= x"00"; endCmd <= '1'; send<='0'; |
||
+ | keybyte(2)<=keydataone(0); |
||
+ | keybyte(6)<=keydataone(4); |
||
+ | when 5 => byte <= x"40"; endCmd <= '1'; -- Set address mode - auto inc |
||
+ | keybyte(3)<=keydataone(0); |
||
+ | keybyte(7)<=keydataone(4); |
||
+ | when 6 => byte <= x"88"; endCmd <= '1'; -- Turn display on, lowest brightness (0) |
||
+ | when 7 => byte <= x"C0"; endCmd <= '0';-- Write at the left display |
||
+ | |||
+ | when others => |
||
+ | if counter <= 15+headersize then |
||
+ | if to_unsigned(counter,1)(0)='0' then |
||
+ | byte<=digits((counter-headersize)/2); |
||
+ | else |
||
+ | byte<=leds((counter-headersize)/2); |
||
+ | end if; |
||
+ | if counter < 15+headersize then |
||
+ | endCmd<='0'; |
||
+ | else |
||
+ | endCmd<='1'; |
||
+ | end if; |
||
+ | else |
||
+ | byte <= x"FF"; endCmd <= '1'; newData <= '0'; -- End of data / idle |
||
+ | end if; |
||
+ | end case; |
||
+ | end if; |
||
+ | end process; |
||
+ | |||
+ | clk_proc: process(clk) |
||
+ | begin |
||
+ | if rising_edge(clk) then |
||
+ | counterPreClk<='0'; |
||
+ | if reset = '1' then |
||
+ | counter <= 31; |
||
+ | elsif counter =31 then |
||
+ | counterPreClk<='1'; |
||
+ | counter <= 0; |
||
+ | elsif adv = '1' and counter < 31 then |
||
+ | counterPreClk<='1'; |
||
+ | counter <= nextcounter; |
||
+ | end if; |
||
+ | end if; |
||
+ | end process; |
||
+ | counterClk_proc:process(clk) |
||
+ | begin |
||
+ | if rising_edge(clk) then |
||
+ | counterClk<=counterPreClk; |
||
+ | end if; |
||
+ | end process; |
||
+ | reset_proc: process(clk) |
||
+ | begin |
||
+ | if rising_edge(clk) then |
||
+ | if resetCount > 0 then |
||
+ | resetCount <=resetCount-1; |
||
+ | else |
||
+ | resetCount<=1*100*1000; |
||
+ | reset<= not reset; |
||
+ | end if; |
||
+ | end if; |
||
+ | end process; |
||
+ | end Behavioral; |
||
+ | </nowiki> |
||
+ | |||
+ | ==File <tt>dx_display_xmit.vhdl</tt>== |
||
+ | <nowiki>---------------------------------------------------------------------------------- |
||
+ | -- Engineer: Mike Field <hamster@snap.net.nz> |
||
+ | -- Modified by: Joost Witteveen |
||
+ | -- |
||
+ | ---------------------------------------------------------------------------------- |
||
+ | library IEEE; |
||
+ | use IEEE.STD_LOGIC_1164.ALL; |
||
+ | use IEEE.NUMERIC_STD.ALL; |
||
+ | |||
+ | entity dx_display_xmit is |
||
+ | Generic (clkDivider: integer); |
||
+ | Port ( |
||
+ | clk : in STD_LOGIC; |
||
+ | reset : in STD_LOGIC; |
||
+ | byte : in STD_LOGIC_VECTOR (7 downto 0); |
||
+ | keydata : out STD_LOGIC_VECTOR (7 downto 0); |
||
+ | endCmd : in STD_LOGIC; |
||
+ | newData : in STD_LOGIC; |
||
+ | send : in STD_LOGIC; |
||
+ | adv : out STD_LOGIC; |
||
+ | d_strobe : out STD_LOGIC; |
||
+ | d_clk : out STD_LOGIC; |
||
+ | d_data : inout STD_LOGIC); |
||
+ | end dx_display_xmit; |
||
+ | |||
+ | architecture Behavioral of dx_display_xmit is |
||
+ | 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(3 downto 0) := (others => '0'); |
||
+ | signal divider : integer range 0 to 1023 := 0; |
||
+ | signal keydataLocal : std_logic_vector(keydata'range); |
||
+ | begin |
||
+ | clk_proc: process(clk) |
||
+ | begin |
||
+ | if rising_edge(clk) then |
||
+ | divider <= divider +1; |
||
+ | adv <= '0'; |
||
+ | if reset = '1' then |
||
+ | thisByte <= (others => '0'); |
||
+ | state <= (others => '0'); |
||
+ | d_strobe <= '1'; |
||
+ | d_clk <= '1'; |
||
+ | d_data <= '1'; |
||
+ | divider <= 0; |
||
+ | elsif divider = (clkDivider-1) then |
||
+ | divider <= 0; |
||
+ | d_strobe <= '1'; |
||
+ | d_clk <= '1'; |
||
+ | if send = '1' then |
||
+ | d_data <= '1'; |
||
+ | else |
||
+ | d_data <='Z'; |
||
+ | end if; |
||
+ | case state is |
||
+ | when "0000" => -- Idle, without an open command |
||
+ | if newData = '1' then |
||
+ | state <= std_logic_vector(unsigned(state)+1); |
||
+ | thisByte <= byte; |
||
+ | d_strobe <= '0'; |
||
+ | d_clk <= '1'; |
||
+ | else |
||
+ | d_strobe <= '1'; |
||
+ | d_clk <= '1'; |
||
+ | end if; |
||
+ | bitsLeftToSend <= (others => '1'); |
||
+ | keydatalocal<=x"55"; |
||
+ | when "0001" => -- transfer a bit |
||
+ | state <= std_logic_vector(unsigned(state)+1); |
||
+ | d_strobe <= '0'; |
||
+ | d_clk <= '0'; |
||
+ | if send = '1' then |
||
+ | d_data <= thisByte(0); |
||
+ | end if; |
||
+ | when "0010" => |
||
+ | if bitsLeftToSend(0) = '1' then -- Still got a bit to send? |
||
+ | state <= std_logic_vector(unsigned(state)-1); |
||
+ | else |
||
+ | if send = '0' then |
||
+ | keydata <= d_data & keydatalocal(7 downto 1); |
||
+ | end if; |
||
+ | adv <= '1'; |
||
+ | if EndCmd = '1' then |
||
+ | state <= "0011"; -- close off command |
||
+ | else |
||
+ | state <= "0100"; -- keep command open |
||
+ | end if; |
||
+ | end if; |
||
+ | d_strobe <= '0'; |
||
+ | d_clk <= '1'; |
||
+ | if send = '1' then |
||
+ | d_data <= thisByte(0); |
||
+ | thisByte <= '1' & thisByte(7 downto 1); |
||
+ | else |
||
+ | keydatalocal <= d_data & keydatalocal(7 downto 1); |
||
+ | end if; |
||
+ | bitsLeftToSend <= '0' & bitsLeftToSend(6 downto 1); |
||
+ | when "0011" => --- ending the command by rasing d_strobe, then going back to idle state |
||
+ | state <= "0000"; |
||
+ | d_strobe <= '1'; |
||
+ | d_clk <= '1'; |
||
+ | if send = '1' then |
||
+ | d_data <= thisByte(0); |
||
+ | else |
||
+ | keydata <= keydatalocal; |
||
+ | end if; |
||
+ | when "0100" => -- Waiting for data, with an open command |
||
+ | d_strobe <= '0'; |
||
+ | d_clk <= '1'; |
||
+ | |||
+ | if newData = '1' then |
||
+ | state <= "0001"; -- start transfering bits |
||
+ | thisByte <= byte; |
||
+ | bitsLeftToSend <= (others => '1'); |
||
+ | end if; |
||
+ | when others => -- performa a reset |
||
+ | thisByte <= (others => '0'); |
||
+ | state <= (others => '0'); |
||
+ | d_strobe <= '1'; |
||
+ | d_clk <= '1'; |
||
+ | end case; |
||
+ | end if; |
||
+ | end if; |
||
+ | end process; |
||
+ | end Behavioral; |
||
+ | </nowiki> |
||
+ | |||
+ | And finally, the file <tt>types.vhdl</tt>, with the to_bdc function and the type declarations: |
||
+ | <nowiki>library IEEE; |
||
+ | use IEEE.STD_LOGIC_1164.ALL; |
||
+ | use IEEE.NUMERIC_STD.ALL; |
||
+ | |||
+ | package types is |
||
+ | type digitdata is array (0 to 15) of STD_LOGIC_VECTOR(7 downto 0); |
||
+ | type bytearray is array (integer range <>) of STD_LOGIC_VECTOR(7 downto 0); |
||
+ | type bitarray is array (integer range <>) of STD_LOGIC; |
||
+ | function to_bcd ( bin : bitarray ) return unsigned; |
||
+ | end package; |
||
+ | |||
+ | package body types is |
||
+ | |||
+ | |||
+ | function to_bcd ( bin : bitarray ) return unsigned is |
||
+ | --http://stackoverflow.com/questions/12951759/how-to-decode-an-unsigned-integer-into-bcd-use-vhdl |
||
+ | --(c)2012 Enthusiasticgeek for Stack Overflow. |
||
+ | --Use at your own risk (includes commercial usage). |
||
+ | --These functions are released in the public domain and |
||
+ | --free to use as long as this copyright notice is retained. |
||
+ | --adapted for variable-length inputs by joost witteveen. |
||
+ | |||
+ | |||
+ | --11515/9563=1.204120046010666; log(16)/log(10) = 1.2041199826559246 |
||
+ | --(11515*i+9563*4-1)/(9563*4)*4 |
||
+ | variable bcd : unsigned(4*((11515*(bin'left+1)+9563*4-1)/(9563*4))-1 downto 0) := (others => '0'); |
||
+ | variable bint : bitarray(bin'range):=bin; |
||
+ | variable i : integer:=0; |
||
+ | begin |
||
+ | for i in bint'REVERSE_RANGE loop |
||
+ | bcd := bcd(bcd'left-1 downto 0) & bint(bint'left); --shifting the bits. |
||
+ | bint := bint((bint'left-1) downto 0)& '0'; |
||
+ | |||
+ | for ibcd in 0 to (bcd'left+1)/4-1 loop |
||
+ | if(i < bin'left and bcd(ibcd*4+3 downto ibcd*4) > "0100") then --add 3 if BCD digit is greater than 4. |
||
+ | bcd(ibcd*4+3 downto ibcd*4) := bcd(ibcd*4+3 downto ibcd*4) + "0011"; |
||
+ | end if; |
||
+ | end loop; |
||
+ | end loop; |
||
+ | return bcd; |
||
+ | end to_bcd; |
||
+ | end package body; |
||
+ | </nowiki> |
||
+ | |||
+ | =See Also= |
||
+ | * [[FPGA]] |
||
+ | * [[Spartan 3E]] |
||
+ | * [[Papilio]] |
Latest revision as of 17:17, 20 May 2013
Documents
Hamsterworks code
I'm using the VHDL code from Hamster on my papilio board, and it works. Thanks Hamsterworks!
Looks like the pinning of the DX display cable as described by Hamster has changed (or was wrong). The silkscreen on my (ordered october 2011) DX display looks like this (this is the 'bottom view', the pins of the socket as seen from above are the mirror-image of this):
pin | func | func | pin |
---|---|---|---|
2 | Gnd | Vcc | 1 |
4 | DIO | CLK | 3 |
6 | STB1 | STB0 | 5 |
8 | STB3 | STB2 | 7 |
10 | STB5 | STB4 | 9 |
So it looks like hamster's pinning has DIO/Data and CLK swapped, and the even Strobes were off by 2. Also, it's Strobe0 that controls this board (not Strobe1), the other strobes pass through to the 'out' connector.
I notice that after I power cycle (powered by USB cable) my papilio board (and the TM1638), I have to send the .bit file several times to the board. Strange.
- When setting d_clock to 32MHz/8/2, it always works the first time.
- Setting d_clock to 32MHz/4/2, it will not work at all
I'm using this constraints.ucf file for my papilio board:
# Crystal Clock - use 32MHz onboard oscillator NET "clk" LOC = "P89" | IOSTANDARD = LVCMOS25 | PERIOD = 31.25ns ; NET "d_data" LOC = "P86" | IOSTANDARD=LVCMOS33; NET "d_clk" LOC = "P85" | IOSTANDARD=LVCMOS33; NET "d_strobe" LOC = "P84" | IOSTANDARD=LVCMOS33;
But other than that, Hamsters code works.
Commands
bits | Description |
---|---|
010000000 | setup TM1638 to write data to display register, auto increment |
010000010 | setup TM 1638 to read data from key scans, auto increment |
010000100 | setup TM 1638 to write data to display register, single address |
010000010 | setup TM 1638 to read data from key scans, single address |
1100aaaa dddddddd | At address aaaa write/read dddddddd - multiple bytes of data can be transferred |
1000abbb | Display control - a = active, bbb = brightness |
After sending 46 the responce will be 4 bytes, key one is in bit zero (LSB) of the first byte, key two is in bit one of the second byte, etc. key 5 is in bit 4 of the first byte, key 6 in bit 4 of the second byte, etc.
VHDL code for numeric data display
The code below can read the TM1638 key status, and show hex and decimal numbers on the display.
File design_top.vhdl
--------------------------------------------------------------------------------- -- Original by: Engineer: Mike Field <hamster@snap.net.nz> -- Modifications by: Joost Witteveen -- -- ---------------------------------------------------------------------------------- library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; library work; use work.types.all; entity design_top is Port ( clk : in STD_LOGIC; d_clk : out STD_LOGIC; d_strobe : out STD_LOGIC; d_data : inout STD_LOGIC); end design_top; architecture Behavioral of design_top is COMPONENT dx_display Generic(clkDivider: integer); PORT( clk : IN std_logic; d_strobe : OUT std_logic; d_clk : OUT std_logic; d_data : inOUT std_logic; digits : in bytearray(0 to 7); leds : in bytearray(0 to 7); keybyte : out std_logic_vector(7 downto 0) ); END COMPONENT; COMPONENT display7seg PORT (digits : out bytearray; data : in integer ); end COMPONENT; signal Count : integer range 0 to 34*1000*1000 :=1000*1000-1; signal digits: bytearray(0 to 7) := (others => x"00"); signal leds: bytearray(0 to 7) := (others => x"00"); signal data: integer range -2000000000 to 2000000000 :=0;--20000/3; signal decdata: integer range -2000000000 to 2000000000 :=0;--20000/3; signal tmp: std_logic_vector(31 downto 0); signal keybyte: std_logic_vector(7 downto 0); signal key_int:integer range 0 to 255; begin Inst_dx_display: dx_display Generic Map(clkDivider => 16) PORT MAP( clk => clk, d_strobe => d_strobe, d_clk => d_clk, d_data => d_data, digits => digits, leds => leds, keybyte => keybyte ); Inst_display7seg:display7seg PORT MAP( digits => digits(0 to 1), data => key_int); Inst_display7segDec:display7seg PORT MAP( digits => digits(2 to 7), data => decdata); reset_proc: process(clk) begin if rising_edge(clk) then if Count > 0 then Count <=Count-1; else Count<=200*1000*1; data<=data+1; --for i in 0 to 3 loop -- tmp(i*8 + 7 downto i*8) <= keydata(i); --end loop; --decdata<=decdata+1; --tmp1<=std_logic_vector(to_unsigned(data,8)); --tmp(31 downto 24) <= keydata(0); --tmp(23 downto 16) <= keydata(1); --tmp(15 downto 8) <= keydata(2); --tmp(7 downto 0) <= keydata(3); --tmp(23 downto 0)<=std_logic_vector(to_unsigned(data+1,24)); --tmp(7 downto 0) <= keybyte; --tmp(15 downto 8) <= keybyte; --tmp(23 downto 16) <= keybyte; --tmp(31 downto 24) <= keybyte; --data<=to_integer(unsigned(tmp)); decdata<=to_integer(to_bcd(bitarray(std_logic_vector(to_unsigned(data,20))))); key_int <= to_integer((unsigned(keybyte))); for i in 0 to 7 loop leds(i)(0)<=keybyte(i); leds(i)(1)<='0'; end loop; end if; end if; end process; end Behavioral;
File dx_display.vhdl
---------------------------------------------------------------------------------- -- Original by: Engineer: Mike Field <hamster@snap.net.nz> -- Modifications by: Joost Witteveen -- -- Description: Driver for the DealExteme display board, TM1638 -- 8 x 7 segs -- 8 x bi-colour LED -- 8 x buttons ---------------------------------------------------------------------------------- library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; library work; use work.types.all; entity dx_display is Generic(clkDivider: integer); PORT( clk : IN std_logic; d_strobe: OUT std_logic; d_clk : OUT std_logic; d_data : inOUT std_logic; digits : in bytearray(0 to 7); leds : in bytearray(0 to 7); keybyte : out std_logic_vector(7 downto 0) ); end dx_display; architecture Behavioral of dx_display is signal counter : integer range 0 to 31 :=0;--std_logic_vector(5 downto 0) := (others => '0'); signal counterPreClk:std_logic:='0'; signal counterClk: std_logic:='0'; signal nextcounter : integer range 0 to 31; --unsigned(counter'range); signal byte : std_logic_vector(7 downto 0); signal endCmd : std_logic; signal newData : std_logic; signal send : std_logic; signal adv : std_logic; signal reset : std_logic := '1'; signal resetCount : integer range 0 to 32*1000*1000 :=1*100-1; signal keydataone : std_logic_vector(7 downto 0); constant headersize: integer := 8; COMPONENT dx_display_xmit Generic(clkDivider: integer); Port ( clk : in STD_LOGIC; reset : in STD_LOGIC; byte : in STD_LOGIC_VECTOR (7 downto 0); keydata : inout STD_LOGIC_VECTOR (7 downto 0); endCmd : in STD_LOGIC; newData : in STD_LOGIC; send : in STD_LOGIC; adv : out STD_LOGIC; d_strobe : out STD_LOGIC; d_clk : out STD_LOGIC; d_data : inout STD_LOGIC); end component; begin --nextcounter <= unsigned(counter) + 1; nextcounter <= counter +1; Inst_dx_display_xmit: dx_display_xmit Generic Map(clkDivider => clkDivider) PORT MAP( clk => clk, reset => reset, byte => byte, keydata => keydataone, endCmd => endCmd, newData => newData, send => send, adv => adv, d_strobe => d_strobe, d_clk => d_clk, d_data => d_data); data_proc: process(counterClk) begin if counterClk'event and counterClk='1' then send<= '1'; newData<='1'; case counter is --when "000000" => byte <= x"00"; endCmd <= '1'; when 0 => byte <= x"46"; endCmd <= '0'; when 1 => byte <= x"00"; endCmd <= '0'; send<='0'; when 2 => byte <= x"00"; endCmd <= '0'; send<='0'; keybyte(0)<=keydataone(0); keybyte(4)<=keydataone(4); when 3 => byte <= x"00"; endCmd <= '0'; send<='0'; keybyte(1)<=keydataone(0); keybyte(5)<=keydataone(4); when 4 => byte <= x"00"; endCmd <= '1'; send<='0'; keybyte(2)<=keydataone(0); keybyte(6)<=keydataone(4); when 5 => byte <= x"40"; endCmd <= '1'; -- Set address mode - auto inc keybyte(3)<=keydataone(0); keybyte(7)<=keydataone(4); when 6 => byte <= x"88"; endCmd <= '1'; -- Turn display on, lowest brightness (0) when 7 => byte <= x"C0"; endCmd <= '0';-- Write at the left display when others => if counter <= 15+headersize then if to_unsigned(counter,1)(0)='0' then byte<=digits((counter-headersize)/2); else byte<=leds((counter-headersize)/2); end if; if counter < 15+headersize then endCmd<='0'; else endCmd<='1'; end if; else byte <= x"FF"; endCmd <= '1'; newData <= '0'; -- End of data / idle end if; end case; end if; end process; clk_proc: process(clk) begin if rising_edge(clk) then counterPreClk<='0'; if reset = '1' then counter <= 31; elsif counter =31 then counterPreClk<='1'; counter <= 0; elsif adv = '1' and counter < 31 then counterPreClk<='1'; counter <= nextcounter; end if; end if; end process; counterClk_proc:process(clk) begin if rising_edge(clk) then counterClk<=counterPreClk; end if; end process; reset_proc: process(clk) begin if rising_edge(clk) then if resetCount > 0 then resetCount <=resetCount-1; else resetCount<=1*100*1000; reset<= not reset; end if; end if; end process; end Behavioral;
File dx_display_xmit.vhdl
---------------------------------------------------------------------------------- -- Engineer: Mike Field <hamster@snap.net.nz> -- Modified by: Joost Witteveen -- ---------------------------------------------------------------------------------- library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; entity dx_display_xmit is Generic (clkDivider: integer); Port ( clk : in STD_LOGIC; reset : in STD_LOGIC; byte : in STD_LOGIC_VECTOR (7 downto 0); keydata : out STD_LOGIC_VECTOR (7 downto 0); endCmd : in STD_LOGIC; newData : in STD_LOGIC; send : in STD_LOGIC; adv : out STD_LOGIC; d_strobe : out STD_LOGIC; d_clk : out STD_LOGIC; d_data : inout STD_LOGIC); end dx_display_xmit; architecture Behavioral of dx_display_xmit is 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(3 downto 0) := (others => '0'); signal divider : integer range 0 to 1023 := 0; signal keydataLocal : std_logic_vector(keydata'range); begin clk_proc: process(clk) begin if rising_edge(clk) then divider <= divider +1; adv <= '0'; if reset = '1' then thisByte <= (others => '0'); state <= (others => '0'); d_strobe <= '1'; d_clk <= '1'; d_data <= '1'; divider <= 0; elsif divider = (clkDivider-1) then divider <= 0; d_strobe <= '1'; d_clk <= '1'; if send = '1' then d_data <= '1'; else d_data <='Z'; end if; case state is when "0000" => -- Idle, without an open command if newData = '1' then state <= std_logic_vector(unsigned(state)+1); thisByte <= byte; d_strobe <= '0'; d_clk <= '1'; else d_strobe <= '1'; d_clk <= '1'; end if; bitsLeftToSend <= (others => '1'); keydatalocal<=x"55"; when "0001" => -- transfer a bit state <= std_logic_vector(unsigned(state)+1); d_strobe <= '0'; d_clk <= '0'; if send = '1' then d_data <= thisByte(0); end if; when "0010" => if bitsLeftToSend(0) = '1' then -- Still got a bit to send? state <= std_logic_vector(unsigned(state)-1); else if send = '0' then keydata <= d_data & keydatalocal(7 downto 1); end if; adv <= '1'; if EndCmd = '1' then state <= "0011"; -- close off command else state <= "0100"; -- keep command open end if; end if; d_strobe <= '0'; d_clk <= '1'; if send = '1' then d_data <= thisByte(0); thisByte <= '1' & thisByte(7 downto 1); else keydatalocal <= d_data & keydatalocal(7 downto 1); end if; bitsLeftToSend <= '0' & bitsLeftToSend(6 downto 1); when "0011" => --- ending the command by rasing d_strobe, then going back to idle state state <= "0000"; d_strobe <= '1'; d_clk <= '1'; if send = '1' then d_data <= thisByte(0); else keydata <= keydatalocal; end if; when "0100" => -- Waiting for data, with an open command d_strobe <= '0'; d_clk <= '1'; if newData = '1' then state <= "0001"; -- start transfering bits thisByte <= byte; bitsLeftToSend <= (others => '1'); end if; when others => -- performa a reset thisByte <= (others => '0'); state <= (others => '0'); d_strobe <= '1'; d_clk <= '1'; end case; end if; end if; end process; end Behavioral;
And finally, the file types.vhdl, with the to_bdc function and the type declarations:
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; package types is type digitdata is array (0 to 15) of STD_LOGIC_VECTOR(7 downto 0); type bytearray is array (integer range <>) of STD_LOGIC_VECTOR(7 downto 0); type bitarray is array (integer range <>) of STD_LOGIC; function to_bcd ( bin : bitarray ) return unsigned; end package; package body types is function to_bcd ( bin : bitarray ) return unsigned is --http://stackoverflow.com/questions/12951759/how-to-decode-an-unsigned-integer-into-bcd-use-vhdl --(c)2012 Enthusiasticgeek for Stack Overflow. --Use at your own risk (includes commercial usage). --These functions are released in the public domain and --free to use as long as this copyright notice is retained. --adapted for variable-length inputs by joost witteveen. --11515/9563=1.204120046010666; log(16)/log(10) = 1.2041199826559246 --(11515*i+9563*4-1)/(9563*4)*4 variable bcd : unsigned(4*((11515*(bin'left+1)+9563*4-1)/(9563*4))-1 downto 0) := (others => '0'); variable bint : bitarray(bin'range):=bin; variable i : integer:=0; begin for i in bint'REVERSE_RANGE loop bcd := bcd(bcd'left-1 downto 0) & bint(bint'left); --shifting the bits. bint := bint((bint'left-1) downto 0)& '0'; for ibcd in 0 to (bcd'left+1)/4-1 loop if(i < bin'left and bcd(ibcd*4+3 downto ibcd*4) > "0100") then --add 3 if BCD digit is greater than 4. bcd(ibcd*4+3 downto ibcd*4) := bcd(ibcd*4+3 downto ibcd*4) + "0011"; end if; end loop; end loop; return bcd; end to_bcd; end package body;