1
\$\begingroup\$

I am trying to create an SPI between the MAX31855 - thermocouple to digital converter, and my FPGA - DE0.

I'm understanding the gist of SPI, and the timing requirements of the MAX31855. My problem is hashing it out in verilog.

BACKGROUND INFO MAX31855: When CS (slave select) is low, data is sent through MISO, for a full reading of temperature and reference junction temperature, 32 cc must pass. 14 cc are required for just the temperature reading.

DATASHEET https://cdn-shop.adafruit.com/datasheets/MAX31855.pdf

My understanding is that to get a temperature reading, the control signal, CS, must be set low and which point the data will come in serially through the MISO.

My plan of attack was to have two clocks, one for SCK (5 mhz) and one for CS. CS would be 1/14th of SCK. The reason being is that the data for the temperature comes from the first 14 bits through MISO. So, I set CS low for 14 SCK clock cycles to get my temperature reading. The data coming through MISO would go into a 14-bit-serial-in-parallel-out shift register.

I made the clock dividers, and I made the shift register. I am having difficulty writing the verilog code... at least in my mind. I am suspicious of it working.

I am thinking that if at the negative edge of CS, I send zero bytes to the CS input of the MAX31855, that should do it.

I have this code, thoughts? I BELIEVE this is correct, but my intuition is saying no because everything I have read about SPI says I need a MASTER module AND a SLAVE module.

NOTE: any blanks you see in the module instances are for RESET, I am just not going to use it.

//MASTER module for seriel peripheral interface of MAX31855


module SPI_MASTER(CLOCK_50,GPIO0_D[21],GPIO0_D[19],GPIO0_D[15],Temperature);

input CLOCK_50;
input GPIO0_D[19]; //MISO
output reg [13:0] Temperature;
output GPIO0_D[21]; //SCK
output GPIO0_D[15]; //CS i.e. slave select
assign GPIO0_D[15] = 1'b1;

//##########################################################################//
//########################CLOCKS############################################//
//##########################################################################//

/*DE0 clock (CLOCK_50) goes into ADC_CLOCK. ADC_CLOCK outputs a 5 mhz clock
that goes to SCK, and to CS_CLOCK. CS_CLOCK outputs a clock, whose 1 cycle is 
the length of 32 of ADC_CLOCK's.*/ 

ADC_CLOCK SCK_CLOCK(CLOCK_50,,SCK_WIRE);
wire SCK_WIRE;

assign GPIO0_D[21] = SCK_WIRE;

CS_CLOCK CS_SIGNAL(SCK_WIRE,,CS);
wire CS;


//##########################################################################//
//##########################MISO############################################//
//##########################################################################//

/*MISO takes the input from MAX31855 through GPIO0_D[19], goes into shift register
after 14 clock cycles the shift register outputs Temperature. Temperature  goes to 
comparator of thermostat state machine, and state machine of LCD*/


SR Temp_Readings(GPIO0_D[19],SCK_WIRE,,Temperature);

/* @negedge of CS, send signal to GPIO0_D[15] (the slave select) for temperature to 
be read and data be send through MISO*/

always @(negedge CS)
    begin
        GPIO0_D[15] => 1'b0;
    end
endmodule
\$\endgroup\$
4
  • 1
    \$\begingroup\$ GPIO0_D[15] => 1'b0;. That line of code does nothing. But even if the assignment operator (<=) was used correctly, you only ever set that output to 0, never to 1. I think what you are intending is to replace that entire always block with simply assign GPIO0_D[15] = CS. \$\endgroup\$ Dec 28, 2016 at 20:22
  • \$\begingroup\$ At the top, below the input/output declarations, I assigned GPIO0_D[15] 1'b1. However, I see you're point though. \$\endgroup\$ Dec 28, 2016 at 20:26
  • 1
    \$\begingroup\$ Any SPI system requires a master and a slave, but the MAX31855 already contains the SPI slave module; your FPGA only needs to provide the SPI master. Check opencores.org for SPI master (wishbone) implementation. \$\endgroup\$
    – MarkU
    Dec 28, 2016 at 22:25
  • 1
    \$\begingroup\$ Personally, I think it makes more sense to implement this as a large state machine that generates all of the signals - SCK, CS, and MOSI. This will probably be much easier to develop and debug than a bunch of clock dividers and shift registers. \$\endgroup\$ Apr 24, 2018 at 0:33

2 Answers 2

0
\$\begingroup\$

My plan of attack was to have two clocks, one for SCK and one for CS.

I think this is not a good idea:

Looking at the data sheet you can see that the clock signal must be low when there is an edge in the CS signal.

