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

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

  1. ////////////////////////////////////////////////////////////////////////////////
  2. module spi_ctrl(
  3. clk,rst_n,
  4. spi_miso,spi_mosi,spi_clk,
  5. spi_tx_en,spi_tx_rdy,spi_rx_en,spi_rx_rdy,spi_tx_db,spi_rx_db
  6. );
  7.  
  8. input clk; //FPAG输入时钟信号25MHz
  9. input rst_n; //FPGA输入复位信号
  10.  
  11. input spi_miso; //SPI主机输入从机输出数据信号
  12. output spi_mosi; //SPI主机输出从机输入数据信号
  13. output spi_clk; //SPI时钟信号,由主机产生
  14.  
  15. input spi_tx_en; //SPI数据发送使能信号,高有效
  16. output spi_tx_rdy; //SPI数据发送完成标志位,高有效
  17. input spi_rx_en; //SPI数据接收使能信号,高有效
  18. output spi_rx_rdy; //SPI数据接收完成标志位,高有效
  19. input[:] spi_tx_db; //SPI数据发送寄存器
  20. output[:] spi_rx_db; //SPI数据接收寄存器
  21.  
  22. //模拟SPI的时序模式为CPOL=1, CPHA=1,模拟速率为25Mbit
  23.  
  24. //-------------------------------------------------
  25. //SPI时序控制计数器,所有SPI时序由该计数器值控制
  26. reg[:] cnt8; //SPI时序控制计数器,计数范围在0-18
  27.  
  28. always @(posedge clk or negedge rst_n)
  29. if(!rst_n) cnt8 <= 'd0;
  30. else if(spi_tx_en || spi_rx_en) begin
  31. if(cnt8 < 'd18)cnt8 <= cnt8+1'b1; //SPI工作使能
  32. else ; //计数到18停止,等待撤销spi使能
  33. end
  34. else cnt8 <= 'd0; //SPI关闭,计数停止
  35.  
  36. //-------------------------------------------------
  37. //SPI时钟信号产生
  38. reg spi_clkr; //SPI时钟信号,由主机产生
  39.  
  40. always @(posedge clk or negedge rst_n)
  41. if(!rst_n) spi_clkr <= 'b1;
  42. else if(cnt8 > 'd1 && cnt8 < 5'd18) spi_clkr <= ~spi_clkr; //在cnt8处于2-17时SPI时钟有效翻转
  43.  
  44. assign spi_clk = spi_clkr;
  45.  
  46. //-------------------------------------------------
  47. //SPI主机输出数据控制
  48. reg spi_mosir; //SPI主机输出从机输入数据信号
  49.  
  50. always @(posedge clk or negedge rst_n)
  51. if(!rst_n) spi_mosir <= 'b1;
  52. else if(spi_tx_en) begin
  53. case(cnt8[:]) //主机发送8bit数据
  54. 'd1: spi_mosir <= spi_tx_db[7]; //发送bit7
  55. 'd2: spi_mosir <= spi_tx_db[6]; //发送bit6
  56. 'd3: spi_mosir <= spi_tx_db[5]; //发送bit5
  57. 'd4: spi_mosir <= spi_tx_db[4]; //发送bit4
  58. 'd5: spi_mosir <= spi_tx_db[3]; //发送bit3
  59. 'd6: spi_mosir <= spi_tx_db[2]; //发送bit2
  60. 'd7: spi_mosir <= spi_tx_db[1]; //发送bit1
  61. 'd8: spi_mosir <= spi_tx_db[0]; //发送bit0
  62. default: spi_mosir <= 'b1; //spi_mosi没有输出时应保持高电平
  63. endcase
  64. end
  65. else spi_mosir <= 'b1; //spi_mosi没有输出时应保持高电平
  66.  
  67. assign spi_mosi = spi_mosir;
  68.  
  69. //-------------------------------------------------
  70. //SPI主机输入数据控制
  71. reg[:] spi_rx_dbr; //SPI主机输入从机输出数据总线寄存器
  72.  
  73. always @(posedge clk or negedge rst_n)
  74. if(!rst_n) spi_rx_dbr <= 'hff;
  75. else if(spi_rx_en) begin
  76. case(cnt8) //主机接收并锁存8bit数据
  77. 'd3: spi_rx_dbr[7] <= spi_miso; //接收bit7
  78. 'd5: spi_rx_dbr[6] <= spi_miso; //接收bit6
  79. 'd7: spi_rx_dbr[5] <= spi_miso; //接收bit5
  80. 'd9: spi_rx_dbr[4] <= spi_miso; //接收bit4
  81. 'd11: spi_rx_dbr[3] <= spi_miso; //接收bit3
  82. 'd13: spi_rx_dbr[2] <= spi_miso; //接收bit2
  83. 'd15: spi_rx_dbr[1] <= spi_miso; //接收bit1
  84. 'd17: spi_rx_dbr[0] <= spi_miso; //接收bit0
  85. default: ;
  86. endcase
  87. end
  88.  
  89. assign spi_rx_db = spi_rx_dbr;
  90.  
  91. //-------------------------------------------------
  92. //SPI数据发送完成标志位,高有效
  93. assign spi_tx_rdy = (cnt8 == 'd18)/* & spi_tx_en)*/;
  94.  
  95. //-------------------------------------------------
  96. //SPI数据接收完成标志位,高有效
  97. assign spi_rx_rdy = (cnt8 == 'd18)/* & spi_rx_en)*/;
  98.  
  99. endmodule

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

  1. module spi_master
  2. (
  3. input sys_clk,
  4. input rst,
  5. output nCS, //chip select (SPI mode)
  6. output DCLK, //spi clock
  7. output MOSI, //spi master data output
  8. input MISO, //spi master input
  9. input CPOL,
  10. input CPHA,
  11. input nCS_ctrl,
  12. input[:] clk_div,
  13. input wr_req,
  14. output wr_ack,
  15. input[:] data_in,
  16. output[:] data_out
  17. );
  18. //状态机状态
  19. localparam IDLE = ;
  20. localparam DCLK_EDGE = ;
  21. localparam DCLK_IDLE = ;
  22. localparam ACK = ;
  23. localparam LAST_HALF_CYCLE = ;
  24. localparam ACK_WAIT = ;
  25.  
  26. reg DCLK_reg;
  27. reg[:] MOSI_shift;//移位寄存器
  28. reg[:] MISO_shift;
  29. reg[:] state;
  30. reg[:] next_state;
  31. reg [:] clk_cnt;
  32. reg[:] clk_edge_cnt;
  33. assign MOSI = MOSI_shift[];
  34. assign DCLK = DCLK_reg;
  35. assign data_out = MISO_shift;
  36. assign wr_ack = (state == ACK);
  37. assign nCS = nCS_ctrl;
  38.  
  39. /*************这个就是状态机的定义**************/
  40. always@(posedge sys_clk or posedge rst)
  41. begin
  42. if(rst)
  43. state <= IDLE;
  44. else
  45. state <= next_state;
  46. end
  47. /****************end*************************/
  48.  
  49. /****************状态机的具体过程*************/
  50. always@(*)
  51. begin
  52. case(state)
  53. IDLE:
  54. if(wr_req == 'b1)
  55. next_state <= DCLK_IDLE;
  56. else
  57. next_state <= IDLE;
  58. DCLK_IDLE:
  59. //half a SPI clock cycle produces a clock edge//半个SPI时钟周期产生时钟边沿
  60. if(clk_cnt == clk_div)
  61. next_state <= DCLK_EDGE;
  62. else
  63. next_state <= DCLK_IDLE;
  64. DCLK_EDGE:
  65. //a SPI byte with a total of 16 clock edges//一个SPI字节,总共有16个时钟边沿
  66. if(clk_edge_cnt == 'd15)
  67. next_state <= LAST_HALF_CYCLE;
  68. else
  69. next_state <= DCLK_IDLE;
  70. //this is the last data edge //这是最后一个数据边缘
  71. LAST_HALF_CYCLE:
  72. if(clk_cnt == clk_div)
  73. next_state <= ACK;
  74. else
  75. next_state <= LAST_HALF_CYCLE;
  76. //send one byte complete//发送一个字节完成
  77. ACK:
  78. next_state <= ACK_WAIT;
  79. //wait for one clock cycle, to ensure that the cancel request signal//等待一个时钟周期,以确保取消请求信号
  80. ACK_WAIT:
  81. next_state <= IDLE;
  82. default:
  83. next_state <= IDLE;
  84. endcase
  85. end
  86.  
  87. /****************时钟翻转************************/
  88. always@(posedge sys_clk or posedge rst)
  89. begin
  90. if(rst) /*在空闲状态之前,SCK一直保持CPOL的极性*/
  91. DCLK_reg <= 'b0;
  92. else if(state == IDLE)
  93. DCLK_reg <= CPOL;
  94. else if(state == DCLK_EDGE) /*边缘检测时,反转SCK*/
  95. DCLK_reg <= ~DCLK_reg;//SPI clock edge
  96. end
  97. /****************end*****************************/
  98.  
  99. //SPI clock wait counter /*一个计数器*/
  100. always@(posedge sys_clk or posedge rst)
  101. begin
  102. if(rst)
  103. clk_cnt <= 'd0;
  104. else if(state == DCLK_IDLE || state == LAST_HALF_CYCLE)
  105. clk_cnt <= clk_cnt + 'd1;
  106. else
  107. clk_cnt <= 'd0;
  108. end
  109. //SPI clock edge counter
  110. always@(posedge sys_clk or posedge rst)
  111. begin
  112. if(rst)
  113. clk_edge_cnt <= 'd0;
  114. else if(state == DCLK_EDGE)
  115. clk_edge_cnt <= clk_edge_cnt + 'd1;
  116. else if(state == IDLE)
  117. clk_edge_cnt <= 'd0;
  118. end
  119.  
  120. //SPI data output /*这里就是SPI输出的移位方式*/
  121. always@(posedge sys_clk or posedge rst)
  122. begin
  123. if(rst)
  124. MOSI_shift <= 'd0;
  125. else if(state == IDLE && wr_req)
  126. MOSI_shift <= data_in;
  127. else if(state == DCLK_EDGE)
  128. if(CPHA == 'b0 && clk_edge_cnt[0] == 1'b1) /*两种方式,取决于CPHA*/
  129. MOSI_shift <= {MOSI_shift[:],MOSI_shift[]}; /*常见的移位语句,大家要敏感*/
  130. else if(CPHA == 'b1 && (clk_edge_cnt != 5'd0 && clk_edge_cnt[] == 'b0))
  131. MOSI_shift <= {MOSI_shift[:],MOSI_shift[]};
  132. end
  133. //SPI data input
  134. always@(posedge sys_clk or posedge rst)
  135. begin
  136. if(rst)
  137. MISO_shift <= 'd0;
  138. else if(state == IDLE && wr_req)
  139. MISO_shift <= 'h00;
  140. else if(state == DCLK_EDGE)
  141. if(CPHA == 'b0 && clk_edge_cnt[0] == 1'b0)
  142. MISO_shift <= {MISO_shift[:],MISO}; /*MISO输入,然后进行移位*/
  143. else if(CPHA == 'b1 && (clk_edge_cnt[0] == 1'b1))
  144. MISO_shift <= {MISO_shift[:],MISO};
  145. end
  146. 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. 问题:win7下配置好服务器就是不能查询数据库。(已解决)

    我用C写访问mysql的CGI程序,可以执行. 但是,当我写好网页再去访问这个CGI,出现下面的错误 我的环境是:IIS作为服务器,MYSQL数据库,VC++6.0写CGI. 跟踪了一下,发现只要我调 ...

  2. python 外键用法 多对多关系 ORM操作 模板相关

    一.app/models中写类(设计表结构) 1.普通类 class  A(models.Model): id=modles.AutoField(primary_key=True) name=mode ...

  3. XLua----热更新

    一.xLua 环境配置 1).Xlua中  Plugin  Xlua复制到 需要热更新的工程中---->Assets子目录 2).开启宏HOTFIX_ENABLE File---->bui ...

  4. Linux atop监控

    200 ? "200px" : this.width)!important;} --> 介绍 atop是一个功能非常强大的linux服务器监控工具,它的数据采集主要包括:CP ...

  5. 如何理解Python装饰器

    如何理解Python装饰器?很多学员对此都有疑问,那么上海尚学堂python培训这篇文章就给予答复. 一.预备知识 首先要理解装饰器,首先要先理解在 Python 中很重要的一个概念就是:“函数是 F ...

  6. [Swift]LeetCode435. 无重叠区间 | Non-overlapping Intervals

    Given a collection of intervals, find the minimum number of intervals you need to remove to make the ...

  7. [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 ...

  8. 微信小程序自动化测试实践

    由于腾讯系QQ.微信等都是基于腾讯自研X5内核,不是google原生webview(其实就是进行了二次定制).实质上也是混合应用的一种,现在很多app产品也开始流行采用X5内核作为其内嵌web浏览服务 ...

  9. C# listview展示表格格式

    有时候我们需要展示表格格式的数据,首先想到的是用datagridview控件,比如更改datagridview某一行的数据,这样操作起来就比较麻烦,而listview属于轻量级,刷新和更改相对来说效率 ...

  10. 「造个轮子」——cicada 设计一个配置模块

    前言 在前两次的 cicada 版本中其实还不支持读取配置文件,比如对端口.路由的配置. 因此我按照自己的想法创建了一个 issue ,也收集到了一些很不错的建议. 最终其实还是按照我之前的想法来做了 ...