FPGA 串口SPI(Serial Peripheral Interface,串行外围设备接口)通讯协议,

`timescale 1ns / 1ps

//spi

module spi#(
          parameter SYSCLK = 50_000_000,
          parameter SPICLK = 20_000_000   
 )(           
            input            sysclk,
            input            rst_n,
  //用户接口
            input                  spi_req,//spi的开始信号
            input                  spi_valid,//数据有效信号
            input    [7:0]         data_din,//数据通过MOSI  发送给从机的数据
            input    [7:0]         spi_byte_number,//字节数,本次通信能够发送的字节数   
            output    reg          spi_byte_done,//每发送一个字节的完成信号
            output     reg            spi_done,     //spi通信结束发送数据完成的信号
            output   reg [7:0]     spi_dout,      //从机给主机的数据(采集MISO的)
  //标准四线spi接口
           output      reg         CS,     //片选信号 用来选择从机拉低有效
           output      reg         SCK,     //时钟线,用于同步数据收发
           output      reg         MOSI  ,  //主机发送 ,从机接收
           input                   MISO     //主机接收,从机发送
          
          
    );
    
          localparam delay  = SYSCLK / SPICLK;    //SCK的分频系数,用于对SCK分频
          localparam MID    = delay >> 1;         //二分之一的SCK的位置     (>>   代表右移一位 ,等效于 /2)
          localparam Q_MID  = delay >> 2;         //四分之一的SCK的位置     (>>   代表右移两位 ,等效于 /4)    
          localparam TQ_MID = MID + Q_MID;        //四分之三的SCK的位置    
          reg     [31:0]    cnt_sck;//用于对SCK计数
          reg     [31:0]     cnt_bit;//对数据进行计数
          reg     [31:0]    cnt_byte;//对发送字节进行计数
          reg     [7:0]     data;//对输入进来的数据打拍目的:将数据同步到当前时钟域下
          //   对输入进来的数据打拍       

          always@(posedge sysclk)
            if(!rst_n)
              data<=0;
            else if(spi_valid)
             data<=data_din;
             else
             data<=data;
          //cnt_sck对分频计数器赋值
          always@(posedge sysclk)
             if(!rst_n)
               cnt_sck<=0;
              else if(CS==0)begin
              if(cnt_sck==delay-1)
               cnt_sck<=0;
              else
               cnt_sck<=cnt_sck+1;
               end
              else
               cnt_sck<=0;
               //cnt_bit
               always@(posedge sysclk)
                 if(!rst_n)
                  cnt_bit<=0;
                  else if(CS==0)begin
                if(cnt_sck==delay-1)begin
                      if(cnt_bit==7)
                      cnt_bit<=0;
                     else
                  cnt_bit<=cnt_bit+1;
                  end
                 else 
                  cnt_bit<=cnt_bit ;
              end
              else
               cnt_bit<=0;
               //----cnt_byte字节计数
   always @(posedge sysclk ) begin
    if(!rst_n)
        cnt_byte <= 0;
    else if(CS==0)begin
        if(cnt_bit == 7 && cnt_sck == delay - 1)begin    //发生完成一个字节
            if(cnt_byte == spi_byte_number - 1) //将所有的字节发生完毕
                cnt_byte <= 0;
            else
                cnt_byte <= cnt_byte + 1;
        end
        else
            cnt_byte <= cnt_byte;
    end
    else
        cnt_byte <= 0;
end

                   //CS
               always@(posedge sysclk)
                if(!rst_n||spi_done) //当复位或者spi通信结束的时候,CS拉高
                 CS<=1;
                else if(spi_req)
                 CS<=0;
                else
                 CS<=CS;
             
                 //spi_byte_done
              always@(posedge sysclk)
                if(!rst_n)
                 spi_byte_done<=0;          
               else if(cnt_sck==delay-1&&cnt_bit==7)
                 spi_byte_done<=1;
                else 
                spi_byte_done<=0;
                  //spi_done
                 always@(posedge sysclk)
                if(!rst_n)
                 spi_done<=0;          
               else if(cnt_sck==delay-1&&cnt_bit==7&&cnt_byte==spi_byte_number-1)//所有的字节发生完毕
                 spi_done<=1;
                else 
                spi_done<=0;
                
                 //spi_dout采集从机返回(MISO)的数据
                 always@(posedge sysclk)
                   if(!rst_n)
                    spi_dout<=0;
                   else if(CS==0)begin
                   if(cnt_sck==MID-1)//数据采样
                   spi_dout<={spi_dout[6:0],MISO};//采集进来的数据是高位
                   else
                   spi_dout<=spi_dout;
                   end
                  else
                  spi_dout<=0;
                   
                   //SCK 0模式
                    always@(posedge sysclk)
                      if(!rst_n)
                        SCK<=0;//空闲时刻 SCK为低电平
                      else if(CS==0)begin
                   if(cnt_sck>=Q_MID&&cnt_sck<=TQ_MID-1) //四分之一 到 四分之三的时候拉高            
                      SCK<=1;
                  else 
                   SCK<=0;
                 end
                  else
                  SCK<=SCK; 
                   //MOSI
                 always@(posedge sysclk)
                  if(!rst_n)
                   MOSI<=0;//空闲时刻 SPI时序没有明确规定是高电平 还是 低电平 
                   else if(CS==0)begin
                  if(cnt_sck==MID-1)//数据跟新
                   MOSI<=data_din[7-cnt_bit];
                 else
                  MOSI<=MOSI;
                 end
               else
                MOSI<=0;
    
    
endmodule

仿真

`timescale 1ns / 1ps



