Verilog实现的SPI通信

SPI,Serial Peripheral Interface,串行外设接口,高速的、全双工、同步通信总线。SPI以主从方式工作,一般需要至少4根线(单向传输时可用3根):
(1)MISO– Master Input Slave Output,主设备数据输入,从设备数据输出;
(2)MOSI– Master Output Slave Input,主设备数据输出,从设备数据输入;
(3)SCLK – Serial Clock,时钟信号,由主设备产生;
(4)CS – Chip Select,从设备使能信号,由主设备控制。

SPI共有4种工作模式,常用的是模式0和模式3,具体如下:

Verilog实现的SPI通信_第1张图片

Verilog实现的SPI通信_第2张图片

 以下为Verilog实现的SPI主机程序,系统时钟为24MHz,分频成1MHz作为SCLK时钟。收发8位数据时,至少需要8个时钟,考虑到CS使能(作为同步信号)以及双向通信的需要,设置10个节拍。起始CS=0使能主机发送,此后1-8节拍按照从高位到低位时钟上升沿依次发送数据,同时在时钟下降沿1-8节拍,读取从机数据。

//非常简单的SPI通信协议,上升沿发送,下降沿接收。
//双工工作。CS=0使能发送。8位数据。无校验。
//实现功能:主机拨码开关控制从机LED,从机拨码开关控制主机LED
module SPI_Master (
    input   reset, //reset
    input   clk_24M, //时钟24M
    output  MOSI,  //主机发送
    input   MISO,  //主机接收
    input   [7:0] Data_SW , //发送的8位数据,开关实现
    output  CS,            //CS
    output  SCLK,          //SPI时钟
    output  [7:0] Data_LED  //接收的8位数据,发光管实现
);
    reg [7:0] rData_LED;
    reg [3:0] N;//分频计数器
    reg rSCLK;
    reg rMOSI;
    reg rCS;
    reg [3:0] cnt; //发收状态计数器       
assign SCLK=rSCLK;
assign MOSI=rMOSI;
assign CS=rCS;
assign Data_LED=rData_LED;

always@(posedge clk_24M or negedge reset)        //分频到1MHz
  if(!reset) 
       begin rSCLK<=1; N<=0; end
  else if(N>=11)                              
			begin N<=0; rSCLK<=~rSCLK; end
		else
			N<=N+1; 
   
always @(posedge SCLK or negedge reset)    //主机发送
   if (!reset) 
        begin rMOSI<=1; rCS<=1;cnt<=0; end
    else
          case(cnt)
            0:begin rCS<=0;cnt<=1; end           //使能主机发送
            1:begin rMOSI<=Data_SW[7];cnt<=2; end
            2:begin rMOSI<=Data_SW[6];cnt<=3; end
            3:begin rMOSI<=Data_SW[5];cnt<=4; end
            4:begin rMOSI<=Data_SW[4];cnt<=5; end
            5:begin rMOSI<=Data_SW[3];cnt<=6; end
            6:begin rMOSI<=Data_SW[2];cnt<=7; end
            7:begin rMOSI<=Data_SW[1];cnt<=8; end
            8:begin rMOSI<=Data_SW[0];cnt<=9; end                                                                                          
            default:begin rCS<=1;cnt<=0; end    //关闭发送        
          endcase       

always @(negedge rSCLK )      //主机接收
          case(cnt)
             1:rData_LED[7]<=MISO;
             2:rData_LED[6]<=MISO;
             3:rData_LED[5]<=MISO;
             4:rData_LED[4]<=MISO;
             5:rData_LED[3]<=MISO;
             6:rData_LED[2]<=MISO;
             7:rData_LED[1]<=MISO;
             8:rData_LED[0]<=MISO;
             default: ;           
          endcase      
 endmodule

从机SCLK和CS均来自主机,0-7节拍上升沿时钟向主机发送数据,当CS=0下降沿1-8节拍读取主机数据。

//非常简单的SPI通信协议,从机程序,上升沿发送,下降沿接收。
//双工工作。8位数据,无校验。
//主机拨码开关控制从机LED,从机拨码开关控制主机LED
//从机时钟来源于主机
module SPI_Slave (
    input    MOSI,  //从机接收
	output   MISO,  //从机发送
    input    [7:0] Data_SW , //发送的8位数据,开关实现
    input    CS,            //CS
    input    SCLK,          //SPI时钟
    output  [7:0] Data_LED  //接收的8位数据,发光管实现
);
    reg [3:0] cnt; //发收状态计数器
    reg rMISO;       
    reg[7:0] rData_LED; 
assign Data_LED=rData_LED;
assign MISO=rMISO;
always @(posedge SCLK )    //从机发送
    	case(cnt)
             0: rMISO<=Data_SW[7];  //由高到低发
             1: rMISO<=Data_SW[6];
             2: rMISO<=Data_SW[5];
             3: rMISO<=Data_SW[4];
             4: rMISO<=Data_SW[3];
             5: rMISO<=Data_SW[2];
             6: rMISO<=Data_SW[1];
             7: rMISO<=Data_SW[0];                 
             default:rMISO<=1;          
            endcase
 always @(negedge SCLK )      //从机接收
       if (CS) cnt<=0;     
          else 
            case(cnt)
			 0:cnt<=1;
             1:begin rData_LED[7]<=MOSI; cnt<=2; end
             2:begin rData_LED[6]<=MOSI; cnt<=3; end
             3:begin rData_LED[5]<=MOSI; cnt<=4; end
             4:begin rData_LED[4]<=MOSI; cnt<=5; end
             5:begin rData_LED[3]<=MOSI; cnt<=6; end
             6:begin rData_LED[2]<=MOSI; cnt<=7; end
             7:begin rData_LED[1]<=MOSI; cnt<=8; end
             8:begin rData_LED[0]<=MOSI; cnt<=9; end                           
             default:cnt<=0;            
          endcase      
