Verilog实现SPI通信(包括对任务和函数用法的讲解)

10/18 13:17
阅读数 0

一、基本知识

   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)任务的定义


 
  1.  
    task <任务名>
  2.  
    端口及数据类型声明语句
  3.  
    其他语句
  4.  
     
  5.  
     
  6.  
    endtask

(2) 任务的特点

  • 任务的定义与调用需在一个module模块内
  • 定义任务时,没有端口名列表,但需要紧接着进行输入、输出端口和数据类型的说明。
  • 当任务被调用时,任务被激活。调用时,须列出端口名列表,端口名的排序和类型必须与任务定义中的相一致。
  • 一个任务可以调用别的任务和函数,可以调用的任务和函数个数不限
  • 为了提高任务的模块化程度,传递给任务的参数名通常不使用与任务内部I/O声明的参数名相同的参数名。
  • 任务定义结构中不能出现initial和always过程块

(3)任务使用


 
  1.  
    module mult(clk,a,b,out,en_mult)
  2.  
     
  3.  
    input clk,en_mult;
  4.  
    input [3:0] a,b;
  5.  
    output [7:0] out;
  6.  
    reg [7:0] out;
  7.  
    task muotme;//任务定义
  8.  
    input [3:0] xme,tome;
  9.  
    output [7:0] result;
  10.  
    wait(en_mult) //电平敏感事件触发
  11.  
    result=xme*tome;
  12.  
    endtask
  13.  
    always @(posedge clk)
  14.  
    muotme(a,b,out);//任务调用时传递给任务的参数与任务I/O声明时的参数顺序相同
  15.  
    endmodule

4、verilog中的函数

       函数和任务一样,也是定义一个可重复调用的模块,但是函数可以返回一个值,因此可以出现在等号右边的表达式中,而任务的返回值只能通过任务的输出端口来获得。

    (1)  函数的定义:


 
  1.  
    function <返回值位宽或类型说明> 函数名;
  2.  
    输入端口与类型说明
  3.  
    局部变量说明
  4.  
    块语句
  5.  
    endfunction

   (2)  函数的特点:

  • 函数定义不能包含任何时序控制语句。
  • 函数必须包含输入,但不能有输出或双向信号。
  • 函数中不能使用非阻塞赋值语句。
  • 一个函数只能返回一个值,该值的变量名与函数名同名。
  • 传递给函数参数的顺序与函数定义时输入参数声明的顺序相同。
  • 函数定义必须包含在模块定义之内。
  • 函数不能调用任务,但任务可以调用函数。

(3)举例用函数定义一个8-3编码器


 
  1.  
    module code_83(din,dout)
  2.  
     
  3.  
    input [7:0] din;
  4.  
    output [2:0] dout;
  5.  
    function [2:0] code; //函数定义
  6.  
    input [7:0] din;//函数只有输入,输出为函数本身
  7.  
    casex(din)
  8.  
    8'b1xxx_xxxx:cade=3'h7;
  9.  
    8'b01xx_xxxx:cade=3'h6;
  10.  
    8'b001x_xxxx:cade=3'h5;
  11.  
    8'b0001_xxxx:cade=3'h4;
  12.  
    8'b0000_1xxx:cade=3'h3;
  13.  
    8'b0000_01xx:cade=3'h2;
  14.  
    8'b0000_001x:cade=3'h1;
  15.  
    8'b0000_0001:cade=3'h0;
  16.  
    default:code=3'hx;
  17.  
    endcase
  18.  
    endfuntion
  19.  
    assign dout=code(din);//函数调用
  20.  
     
  21.  
    endmodule

二、实现过程

设计两段式状态机,完成SPI的模式1

一个模块控制读还是写,一个完成对SPI引脚控制

1、定义模块输入输出引脚


 
  1.  
    module tem_rec(
  2.  
    input clk_10m,//输入10Mhz的时钟
  3.  
    input rst, //高电平有效
  4.  
    input spidi,//spi数据输入端
  5.  
    output spiclk,//spi中的时钟
  6.  
    output spido,//spi的发送端
  7.  
    output spics,//片选信号
  8.  
    output [7:0] data,
  9.  
    );
  10.  
    reg spics;//spi片选信号
  11.  
    reg spiclk;//spi时钟
  12.  
    reg spido;spi写信号
  13.  
    reg [7:0] data;//读的数据转换成8位并行数据输出

