zoukankan      html  css  js  c++  java
  • ARM与FPGA通过spi通信设计2.spi master的实现

    这里主要放两个代码第一个是正常的不使用状态机的SPI主机代码;第二个是状态机SPI代码

    1.不使用状态机:特权同学《深入浅出玩转FPGA》中DIY数码相框部分代码:

    ////////////////////////////////////////////////////////////////////////////////
    module spi_ctrl(
                clk,rst_n,
                spi_miso,spi_mosi,spi_clk,
                spi_tx_en,spi_tx_rdy,spi_rx_en,spi_rx_rdy,spi_tx_db,spi_rx_db
                );
    
    input clk;        //FPAG输入时钟信号25MHz
    input rst_n;    //FPGA输入复位信号
    
    input spi_miso;        //SPI主机输入从机输出数据信号
    output spi_mosi;    //SPI主机输出从机输入数据信号
    output spi_clk;        //SPI时钟信号,由主机产生
    
    input spi_tx_en;        //SPI数据发送使能信号,高有效
    output spi_tx_rdy;        //SPI数据发送完成标志位,高有效
    input spi_rx_en;        //SPI数据接收使能信号,高有效
    output spi_rx_rdy;        //SPI数据接收完成标志位,高有效
    input[7:0] spi_tx_db;    //SPI数据发送寄存器
    output[7:0] spi_rx_db;    //SPI数据接收寄存器
    
    
    //模拟SPI的时序模式为CPOL=1, CPHA=1,模拟速率为25Mbit
    
    //-------------------------------------------------
    //SPI时序控制计数器,所有SPI时序由该计数器值控制
    reg[4:0] cnt8;    //SPI时序控制计数器,计数范围在0-18
    
    always @(posedge clk or negedge rst_n)
        if(!rst_n) cnt8 <= 5'd0;
        else if(spi_tx_en || spi_rx_en) begin
                if(cnt8 < 5'd18)cnt8 <= cnt8+1'b1;    //SPI工作使能
                else ;    //计数到18停止,等待撤销spi使能
            end
        else cnt8 <= 5'd0;    //SPI关闭,计数停止
    
    //-------------------------------------------------
    //SPI时钟信号产生
    reg spi_clkr;    //SPI时钟信号,由主机产生
    
    always @(posedge clk or negedge rst_n)
        if(!rst_n) spi_clkr <= 1'b1;
        else if(cnt8 > 5'd1 && cnt8 < 5'd18) spi_clkr <= ~spi_clkr;    //在cnt8处于2-17时SPI时钟有效翻转
    
    assign spi_clk = spi_clkr;
    
    //-------------------------------------------------
    //SPI主机输出数据控制
    reg spi_mosir;    //SPI主机输出从机输入数据信号
    
    always @(posedge clk or negedge rst_n)
        if(!rst_n) spi_mosir <= 1'b1;
        else if(spi_tx_en) begin
                case(cnt8[4:1])        //主机发送8bit数据
                    4'd1: spi_mosir <= spi_tx_db[7];    //发送bit7
                    4'd2: spi_mosir <= spi_tx_db[6];    //发送bit6
                    4'd3: spi_mosir <= spi_tx_db[5];    //发送bit5
                    4'd4: spi_mosir <= spi_tx_db[4];    //发送bit4
                    4'd5: spi_mosir <= spi_tx_db[3];    //发送bit3
                    4'd6: spi_mosir <= spi_tx_db[2];    //发送bit2
                    4'd7: spi_mosir <= spi_tx_db[1];    //发送bit1
                    4'd8: spi_mosir <= spi_tx_db[0];    //发送bit0
                    default: spi_mosir <= 1'b1;    //spi_mosi没有输出时应保持高电平
                    endcase
            end
        else spi_mosir <= 1'b1;    //spi_mosi没有输出时应保持高电平
    
    assign spi_mosi = spi_mosir;
    
    //-------------------------------------------------
    //SPI主机输入数据控制
    reg[7:0] spi_rx_dbr;    //SPI主机输入从机输出数据总线寄存器
    
    always @(posedge clk or negedge rst_n)
        if(!rst_n) spi_rx_dbr <= 8'hff;
        else if(spi_rx_en) begin
                case(cnt8)        //主机接收并锁存8bit数据
                    5'd3: spi_rx_dbr[7] <= spi_miso;    //接收bit7
                    5'd5: spi_rx_dbr[6] <= spi_miso;    //接收bit6
                    5'd7: spi_rx_dbr[5] <= spi_miso;    //接收bit5
                    5'd9: spi_rx_dbr[4] <= spi_miso;    //接收bit4
                    5'd11: spi_rx_dbr[3] <= spi_miso;    //接收bit3
                    5'd13: spi_rx_dbr[2] <= spi_miso;    //接收bit2
                    5'd15: spi_rx_dbr[1] <= spi_miso;    //接收bit1
                    5'd17: spi_rx_dbr[0] <= spi_miso;    //接收bit0
                    default: ;
                    endcase
            end
    
    assign spi_rx_db = spi_rx_dbr;
    
    //-------------------------------------------------
    //SPI数据发送完成标志位,高有效
    assign spi_tx_rdy = (cnt8 == 5'd18)/* & spi_tx_en)*/;
    
    //-------------------------------------------------
    //SPI数据接收完成标志位,高有效
    assign spi_rx_rdy = (cnt8 == 5'd18)/* & spi_rx_en)*/;
    
    
    endmodule

    2.使用状态机的SPI master(来源网络)

    module spi_master
    (
        input                       sys_clk,
        input                       rst,
        output                      nCS,       //chip select (SPI mode)
        output                      DCLK,      //spi clock
        output                      MOSI,      //spi master data output
        input                       MISO,      //spi master input
        input                       CPOL,
        input                       CPHA,
        input                       nCS_ctrl,
        input[15:0]                 clk_div,
        input                       wr_req,
        output                      wr_ack,
        input[7:0]                  data_in,
        output[7:0]                 data_out
    );
    //状态机状态
    localparam                   IDLE            = 0;
    localparam                   DCLK_EDGE       = 1;
    localparam                   DCLK_IDLE       = 2;
    localparam                   ACK             = 3;
    localparam                   LAST_HALF_CYCLE = 4;
    localparam                   ACK_WAIT        = 5;
    
    reg                          DCLK_reg;
    reg[7:0]                     MOSI_shift;//移位寄存器
    reg[7:0]                     MISO_shift;
    reg[2:0]                     state;
    reg[2:0]                     next_state;
    reg [15:0]                   clk_cnt;
    reg[4:0]                     clk_edge_cnt;
    assign MOSI = MOSI_shift[7];
    assign DCLK = DCLK_reg;
    assign data_out = MISO_shift;
    assign wr_ack = (state == ACK);
    assign nCS = nCS_ctrl;
     
    /*************这个就是状态机的定义**************/
    always@(posedge sys_clk or posedge rst)
    begin
        if(rst)
            state <= IDLE;
        else
            state <= next_state;
    end
    /****************end*************************/
     
    /****************状态机的具体过程*************/
    always@(*)
    begin
        case(state)
            IDLE:
                if(wr_req == 1'b1)
                    next_state <= DCLK_IDLE;
                else
                    next_state <= IDLE;
            DCLK_IDLE:
                //half a SPI clock cycle produces a clock edge//半个SPI时钟周期产生时钟边沿
                if(clk_cnt == clk_div)
                    next_state <= DCLK_EDGE;
                else
                    next_state <= DCLK_IDLE;
            DCLK_EDGE:
                //a SPI byte with a total of 16 clock edges//一个SPI字节,总共有16个时钟边沿
                if(clk_edge_cnt == 5'd15)
                    next_state <= LAST_HALF_CYCLE;
                else
                    next_state <= DCLK_IDLE;
            //this is the last data edge  //这是最后一个数据边缘      
            LAST_HALF_CYCLE:
                if(clk_cnt == clk_div)
                    next_state <= ACK;
                else
                    next_state <= LAST_HALF_CYCLE;
            //send one byte complete//发送一个字节完成
            ACK:
                next_state <= ACK_WAIT;
            //wait for one clock cycle, to ensure that the cancel request signal//等待一个时钟周期,以确保取消请求信号
            ACK_WAIT:
                next_state <= IDLE;
            default:
                next_state <= IDLE;
        endcase
    end
    
     
    /****************时钟翻转************************/
    always@(posedge sys_clk or posedge rst)
    begin
        if(rst)                        /*在空闲状态之前,SCK一直保持CPOL的极性*/               
            DCLK_reg <= 1'b0;
        else if(state == IDLE)
            DCLK_reg <= CPOL;
        else if(state == DCLK_EDGE)    /*边缘检测时,反转SCK*/
            DCLK_reg <= ~DCLK_reg;//SPI clock edge
    end
    /****************end*****************************/
     
    //SPI clock wait counter                /*一个计数器*/
    always@(posedge sys_clk or posedge rst)
    begin
        if(rst)
            clk_cnt <= 16'd0;
        else if(state == DCLK_IDLE || state == LAST_HALF_CYCLE)
            clk_cnt <= clk_cnt + 16'd1;
        else
            clk_cnt <= 16'd0;
    end
    //SPI clock edge counter
    always@(posedge sys_clk or posedge rst)
    begin
        if(rst)
            clk_edge_cnt <= 5'd0;
        else if(state == DCLK_EDGE)
            clk_edge_cnt <= clk_edge_cnt + 5'd1;
        else if(state == IDLE)
            clk_edge_cnt <= 5'd0;
    end
     
    //SPI data output                    /*这里就是SPI输出的移位方式*/
    always@(posedge sys_clk or posedge rst)
    begin
        if(rst)
            MOSI_shift <= 8'd0;
        else if(state == IDLE && wr_req)
            MOSI_shift <= data_in;
        else if(state == DCLK_EDGE)
            if(CPHA == 1'b0 && clk_edge_cnt[0] == 1'b1)          /*两种方式,取决于CPHA*/
                MOSI_shift <= {MOSI_shift[6:0],MOSI_shift[7]};   /*常见的移位语句,大家要敏感*/
            else if(CPHA == 1'b1 && (clk_edge_cnt != 5'd0 && clk_edge_cnt[0] == 1'b0))
                MOSI_shift <= {MOSI_shift[6:0],MOSI_shift[7]};
    end
    //SPI data input
    always@(posedge sys_clk or posedge rst)
    begin
        if(rst)
            MISO_shift <= 8'd0;
        else if(state == IDLE && wr_req)    
            MISO_shift <= 8'h00;
        else if(state == DCLK_EDGE)
            if(CPHA == 1'b0 && clk_edge_cnt[0] == 1'b0)  
                MISO_shift <= {MISO_shift[6:0],MISO}; /*MISO输入,然后进行移位*/
            else if(CPHA == 1'b1 && (clk_edge_cnt[0] == 1'b1))
                MISO_shift <= {MISO_shift[6:0],MISO};
    end
    endmodule

    第二个例子实现了较为全面的spi主机功能,可以设置SPI相位和极性,有较高的参考价值

    以上两个源代码可供大家参考

  • 相关阅读:
    编译Android源码
    Android Studio 更新
    ANDROID:替换系统桌面
    Linux目录树与文件系统
    主引导记录MBR
    Android开发使用run-as获取应用数据
    桥接模式
    工厂方法模式
    Floyd's Cycle Detection Algorithm
    用两个stack实现一个队列
  • 原文地址:https://www.cnblogs.com/yongleili717/p/10610740.html
Copyright © 2011-2022 走看看