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[:] 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的实现的更多相关文章
- ARM与FPGA通过spi通信设计1.spi基础知识
SPI(Serial Peripheral Interface--串行外设接口)总线系统是一种同步串行外设接口,它可以使MCU与各种外围设备以串行方式进行通信以交换信息.SPI总线可直接与各个厂家生产 ...
- SPI通信实验---verilog(FPGA作为从机,使用可读可写)
本实验讲究实用性,故设计思想为:主机先向从机发送地址,若是向从机写入数据,则向从机发送数据,若是读取从机数据,则向从机发送时钟,然后在时钟下降沿读取数据即可.cs信号上升沿作为SPI通信的结束信号.r ...
- 【6集iCore3_ADP触摸屏驱动讲解视频】6-2 基于FSMC总线的ARM与FPGA通信
视频简介: 该视频介绍基于FSMC总线的ARM与FPGA通信 源视频包下载地址: 链接:http://pan.baidu.com/s/1slJDoQD 密码:tmw7 银杏科技优酷视频发布区: ...
- 012 基于FPGA的网口通信实例设计【转载】
一.网口通信设计分类 通过上面其他章节的介绍,网口千兆通信,可以使用TCP或者UDP协议,可以外挂PHY片或者不挂PHY片,总结下来就有下面几种方式完成通信: 图8‑17基于FPGA的网口通信实例设计 ...
- FPGA+ARM or FPGA+DSP?
网上有人说.现在的FPGA,ARM功能已经强大到无需DSP协助处理了,未来DSP会不会消声灭迹?是DSP取代FPGA和ARM,还是ARM,FPGA取代DSP呢?担心好不容易学精了DSP,结果DSP变成 ...
- 干货分享,FPGA硬件系统的设计技巧
PGA的硬件设计不同于DSP和ARM系统,比较灵活和自由.只要设计好专用管脚的电路,通用I/O的连接可以自己定义.因此,FPGA的电路设计中会有一些特殊的技巧可以参考. 1. FPGA管脚兼容性设计 ...
- OLED的波形曲线、进度条、图片显示(STM32 HAL库 模拟SPI通信 5线OLED屏幕)详细篇
少废话,先上效果图 屏幕显示效果 全家福 一.基础认识及引脚介绍 屏幕参数: 尺寸:0.96英寸 分辨率:128*64 驱动芯片:SSD1306 驱动接口协议:SPI 引脚说明: 二. ...
- [转]什么是SPI通信
SPI:高速同步串行口.3-4线接口,收发独立.可同步进行. SPI,是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口.是Motorola首先在其MC6 ...
- 理解一下单片机的I2C和SPI通信
应某位网友要求,今天说一下单片机的I2C SPI通信,可能说不清楚,因为这毕竟要做实验才可完全理解. I2C和SPI是两种不同的通信协议. 听到协议,似乎高不可攀,其实协议就是人们定义的一个标准而已, ...
随机推荐
- 问题:win7下配置好服务器就是不能查询数据库。(已解决)
我用C写访问mysql的CGI程序,可以执行. 但是,当我写好网页再去访问这个CGI,出现下面的错误 我的环境是:IIS作为服务器,MYSQL数据库,VC++6.0写CGI. 跟踪了一下,发现只要我调 ...
- python 外键用法 多对多关系 ORM操作 模板相关
一.app/models中写类(设计表结构) 1.普通类 class A(models.Model): id=modles.AutoField(primary_key=True) name=mode ...
- XLua----热更新
一.xLua 环境配置 1).Xlua中 Plugin Xlua复制到 需要热更新的工程中---->Assets子目录 2).开启宏HOTFIX_ENABLE File---->bui ...
- Linux atop监控
200 ? "200px" : this.width)!important;} --> 介绍 atop是一个功能非常强大的linux服务器监控工具,它的数据采集主要包括:CP ...
- 如何理解Python装饰器
如何理解Python装饰器?很多学员对此都有疑问,那么上海尚学堂python培训这篇文章就给予答复. 一.预备知识 首先要理解装饰器,首先要先理解在 Python 中很重要的一个概念就是:“函数是 F ...
- [Swift]LeetCode435. 无重叠区间 | Non-overlapping Intervals
Given a collection of intervals, find the minimum number of intervals you need to remove to make the ...
- [Swift]LeetCode863. 二叉树中所有距离为 K 的结点 | All Nodes Distance K in Binary Tree
We are given a binary tree (with root node root), a targetnode, and an integer value K. Return a lis ...
- 微信小程序自动化测试实践
由于腾讯系QQ.微信等都是基于腾讯自研X5内核,不是google原生webview(其实就是进行了二次定制).实质上也是混合应用的一种,现在很多app产品也开始流行采用X5内核作为其内嵌web浏览服务 ...
- C# listview展示表格格式
有时候我们需要展示表格格式的数据,首先想到的是用datagridview控件,比如更改datagridview某一行的数据,这样操作起来就比较麻烦,而listview属于轻量级,刷新和更改相对来说效率 ...
- 「造个轮子」——cicada 设计一个配置模块
前言 在前两次的 cicada 版本中其实还不支持读取配置文件,比如对端口.路由的配置. 因此我按照自己的想法创建了一个 issue ,也收集到了一些很不错的建议. 最终其实还是按照我之前的想法来做了 ...