I would use a counter - for example a 6 bit counter driven by the clock. Depending on the counter value I would do:

  • 0: SCK=low, CS=low
  • 1...32: SCK=clock, CS=low
  • 33: SCK=low, CS=low
  • 34...Max: SCK=low, CS=high

The shift register would shift on the falling edges of the SCK signal; the value in the shift register would be evaluated on the rising edge of the CS signal.

\$\endgroup\$
0
\$\begingroup\$

The way I've found best to write these things is to have a counter that divides down the global clock.

Then SR registers are used to switch on at the appropriate times.

Draw a timing diagram with the

Something like this (but this is for driving a DAC, or writing a register with SPI, you'll need to modify the code so the shift register is going the other way.)

port (
  DAC_START_H    : in std_logic;   -- Serial Data In: shift in config word
  DAC_VALUE      : in std_logic_vector(15 downto 0); -- 16=bit value to load into the DAC

  ARST_L         : in std_logic;   -- Reset for testbench only

  DAC_RST_H      : in std_logic;   -- Software Reset
  SYS_CLK        : in std_logic;   -- system clock

  DAC_CLK        : out std_logic;   -- Input Clock
  DAC_CS_L       : out std_logic;   -- Convert Start
  DAC_SDI_H      : out std_logic   -- Output Serial Data
  );
end DAC2641;



architecture DAC2641 of DAC2641 is
  signal DAC_COUNT            : std_logic_vector(4 downto 0);
  signal DAC_SHIFTOUT         : std_logic_vector(15 downto 0);

  signal DAC_SHIFTOUT_EN_H    : std_logic;
  signal DAC_CS_L_TEMP        : std_logic; 
  signal DAC_CLK_TEMP         : std_logic; 

begin




DACCLK_CNT : process( ARST_L, SYS_CLK)
begin
  if ARST_L = '0'  then
    DAC_COUNT <= conv_std_logic_vector(10#22#, 5);
  elsif rising_edge(SYS_CLK) then
    if DAC_START_H = '1'  or DAC_RST_H = '1'  then
      DAC_COUNT <= conv_std_logic_vector(10#0#, 5);
    elsif (DAC_COUNT < conv_std_logic_vector(10#21#, 5) )     then
      DAC_COUNT <= DAC_COUNT + 1;
    else
      DAC_COUNT <= DAC_COUNT;
    end if;
  end if;
end process;



DAC_CNTLD_REG : process(ARST_L,DAC_COUNT,SYS_CLK)
begin
  if ARST_L = '0'  then
    DAC_CS_L_TEMP <= '1';
  elsif rising_edge(SYS_CLK) then 
    if DAC_COUNT = conv_std_logic_vector(10#01#,6) or DAC_RST_H = '1' then
      DAC_CS_L_TEMP <= '0'; 
    elsif DAC_COUNT = conv_std_logic_vector(10#18#,6) then
      DAC_CS_L_TEMP <= '1'; 
    end if;
  end if;
end process;
DAC_CS_L <= DAC_CS_L_TEMP;


DAC_SHFTOUT_COMB : process(ARST_L,SYS_CLK)
begin
  if ARST_L = '0'  then
    DAC_SHIFTOUT_EN_H <= '0';  
  elsif rising_edge(SYS_CLK) then  
    if DAC_COUNT = conv_std_logic_vector(10#02#,6)  then
      DAC_SHIFTOUT_EN_H <= '1'; 
    elsif DAC_COUNT = conv_std_logic_vector(10#18#,6) then
      DAC_SHIFTOUT_EN_H <= '0'; 
    end if;
  end if;
end process;




DAC_LDVAL_REG : process( ARST_L, SYS_CLK, DAC_START_H)
begin
  if ARST_L = '0' then
    DAC_SHIFTOUT <= conv_std_logic_vector(10#00#, 16);
  elsif DAC_START_H = '1' then
    DAC_SHIFTOUT <= DAC_VALUE;
  elsif falling_edge(SYS_CLK) then
    if DAC_SHIFTOUT_EN_H = '1' then
      DAC_SHIFTOUT <= DAC_SHIFTOUT(14 downto 0) & '0';
    else
      DAC_SHIFTOUT <= DAC_SHIFTOUT;
    end if;
  end if;
end process;

DAC_SDI_H <= DAC_SHIFTOUT(15);


DAC_CLK_COMB : process(SYS_CLK,DAC_SHIFTOUT_EN_H)
begin
  if  DAC_SHIFTOUT_EN_H = '0' then
    DAC_CLK_TEMP <=  '0';
  else
    DAC_CLK_TEMP <=  SYS_CLK;
  end if;
end process;
DAC_CLK <= DAC_CLK_TEMP ;

end DAC2641;
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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