endmodule

作为主从机的两块开发板通信测试时,注意要共地,即GND需用线连接。此代码在安陆ELF1A650、高云GW1NSR开发板上获得通过。

切记,以上为非标准的SPI协议,若与其他设备通信时,有可能失败。有时候不需要双向传输,此时仅需MOSI、CS、SCLK三根线即可,为方便计,从机端可进一步译码,用数码管显示。

//非常简单的SPI通信协议,上升沿发送,无接收。
//CS=0使能发送。8位数据。无校验。
//实现功能:主机拨码开关发送数据。
module SPI_Master (
    input   reset, //reset
    input   clk_24M, //时钟24M
    output  MOSI,  //主机发送
    input   [7:0] Data_SW , //发送的8位数据,开关实现
    output  CS,            //CS
    output  SCLK          //SPI时钟
);
    reg [3:0] N;//分频计数器
    reg rSCLK;
    reg rMOSI;
    reg rCS;
    reg [3:0] cnt; //状态计数器       
assign SCLK=rSCLK;
assign MOSI=rMOSI;
assign CS=rCS;

always@(posedge clk_24M or negedge reset)        //分频到1MHz
  if(!reset) 
       begin rSCLK<=1; N<=0; end
  else if(N>=11)                              
			begin N<=0; rSCLK<=~rSCLK; end
		else
			N<=N+1; 
   
always @(posedge SCLK or negedge reset)    //主机发送
   if (!reset) 
        begin rMOSI<=1; rCS<=1;cnt<=0; end
    else
          case(cnt)
            0:begin rCS<=0;cnt<=1; end           //使能主机发送
            1:begin rMOSI<=Data_SW[7];cnt<=2; end
            2:begin rMOSI<=Data_SW[6];cnt<=3; end
            3:begin rMOSI<=Data_SW[5];cnt<=4; end
            4:begin rMOSI<=Data_SW[4];cnt<=5; end
            5:begin rMOSI<=Data_SW[3];cnt<=6; end
            6:begin rMOSI<=Data_SW[2];cnt<=7; end
            7:begin rMOSI<=Data_SW[1];cnt<=8; end
            8:begin rMOSI<=Data_SW[0];cnt<=9; end                                                                                          
            default:begin rCS<=1;cnt<=0; end    //关闭发送        
          endcase       
 endmodule
//非常简单的SPI通信协议,从机程序,下降沿接收。
//8位数据,无校验。
//主机拨码开关发送数据,从机接收并译码数码管显示。
//从机时钟来源于主机
module SPI_Slave (
    input    MOSI,  //从机接收
    input    CS,            //CS
    input    SCLK,          //SPI时钟
    output  [1:0] DG,        //两位数码管位码  
    output  [7:0] SEG  //数码管段码
);
    reg [3:0] cnt; //状态计数器    
    reg[7:0] rData; 
    reg[3:0] count; //要显示的数,四位2进制
    reg[1:0] rDG; 
    reg[7:0] rSEG; 
    assign DG=rDG; 
    assign SEG=rSEG; 
always @(negedge SCLK )      //从机接收
       if (CS) cnt<=0;     
          else 
            case(cnt)
			 0:cnt<=1;
             1:begin rData[7]<=MOSI; cnt<=2; end
             2:begin rData[6]<=MOSI; cnt<=3; end
             3:begin rData[5]<=MOSI; cnt<=4; end
             4:begin rData[4]<=MOSI; cnt<=5; end
             5:begin rData[3]<=MOSI; cnt<=6; end
             6:begin rData[2]<=MOSI; cnt<=7; end
             7:begin rData[1]<=MOSI; cnt<=8; end
             8:begin rData[0]<=MOSI; cnt<=9; end                           
             default:cnt<=0;            
          endcase
always @ (posedge SCLK)
    case(DG)
      2'b10: begin rDG<=2'b01;count=rData[7:4];end  //高四位 
      2'b01: begin rDG<=2'b10;count=rData[3:0];end  //低4位
      default:  begin rDG<=2'b10;end
   endcase
always@(count)      //数码管译码输出
  case(count)
        0:rSEG=8'b11000000;
        1:rSEG=8'b11111001;
		2:rSEG=8'b10100100;
		3:rSEG=8'b10110000;
		4:rSEG=8'b10011001;
		5:rSEG=8'b10010010;
		6:rSEG=8'b10000010;
		7:rSEG=8'b11111000;
		8:rSEG=8'b10000000;
		9:rSEG=8'b10010000;
        10:rSEG=8'b10001000;
        11:rSEG=8'b10000011;
        12:rSEG=8'b11000110;
        13:rSEG=8'b10100001;
        14:rSEG=8'b10000110;
        default:rSEG=8'b10001110;
  endcase		      
endmodule

附:ESP8266(Node MCU)有SPI的Demo,包括Master和Slave,如下图。不过需要注意的是,其引脚应接HSCLK(GPIO14,D5)、HMISO(GPIO12,D6)、HMOSI(GPIO13,D7)、HCS(GPIO15,D8)。

Verilog实现的SPI通信_第3张图片

 

 

你可能感兴趣的:(Verilog,fpga开发)