module tb_spi(

    );
        reg        sysclk     ;
        reg        rst_n      ;
        reg       spi_req    ;
        reg   [7:0]     data_din   ;
        reg     [7:0]   spi_byte_number;
        reg         MISO;
        reg        spi_valid;
        wire       spi_byte_done  ;
        wire       spi_done       ;
        wire   [7:0]    spi_dout       ;
        wire       CS             ;
        wire       SCK            ;
        wire       MOSI           ;
    
    initial begin
         sysclk         =0;
         rst_n          =0;
         spi_req        =0;
         spi_valid       =1;
         MISO           =1; 
         data_din       =8'b1010_1010;
        spi_byte_number =5;   
      #101
       rst_n=1;
       spi_req=1;
        #500 spi_req = 1;   //开始信号
    #20  spi_req = 0;

    end
    always #10 sysclk=~sysclk;
    
    
    
    
    
    
    
    
 spi#(
          . SYSCLK ( 50_0),
          . SPICLK ( 20_0)   
 )spi_u(           
          .     sysclk             (sysclk          )          ,
          .     rst_n              (rst_n           )         ,
          .     spi_req           ( spi_req        ) ,
            .   spi_valid      (spi_valid      )           ,//spi的开始信号
          .     data_din          ( data_din       )           ,//数据通过MOSI  发送给从机的数据
          .     spi_byte_number   ( spi_byte_number)            ,//字节数,本次通信能够发送的字节数   
          .     spi_byte_done     ( spi_byte_done  )            ,//每发送一个字节的完成信号
          .     spi_done          ( spi_done       )            ,     //spi通信结束发送数据完成的信号
          .     spi_dout          ( spi_dout       )             ,      //从机给主机的数据(采集MISO的)
          .     CS                ( CS             )                    ,     //片选信号 用来选择从机拉低有效
          .     SCK               ( SCK            )            ,     //时钟线,用于同步数据收发
          .     MOSI              ( MOSI           )                   ,  //主机发送 ,从机接收
          .     MISO              ( MISO           )                  //主机接收,从机发送
          
          
    );   
    
    
    
    
    
    
    
    
    
endmodule

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值