2、读写控制模块编写:


 
  1.  
    //总状态控制
  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,
  3.  
    state_6=4'd6,state_7=4'd7,state_8=4'd8,state_9=4'd9,state_10=4'd10,state_11=4'd11,
  4.  
    state_12=4'd12,state_13=4'd13,state_14=4'd14,state_end=4'd15;//使用循环码
  5.  
    reg wr;
  6.  
    reg rd;//读写命令
  7.  
    reg [3:0] next_state;//状态转移
  8.  
    always@(posedge clk_10m or posedge rst)
  9.  
    begin
  10.  
    if(rst) begin
  11.  
    next_state<=ini;
  12.  
    wr<=0;
  13.  
    rd<=0;
  14.  
    data<=8'd0;//待发送
  15.  
    end
  16.  
    else begin
  17.  
    case(next_state)
  18.  
    ini://初始化
  19.  
    begin
  20.  
    next_state<=state_1;
  21.  
    end
  22.  
    state_1: //写
  23.  
    begin
  24.  
    if(send_rdy) begin
  25.  
    wr<=0;
  26.  
    next_state<=state_2;
  27.  
    end
  28.  
    else//写寄存器
  29.  
    wr<=1;
  30.  
    end
  31.  
    state_2://读
  32.  
    begin
  33.  
    if(rec_rdy) begin
  34.  
    rd<=0;
  35.  
    end
  36.  
    else begin
  37.  
    rd<=1;
  38.  
    end
  39.  
    end
  40.  
    endcase
  41.  
    end
  42.  
    end

