Jump to content
  • 0

Create sine,triangular and square wave on dac using vhdl


Alturan

Question

Hello everyone.

I want to generate sine, triangular and square wave on dac (pmodda3) https://www.analog.com/media/en/technical-documentation/data-sheets/AD5541A.pdf?_ga=2.232955870.1153024710.1713700607-1700450542.1713700607 and I am using Basys3 (https://digilent.com/reference/_media/reference/programmable-logic/basys-3/basys3_rm.pdf).

I made the necessary adjustments for pmod in the code I added below. I can get output by sending a single data. However, I want to set the frequency of the data I want to obtain to 1Mhz. In the current situation, when I change the data, the frequency also changes. How do I keep this constant? 

Also, how can I do the necessary encoding when I want to send the data not as a single 16-bit data, but as 15 pieces of 16-bit data, for example, using array logic?

Thanks for your answers.

-------------------------------------------------------------------------

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity spi_module is
    Port ( 
           clk  : in  STD_LOGIC;
           sclk : out  STD_LOGIC;
           din  : out  STD_LOGIC;
           ldac : out  STD_LOGIC;
           cs   : out  STD_LOGIC);
           
end spi_module;

architecture Behavioral of spi_module is
    signal data_i :  STD_LOGIC_VECTOR (15 downto 0):="0000000000000001";
    signal run_stop : STD_LOGIC_VECTOR (1 downto 0):=(others=> '0');
     
    constant CLOCK_DIVIDER : integer := 5;  
    signal clk_div_counter : integer range 0 to CLOCK_DIVIDER - 1 := 0;
    signal clk_divided : std_logic := '0';
    signal set : integer range 0 to 23 := 0;

begin

process(clk)   
begin
    if rising_edge(clk) then
       
        
       
        if clk_div_counter = CLOCK_DIVIDER - 1 then
            clk_div_counter <= 0;
            clk_divided <= not clk_divided;  
        else
            clk_div_counter <= clk_div_counter + 1;
        end if;

  
        sclk <= clk_divided;
        
        if clk_divided = '1' then
            if run_stop (0) <= '0' then
                run_stop (0) <= '1';
                 
                 case set is
                        when 0 =>
                            cs <= '0';
                            ldac <= '1';
                        when 1 =>
                            cs <= '1';
                            ldac <= '1';
                        when 2 =>
                            cs  <= '1';
                            ldac <= '1';                      
                        when 15 =>
                             cs <= '1';
                             ldac <= '1';
                             din <= '0';
                        when 16 =>
                             cs <= '1';
                             ldac <= '0';    
                        when 17 =>
                             cs <= '1';
                             ldac <= '0';
                        when 18 to 23 =>    
                            cs <= '1';
                            ldac <= '1';                        
                        when others =>
                              
                            cs <= '0';
                            ldac <= '1';
                            din <= data_i(15);
                            data_i<=data_i(14 downto 0) & data_i(15);
                    end case;
                set <= (set + 1) mod 24; 
                end if;
            
        else 
        run_stop (0) <= '0';
       end if;
    end if;
end process;

end Behavioral;
 

Link to comment
Share on other sites

3 answers to this question

Recommended Posts

  • 0

Hi @Alturan

Welcome to the forum.

On 4/21/2024 at 5:18 AM, Alturan said:

I want to generate sine, triangular and square wave on dac (pmodda3).

Storing data in block RAM (or just LUTs) and counting through addresses is a standard way of doing this kind of thing. Each SPI transfer sent to the DAC would have a new value - like your data_i signal would be a new piece of data read out of a BRAM at the start of every transfer, incrementing the address every transfer. You would use a separate counter to control when each new SPI transaction starts to control the sample rate - assuming a 100 MHz clock, you could get a 1 MS/s DAC update rate if whenever a counter counts to 100, a new transfer is initiated, although it looks like your controller currently takes 120 clocks to send out a transfer.

You can even control the frequency of the output signal by changing how much the address counter goes up each transfer - adding 10 to a counter that rolls over when it goes above 255 lets you count through a lookup table 10x faster than adding 1 each time.

I'd also recommend simulating your HDL as you go, before testing in hardware - using "if clk_divided = '1' then" instead of "if clk_div_counter = CLOCK_DIVIDER - 1 then" for the shift register enable is concerning - it's probably active for 5 clocks in a row, then idle for the next 5, rather than active for one in every five clocks, like I assume is intended. Check out this guide: https://digilent.com/reference/programmable-logic/guides/simulation.

Thanks,

Arthur

Link to comment
Share on other sites

  • 0

First of all, thank you for your answer. I solved the problem and now I can send and output data. I provided data transfer using array logic on SPI. In the current situation, I need to make a top module by combining uart and spi. Currently, I can send 8-bit data using the Hterm application on UART and I can observe the data I send through the LEDs in fpga. What I want to do is to facilitate the application by sending the data from the computer instead of sending it with array logic over SPI. Can you help with this? Thanks.

Edited by Alturan
Link to comment
Share on other sites

  • 0

If you implement a dual-port block RAM then one piece of hardware could take data from the UART interface and write it into memory, while the existing hardware reads from memory via the other port. Dual-port BRAMs can even have different data widths for each port. You might also want to have a way to prevent the existing hardware from sending anything until the BRAM is full, whether it gets enabled with a switch, or by a specific character or sequence of characters coming from the UART.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...