W5500 FPGA 之一(SPI接口)
FPGA似乎成为我的救命稻草。2007年开始接触Xilinx的FPGA,经过五年的学习,竟又放下了七年。近两年又开始做Altera的FPGA。现在用低成本EP4CE10E22比较顺手,可以把FPGA当作CPU来用。
最近要做一个语音采集,并要把语音推送到局域网上,不得已要用到W5500(其实对FPGA而言,应该选用W5300,不过换芯片应该是容易的,就是把SPI接口换成并口)。首先记录一下SPI接口。
一 SPI接口
多年已经习惯直接用SignalTap或者Chipscope,都忘记Modsim怎么用了。太喜欢Quartus,因为Vivado的Chipscope做得太烂,动不动就编译不过。先放源代码
//端口定义
module W5500(
input Clk,
output reg Reset, //0:Reset 1:Work
output reg CH378SPI_DO, //CH378
input CH378SPI_DI, //CH378
output reg CH378SPI_Clk, //CH378
output reg CH378SPI_CS
);
//硬件复位
reg[15:0] Reset_Counter;
always@(posedge Clk)
begin
if(Reset_Counter<60000) begin Reset_Counter<=Reset_Counter+1; end
else begin Reset_Counter<=Reset_Counter; end
if((100<Reset_Counter)&&(Reset_Counter<50000)) Reset<=0;
else Reset<=1;
end
//变量定义
reg SPI_Send_Start,SPI_Send_Start_1,SPI_Send_Start_2,SPI_Send_Start_3;
reg[7:0] SPI_Period;
reg[15:0] SPI_Address,SPI_Address_1;
reg[7:0] SPI_Control,SPI_Control_1;
reg[47:0] SPI_Data;
reg[7:0] SPI_Data1,SPI_Data2,SPI_Data3,SPI_Data4,SPI_Data5,SPI_Data6;
reg[7:0] Address_Len,Control_Len,Data_Len,Data_Len_1;
reg WR; //1:Write 0:Read
//SPI接口
always@(posedge Clk)
begin
if(Reset==0)
begin
CH378SPI_CS<=1;
CH378SPI_Clk<=0;
CH378SPI_DO<=0;
end
else if((SPI_Send_Start_2==1)&&(SPI_Send_Start_3==0)) //上升沿触发SPI
begin
CH378SPI_CS<=0;
CH378SPI_Clk<=0;
SPI_Address<=SPI_Address_1; //参数装载
SPI_Control<=SPI_Control_1;
SPI_Data<={SPI_Data1,SPI_Data2,SPI_Data3,SPI_Data4,SPI_Data5,SPI_Data6};
Address_Len<=16; //地址长度是固定的
Control_Len<=8; //控制段长度也是固定的
Data_Len<=Data_Len_1; //数据段是可变的,本段程序最多可收发6个字节
end
else if(Address_Len>=1)
begin
if (SPI_Period==0) CH378SPI_DO<=SPI_Address[Address_Len-1];
else if(SPI_Period==15) CH378SPI_Clk<=1;
else if(SPI_Period==30) CH378SPI_Clk<=0;
else if(SPI_Period==34) Address_Len<=Address_Len-1;
end
else if(Control_Len>=1)
begin
if (SPI_Period==0) CH378SPI_DO<=SPI_Control[Control_Len-1];
else if(SPI_Period==15) CH378SPI_Clk<=1;
else if(SPI_Period==30) CH378SPI_Clk<=0;
else if(SPI_Period==34) Control_Len<=Control_Len-1;
end
else if(Data_Len>=1)
begin
if (SPI_Period==0) CH378SPI_DO<=SPI_Data[Data_Len-1]&WR;
else if(SPI_Period==15) CH378SPI_Clk<=1;
else if(SPI_Period==30) CH378SPI_Clk<=0;
else if(SPI_Period==34) Data_Len<=Data_Len-1;
end
else if(Data_Len==0)
begin
CH378SPI_CS<=1;
end
if((SPI_Send_Start_2==1)&&(SPI_Send_Start_3==0)) SPI_Period<=0;
else if(SPI_Period<35) SPI_Period<=SPI_Period+1;
else SPI_Period<=0;
SPI_Send_Start_3<=SPI_Send_Start_2;
SPI_Send_Start_2<=SPI_Send_Start_1;
SPI_Send_Start_1<=SPI_Send_Start;
end
要启动“SPI接口”这段程序,需要有几点前提:1.装置参数 SPI_Address_1; SPI_Control_1;{SPI_Data1,SPI_Data2,SPI_Data3,SPI_Data4,SPI_Data5,SPI_Data6};
2.开启触发信号SPI_Send_Start的上升沿
下面一段程序就是设置各个寄存器
reg[32:0] Cycle_Counter;
always@(posedge Clk)
begin
if(Cycle_Counter<330000000) Cycle_Counter<=Cycle_Counter+1;
else Cycle_Counter<=Cycle_Counter;
//定时器支持8秒运行,不循环
if(Cycle_Counter==200012000) //写入 通用MR 软件复位
begin
SPI_Send_Start<=0;
end
else if(Cycle_Counter==200012010)
begin
WR<=1;
SPI_Control_1<=8'h04; //Write
SPI_Address_1<=MR;
SPI_Data6<=8'h80;
Data_Len_1<=8; //32Bit
SPI_Send_Start<=1;
end
330000000
else if (Cycle_Counter==300000000) //写入网关
begin
SPI_Send_Start<=0;
end
else if(Cycle_Counter==300000010)
begin
WR<=1;
SPI_Control_1<=8'h04; //Write
SPI_Address_1<=GAR;
SPI_Data3<=192;
SPI_Data4<=168;
SPI_Data5<=1;
SPI_Data6<=1;
Data_Len_1<=32; //32Bit
SPI_Send_Start<=1;
end
else if(Cycle_Counter==300003000) //写入子网掩码
begin
SPI_Send_Start<=0;
end
else if(Cycle_Counter==300003010)
begin
WR<=1;
SPI_Control_1<=8'h04; //Write
SPI_Address_1<=SUBR;
SPI_Data3<=255;
SPI_Data4<=255;
SPI_Data5<=255;
SPI_Data6<=0;
Data_Len_1<=32; //32Bit
SPI_Send_Start<=1;
end
else if(Cycle_Counter==300006000) //写入物理地址
begin
SPI_Send_Start<=0;
end
else if(Cycle_Counter==300006010)
begin
WR<=1;
SPI_Control_1<=8'h04; //Write
SPI_Address_1<=SHAR;
SPI_Data1<=8'h0C;
SPI_Data2<=8'h29;
SPI_Data3<=8'hAB;
SPI_Data4<=8'h7C;
SPI_Data5<=8'h00;
SPI_Data6<=8'h01;
Data_Len_1<=48; //32Bit
SPI_Send_Start<=1;
end
else if(Cycle_Counter==300009000) //写入IP
begin
SPI_Send_Start<=0;
end
else if(Cycle_Counter==300009010)
begin
WR<=1;
SPI_Control_1<=8'h04; //Write
SPI_Address_1<=SIPR;
SPI_Data3<=192;
SPI_Data4<=168;
SPI_Data5<=1;
SPI_Data6<=199;
Data_Len_1<=32; //32Bit
SPI_Send_Start<=1;
end
else if(Cycle_Counter==300012000) //写入Mode
begin
SPI_Send_Start<=0;
end
else if(Cycle_Counter==300012010)
begin
WR<=1;
SPI_Control_1<=8'h04; //Write
SPI_Address_1<=MR;
SPI_Data6<=8'h00;
Data_Len_1<=8; //32Bit
SPI_Send_Start<=1;
end
读的指令如下
else if(Cycle_Counter==300024000)
begin
SPI_Send_Start<=0;
end
else if(Cycle_Counter==300024010)
begin
WR<=0;
SPI_Control_1<=8'h00; //Read
SPI_Address_1<=MR;
Data_Len_1<=8; //8Bit
SPI_Send_Start<=1;
end
SPI的波形如下,只显示了一段。DI端有些奇怪,W5500自己会蹦一些波形出来,但不会影响SPI读的操作,到DI回传数据时,DI就恢复正常了。
这是我第一次写博客,感觉还是有些麻烦。但这个过程可以分享自己的程序,也能给自己一个总结,很好!