3、SPI引脚控制模块编写:


 
  1.  
    //*spi的数据接收和发送*//
  2.  
    reg send_rdy;//发送完一字节标志位
  3.  
    reg rec_rdy;//接收完成一字节数据标志位
  4.  
    always @(posedge clk_10m)
  5.  
    begin
  6.  
    if(rst) begin
  7.  
    spistate<=idle;
  8.  
    spics<=1'b1;
  9.  
    spiclk<=1'b1;
  10.  
    spido<=1'b1;
  11.  
    send_rdy<=1'b0;
  12.  
    rec_rdy<=1'b0;
  13.  
    else
  14.  
    begin
  15.  
    case(spistate)//读写状态控制
  16.  
    idle:
  17.  
    begin
  18.  
    if((wr==1'b1)&&(rd==1'b0)) begin//发送数据转换
  19.  
    spistate<=send_data;//转发送状态
  20.  
    dsend<=datain;//准备待发送数据
  21.  
    send_rdy<=1'b0;
  22.  
    rec_rdy<=1'b0;
  23.  
    end
  24.  
    else if((wr==1'b0)&&(rd==1'b1) begin
  25.  
    spistate<=receive_data;//转接收转态
  26.  
    dstate<=8'd0;
  27.  
    rec_rdy<=1'b0;
  28.  
    send_rdy<=1'b0;
  29.  
    end
  30.  
    end
  31.  
    send_data://发送数据状态
  32.  
    begin
  33.  
    case(dstate)
  34.  
    8'd0://产生片选信号有效
  35.  
    begin
  36.  
    spics<=1'b1;
  37.  
    spiclk<=1'b1;
  38.  
    spido<=1'b1;//发送出去的数据
  39.  
    dstate<=8'd1;
  40.  
    end
  41.  
    8'd1:
  42.  
    begin
  43.  
    spics<=1'b1;
  44.  
    spiclk<=1'b1;
  45.  
    spido<=1'b1;
  46.  
    dstate<=8'd2;
  47.  
    end
  48.  
    8'd2:
  49.  
    begin
  50.  
    spics<=1'b0;
  51.  
    spiclk<=1'b1;
  52.  
    spido<=1'b1;
  53.  
    dstate<=8'd3;
  54.  
    end
  55.  
    8'd3:
  56.  
    begin
  57.  
    spics<=1'b0;
  58.  
    spiclk<=1'b0;
  59.  
    spido<=datain[7];//发送数据最高位
  60.  
    dstate<=8'd4;
  61.  
    end
  62.  
    8'd4:
  63.  
    begin
  64.  
    spics<=1'b0;
  65.  
    spiclk<=1'b1;
  66.  
    spido<=datain[7];
  67.  
    dstate<=8'd5;
  68.  
    end
  69.  
    8'd5:
  70.  
    begin
  71.  
    spics<=1'b0;
  72.  
    spiclk<=1'b0;
  73.  
    spido<=datain[6];
  74.  
    dstate<=8'd6;
  75.  
    end
  76.  
    8'd6:
  77.  
    begin
  78.  
    spics<=1'b0;
  79.  
    spiclk<=1'b1;
  80.  
    spido<=datain[6];
  81.  
    dstate<=8'd7;
  82.  
    end
  83.  
    8'd7:
  84.  
    begin
  85.  
    spics<=1'b0;
  86.  
    spiclk<=1'b0;
  87.  
    spido<=datain[5];
  88.  
    dstate<=8'd8;
  89.  
    end
  90.  
    8'd8:
  91.  
    begin
  92.  
    spics<=1'b0;
  93.  
    spiclk<=1'b1;
  94.  
    spido<=datain[5];
  95.  
    dstate<=8'd9;
  96.  
    end
  97.  
    8'd9:
  98.  
    begin
  99.  
    spics<=1'b0;
  100.  
    spiclk<=1'b0;
  101.  
    spido<=datain[4];
  102.  
    dstate<=8'd10;
  103.  
    end
  104.  
    8'd10:
  105.  
    begin
  106.  
    spics<=1'b0;
  107.  
    spiclk<=1'b1;
  108.  
    spido<=datain[4];
  109.  
    dstate<=8'd11;
  110.  
    end
  111.  
    8'd11:
  112.  
    begin
  113.  
    spics<=1'b0;
  114.  
    spiclk<=1'b0;
  115.  
    spido<=datain[3];
  116.  
    dstate<=8'd12;
  117.  
    end
  118.  
    8'd12:
  119.  
    begin
  120.  
    spics<=1'b0;
  121.  
    spiclk<=1'b1;
  122.  
    spido<=datain[3];
  123.  
    dstate<=8'd13;
  124.  
    end
  125.  
    8'd13:
  126.  
    begin
  127.  
    spics<=1'b0;
  128.  
    spiclk<=1'b0;
  129.  
    spido<=datain[2];
  130.  
    dstate<=8'd14;
  131.  
    end
  132.  
    8'd14:
  133.  
    begin
  134.  
    spics<=1'b0;
  135.  
    spiclk<=1'b1;
  136.  
    spido<=datain[2];
  137.  
    dstate<=8'd15;
  138.  
    end
  139.  
    8'd15:
  140.  
    begin
  141.  
    spics<=1'b0;
  142.  
    spiclk<=1'b0;
  143.  
    spido<=datain[1];
  144.  
    dstate<=8'd16;
  145.  
    end
  146.  
    8'd16:
  147.  
    begin
  148.  
    spics<=1'b0;
  149.  
    spiclk<=1'b1;
  150.  
    spido<=datain[1];
  151.  
    dstate<=8'd17;
  152.  
    end
  153.  
    8'd17:
  154.  
    begin
  155.  
    spics<=1'b0;
  156.  
    spiclk<=1'b0;
  157.  
    spido<=datain[0];
  158.  
    dstate<=8'd18;
  159.  
    end
  160.  
    8'd18:
  161.  
    begin
  162.  
    spics<=1'b0;
  163.  
    spiclk<=1'b1;
  164.  
    spido<=datain[0];
  165.  
    dstate<=8'd19;
  166.  
    end
  167.  
    8'd19:
  168.  
    begin
  169.  
    spics<=1'b1;
  170.  
    spiclk<=1'b1;
  171.  
    spido<=1'b1;
  172.  
    dstate<=8'd20;
  173.  
    // send_rdy<=1'b1;
  174.  
    end
  175.  
    8'd20://一个字节数据发送完成
  176.  
    begin
  177.  
    spics<=1'b1;
  178.  
    spiclk<=1'b1;
  179.  
    spido<=1'b1;
  180.  
    dstate<=8'd0;
  181.  
    spistate<=idle;
  182.  
    send_rdy<=1'b1;//发送完成一帧数据标志位
  183.  
    end
  184.  
    default
  185.  
    begin
  186.  
    spics<=1'b1;
  187.  
    spiclk<=1'b1;
  188.  
    spido<=1'b1;
  189.  
    spistate<=idle;
  190.  
    end
  191.  
    endcase//对应发送状态
  192.  
    end
  193.  
    receive_data://接收数据状态
  194.  
    begin
  195.  
    case (dstate) //片选信号有效
  196.  
    8'd0:
  197.  
    begin
  198.  
    spics <= 1'b1;
  199.  
    spiclk <= 1'b1;
  200.  
    spido <= 1'b1;
  201.  
    dstate <= 8'd1;
  202.  
    end
  203.  
    8'd1:
  204.  
    begin
  205.  
    spics <= 1'b1;
  206.  
    spiclk <= 1'b1;
  207.  
    spido <= 1'b1;
  208.  
    dstate <= 8'd2;
  209.  
    end
  210.  
    8'd2:
  211.  
    begin
  212.  
    spics <= 1'b0;
  213.  
    spiclk <= 1'b1;
  214.  
    spido <= 1'b1;
  215.  
    dstate <= 8'd3;
  216.  
    end
  217.  
    8'd3:
  218.  
    begin
  219.  
    spics <= 1'b0;
  220.  
    spiclk <= 1'b0;
  221.  
    dreceive[7] <= spidi;
  222.  
    dstate <= 8'd4;
  223.  
    end
  224.  
    8'd4:
  225.  
    begin
  226.  
    spics <= 1'b0;
  227.  
    spiclk <= 1'b1; //紧接着上升沿的下降沿数据被读取
  228.  
    dreceive[7] <= spidi; //接收数据最高位
  229.  
    dstate <= 8'd5;
  230.  
    end
  231.  
    8'd5:
  232.  
    begin
  233.  
    spics <= 1'b0;
  234.  
    spiclk <= 1'b0;
  235.  
    dstate <= 8'd6;
  236.  
    dreceive[6] <= spidi;
  237.  
    end
  238.  
    8'd6:
  239.  
    begin
  240.  
    spics <= 1'b0;
  241.  
    spiclk <= 1'b1;
  242.  
    dreceive[6] <= spidi;
  243.  
    dstate <= 8'd7;
  244.  
    end
  245.  
    8'd7:
  246.  
    begin
  247.  
    spics <= 1'b0;
  248.  
    spiclk <= 1'b0;
  249.  
    dstate <= 8'd8;
  250.  
    dreceive[5] <= spidi;
  251.  
    end
  252.  
    8'd8:
  253.  
    begin
  254.  
    spics <= 1'b0;
  255.  
    spiclk <= 1'b1;
  256.  
    dreceive[5] <= spidi;
  257.  
    dstate <= 8'd9;
  258.  
    end
  259.  
    8'd9:
  260.  
    begin
  261.  
    spics <= 1'b0;
  262.  
    spiclk <= 1'b0;
  263.  
    dstate <= 8'd10;
  264.  
    dreceive[4] <= spidi;
  265.  
    end
  266.  
    8'd10:
  267.  
    begin
  268.  
    spics <= 1'b0;
  269.  
    spiclk <= 1'b1;
  270.  
    dreceive[4] <= spidi;
  271.  
    dstate <= 8'd11;
  272.  
    end
  273.  
    8'd11:
  274.  
    begin
  275.  
    spics <= 1'b0;
  276.  
    spiclk <= 1'b0;
  277.  
    dstate <= 8'd12;
  278.  
    dreceive[3] <= spidi;
  279.  
    end
  280.  
    8'd12:
  281.  
    begin
  282.  
    spics <= 1'b0;
  283.  
    spiclk <= 1'b1;
  284.  
    dreceive[3] <= spidi;
  285.  
    dstate <= 8'd13;
  286.  
    end
  287.  
    8'd13:
  288.  
    begin
  289.  
    spics <= 1'b0;
  290.  
    spiclk <= 1'b0;
  291.  
    dstate <= 8'd14;
  292.  
    dreceive[2] <= spidi;
  293.  
    end
  294.  
    8'd14:
  295.  
    begin
  296.  
    spics <= 1'b0;
  297.  
    spiclk <= 1'b1;
  298.  
    dreceive[2] <= spidi;
  299.  
    dstate <= 8'd15;
  300.  
    end
  301.  
    8'd15:
  302.  
    begin
  303.  
    spics <= 1'b0;
  304.  
    spiclk <= 1'b0;
  305.  
    dstate <= 8'd16;
  306.  
    dreceive[1] <= spidi;
  307.  
    end
  308.  
    8'd16:
  309.  
    begin
  310.  
    spics <= 1'b0;
  311.  
    spiclk <= 1'b1;
  312.  
    dreceive[1] <= spidi;
  313.  
    dstate <= 8'd17;
  314.  
    end
  315.  
    8'd17:
  316.  
    begin
  317.  
    spics <= 1'b0;
  318.  
    spiclk <= 1'b0;
  319.  
    dstate <= 8'd18;
  320.  
    dreceive[0] <= spidi;
  321.  
    end
  322.  
    8'd18:
  323.  
    begin
  324.  
    spics <= 1'b0;
  325.  
    spiclk <= 1'b1;
  326.  
    dreceive[0] <= spidi; //接收数据最低位
  327.  
    dstate <= 8'd19;
  328.  
    end
  329.  
    8'd19:
  330.  
    begin
  331.  
    spics <= 1'b1;
  332.  
    spiclk <= 1'b1;
  333.  
    spido<= 1'b1;
  334.  
    dstate <= 8'd20;
  335.  
    dataout[7:0]<= dreceive[7:0];
  336.  
    //rec_rdy<=1'b1;
  337.  
    end
  338.  
    8'd20:
  339.  
    begin
  340.  
    spics <= 1'b1;//片选信号无效
  341.  
    spiclk <= 1'b1;
  342.  
    spido <= 1'b1;
  343.  
    dstate <= 8'd0;
  344.  
    spistate <= idle;
  345.  
    rec_rdy<=1'b1;//接收完一个字节标志位有效
  346.  
    end
  347.  
    default
  348.  
    begin
  349.  
    spics<=1'b1;
  350.  
    spiclk<=1'b1;
  351.  
    spido<=1'b1;
  352.  
     
  353.  
    end
  354.  
    endcase//对应接收状态
  355.  
    end
  356.  
    endcase
  357.  
    end
  358.  
    end
展开阅读全文
spi
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部