So now that I have a working display I guess I had better get started on the SPI temperature sensor. The design process here is a little different as we are not just passing data or signals from one block to the next. What we need is a sub block that can process a few instruction to pull in the data from the SPI device.
Now in some cases you may consider a state machine, but no. we first need to look at and think about what the SPI device delivers. The device I’ll be using is one I got from TAUTIChttp://store.tautic.com/ and uses a MAX31855http://datasheets.maxim-ic.com/en/ds/MAX31855.pdf. On page 5 we can the data pattern used that comes out of the device. It’s actually a very basic device to interface to. The chip select line goes low (CS) and then as the clock line is driven one bit of data comes out the device. There is a total of 32 bits (31 to 0) that need clocking out.
So the design can be done using a counter that counts out a number steps and during each step a different action takes place. We will need a few clock cycles to drive the CS line high, reseting the SPI device back to the start. Lets say we need 36 steps, that 32 for the data and a extra 4 for setting up and CS line etc. That’s more than enough.
So the code we write is going to need two sections, one that is a counter from 0 to 35 and another to drive the CS line and store data. It also needs to push the data out at the very end into a 32 bit wide signal what we can use. The counter is easy but what about the other sections?
Well we can start by just looking at the counter. When the counter has a value of 0 or 1 we can set the CS line high. At count 2 we can drive the CS line low knowing that on the next clock we will get our first bit out. So thats count value 3 to 34 that will have valid data in them. These can be placed into a buffer that is shifted one bit every time too. hence data in collected one bit at a time. Once we get to count 34 we can ‘register’ the buffer into our 32 bit output. Registering it means its gets locked in so the buffer can be used again without effecting the output. It's actually nothing more that a big 32 bit latch. Also on count 34 and also during count 35 we will drive the CS line high and because our counter resets itself back to 0 when it sees 35, the whole thing starts all over again with no outside help. This is useful as we want a continuous flow of data from the temperature device.
Now this so far has all sounded quite a lot to do but as you will now see is not very much code at all.
entity SPI_Temp_Master is
Port ( clk_i : in STD_LOGIC;
SPI_clk : out STD_LOGIC;
SPI_cs : out STD_LOGIC;
SPI_Data_In : in STD_LOGIC;
SPI_Data_Out : out STD_LOGIC_VECTOR (31 downto 0));
end SPI_Temp_Master;
Starting with the entity for this block we can see we have a incoming clock. We then have the interface to the SPI device which is a SPI_clk (clock output), SPI_cs (chip select) and SPI_Data_In which is the serial data from the device. Then we have the 32 bit output from the block which is called SPI_Data_Out.
signal SPI_rx_buff: std_logic_vector(31 downto 0);
signal SPI_cnt : std_logic_vector(5 downto 0) := B"000000";
signal SPI_cs_sig : std_logic := '0';
The above signals will be used internally to the block as consist of the 32 bit buffer, our 6 bit counter (note its std_logic_vector) and then a chip select signal thats internal.
I then do a bit of quick driving of the CS and clock line by having the following two lines.
SPI_cs <= SPI_cs_sig;
SPI_clk <= clk_i;
Next we need our counter noting that it resets when it sees a value of 35, back to 0. I have used the counter as a std_logic_vector as it fits with the rest of the design we have seen. Keeping consistent. People will be critical of me using the IEEE.STD_LOGIC_UNSIGNED.ALL library but for now this is about keeping it simple so I don't have to start explaining the whole which library debate. Just for now except that IEEE.STD_LOGIC_UNSIGNED.ALL is not great, but I’m using it anyway for now.
So our counter will look like this.
if SPI_cnt = 35 then
SPI_cnt <= (others => '0');
else
SPI_cnt <= SPI_cnt + 1;
end if;
Notice that I’ve not included the process around this as yet, that because I’ll be wrapping the counter and the SPI block inside one process. This other block is as follows and should be easy to read.
if SPI_cnt < 2 then
SPI_cs_sig <= '0';
SPI_rx_buff <= (others => '0');
elsif SPI_cnt < 34 then
SPI_rx_buff(31 downto 1) <= SPI_rx_buff(30 downto 0);
SPI_rx_buff(0) <= SPI_Data_In;
SPI_cs_sig <= '0';
elsif SPI_cnt = 34 then
SPI_Data_Out(31 downto 0) <= SPI_rx_buff(31 downto 0);
SPI_cs_sig <= '1';
else
SPI_cs_sig <= '1';
end if;
So as you can see this block of code is lot shorter than my explanation.! The full file is included at the end of this post.
So we now have a working SPI unit that reads data out of our SPI device and pushes the result out once every 35 clock cycles, that’s quick. Our next stage is to plug this SPI block into our display logic we have done so far to get the final project working. That however will have to wait till next time.
Thanks
Paul (@monpjc)