3
\$\begingroup\$

I am trying to implement a simple UART transmitter, where the Nexys4 DDR board is sending ASCII characters to my PC, which I can view using Tera Term. The problem I am having is that when I press one of the buttons to send the UART data, I keep getting a lot of the same character at once, and the number is not exact (e.g. if I try to send 0x66 I get the character 'f' sometimes 102 times, sometimes 97 times, etc). I thought I needed to debounce the button so I went to find a module for it. But it seems this has no effect. I would like some help on what is wrong with the way I have done it.

Here is the code for the UART: (based from this example http://labs.domipheus.com/blog/a-uart-implementation-in-vhdl/)

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx leaf cells in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity uart_deb is
    Port ( I_CLK, I_RST, I_TXSIG : in STD_LOGIC;
    I_TXDATA : in STD_LOGIC_VECTOR(7 downto 0);    
    O_TXRDY, O_TX : out STD_LOGIC );
end uart_deb;

architecture STRUCTURE of uart_deb is

component uart_tx 
    Port (
         I_CLK : in STD_LOGIC;
         I_RST : in STD_LOGIC;
         I_TXSIG : in STD_LOGIC;

         I_TXDATA : in STD_LOGIC_VECTOR(7 downto 0);
         O_TXRDY : out STD_LOGIC;
         O_TX : out STD_LOGIC

     );
end component;



component debounce 
  GENERIC(
    counter_size  :  INTEGER := 19); --counter size (19 bits gives 10.5ms with 50MHz clock)
  PORT(
    I_CLK     : IN  STD_LOGIC;  --input clock
    button  : IN  STD_LOGIC;  --input signal to be debounced
    result  : OUT STD_LOGIC); --debounced signal
END component;

signal RES : STD_LOGIC;

begin
    G1: debounce port map (I_CLK, I_TXSIG, RES);
    G2: uart_tx port map (I_CLK, I_RST, RES, I_TXDATA, O_TXRDY, O_TX);
end;

the uart_tx source:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx leaf cells in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity uart_tx is
    Generic ( baud : integer := 9600);
    Port (
         I_CLK : in STD_LOGIC;
         I_RST : in STD_LOGIC;
         I_TXSIG : in STD_LOGIC;

         I_TXDATA : in STD_LOGIC_VECTOR(7 downto 0);
         O_TXRDY : out STD_LOGIC;
         O_TX : out STD_LOGIC

     );
end uart_tx;

architecture Behavioral of uart_tx is
    signal tx_data : STD_LOGIC_VECTOR(7 downto 0) := (others => '0');
    signal tx_state : integer := 0;
    signal tx_rdy : STD_LOGIC := '1';
    signal tx : STD_LOGIC := '1';

    constant BIT_PD : integer := integer(50000000 / baud); -- 100MHz clock limit    
    signal tx_clk_counter : integer := BIT_PD;
    signal tx_clk : STD_LOGIC := '0'; 
begin

    clk_gen: process (I_CLK)
    begin
            if rising_edge(I_CLK) then
                if tx_clk_counter = 0 then
                    tx_clk_counter <= BIT_PD;
                    tx_clk <= not tx_clk;
                else
                    tx_clk_counter <= tx_clk_counter - 1;
                end if;
            end if;
    end process;                

    O_TX <= tx;
    O_TXRDY <= tx_rdy;

    tx_proc: process (tx_clk, I_RST, I_TXSIG, tx_state)
    begin
        if rising_edge(tx_clk) then
            if I_RST = '1' then
                tx_state <= 0;
                tx_data <= X"00";
                tx_rdy <= '1';
                tx <= '1';
            else
                if tx_state = 0 and I_TXSIG = '1' then
                    tx_state <= 1;
                    tx_data <= I_TXDATA;
                    tx_rdy <= '0';
                    tx <= '0';
                elsif tx_state < 9 and tx_rdy = '0' then
                    tx <= tx_data(0);
                    tx_data <= '0' & tx_data(7 downto 1);
                    tx_state <= tx_state + 1;
                elsif tx_state = 9 and tx_rdy = '0' then
                    tx <= '1';
                    tx_rdy <= '1';
                    tx_state <= 0;
                end if;
            end if;
        end if;                
    end process;

end Behavioral;

and the debouncer source:

LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_unsigned.all;

ENTITY debounce IS
  GENERIC(
    counter_size  :  INTEGER := 19); --counter size (19 bits gives 10.5ms with 50MHz clock)
  PORT(
    I_CLK     : IN  STD_LOGIC;  --input clock
    button  : IN  STD_LOGIC;  --input signal to be debounced
    result  : OUT STD_LOGIC); --debounced signal
END debounce;

ARCHITECTURE logic OF debounce IS
  SIGNAL flipflops   : STD_LOGIC_VECTOR(1 DOWNTO 0); --input flip flops
  SIGNAL counter_set : STD_LOGIC;                    --sync reset to zero
  SIGNAL counter_out : STD_LOGIC_VECTOR(counter_size DOWNTO 0) := (OTHERS => '0'); --counter output
BEGIN

  counter_set <= flipflops(0) xor flipflops(1);   --determine when to start/reset counter

  PROCESS(I_CLK)
  BEGIN
    IF(I_CLK'EVENT and I_CLK = '1') THEN
      flipflops(0) <= button;
      flipflops(1) <= flipflops(0);
      If(counter_set = '1') THEN                  --reset counter because input is changing
        counter_out <= (OTHERS => '0');
      ELSIF(counter_out(counter_size) = '0') THEN --stable input time is not yet met
        counter_out <= counter_out + 1;
      ELSE                                        --stable input time is met
        result <= flipflops(1);
      END IF;    
    END IF;
  END PROCESS;
END logic;

the constraints file:

## Clock signal
set_property -dict { PACKAGE_PIN E3    IOSTANDARD LVCMOS33 } [get_ports { I_CLK }]; #IO_L12P_T1_MRCC_35 Sch=clk100mhz
create_clock -add -name sys_clk_pin -period 20.00 -waveform {0 5} [get_ports {I_CLK}];


##Switches

set_property -dict { PACKAGE_PIN J15   IOSTANDARD LVCMOS33 } [get_ports { I_TXDATA[0] }]; #IO_L24N_T3_RS0_15 Sch=sw[0]
set_property -dict { PACKAGE_PIN L16   IOSTANDARD LVCMOS33 } [get_ports { I_TXDATA[1] }]; #IO_L3N_T0_DQS_EMCCLK_14 Sch=sw[1]
set_property -dict { PACKAGE_PIN M13   IOSTANDARD LVCMOS33 } [get_ports { I_TXDATA[2] }]; #IO_L6N_T0_D08_VREF_14 Sch=sw[2]
set_property -dict { PACKAGE_PIN R15   IOSTANDARD LVCMOS33 } [get_ports { I_TXDATA[3] }]; #IO_L13N_T2_MRCC_14 Sch=sw[3]
set_property -dict { PACKAGE_PIN R17   IOSTANDARD LVCMOS33 } [get_ports { I_TXDATA[4] }]; #IO_L12N_T1_MRCC_14 Sch=sw[4]
set_property -dict { PACKAGE_PIN T18   IOSTANDARD LVCMOS33 } [get_ports { I_TXDATA[5] }]; #IO_L7N_T1_D10_14 Sch=sw[5]
set_property -dict { PACKAGE_PIN U18   IOSTANDARD LVCMOS33 } [get_ports { I_TXDATA[6] }]; #IO_L17N_T2_A13_D29_14 Sch=sw[6]
set_property -dict { PACKAGE_PIN R13   IOSTANDARD LVCMOS33 } [get_ports { I_TXDATA[7] }]; #IO_L5N_T0_D07_14 Sch=sw[7]

## LEDs

set_property -dict { PACKAGE_PIN H17   IOSTANDARD LVCMOS33 } [get_ports { O_TXRDY }]; #IO_L18P_T2_A24_15 Sch=led[0]


##Buttons

set_property -dict { PACKAGE_PIN N17   IOSTANDARD LVCMOS33 } [get_ports { I_RST }]; #IO_L9P_T1_DQS_14 Sch=btnc
set_property -dict { PACKAGE_PIN M18   IOSTANDARD LVCMOS33 } [get_ports { I_TXSIG }]; #IO_L4N_T0_D05_14 Sch=btnu


##USB-RS232 Interface

#set_property -dict { PACKAGE_PIN C4    IOSTANDARD LVCMOS33 } [get_ports { UART_TXD_IN }]; #IO_L7P_T1_AD6P_35 Sch=uart_txd_in
set_property -dict { PACKAGE_PIN D4    IOSTANDARD LVCMOS33 } [get_ports { O_TX }]; #IO_L11N_T1_SRCC_35 Sch=uart_rxd_out
#set_property -dict { PACKAGE_PIN D3    IOSTANDARD LVCMOS33 } [get_ports { UART_CTS }]; #IO_L12N_T1_MRCC_35 Sch=uart_cts
#set_property -dict { PACKAGE_PIN E5    IOSTANDARD LVCMOS33 } [get_ports { UART_RTS }]; #IO_L5N_T0_AD13N_35 Sch=uart_rts
\$\endgroup\$
2
\$\begingroup\$

I did not go through all your submitted materials but I would have to make a guess that you are not properly handling the switch input. Yes you do have to debounce it but then you have to properly edge detect the transition you want to trigger the UART transmission to occur from. That could of course be from the push transition or from the release transition.

If you do not properly edge (transition) detect you will end up detecting the pressed switch over and over again resulting in the many UART transmissions until the switch is released. The variable number you are seeing is most likely related to how long the button was pressed.

\$\endgroup\$
  • \$\begingroup\$ Yea, exactly the reason. \$\endgroup\$ – Mitu Raj Jan 26 '18 at 19:47
  • \$\begingroup\$ Thanks for your response, but I am kinda new to this. Would it be possible if you can provide a diagram/sketch of what is happening so I can try to implement it in the design source? \$\endgroup\$ – nnja Jan 26 '18 at 19:52
1
\$\begingroup\$

The baud rate you have chosen is 9600 bps. Which means, your UART will take around \$ 1/9600 \approx 0.1 ms \$ to send one bit. Which means to send a complete frame with 8 bits, one start bit and stop bit, the UART will take around only \$ 1 ms \$.

You have fed some TX_DATA and pressed the button to enable transmission. So what will happen if the button stayed in ON state for say, 100 ms ? It will send the data frame 100 times right ? Your debouncing logic doesn't do anything to help in this case. It just checks whether the switch input is stable for atleast 10 ms, and feed the same logic to the enable pin TXSIG of the UART.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.