简介
SPI(serial peripheral interface):串行外设接口,高速全双工同步通信总线
接口信号
信 号 名 | 方 向 | 位 宽 | 描 述 |
sclk/sck | output | 1 | SPI时钟 |
csn | output | 1 | 片选信号 |
mosi | output | 1 | master output slave input |
miso | output | 1 | master input slave output |
可以一对一,一对多的连接:
注意是同名相连,MOSI—>MOSI,MISO—>MISO
四种传输模式:
时钟极性CPOL(clock polarity):时钟的初始状态是高电平还是低电平
- 0 :时钟高电平时有效,空闲时为低
- 1 :时钟低电平时有效,空闲时为高
时钟相位CPHA(clock phase) :采样(按MISO方向来说)在第一个边沿还是第二个边沿
- 0 :MISO数据采样发生在时钟(sclk)第一个边沿,MOSI反之
- 1 :MISO数据采样发生在时钟(sclk)第二个边沿,MOSI反之
下列代码按模式0,描述的是master:
`timescale 1ns/1ps
module m_spi #(
parameter MCLK =100,
parameter SCLK = 10,
parameter DATA_WIDTH = 8,
parameter CMD_DATA = 12)(
input clk ,
input rst_n ,
input cmd_vld ,
input [CMD_DATA-1:0] cmd_in ,
output reg cmd_rdy ,
output reg sclk ,
output reg csn ,
output mosi ,
input miso ,
output reg read_vld ,
output reg [DATA_WIDTH-1:0] read_data
);
localparam BAUD_NUM = MCLK/SCLK;
localparam [2:0] IDLE = 3'b000;
localparam [2:0] WADDR = 3'b001;
localparam [2:0] WDATA = 3'b010;
localparam [2:0] RWAIT = 3'b011;
localparam [2:0] RDATA = 3'b100;
reg [CMD_DATA-1:0] cmd_in_buf;
reg [11:0] shift_data;
reg [4:0] baud_cnt;
reg [4:0] bit_cnt ;
reg [2:0] wait_time;
reg swr_done;
wire sclk_pdg;
wire sclk_ndg;
reg [2:0] cur_sta;
reg [2:0] nxt_sta;
// cmd_in buffer
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
cmd_in_buf <= {(CMD_DATA){1'b0}};
else if(cmd_vld&&cmd_rdy)
cmd_in_buf <= cmd_in;
else
cmd_in_buf <= cmd_in_buf;
end
//generate cmd_rdy
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
cmd_rdy <= 1'b1;
else if(cmd_vld && cmd_rdy)
cmd_rdy <= 1'b0;
else if((cur_sta==WDATA&&swr_done) || (cur_sta==RDATA&&read_vld))
cmd_rdy <= 1'b1;
else
cmd_rdy <= cmd_rdy;
end
//generate baud
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
baud_cnt <= 4'b0;
else if(baud_cnt==BAUD_NUM-1)
baud_cnt <= 4'b0;
else if(cur_sta==IDLE)
baud_cnt <= 4'b0;
else if(cur_sta!=IDLE&&cur_sta!=RWAIT)
baud_cnt <= baud_cnt+1'b1;
end
// bit cnt
always@( posedge clk or negedge rst_n)
begin
if( !rst_n)
bit_cnt<=4'b0;
else if(bit_cnt==CMD_DATA-1&&sclk_ndg)
bit_cnt <= 4'b0;
else if(sclk_ndg)
bit_cnt <= bit_cnt+1'b1;
end
//wait_time
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
wait_time = 3'b0;
else if(wait_time==3'd2)//set wait time
wait_time <= 3'b0;
else if(cur_sta==RWAIT)
wait_time <= wait_time+1'b1;
else
wait_time <= wait_time;
end
// generate csn singal
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
csn <= 1'b1;
else if(cmd_vld&&cmd_rdy || wait_time==3'd2)//低电平有效
csn <= 1'b0;
else if(cur_sta==WDATA&&swr_done)
csn <= 1'b1;
else if(cur_sta==RDATA&&swr_done)
csn <= 1'b1;
else if(cur_sta==RWAIT)
csn <= 1'b1;
else
csn <= csn;
end
//generate sclk singal
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
sclk <= 1'b0;
else if(sclk_pdg||sclk_ndg)
sclk <= ~sclk;
else if(csn)
sclk <= 1'b0;
end
//generate mosi singal
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
shift_data <= 12'b0;
else if(!cmd_in_buf[11]&&bit_cnt==4'd3&&sclk_ndg)//读
shift_data <= 12'b0;
else if(cmd_vld)
shift_data <= cmd_in;
else if((cur_sta==WADDR||cur_sta==WDATA)&&sclk_ndg)
shift_data<= {shift_data[10:0],1'b0};
end
assign mosi = shift_data[11];//mosi下降沿发送数据,在模式0下工作,时钟极性CPOL=0,时钟相位CPHA=0(miso第一个沿采样)
//generate read data
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
read_data <= 8'b0;
else if(cur_sta==RDATA&&sclk_pdg)
read_data= {read_data[6:0],miso};
end
//generate read_vld singal
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
read_vld <= 1'b0;
else if (cur_sta==RDATA&&swr_done)
read_vld <= 1'b1;
else
read_vld <= 1'b0;
end
assign sclk_pdg = (baud_cnt==(BAUD_NUM>>1)-1);//表示sclk的上升沿
assign sclk_ndg = (baud_cnt==BAUD_NUM-1); //表示sclk的下降沿
assign swr_done = (bit_cnt==4'd11&&sclk_ndg); //write、read finish
//state machine
always@( posedge clk or negedge rst_n)
begin
if(!rst_n)
cur_sta <= IDLE;
else
cur_sta <= nxt_sta;
end
always@(*)
begin
case(cur_sta)
IDLE :
nxt_sta = (cmd_vld && cmd_rdy) ? WADDR : IDLE;
WADDR : begin
if(cmd_in_buf[CMD_DATA-1]&&(bit_cnt==4'd3)&&sclk_ndg)//写地址完成
nxt_sta = WDATA;
else if (!cmd_in_buf[CMD_DATA-1]&&(bit_cnt==4'd3)&&sclk_ndg)//读
nxt_sta = RWAIT;
else
nxt_sta = WADDR;
end
WDATA :
nxt_sta = swr_done ? IDLE : WDATA;
RWAIT :
nxt_sta = wait_time==3'd2 ? RDATA : RWAIT;
RDATA :
nxt_sta = read_vld ? IDLE : RDATA;
default:
nxt_sta = IDLE;
endcase
end
endmodule
`timescale 1ns/1ps
module s_spi(
input sclk ,
input rst_n,
input csn ,
input mosi ,
output miso
);
reg [3:0] bit_cnt;
reg wr_flag;
reg [2:0] addr;
reg [7:0] data;
reg [7:0] shift_data;
reg [7:0] mem [0:7];
integer i;
//bit cnt
always@(posedge sclk or negedge rst_n)
begin
if(!rst_n)
bit_cnt <= 4'b0;
else if(bit_cnt==4'd11)
bit_cnt <= 4'b0;
else
bit_cnt <= bit_cnt+1'b1;
end
//wr_flag
always @( posedge sclk or negedge rst_n)
begin
if(!rst_n)
wr_flag <= 1'b0;
else if(bit_cnt == 4'b0 && !csn)
wr_flag <= mosi;
end
// addr
always @(posedge sclk or negedge rst_n)begin
if(!rst_n)
addr <= 3'b0;
else if(!csn && bit_cnt >= 1 && bit_cnt <= 3)
addr <= {addr[1:0],mosi};
end
//write_data
always @(posedge sclk or negedge rst_n)
begin
if(!rst_n)
data <= 8'b0;
else if(!csn && bit_cnt >= 4&& bit_cnt<= 11)
data<= {data[6:0],mosi};
end
// mem
always @(negedge sclk or negedge rst_n)
begin
if(!rst_n)begin
for(i=0;i<=7;i=i+1)
mem[i] <= {8{1'b0}};
end
else if(bit_cnt == 4'd0 && wr_flag)
mem[addr] <= data;
end
always@(negedge sclk or negedge rst_n or negedge csn)
begin
if(!rst_n || csn)
shift_data <= 8'b0;
else if(bit_cnt == 4'd4 && !wr_flag && !csn)
shift_data <= mem[addr] ;
else if(!csn)
shift_data <= {shift_data[6:0],1'b0};
end
assign miso = shift_data[7] ;
endmodule