前言
当你器件的引脚贼少的时候,需要主机和从机通信,spi就派上了用场,它可以一对多,但只是片选到的从机能和主机通信,其他的挂机。
spi:serial peripheral interface 串行外围接口
大致了解:
spi是个同步协议,数据在master和slaver间交换通过时钟sck,由于它是同步协议,时钟速率就可以各种变换。
sck:主机提供,从机不能操控,从器件由主机产生的时钟控制。数据只有在sck来了的上升沿或者下降沿才传输。
高级一点的spi芯片有配置寄存器,高级一点的工作有四种模式,采样相位和sck空闲电平可配置。
当然在这里我们主要实现简单的spi协议:sck是系统时钟的四分频,wr请求信号有效时,主机开始工作,数据位8bit,sck空闲时低电平,工作时第一个沿数据传输。只有一个从机,cs低电平片选。
看下结构:
接口定义:
编码实现:(版权所有,请勿用于商业用途,仅供学习使用)
//************************************************ // Filename : spi_ms_test1.v // Author : Kingstacker // Company : School // Email : [email protected] // Device : Altera cyclone4 ep4ce6f17c8 // Description : spi master module;data 8bit;sck is 4 div of the clk; //************************************************ )( //input; input wire clk, input wire rst_n, input wire wr, //send request; :] master_din, //the data you want send; input wire miso, //the data form slave; //output; output reg cs, //slave select; output reg sck, //data exchange clock; output reg mosi, //master out; :] master_dout //the data you received; ); :] DIV_NUMBER = ; //div number,you can change; ) - 'b1; //max cnt number,6/2 -1 ; reg cnt; //sck cnt; reg sck_en; //enable sck; reg data_cnt_en; reg sck_reg1; reg sck_reg2; wire sck_p; //posedge sck; wire sck_n; //negedge sck; wire send_over; 'b00; 'b01; 'b10; :] cstate; :] data_cnt; //cnt the send data; :] master_din_reg; :] master_dout_reg; :] mosi_cnt; //produce sck; always @(posedge clk or negedge rst_n) begin if (~rst_n) begin cnt <= ; sck <= 'b0; end //if else begin 'b1) begin if (cnt == CNT_MAX) begin cnt <= ; sck <= ~sck; end else begin cnt <= cnt + 'b1; sck <= sck; end end else begin cnt <= ; sck <= 'b0; end end //else end //always //produce sck_p and sck_n; always @(posedge clk or negedge rst_n) begin if (~rst_n) begin sck_reg1 <= 'b0; sck_reg2 <= 'b0; end //if else begin sck_reg1 <= sck; sck_reg2 <= sck_reg1; end //else end //always assign sck_p = (sck_reg1 & (~sck_reg2)); //sck posedge; assign sck_n = ((~sck_reg1) & sck_reg2); //sck negedge; //fsm;hot code; always @(posedge clk or negedge rst_n) begin if (~rst_n) begin cstate <= IDEL; end else begin case (cstate) IDEL: cstate <= (wr)? SEND : IDEL; SEND: cstate <= (send_over) ? FINISH : SEND; FINISH: cstate <= IDEL; default: cstate <= IDEL; endcase //case end end always @(posedge clk or negedge rst_n) begin if (~rst_n) begin cs <= 'b1; data_cnt_en <= 'b0; sck_en <= 'b0; master_din_reg <= ; master_dout <= ; end else begin case (cstate) IDEL: begin data_cnt_en <= 'b0; master_din_reg <= (wr) ? master_din : master_din_reg; //load the data you want send to slaver; end SEND: begin data_cnt_en <= 'b1; cs <= 'b0; sck_en <= 'b1; master_dout <= (send_over) ? master_dout_reg : master_dout; //master receiverd data; end FINISH: begin //send and load ok; sck_en <= 'b0; cs <= 'b1; data_cnt_en <= 'b0; end default: begin cs <= 'b1; sck_en <= 'b0; data_cnt_en <= 'b0; end endcase //case end end always @(posedge clk or negedge rst_n) begin if (~rst_n) begin data_cnt <= ; end else begin data_cnt <= (data_cnt_en) ? (data_cnt + 'b1) : 5'd0; //4 div * 8bit = 32 cnt; end end 'b0; //rising edge miso; always @(posedge clk or negedge rst_n) begin if (~rst_n) begin master_dout_reg <= ; end else begin master_dout_reg <= (sck_p) ? {master_dout_reg[:],miso} : master_dout_reg; end end //miso; always @(posedge clk or negedge rst_n) begin if (~rst_n) begin mosi_cnt <= ; end else begin if (sck_n) begin 'd7) begin mosi_cnt <= ; end else begin mosi_cnt <= mosi_cnt + 'b1; end end else begin mosi_cnt <= mosi_cnt; end end end always @(posedge clk or negedge rst_n) begin if (~rst_n) begin mosi <= 'b0; end else begin mosi <= (sck_n) ? master_din_reg['d7-mosi_cnt] : mosi; end end endmodule
仿真:
综合资源使用:
Fmax:
以上。