0
\$\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
try-catch-finally
1,2743 gold badges19 silver badges37 bronze badges
asked Dec 28, 2016 at 20:11
\$\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\$ Commented 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\$ Commented 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\$ Commented 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\$ Commented Apr 24, 2018 at 0:33

2 Answers 2

1
\$\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.

answered Dec 28, 2016 at 20:26
\$\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;
answered Jul 26, 2018 at 18:10
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.