一、基本知识
1、SPI
SPI是串行外设接口(Serial Peripheral Interface)的缩写。它是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线。
SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4根线,事实上3根也可以(单向传输时)。也是所有基于SPI的设备共有的,它们是SDI(数据输入)、SDO(数据输出)、SCLK(时钟)、CS(片选)。
(1)SDI – SerialData In,串行数据输入;
(2)SDO – SerialDataOut,串行数据输出;
(3)SCLK – Serial Clock,时钟信号,由主设备产生;
(4)CS – Chip Select,从设备使能信号,由主设备控制。
2、SPI的工作模式
3、Verilog中的任务
任务是通过调用来执行的,而且只有在调用时才执行。在定义任务时,设计者可以为其添加输入和输出端口,用于在任务调用时传递参数。任务可以包含带时序控制的语句,当调用带时序控制的任务时,任务返回时的时间和调用时的时间可能不相同。任务可以彼此调用,而且任务还可以调用函数。
(1)任务的定义
task <任务名>
端口及数据类型声明语句
其他语句
endtask
(2) 任务的特点
(3)任务使用
module mult(clk,a,b,out,en_mult)
input clk,en_mult;
input [3:0] a,b;
output [7:0] out;
reg [7:0] out;
task muotme;//任务定义
input [3:0] xme,tome;
output [7:0] result;
wait(en_mult) //电平敏感事件触发
result=xme*tome;
endtask
always @(posedge clk)
muotme(a,b,out);//任务调用时传递给任务的参数与任务I/O声明时的参数顺序相同
endmodule
4、verilog中的函数
函数和任务一样,也是定义一个可重复调用的模块,但是函数可以返回一个值,因此可以出现在等号右边的表达式中,而任务的返回值只能通过任务的输出端口来获得。
(1) 函数的定义:
function <返回值位宽或类型说明> 函数名;
输入端口与类型说明
局部变量说明
块语句
endfunction
(2) 函数的特点:
(3)举例用函数定义一个8-3编码器
module code_83(din,dout)
input [7:0] din;
output [2:0] dout;
function [2:0] code; //函数定义
input [7:0] din;//函数只有输入,输出为函数本身
casex(din)
8'b1xxx_xxxx:cade=3'h7;
8'b01xx_xxxx:cade=3'h6;
8'b001x_xxxx:cade=3'h5;
8'b0001_xxxx:cade=3'h4;
8'b0000_1xxx:cade=3'h3;
8'b0000_01xx:cade=3'h2;
8'b0000_001x:cade=3'h1;
8'b0000_0001:cade=3'h0;
default:code=3'hx;
endcase
endfuntion
assign dout=code(din);//函数调用
endmodule
二、实现过程
设计两段式状态机,完成SPI的模式1
一个模块控制读还是写,一个完成对SPI引脚控制
1、定义模块输入输出引脚
module tem_rec(
input clk_10m,//输入10Mhz的时钟
input rst, //高电平有效
input spidi,//spi数据输入端
output spiclk,//spi中的时钟
output spido,//spi的发送端
output spics,//片选信号
output [7:0] data,
);
reg spics;//spi片选信号
reg spiclk;//spi时钟
reg spido;spi写信号
reg [7:0] data;//读的数据转换成8位并行数据输出
2、读写控制模块编写:
//总状态控制
parameter ini=4'd0, state_1=4'd1, state_2=4'd2, state_3 =4'd3,state_4=4'd4,state_5=4'd5,
state_6=4'd6,state_7=4'd7,state_8=4'd8,state_9=4'd9,state_10=4'd10,state_11=4'd11,
state_12=4'd12,state_13=4'd13,state_14=4'd14,state_end=4'd15;//使用循环码
reg wr;
reg rd;//读写命令
reg [3:0] next_state;//状态转移
always@(posedge clk_10m or posedge rst)
begin
if(rst) begin
next_state<=ini;
wr<=0;
rd<=0;
data<=8'd0;//待发送
end
else begin
case(next_state)
ini://初始化
begin
next_state<=state_1;
end
state_1: //写
begin
if(send_rdy) begin
wr<=0;
next_state<=state_2;
end
else//写寄存器
wr<=1;
end
state_2://读
begin
if(rec_rdy) begin
rd<=0;
end
else begin
rd<=1;
end
end
endcase
end
end
3、SPI引脚控制模块编写:
//*spi的数据接收和发送*//
reg send_rdy;//发送完一字节标志位
reg rec_rdy;//接收完成一字节数据标志位
always @(posedge clk_10m)
begin
if(rst) begin
spistate<=idle;
spics<=1'b1;
spiclk<=1'b1;
spido<=1'b1;
send_rdy<=1'b0;
rec_rdy<=1'b0;
else
begin
case(spistate)//读写状态控制
idle:
begin
if((wr==1'b1)&&(rd==1'b0)) begin//发送数据转换
spistate<=send_data;//转发送状态
dsend<=datain;//准备待发送数据
send_rdy<=1'b0;
rec_rdy<=1'b0;
end
else if((wr==1'b0)&&(rd==1'b1) begin
spistate<=receive_data;//转接收转态
dstate<=8'd0;
rec_rdy<=1'b0;
send_rdy<=1'b0;
end
end
send_data://发送数据状态
begin
case(dstate)
8'd0://产生片选信号有效
begin
spics<=1'b1;
spiclk<=1'b1;
spido<=1'b1;//发送出去的数据
dstate<=8'd1;
end
8'd1:
begin
spics<=1'b1;
spiclk<=1'b1;
spido<=1'b1;
dstate<=8'd2;
end
8'd2:
begin
spics<=1'b0;
spiclk<=1'b1;
spido<=1'b1;
dstate<=8'd3;
end
8'd3:
begin
spics<=1'b0;
spiclk<=1'b0;
spido<=datain[7];//发送数据最高位
dstate<=8'd4;
end
8'd4:
begin
spics<=1'b0;
spiclk<=1'b1;
spido<=datain[7];
dstate<=8'd5;
end
8'd5:
begin
spics<=1'b0;
spiclk<=1'b0;
spido<=datain[6];
dstate<=8'd6;
end
8'd6:
begin
spics<=1'b0;
spiclk<=1'b1;
spido<=datain[6];
dstate<=8'd7;
end
8'd7:
begin
spics<=1'b0;
spiclk<=1'b0;
spido<=datain[5];
dstate<=8'd8;
end
8'd8:
begin
spics<=1'b0;
spiclk<=1'b1;
spido<=datain[5];
dstate<=8'd9;
end
8'd9:
begin
spics<=1'b0;
spiclk<=1'b0;
spido<=datain[4];
dstate<=8'd10;
end
8'd10:
begin
spics<=1'b0;
spiclk<=1'b1;
spido<=datain[4];
dstate<=8'd11;
end
8'd11:
begin
spics<=1'b0;
spiclk<=1'b0;
spido<=datain[3];
dstate<=8'd12;
end
8'd12:
begin
spics<=1'b0;
spiclk<=1'b1;
spido<=datain[3];
dstate<=8'd13;
end
8'd13:
begin
spics<=1'b0;
spiclk<=1'b0;
spido<=datain[2];
dstate<=8'd14;
end
8'd14:
begin
spics<=1'b0;
spiclk<=1'b1;
spido<=datain[2];
dstate<=8'd15;
end
8'd15:
begin
spics<=1'b0;
spiclk<=1'b0;
spido<=datain[1];
dstate<=8'd16;
end
8'd16:
begin
spics<=1'b0;
spiclk<=1'b1;
spido<=datain[1];
dstate<=8'd17;
end
8'd17:
begin
spics<=1'b0;
spiclk<=1'b0;
spido<=datain[0];
dstate<=8'd18;
end
8'd18:
begin
spics<=1'b0;
spiclk<=1'b1;
spido<=datain[0];
dstate<=8'd19;
end
8'd19:
begin
spics<=1'b1;
spiclk<=1'b1;
spido<=1'b1;
dstate<=8'd20;
// send_rdy<=1'b1;
end
8'd20://一个字节数据发送完成
begin
spics<=1'b1;
spiclk<=1'b1;
spido<=1'b1;
dstate<=8'd0;
spistate<=idle;
send_rdy<=1'b1;//发送完成一帧数据标志位
end
default
begin
spics<=1'b1;
spiclk<=1'b1;
spido<=1'b1;
spistate<=idle;
end
endcase//对应发送状态
end
receive_data://接收数据状态
begin
case (dstate) //片选信号有效
8'd0:
begin
spics <= 1'b1;
spiclk <= 1'b1;
spido <= 1'b1;
dstate <= 8'd1;
end
8'd1:
begin
spics <= 1'b1;
spiclk <= 1'b1;
spido <= 1'b1;
dstate <= 8'd2;
end
8'd2:
begin
spics <= 1'b0;
spiclk <= 1'b1;
spido <= 1'b1;
dstate <= 8'd3;
end
8'd3:
begin
spics <= 1'b0;
spiclk <= 1'b0;
dreceive[7] <= spidi;
dstate <= 8'd4;
end
8'd4:
begin
spics <= 1'b0;
spiclk <= 1'b1; //紧接着上升沿的下降沿数据被读取
dreceive[7] <= spidi; //接收数据最高位
dstate <= 8'd5;
end
8'd5:
begin
spics <= 1'b0;
spiclk <= 1'b0;
dstate <= 8'd6;
dreceive[6] <= spidi;
end
8'd6:
begin
spics <= 1'b0;
spiclk <= 1'b1;
dreceive[6] <= spidi;
dstate <= 8'd7;
end
8'd7:
begin
spics <= 1'b0;
spiclk <= 1'b0;
dstate <= 8'd8;
dreceive[5] <= spidi;
end
8'd8:
begin
spics <= 1'b0;
spiclk <= 1'b1;
dreceive[5] <= spidi;
dstate <= 8'd9;
end
8'd9:
begin
spics <= 1'b0;
spiclk <= 1'b0;
dstate <= 8'd10;
dreceive[4] <= spidi;
end
8'd10:
begin
spics <= 1'b0;
spiclk <= 1'b1;
dreceive[4] <= spidi;
dstate <= 8'd11;
end
8'd11:
begin
spics <= 1'b0;
spiclk <= 1'b0;
dstate <= 8'd12;
dreceive[3] <= spidi;
end
8'd12:
begin
spics <= 1'b0;
spiclk <= 1'b1;
dreceive[3] <= spidi;
dstate <= 8'd13;
end
8'd13:
begin
spics <= 1'b0;
spiclk <= 1'b0;
dstate <= 8'd14;
dreceive[2] <= spidi;
end
8'd14:
begin
spics <= 1'b0;
spiclk <= 1'b1;
dreceive[2] <= spidi;
dstate <= 8'd15;
end
8'd15:
begin
spics <= 1'b0;
spiclk <= 1'b0;
dstate <= 8'd16;
dreceive[1] <= spidi;
end
8'd16:
begin
spics <= 1'b0;
spiclk <= 1'b1;
dreceive[1] <= spidi;
dstate <= 8'd17;
end
8'd17:
begin
spics <= 1'b0;
spiclk <= 1'b0;
dstate <= 8'd18;
dreceive[0] <= spidi;
end
8'd18:
begin
spics <= 1'b0;
spiclk <= 1'b1;
dreceive[0] <= spidi; //接收数据最低位
dstate <= 8'd19;
end
8'd19:
begin
spics <= 1'b1;
spiclk <= 1'b1;
spido<= 1'b1;
dstate <= 8'd20;
dataout[7:0]<= dreceive[7:0];
//rec_rdy<=1'b1;
end
8'd20:
begin
spics <= 1'b1;//片选信号无效
spiclk <= 1'b1;
spido <= 1'b1;
dstate <= 8'd0;
spistate <= idle;
rec_rdy<=1'b1;//接收完一个字节标志位有效
end
default
begin
spics<=1'b1;
spiclk<=1'b1;
spido<=1'b1;
end
endcase//对应接收状态
end
endcase
end
end