当前位置:   article > 正文

Verilog 代码编写 IIC通信-主到从向芯片写入数据_fpga实现iic通信向ad芯片配置数据

fpga实现iic通信向ad芯片配置数据

题目:

        根据课堂讲授的基本原理,自己尝试编写一个 IIC 控制逻辑,FPGA 的输 入时钟为 10MHz,IIC 的通信频率为 400KHz,要求 FPGA 向 AD 芯片写入控 制指令,AD 芯片的地址为 0000123(改为十进制 123,即 01111011),AD 芯片 中有三个地址连续的寄存器, 地址为 0x48,配置数据为 0x55,地址为 0x49, 配置数据为 0xAA,地址为 0x50,配置数据为 0xCC,试画出电路连接框图、状 态转移图、完成代码编写并仿真。
分析:
      
       根据要求,此 IIC 通信模块应该在 FPGA 与 AD 芯片间建立连接,其中最主要的就是 SCL 时钟线输出给 AD 芯片,建立通信时钟,还有 SDA 线实现双向数 据传输。
       电路模块框图如下所示:
        其中两个上拉电阻 R1 和 R2 保证了在空闲状态时,SCL 线与 SDA 线上均保 持高电平。
        根据 IIC 的通信方法,我们按照 Moore 型状态机 进行时序编写,这是因为整个通信过程中状态的转移不受输入条件的影响,仅依赖过程中时序的变化,从而改变状态。设计思路如下:
1.按照题目意思,需要 17 个状态来表示整个通信过程,即:
不定态+起始态+发出芯片地址+从机应答+发出寄存器 1 地址+从机应答+发 出寄存器 1 数据+从机应答+发出寄存器 2 地址+从机应答+发出寄存器 2 数据+ 从机应答+发出寄存器 3 地址+从机应答+发出寄存器 3 数据+从机应答+终止态
        由此在编写 Verilog 语言时应定义以上 17 个状态。当满足触发条件时将发生状态转移。
