这里主要放两个代码第一个是正常的不使用状态机的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[:] spi_tx_db; //SPI数据发送寄存器
output[:] spi_rx_db; //SPI数据接收寄存器 //模拟SPI的时序模式为CPOL=1, CPHA=1,模拟速率为25Mbit //-------------------------------------------------
//SPI时序控制计数器,所有SPI时序由该计数器值控制
reg[:] cnt8; //SPI时序控制计数器,计数范围在0-18 always @(posedge clk or negedge rst_n)
if(!rst_n) cnt8 <= 'd0;
else if(spi_tx_en || spi_rx_en) begin
if(cnt8 < 'd18)cnt8 <= cnt8+1'b1; //SPI工作使能
else ; //计数到18停止,等待撤销spi使能
end
else cnt8 <= 'd0; //SPI关闭,计数停止 //-------------------------------------------------
//SPI时钟信号产生
reg spi_clkr; //SPI时钟信号,由主机产生 always @(posedge clk or negedge rst_n)
if(!rst_n) spi_clkr <= 'b1;
else if(cnt8 > '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 <= 'b1;
else if(spi_tx_en) begin
case(cnt8[:]) //主机发送8bit数据
'd1: spi_mosir <= spi_tx_db[7]; //发送bit7
'd2: spi_mosir <= spi_tx_db[6]; //发送bit6
'd3: spi_mosir <= spi_tx_db[5]; //发送bit5
'd4: spi_mosir <= spi_tx_db[4]; //发送bit4
'd5: spi_mosir <= spi_tx_db[3]; //发送bit3
'd6: spi_mosir <= spi_tx_db[2]; //发送bit2
'd7: spi_mosir <= spi_tx_db[1]; //发送bit1
'd8: spi_mosir <= spi_tx_db[0]; //发送bit0
default: spi_mosir <= 'b1; //spi_mosi没有输出时应保持高电平
endcase
end
else spi_mosir <= 'b1; //spi_mosi没有输出时应保持高电平 assign spi_mosi = spi_mosir; //-------------------------------------------------
//SPI主机输入数据控制
reg[:] spi_rx_dbr; //SPI主机输入从机输出数据总线寄存器 always @(posedge clk or negedge rst_n)
if(!rst_n) spi_rx_dbr <= 'hff;
else if(spi_rx_en) begin
case(cnt8) //主机接收并锁存8bit数据
'd3: spi_rx_dbr[7] <= spi_miso; //接收bit7
'd5: spi_rx_dbr[6] <= spi_miso; //接收bit6
'd7: spi_rx_dbr[5] <= spi_miso; //接收bit5
'd9: spi_rx_dbr[4] <= spi_miso; //接收bit4
'd11: spi_rx_dbr[3] <= spi_miso; //接收bit3
'd13: spi_rx_dbr[2] <= spi_miso; //接收bit2
'd15: spi_rx_dbr[1] <= spi_miso; //接收bit1
'd17: spi_rx_dbr[0] <= spi_miso; //接收bit0
default: ;
endcase
end assign spi_rx_db = spi_rx_dbr; //-------------------------------------------------
//SPI数据发送完成标志位,高有效
assign spi_tx_rdy = (cnt8 == 'd18)/* & spi_tx_en)*/; //-------------------------------------------------
//SPI数据接收完成标志位,高有效
assign spi_rx_rdy = (cnt8 == '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[:] clk_div,
input wr_req,
output wr_ack,
input[:] data_in,
output[:] data_out
);
//状态机状态
localparam IDLE = ;
localparam DCLK_EDGE = ;
localparam DCLK_IDLE = ;
localparam ACK = ;
localparam LAST_HALF_CYCLE = ;
localparam ACK_WAIT = ; reg DCLK_reg;
reg[:] MOSI_shift;//移位寄存器
reg[:] MISO_shift;
reg[:] state;
reg[:] next_state;
reg [:] clk_cnt;
reg[:] clk_edge_cnt;
assign MOSI = MOSI_shift[];
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 == '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 == '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 <= '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 <= 'd0;
else if(state == DCLK_IDLE || state == LAST_HALF_CYCLE)
clk_cnt <= clk_cnt + 'd1;
else
clk_cnt <= 'd0;
end
//SPI clock edge counter
always@(posedge sys_clk or posedge rst)
begin
if(rst)
clk_edge_cnt <= 'd0;
else if(state == DCLK_EDGE)
clk_edge_cnt <= clk_edge_cnt + 'd1;
else if(state == IDLE)
clk_edge_cnt <= 'd0;
end //SPI data output /*这里就是SPI输出的移位方式*/
always@(posedge sys_clk or posedge rst)
begin
if(rst)
MOSI_shift <= 'd0;
else if(state == IDLE && wr_req)
MOSI_shift <= data_in;
else if(state == DCLK_EDGE)
if(CPHA == 'b0 && clk_edge_cnt[0] == 1'b1) /*两种方式,取决于CPHA*/
MOSI_shift <= {MOSI_shift[:],MOSI_shift[]}; /*常见的移位语句,大家要敏感*/
else if(CPHA == 'b1 && (clk_edge_cnt != 5'd0 && clk_edge_cnt[] == 'b0))
MOSI_shift <= {MOSI_shift[:],MOSI_shift[]};
end
//SPI data input
always@(posedge sys_clk or posedge rst)
begin
if(rst)
MISO_shift <= 'd0;
else if(state == IDLE && wr_req)
MISO_shift <= 'h00;
else if(state == DCLK_EDGE)
if(CPHA == 'b0 && clk_edge_cnt[0] == 1'b0)
MISO_shift <= {MISO_shift[:],MISO}; /*MISO输入,然后进行移位*/
else if(CPHA == 'b1 && (clk_edge_cnt[0] == 1'b1))
MISO_shift <= {MISO_shift[:],MISO};
end
endmodule

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

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

ARM与FPGA通过spi通信设计2.spi master的实现的更多相关文章

  1. ARM与FPGA通过spi通信设计1.spi基础知识

    SPI(Serial Peripheral Interface--串行外设接口)总线系统是一种同步串行外设接口,它可以使MCU与各种外围设备以串行方式进行通信以交换信息.SPI总线可直接与各个厂家生产 ...

  2. SPI通信实验---verilog(FPGA作为从机,使用可读可写)

    本实验讲究实用性,故设计思想为:主机先向从机发送地址,若是向从机写入数据,则向从机发送数据,若是读取从机数据,则向从机发送时钟,然后在时钟下降沿读取数据即可.cs信号上升沿作为SPI通信的结束信号.r ...

  3. 【6集iCore3_ADP触摸屏驱动讲解视频】6-2 基于FSMC总线的ARM与FPGA通信

    视频简介: 该视频介绍基于FSMC总线的ARM与FPGA通信   源视频包下载地址: 链接:http://pan.baidu.com/s/1slJDoQD 密码:tmw7   银杏科技优酷视频发布区: ...

  4. 012 基于FPGA的网口通信实例设计【转载】

    一.网口通信设计分类 通过上面其他章节的介绍,网口千兆通信,可以使用TCP或者UDP协议,可以外挂PHY片或者不挂PHY片,总结下来就有下面几种方式完成通信: 图8‑17基于FPGA的网口通信实例设计 ...

  5. FPGA+ARM or FPGA+DSP?

    网上有人说.现在的FPGA,ARM功能已经强大到无需DSP协助处理了,未来DSP会不会消声灭迹?是DSP取代FPGA和ARM,还是ARM,FPGA取代DSP呢?担心好不容易学精了DSP,结果DSP变成 ...

  6. 干货分享,FPGA硬件系统的设计技巧

    PGA的硬件设计不同于DSP和ARM系统,比较灵活和自由.只要设计好专用管脚的电路,通用I/O的连接可以自己定义.因此,FPGA的电路设计中会有一些特殊的技巧可以参考. 1. FPGA管脚兼容性设计 ...

  7. OLED的波形曲线、进度条、图片显示(STM32 HAL库 模拟SPI通信 5线OLED屏幕)详细篇

    少废话,先上效果图 屏幕显示效果         全家福 一.基础认识及引脚介绍 屏幕参数: 尺寸:0.96英寸 分辨率:128*64 驱动芯片:SSD1306 驱动接口协议:SPI 引脚说明: 二. ...

  8. [转]什么是SPI通信

    SPI:高速同步串行口.3-4线接口,收发独立.可同步进行. SPI,是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口.是Motorola首先在其MC6 ...

  9. 理解一下单片机的I2C和SPI通信

    应某位网友要求,今天说一下单片机的I2C SPI通信,可能说不清楚,因为这毕竟要做实验才可完全理解. I2C和SPI是两种不同的通信协议. 听到协议,似乎高不可攀,其实协议就是人们定义的一个标准而已, ...

随机推荐

  1. [tkinter]Radiobutton单选按钮的使用

    首先因为单选按钮有一个特性(一个被选中后,自动清除其它按钮的选中状态) 所以使用方式也有点不同 错误示例 from tkinter import * root = Tk() r1 = Radiobut ...

  2. 玩转vue前进刷新,后退不刷新and按需刷新

    大白萝卜小课堂开讲了!带你玩转vue前进后退按需刷新! 用vue做后台管理项目,特别是有列表页.列表数据详情页.列表数据修改页功能的码友们,几乎都被vue前进后退都刷新的逻辑坑过,本萝卜更是! 萝卜的 ...

  3. 基于js的数据结构与算法-数组

    Given an array of integers, return indices of the two numbers such that they add up to a specific ta ...

  4. gc笔记(转)

    GC,即就是Java垃圾回收机制.目前主流的JVM(HotSpot)采用的是分代收集算法.与C++不同的是,Java采用的是类似于树形结构的可达性分析法来判断对象是否还存在引用.即:从gcroot开始 ...

  5. Web测试入门:Selenium+Chrome+Python+Mac OS

    一.环境配置 Chromedriver 下载及环境配置 url:使用WebDriver在Chrome浏览器上进行测试时,需要从http://chromedriver.storage.googleapi ...

  6. Thread.join(), CountDownLatch、CyclicBarrier和 Semaphore区别,联系及应用

    在java 1.5中,提供了一些非常有用的辅助类来帮助我们进行并发编程,比如CountDownLatch,CyclicBarrier和Semaphore,今天我们就来学习一下这三个辅助类的用法, 由于 ...

  7. [Swift]LeetCode823. 带因子的二叉树 | Binary Trees With Factors

    Given an array of unique integers, each integer is strictly greater than 1. We make a binary tree us ...

  8. 《关于长沙.NET技术社区未来发展规划》问卷调查结果公布

    那些开发者们对于社区的美好期待 2月,长沙.net 技术社区自从把群拉起来开始,做了一次比较正式.题目为<关于长沙.NET技术社区未来发展规划>的问卷调查,在问卷调查中,溪源写道: 随着互 ...

  9. JVM基础系列第8讲:JVM 垃圾回收机制

    在第 6 讲中我们说到 Java 虚拟机的内存结构,提到了这部分的规范其实是由<Java 虚拟机规范>指定的,每个 Java 虚拟机可能都有不同的实现.其实涉及到 Java 虚拟机的内存, ...

  10. ElasticSearch(2)---SpringBoot整合ElasticSearch

    SpringBoot整合ElasticSearch 一.基于spring-boot-starter-data-elasticsearch整合 开发环境:springboot版本:2.0.1,elast ...