问答
直播中

何立立

9年用户 86经验值
擅长:可编程逻辑 嵌入式技术
私信 关注

【Z-turn Board试用体验】+SPI发送模块

本帖最后由 何立立 于 2015-7-5 20:04 编辑

  许多设备都是例如某些液晶屏是需要用到SPI协议传输数据的。  在SPI传输中有分为主机和从机之分。主机的定义,有CS使能权,产生串行时钟。反之从机的定义是CS被使能,接收串行时钟。此实验中FPGA是主机。
spi_time.png        在接收端很简单从机只要检测到CLK,在CLK的上升沿时采集数据即可接保存发送端发送的数据。
  在这里我们要干的事情只有一件而已,就是主机(FPGA)向从机写数据。SI端,SCL端和CS端都是由主机输出,从机输入。从机读取(锁存)数据都是CS被拉低,并且发生在SCL信号的上升沿。
SCL信号在空闲的时候总是处于高电平。当主机开始向从机写入数据,主机会先拉低CS信号,再拉低SCL信号,然后“设置”数据,亦即主机(FPGA)更新SI的数据(主机数据移位操作),最后再拉高SCL信号。同一时间,从机会因为SCL的上升沿变化,从机“锁存”(从机读取数据操作)SI上的数据。

  下面是此实验的RTL 原理图:
spi_weite.png


上图所示是要建立的功能模块spi_write_module.v ,亦即是主机的SPI发送模块。为了最大发挥 Verilog HDL语言特性,SPI_Data 和 SPI_Out 的位配置如下:

spi_bit.png
在对其它设备控制时候用到CS和A0,CS是使能被控制设备的信号(低电平有效)。A0 给被控设备的命令或者数据决定信号(0 = 命令,1 = 数据 )。SPI_Data : 第9位表示CS,第8位表示A0,第7 .. 0 位表示一字节数据。
SPI_Out : 第3位表示CS,第2位表示A0,第1位表示SCL,第0位表示SI。

  下面给出本实验的代码:
  1. module spi_write_module
  2. (
  3.     CLK, RSTn,
  4.          Start_Sig,
  5.          SPI_Data,
  6.          Done_Sig,
  7.          SPI_Out,
  8.          SPI_CLK,
  9.          SPI_DATA
  10. );
  11.     input CLK;
  12.          input RSTn;
  13.          input Start_Sig;
  14.          input [9:0]SPI_Data;
  15.          output Done_Sig;
  16.          output [3:0]SPI_Out; // [3]CS [2]A0 [1]rCLK [0]rDO
  17.          
  18.          output SPI_CLK;
  19.          output SPI_DATA;
  20.          /*****************************/
  21.          parameter T0P5US = 3'd6;                                        //半周期为0.5us 这里可以根据需要来更改SPI的时钟周期大小
  22.          /*****************************/
  23.          reg [2:0]Count1;
  24.          always @ ( posedge CLK or negedge RSTn )
  25.         if( !RSTn )
  26.                       Count1 <= 3'd0;
  27.                   else if( Count1 == T0P5US )  
  28.                       Count1 <= 3'd0;
  29.                   else if( Start_Sig )
  30.                       Count1 <= Count1 + 1'b1;
  31.                   else
  32.                       Count1 <= 3'd0;
  33.          /*****************************/
  34.          reg [4:0]i;
  35.          reg rCLK;
  36.          reg rDO;
  37.          reg isDone;
  38.          always @ ( posedge CLK or negedge RSTn )
  39.              if( !RSTn )
  40.                       begin
  41.                            i <= 5'd0;
  42.                                           rCLK <= 1'b1;
  43.                                           rDO <= 1'b0;
  44.                                           isDone <= 1'b0;
  45.                            end
  46.                   else if( Start_Sig )
  47.                       case( i )
  48.                                          5'd0, 5'd2, 5'd4, 5'd6, 5'd8, 5'd10, 5'd12, 5'd14:
  49.                                          if( Count1 == T0P5US ) begin rCLK <= 1'b0; rDO <= SPI_Data[ 7 - ( i >> 1) ]; i <= i + 1'b1; end
  50.                                          5'd1, 5'd3, 5'd5, 5'd7, 5'd9, 5'd11, 5'd13, 5'd15 :
  51.                                          if( Count1 == T0P5US ) begin rCLK <= 1'b1; i <= i + 1'b1; end
  52.                                          5'd16:
  53.                                          begin isDone <= 1'b1; i <= i + 1'b1; end
  54.                                          5'd17:
  55.                                          begin isDone <= 1'b0; i <= 5'd0; end
  56.                                 endcase
  57.     /*******************************************/
  58.          assign Done_Sig = isDone;
  59.     assign SPI_Out = { SPI_Data[9], SPI_Data[8], rCLK, rDO };
  60.           //为了方便观察把spi时序时钟信号拎出来看
  61.          assign SPI_DATA=rDO;
  62.          assign SPI_CLK=rCLK;
  63.          /*******************************************/
  64. endmodule
SPI_Out 第3位:表示了CS,所以直接由 SPI_Data的第9位驱动。
SPI_Out 第2位:表示了A0,同样也是直接由 SPI_Data 的第8位驱动。
SPI_Out 第1位:表示了SCL,以寄存器rCLK来驱动。
SPI_Out 第0位:表示了SI,以寄存器rDO来驱动。


下面给出仿真代码和结果:
设置传送数据为:SPI_Data=9'b01_10000001;//传送数据129。仿真代码:
  1. module TB;

  2.         // Inputs
  3.         reg CLK;
  4.         reg RSTn;
  5.         reg Start_Sig;
  6.         reg [9:0] SPI_Data;

  7.         // Outputs
  8.         wire Done_Sig;
  9.         wire [3:0] SPI_Out;
  10.         wire SPI_CLK;
  11.         wire SPI_DATA;

  12.         // Instantiate the Unit Under Test (UUT)
  13.         spi_write_module uut (
  14.                 .CLK(CLK),
  15.                 .RSTn(RSTn),
  16.                 .Start_Sig(Start_Sig),
  17.                 .SPI_Data(SPI_Data),
  18.                 .Done_Sig(Done_Sig),
  19.                 .SPI_Out(SPI_Out),
  20.                 .SPI_CLK(SPI_CLK),
  21.                 .SPI_DATA(SPI_DATA)
  22.         );

  23.         initial begin
  24.                 // Initialize Inputs
  25.                 CLK = 0;
  26.                 RSTn = 0;
  27.                 Start_Sig = 0;
  28.                 SPI_Data = 0;

  29.                 // Wait 100 ns for global reset to finish
  30.                 #100;
  31.         
  32.                 // Add stimulus here
  33.                 RSTn=1;
  34.                 Start_Sig = 1;
  35.                 SPI_Data=10'b01_10000001;//不妨就设传送数据为10000001(129)
  36.         end
  37.         
  38.       always #83.333 CLK=!CLK;
  39.       
  40. endmodule


为了方便看出传输的时序关系,特将SPI_CLK,和SPI_DATA即SPI_DATA拎出来看,仿真结果如下:
可以看出:SPI_DATA=10000001(129),SPI_CLK,都符合SPI时序关系。那么接收端的数据应该为

spidata.png










更多回帖

发帖
×
20
完善资料,
赚取积分