2.采用三端式状态机进行编写。除了正常的三部分之外,还应该 有一个数 据寄存器转移的部分,该部分提供了状态转移的动力和条件。
3.设计计数器产生所需频率的时钟 SCL 以及相应的变换节点,即在本设计中视为 SCL 高电平中点为稳定状态,判断起始与终止条件;将 SCL 低电平中点 看作数据变换点,即仅在此时发生数据变化。
4.SDA 的双向传输特性使用 三态门 来实现。
        设计状态转移图如下:

 

        具体代码如下:

  1. module IIC_control(clk,rst,SCL,SDA,en);
  2. input clk,rst,en;
  3. output reg SCL;
  4. inout SDA;
  5. reg sdareg; //SDA 数据寄存器
  6. reg sdalink; //双向端口控制
  7. assign SDA=sdalink?sdareg:1'bz;//三态门控制双向口
  8. parameter IIC_idle=5'D0, //起始态
  9. IIC_start=5'D1, //开始
  10. IIC_icaddr=5'D2, //发出芯片地址
  11. IIC_icaddrask=5'D3, //从机应答
  12. IIC_regaddr1=5'D4, //发出寄存器 1 地址
  13. IIC_regaddrask1=5'D5, //从机应答
  14. IIC_regdata1=5'D6, //寄存器 1 数据
  15. IIC_regdataask1=5'D7, //从机应答
  16. IIC_regaddr2=5'D8, //发出寄存器 2 地址
  17. IIC_regaddrask2=5'D9, //从机应答
  18. IIC_regdata2=5'D10, //寄存器 2 数据
  19. IIC_regdataask2=5'D11, //从机应答
  20. IIC_regaddr3=5'D12, //发出寄存器 3 地址
  21. IIC_regaddrask3=5'D13, //从机应答
  22. IIC_regdata3=5'D14, //寄存器 3 数据
  23. IIC_regdataask3=5'D15, //从机应答
  24. IIC_stop=5'D16, //终止状态
  25. icaddr=8'b0111_1011, //芯片地址
  26. icaddrout={icaddr[6:0],1'b0}, //发出地址
  27. regaddr1=8'h48, //寄存器 1 地址
  28. regdata1=8'h55, //寄存器 1 数据
  29. regaddr2=8'h49, //寄存器 2 地址
  30. regdata2=8'hAA, //寄存器 2 数据
  31. regaddr3=8'h50, //寄存器 3 地址
  32. regdata3=8'hCC, //寄存器 3 数据
  33. freqcnt=(10000000/400000); //SCL 与 clk 的周期关系
  34. reg [7:0] cnt; //计数器产生 SCL
  35. reg [3:0] bytecnt; //数据或地址传输的位计数
  36. reg [4:0] state,nstate;
  37. assign SCL_h=(cnt==(freqcnt>>2));//SCL 时钟四分之一周期处,高电平中点
  38. assign SCL_l=(cnt==(freqcnt>>2)*3);//SCL 时钟周期四分之三处,低电平
  39. 中点
  40. //cnt 计数,从 0 到(freqcnt-1)
  41. always @(posedge clk or negedge rst)
  42. begin
  43. if(!rst)
  44. cnt<=1'b0;
  45. else if(cnt==freqcnt-1'b1)//计数到最大
  46. cnt<=1'b0;
  47. else
  48. cnt<=cnt+1'b1;
  49. end
  50. //产生 SCL 的时钟信号
  51. always @(posedge clk or negedge rst)//SCL 时钟跳变
  52. begin
  53. if(!rst)
  54. SCL<=1'b0;
  55. else begin
  56. if(cnt>=1'b0&&cnt<=(freqcnt>>1)-1'b1)
  57. SCL<=1'b1;//SCL 前半周期为高
  58. else
  59. SCL<=1'b0;//SCL 后半周期为低
  60. end
  61. end
  62. //IIC 三段式状态机:
  63. //第一部分:
  64. always @(posedge clk or negedge rst)
  65. begin
  66. if(!rst)
  67. state<=IIC_idle;
  68. else
  69. state<=nstate;
  70. end
  71. //第二部分 状态转移:
  72. always @(*)
  73. begin
  74. nstate<=state;
  75. case(state)
  76. IIC_idle: if(en)
  77. nstate<=IIC_start;
  78. IIC_start: if(SCL_h)//SCL 为高时,SDA 跳变,进入起始
  79. nstate<=IIC_icaddr;
  80. IIC_icaddr: if(SCL_l==1'b1&&bytecnt==3'b0)//地址发送完成
  81. nstate<=IIC_icaddrask;
  82. IIC_icaddrask: if(SCL_l)//SCL 为低时数据变化
  83. nstate<=IIC_regaddr1;
  84. IIC_regaddr1:if(SCL_l==1'b1&&bytecnt==3'b0)
  85. nstate<=IIC_regaddrask1;
  86. IIC_regaddrask1:if(SCL_l)
  87. nstate<=IIC_regdata1;
  88. IIC_regdata1:if(SCL_l==1'b1&&bytecnt==3'b0)
  89. nstate<=IIC_regdataask1;
  90. IIC_regdataask1:if(SCL_l)
  91. nstate<=IIC_regaddr2;
  92. IIC_regaddr2:if(SCL_l==1'b1&&bytecnt==3'b0)
  93. nstate<=IIC_regaddrask2;
  94. IIC_regaddrask2:if(SCL_l)
  95. nstate<=IIC_regdata2;
  96. IIC_regdata2: if(SCL_l==1'b1&&bytecnt==3'b0)
  97. nstate<=IIC_regdataask2;
  98. IIC_regdataask2: if(SCL_l)
  99. nstate<=IIC_regaddr3;
  100. IIC_regaddr3:if(SCL_l==1'b1&&bytecnt==3'b0)
  101. nstate<=IIC_regaddrask3;
  102. IIC_regaddrask3:if(SCL_l)
  103. nstate<=IIC_regdata3;
  104. IIC_regdata3:if(SCL_l==1'b1&&bytecnt==3'b0)
  105. nstate<=IIC_regdataask3;
  106. IIC_regdataask3:if(SCL_l)
  107. nstate<=IIC_stop;
  108. IIC_stop: if(SCL_h)
  109. nstate<=IIC_stop;
  110. default: nstate<=state;
  111. endcase
  112. end
  113. //第三部分 数据输出控制:
  114. always @(posedge clk or negedge rst)
  115. begin
  116. if(!rst) begin
  117. sdareg<=1;
  118. sdalink<=1;
  119. end
  120. else begin
  121. case(state)
  122. IIC_idle: begin
  123. sdareg<=1;
  124. sdalink<=1;
  125. end
  126. IIC_start:begin
  127. if(SCL_h) begin
  128. sdareg<=0;
  129. sdalink<=1;
  130. end
  131. end
  132. IIC_icaddr:begin //输出芯片地址
  133. if(SCL_l) begin
  134. sdareg<=icaddr[bytecnt];
  135. sdalink<=1;
  136. end
  137. end
  138. IIC_icaddrask,IIC_regaddrask1,IIC_regaddrask2,
  139. IIC_regaddrask3,IIC_regdataask1,IIC_regdataask2,
  140. IIC_regdataask3:begin //输入应答信号
  141. if(SCL_l)begin
  142. sdareg<=0;
  143. sdalink<=0;
  144. end
  145. end
  146. IIC_regaddr1:begin
  147. if(SCL_l) begin
  148. sdareg<=regaddr1[bytecnt];
  149. sdalink<=1;
  150. end
  151. end
  152. IIC_regaddr2:begin
  153. if(SCL_l) begin
  154. sdareg<=regaddr2[bytecnt];
  155. sdalink<=1;
  156. end
  157. end
  158. IIC_regaddr3:begin
  159. if(SCL_l) begin
  160. sdareg<=regaddr3[bytecnt];
  161. sdalink<=1;
  162. end
  163. end
  164. IIC_regdata1:begin
  165. if(SCL_l) begin
  166. sdareg<=regdata1[bytecnt];
  167. sdalink<=1;
  168. end
  169. end
  170. IIC_regdata2:begin
  171. if(SCL_l) begin
  172. sdareg<=regdata2[bytecnt];
  173. sdalink<=1;
  174. end
  175. end
  176. IIC_regdata3:begin
  177. if(SCL_l) begin
  178. sdareg<=regdata3[bytecnt];
  179. sdalink<=1;
  180. end
  181. end
  182. IIC_stop:begin
  183. if(SCL_h) begin
  184. sdareg<=1;
  185. sdalink<=1;
  186. end
  187. end
  188. endcase
  189. end
  190. end
  191. //数据寄存器控制:
  192. always @(posedge clk or negedge rst)
  193. begin
  194. if(!rst)
  195. bytecnt<=3'b0;
  196. else
  197. case(state)
  198. IIC_icaddr,IIC_regaddr1,IIC_regaddr2,
  199. IIC_regaddr3,IIC_regdata1,IIC_regdata2,
  200. IIC_regdata3://传输 8 位数据或地址
  201. if(SCL_l)
  202. bytecnt<=bytecnt-1;
  203. default: bytecnt<=3'd7;
  204. endcase
  205. end
        最后设计仿真文件,得到如下波形图,进行结果的分析:
1.起始态与芯片地址发送
        图中当使能 en 为 1 且复位 rst 打开时,可以开始传输,由图中黄线标注处可以看到,当 SCL 为高时,SDA 由高变低,传输开始。 接下来传输芯片地址,SDA 输出,由图中红框处可以看到,11110110 ,正是我们的发出地址(芯片地址七位+0)。然后是 SDA 的输入状态,此时为高阻,手动输入 0,视为应答信号。
2.寄存器 1 地址和寄存器 1 数据:
        接上图,芯片地址传输完成后,传输寄存器 1 地址,图中红框 1 处,为01001000,即0x48,然后高阻应答 ,继续传输寄存器 1 数据,图中红框 2 处,为 01010101,即 0x55,然后高阻应答 。可见,与之前设计的数据一样,仿真设计成功。
3.寄存器 2 地址和寄存器 2 数据:
       接上图,同理传输寄存器 2 的地址和数据:地址 01001001, 即 0x49 ,数据 10101010,即 0xAA 。与设计一致。
4.寄存器 3 地址和寄存器 3 数据及终止态:
        接上图,同理传输寄存器 3 的地址和数据:地址 01010000, 即 0x50 ,数据 11001100,即 0xCC 。与设计一致。然后在下一个 SCL 为高时, SDA 恢复高电 平,终止传输。
        仿真结束,结果与预期一致,仿真成功,以下为 testbench:
  1. `timescale 1ns / 1ps
  2. module IIC_control_tb( );
  3. reg clk,rst,en;
  4. wire SCL,SDA;
  5. always #50 clk=~clk; //产生 10MHz 时钟
  6. initial begin
  7. rst<=0;
  8. clk<=0;
  9. en<=0;
  10. #2000 rst<=1;
  11. #1000 en<=1;
  12. end
  13. IIC_control iic(
  14. .clk(clk),
  15. .rst(rst),
  16. .SCL(SCL),
  17. .SDA(SDA),
  18. .en(en)
  19. );
  20. endmodule

 

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/205796
推荐阅读
相关标签
  

闽ICP备14008679号