0

我需要进行持续的 SPI 通信以从我拥有的双通道 ADC 读取值,并且为此编写了一个状态机。但是,它似乎没有进入读取第二个通道的状态,我不知道为什么。这是VHDL ...

SPI_read: process (mclk)
                                                        --command bits: Start.Single.Ch.MSBF....
    constant query_x: unsigned(ADC_datawidth-1 downto 0) := "11010000000000000";    -- Query ADC Ch0 ( inclinometer x-axis)
    constant query_y: unsigned(ADC_datawidth-1 downto 0) := "11110000000000000";    -- Query ADC Ch1 ( inclinometer y-axis)

begin

    if rising_edge(mclk) then

        -- when SPI is not busy, change state and latch Rx data from last communication
        if (SPI_busy = '0') then

            case SPI_action is
                when SETUP => 
                    SPI_pol <= '0'; -- Clk low when not active
                    SPI_pha <= 1;       -- First edge is half an SCLK period after CS activated
                    SPI_action <= READ_X;
                when READ_X =>
                    SPI_Tx_buf <= query_x; -- Load in command
                    y_data <= "00000" & SPI_Rx_buf(11 downto 1);
                    SPI_send <= '1';
                    SPI_action <= READ_Y;
                when READ_Y =>
                    SPI_Tx_buf <= query_y; -- Load in command
                    x_data <= "00000" & SPI_Rx_buf(11 downto 1);
                    SPI_send <= '1';
                    SPI_action <= READ_X;
            end case;

        else
            SPI_send <= '0'; -- Deassert send pin
        end if;

    end if;

end process SPI_read;

该命令被发送到 Tx 缓冲区,最后接收到的数据的值被写入一个信号,该信号输出到一些七段显示器。需要来自 SPI_send 的脉冲来启动传输,启动时,SPI_busy 设置为高电平,直到传输完成。

现在它只会通过 SPI 发送 query_x,我可以知道这一点,因为我可以在示波器上看到它。然而,有趣的是,它向两个显示器输出相同的值,这让我认为它仍在进入它的 READ_Y 状态,但没有改变它输出的 Tx 数据。

我已经盯着这段代码好几个小时了,我想不通。有时一双新鲜的眼睛会让生活更轻松,所以如果你发现了什么,请告诉我。此外,我对处理这个问题的更好方法的建议非常开放,我只是在学习 VHDL,所以我什至不确定我是否以正确的方式做事!

4

3 回答 3

1

将我迄今为止的评论总结为答案。

使用您的 SPI 主组件模拟此过程/模块。

您的方法通常是正确的,但我建议您稍微重新构建您的状态机,以便在每个 spi 事务(wait_x,wait_y)之间放置明确的等待状态,并且可能在模块之间进行更强大的握手,即保持 read_x 直到忙碌高,然后留在 wait_x 直到忙变低。

看起来 send 被断言了两个周期,并且您在每个周期都通过 read_x 和 read_y 进行转换。 时序图

来自这个程序 http://wavedrom.googlecode.com/svn/trunk/editor.html 与此来源:

{ "signal" : [
  { "name": "clk",           "wave": "P........" },
  { "name": "busy",          "wave": "0..1.|0..1"},
  { "name": "SPI_Action",    "wave": "====.|.==.",   "data": ["SETUP", "READ_X", "READ_Y", "READ_X", "READ_Y", "READ_X", "READ_Y", ] },
  { "name": "SPI_send",      "wave": "0.1.0|.1.0",   "data": ["0", "1", "Load", "Start","WaitA"] },
  { "name": "SPI_Tx_buf",    "wave": "x.===|..==", "data": ["query_x","query_y","query_x","query_y","query_x"],},
]}
于 2013-01-04T01:19:46.203 回答
0

我必须同意大卫提出的问题并添加一些注释。

您的代码有一些不太好的地方,首先,您的状态列表中必须有一个“当其他人=>”。除了 SPI_send,您的所有信号似乎都没有默认值。将状态机放在 if 语句中也不是一个好主意。如果你必须这样做,那么你必须在这两种情况下设置所有信号,否则你最终会得到锁存器而不是触发器。一种简单的方法是将代码请求时的所有信号设置为默认值,然后在需要时更改它们。

这样,综合工具就知道如何处理它们并且您得到正确的结果。

好吧,这是来自 Siemence 的 VHDL-for-synthesis 文档:

如果在 if 语句的所有可能的*分支中没有为信号或变量赋值,则推断为锁存器*。如果不打算推断锁存器,则必须在语句的所有分支中显式地为信号或变量赋值。

您可以在以下网址找到 PDF 格式的指南:http ://web.ewu.edu/groups/technology/Claudio/ee360/Lectures/vhdl-for-synthesis.pdf

于 2013-01-04T09:23:48.857 回答
0

您正在按照正确的基本思路进行思考,但是您的状态机有很多不完全正确的事情 - 正如其他答案所说 - 这些在模拟中很容易发现。

例如

        when READ_X =>
            SPI_Tx_buf <= query_x; -- Load in command
            y_data <= "00000" & SPI_Rx_buf(11 downto 1);
            SPI_send <= '1';
            SPI_action <= READ_Y;

现在,如果 SPI_Busy 在第二个周期内保持低电平,这显然不会停留在状态 READ_X,而是直接转换到 READ_Y,这可能不是您想要的。

更正常的状态机会将状态视为最外层,并对每个状态的输入信号进行不同的解释:

   case SPI_Action is             
      when READ_X => 
                     if SPI_Busy = '0' then    -- Wait here if busy
                        SPI_Tx_buf <= query_x; -- Load in command
                        y_data <= "00000" & SPI_Rx_buf(11 downto 1);
                        SPI_send <= '1';
                        SPI_action <= READING_X;
                     end if;
      when READING_X =>
                     if SPI_Busy = '1' then   -- command accepted
                        SPI_action <= READ_Y; -- ready to send next one
                     end if;
      when READ_Y => 
                     if SPI_Busy = '0' then    -- Wait here if busy

可以看到,这个版本把 Busy 信号当作握手,只有在 Busy 改变状态时才会进行。我敢肯定,如果您愿意,您可以使您的方法(如果最外层)起作用,但是您必须自己弄清楚如何应用握手原则。

也没有读取 X 或 Y 的“空闲”状态;该 SM 将尽可能快地交替读取 X 和 Y。通常,您会同时读取它们,然后返回空闲状态,直到其他一些开始信号命令您离开空闲并执行一组新的读取。

您还可以使用“当其他人”子句使状态机更加健壮。如果您保证涵盖所有定义的状态,这不是必须的,但它可以使维护更容易。另一方面,如果没有这样的子句,编译器会让你知道任何未发现的状态,以防出错。

有一种说法是“当其他人”子句是必不可少的,综合工具从“当其他人”子句生成更安全但不太优化的状态机。然而,有合成属性或命令行选项来控制合成工具如何生成状态机。

于 2013-01-04T11:58:58.223 回答