文档章节

ARM与FPGA通过spi通信设计2.spi master的实现

o
 osc_a22drz29
发布于 2019/03/27 21:08
字数 1378
阅读 46
收藏 1

精选30+云产品,助力企业轻松上云!>>>

这里主要放两个代码第一个是正常的不使用状态机的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相位和极性,有较高的参考价值

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

 

o
粉丝 0
博文 500
码字总数 0
作品 0
私信 提问
加载中
请先登录后再评论。
创龙基于TI AM437x ARM Cortex-A9 + Xilinx Spartan-6 FPGA的SPI FLASH、硬件加密芯片

TL437xF-EVM是一款广州创龙基于TI AM437x ARM Cortex-A9 + Xilinx Spartan-6 FPGA设计的开发板,底板采用沉金无铅工艺的4层板设计,尺寸为240mm*130mm,它为用户提供了SOM-TL437xF核心板的测...

Tronlong创龙
2019/11/13
11
0
verilog spi slave源码学习

参考 https://www.fpga4fun.com/SPI2.html 和 https://my.oschina.net/u/2963604/blog/2222189 ARM processor To get an opportunity to test our newly acquired SPI knowledge, we use a S......

whoisliang
06/24
19
0
创龙TI AM437x ARM Cortex-A9 + Xilinx Spartan-6 FPGA核心板规格书

SOM-TL437xF是一款广州创龙基于TI AM437x ARM Cortex-A9 + Xilinx Spartan-6 FPGA芯片设计的核心板,采用沉金无铅工艺的10层板设计,适用于高速数据采集和处理系统、汽车导航、工业自动化等领...

Tronlong创龙
2019/09/19
14
0
OMAPL138+FPGA三核开发板

Revision History Revision No. Description Draft Date Remark V1.0 1.初始版本 2014/4/13 阅前须知 版权声明 广州创龙电子科技有限公司保留随时对其产品进行修改和完善的权利,同时也保留在...

gzchuanglong
2014/05/15
399
0
创龙基于TISitaraAM5728(浮点双DSPC66x+双ARMCortex-A15)+XilinxArtix-7FPGA开发板规格书

广州创龙基于TI Sitara AM5728(浮点双DSP C66x +双ARM Cortex-A15) + Xilinx Artix-7 FPGA设计的TL5728F-EVM开发板是一款DSP+ARM+FPGA架构的开发平台,该平台适用于电力采集、电机控制器、...

Tronlong创龙
2019/09/05
11
0

没有更多内容

加载失败,请刷新页面

加载更多

macz技巧分享—macOS高端使用技巧

Macos 的占有量不如 Windows,两者之间当操作方式也有很大的不同,当很多人熱悉 Windows 的操作之后,再接触 macos,觉得难上手,其实是习惯问题。如果你学习一些技巧,会觉得 macos 其实也不...

mac小叮当
58分钟前
11
0
手把手教你如何用黑白显示器显示彩色!

来源:大数据文摘 本文约1000字,建议阅读6分钟。 本文为你介绍如何通过黑白显示器上也能显示出彩色。 原来在黑白显示器上也能显示出彩色啊!通过在监视器上覆盖拜耳滤色镜,并拼接彩色图像,...

osc_jklrr90y
58分钟前
18
0
key-value结构排序:给定一个字符串,统计每个字符出现频率,先按value降序,再按key升序

对于key-value结构的排序 第一种:lambda表达式 第二种:函数 第三种:类对()的重载,仿函数形式 #include <iostream>#include <vector>#include <unordered_map>#include <string>#in......

osc_gwtkg2dc
59分钟前
0
0
BlockChain:2020年7月10日世界人工智能大会WAIC《链智未来 赋能产业区块链主题论坛——2020全球区块链创新50强》

BlockChain:2020年7月10日世界人工智能大会WAIC《链智未来 赋能产业区块链主题论坛——2020全球区块链创新50强》 目录 世界人工智能大会WAIC《链智未来 赋能产业区块链主题论坛——2020全球...

osc_vew1u0h0
今天
0
0
BlockChain:2020年7月10日世界人工智能大会WAIC《链智未来 赋能产业区块链主题论坛》(三)

BlockChain:2020年7月10日世界人工智能大会WAIC《链智未来 赋能产业区块链主题论坛》(三) 目录 2020年7月10日世界人工智能大会WAIC《链智未来 赋能产业区块链主题论坛》 演讲嘉宾 演讲内容 ...

osc_8o71811p
今天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部