• IIC学习笔记(参考小梅哥教程)


    基本知识

    IIC: inter-integrated circuit bus ,即 集成电路总线,串行通信,多主从架构,半双工(对讲机),小数据量场合,短距离传输。

    速率:100kb/s 、 400kb/s 、 3.4Mkb/s

    传输单位:8位(一个字节)(双向)

    架构:每个IIC器件都可作为主/从器件,一个时间只有一个主设备

    两条总线线路:SCL(串行时钟线)、 SDA(串行数据线)总线拓扑结构如图

           图中,每个IIC器件拥有唯一地址(识别码)。

           主机往总线上发送地址,所有的从机都能接收到主机发出的地址,然后每个从机都将主机发出的地址与自己的地址比较,如果匹配上了,这个从机就会向总线发出一个响应信号。主机收到响应信号后,开始向总线上发送数据,与这个从机的通讯就建立起来了。如果主机没有收到响应信号,则表示寻址失败。

    IIC帧格式

    默认规定:SCL高电平时,SDA稳定;SCL低电平时,SDA变化。(依此设定SDA的数据变化时刻,在SCL的低电平中点变化较为靠谱).在scl高电平时读/写数据。

    响应位:当 IIC 主机(不一定是发送端还是接受端)将8位数据或命令传出后, SDA释放,即设置为输入,然后等待从机应答(低电平 0 表示应答,1 表示非 应答),此时的时钟仍然是主机提供的。

    起始信号:SCL高且SDA出现下降沿。

    停止信号:SCL高且SDA出现上升沿。

    器件地址

            每个I2C器件都有一个器件地址,有的器件地址在出厂时地址就设置好了,用户不可以 更改(例如 OV7670 器件地址为固定的 0x42),有的确定了几位,剩下几位由硬件确定(比 如常见的I2C接口的EEPROM存储器,留有3个控地址的引脚,由用户自己在硬件设计时确定)。

            对于AT24C64这样一颗EEPROM器件,其器件地址为1010加3位的片选信号。3位片选信号由硬件连接设定。当硬件电路上分别将这三个引脚连接到 GND 或 VCC 时,就可以设置不同的片选地址。

            I2C传输时,按照从高到低的位序进行传输。控制字节的最低位为读写控制位,当该位 为0时表示主机对从机进行写操作,当该位为1时表示主机对从机进行读操作。

            在某些IIC器件中,A2 A1 A0可以当作存储单元地址来用。存储器地址一般为1或2字节。取决于IIC器件容量。

    IIC写时序

    时序:

    1、主机设置 SDA 为输出;发送起始信号;

    3、主机传输器件地址字节,其中最低位为 0,表明为写操作;

    4、主机设置 SDA 为三态门输入,读取从机应答信号;

    5、读取应答信号成功,主机设置 SDA 为输出,传输地址数据高字节;

    6、主机设置 SDA 为三态门输入,读取从机应答信号;

    7、读取应答信号成功,主机设置 SDA 为输出,传输地址数据低字节;

    8、设置 SDA 为三态门输入,读取从机应答信号;

    9、读取应答信号成功,主机设置 SDA 为输出,传输待写入的数据;

    10、设置 SDA 为三态门输入,读取从机应答信号;

    11、读取应答信号成功,主机产生 STOP 位,终止传输。

    IIC页写时序(连续写)

    主机连续写多个字节数据到从机,I2C连续写时序仅部分器件支持。

    时序类似上面,只是变成了连续写n个数据。

    IIC读时序

    注意:虽然是读时序,但是这里先用了写信号告诉从机要读的存储器地址,然后再换成读信号把该地址的数据读到主机。

    1、主机设置 SDA 为输出;发送起始信号

    3、主机传输器件地址字节,其中最低位为 0,表明为写操作

    4、主机设置 SDA 为三态门输入,读取从机应答信号;

    5、读取应答信号成功,主机设置 SDA 为输出,传输地址数据高字节;

    6、主机设置 SDA 为三态门输入,读取从机应答信号;

    7、读取应答信号成功,主机设置 SDA 为输出,传输地址数据低字节;

    8、设置 SDA 为三态门输入,读取从机应答信号;

    9、读取应答信号成功,主机发起起始信号

    10、主机传输器件地址字节,其中最低位为 1,表明为读操作

    11、设置 SDA 为三态门输入,读取从机应答信号;

    12、读取应答信号成功,主机设置 SDA 为三态门输入,读取 SDA 总线上的一个字节的 数据;

    13、主机设置 SDA 输出,产生无应答信号(高电平)(不想继续读数据了)(无需设置为输出高电平,因为总线会被自动拉高);

    14、主机产生 STOP 位,终止传输。

    IIC页读时序

    读数据时用ACK应答,不想继续读时用NOACK应答即可。

    IIC控制器设计

    对完整的读和写时序进行分析,可以总结为五种情况:

    每段在传输的时候,只需要确定当前这个字节的传输 之前是否需要加入起始位,以及当前这个字节的传输结束后是否需要加入停止位就结束了。

    注意事项:

    verilog inout类型设计要点:三态门:用   x = ctrl ? A : 1'bz  即可控制为输出或者高阻态(成为输入)

    对于IIC器件,由于芯片无法输出高电平(防止多个芯片同时产生高电平,导致冲突),故需在电路上连接上拉电阻与电源,然后让芯片输出高阻态来实现高电平,故逻辑设计为:IIC_SDA = ctrl ?( a ? 1'bz : 0 ) : 1'bz  (这是inout类型的最好写法)化简合并:IIC_SDA = ( ctrl && !a )1‘b0 : 1'bz 

    如果不修改,直接用   x = ctrl ? A : 1'bz  ,可能出现冲突的情况如下:

    仿真时可以在testbench文件中用pullup() 或者 pulldown() 来模拟上拉和下拉。(高阻态时即被上拉或者下拉)

    Modelsim仿真时,如果有模块只在testbench文件中存在,没有在quartus中编译(如:引用别人的模块),则需要把该模块文件加入modelsim的编译中,如下图:

                                            verilog设计

    1.时钟设计

            根据IIC时序图,设计时钟需要从两点出发:

    ①在IIC_scl高电平中点读取数据,则我们需要在IIC_scl低电平中点改变数据,从而保证读取数据时数据的稳定性。

    ②起始信号和终止信号是指在IIC_scl高电平时IIC_sda出现变化沿。

                          

    所以,我们需要用比IIC_scl频率更高的时钟来设计IIC_sda的输出。

    IIC_scl的时钟为100kb/s、400kb/s和3.4Mb/s,这里分别用400kb/s、1600kb/s来设计IIC_sda(即4倍时钟频率)。

    由于IIC_sda数据的变化点在IIC_scl的前一个周期(当前周期数据需要稳定可读取),而变化沿的产生又在同一个周期(设定),如果直接用IIC_scl来设计状态机的话,会出现如图情况(即需要在start_bit_state状态里同时设计上升沿和第一个要写的数据,非常不方便)。为了状态机的书写方便,用于设计状态机的驱动时钟相对IIC_scl移相90°(这样就可以在start_bit_state状态只设计上升沿,所有要写的数据都在wr_data_state状态里设计),具体看图(可以先看状态机再回头看这儿)

    2.架构设计

            IIC协议的目的是为了:

    1.向某个IIC器件的某个存储单元写入数据;

    2.从某个IIC器件的某个存储单元读出数据;

    2.1 驱动逻辑模块

    为了实现这两个功能,需要根据时序图,用两个线性序列机分别设计读和写的状态顺序和时序。其中,写控制信息、写存储单元地址、写数据均可以归结为写数据一个状态,具体状态划分如下:

                                                    

    写逻辑:空闲 --> 产生起始位 --> 写控制信息 --> 检查应答 --> 写存储器高位地址 --> 检查应答 --> 写存储器低位地址 --> 检查应答 --> 写数据 --> 检查应答 --> 产生结束位 --> 空闲

    即: idel_state --> start_bit_state --> wr_data_state --> ack_check_state --> wr_data_state --> ack_check_state --> wr_data_state --> ack_check_state --> wr_data_state --> ack_check_state --> stop_bit_state --> idel_state_state

    读逻辑:空闲 --> 产生起始位 --> 写控制信息 --> 检查应答 --> 写存储器高位地址 --> 检查应答 --> 写存储器低位地址 --> 检查应答 --> 产生起始位 --> 写控制信息 --> 检查应答 --> 读数据 --> 产生应答 -->产生结束位 --> 空闲

    即: idel_state --> start_bit_state --> wr_data_state --> ack_check_state --> wr_data_state --> ack_check_state --> wr_data_state --> ack_check_state --> start_bit_state --> wr_data_state --> ack_check_state --> rd_data_state --> gen_noack_state--> stop_bit_state --> idel_state

    从而,IIC的驱动模块架构如下:

                         

            其中,根据wr_sig / rd_sig 来产生开启一次读/写的完整操作,由cmd控制状态,通过时序逐个把IIC_id、wr_addr、rd_addr、wr_data通过write_data输出,通过接受trans_done信号来结束一次读/写操作。

    2.2 底层逻辑模块

            这一层模块用于接受驱动模块发出的命令(即状态指令),根据指令对接受的wr_data进行输出;设计8种状态需要对IIC_sda总线的输出和输入进行的操作。根据时序图,结合线性序列机和状态机进行编写即可,重点在于数据变化的时刻以及IIC_scl这种inout三态门的输入输出设计(前面已经介绍)。

                                            

    3.代码设计

    1. module IIC_design(
    2. input fpga_clk,//50MHz
    3. input rst_n ,
    4. input rate_sel ,
    5. input read_pulse ,
    6. input write_pulse ,
    7. input [7:0]write_data ,
    8. input [7:0]IIC_id ,
    9. input [15:0]wr_addr ,
    10. input [15:0]rd_addr ,
    11. output trans_done ,
    12. output [7:0]read_data ,
    13. output IIC_scl ,
    14. inout IIC_sda
    15. );
    16. //******* IIC_scl设计 ******* //
    17. wire clk_16M ; //
    18. wire clk_4M ;
    19. wire clk_4M90 ;
    20. wire clk_1M ;
    21. wire clk_1M90 ;
    22. wire clk_1600k ;
    23. wire clk_400k ;//对应 scl 400kb/s 速率
    24. wire clk_400k108 ;
    25. wire clk_100k ;//对应 scl 100kb/s 速率
    26. wire clk_100k108 ;
    27. wire sda_clk ;//控制时序的时钟
    28. wire drive_clk ; //产生cmd的时钟
    29. //锁相环分频
    30. pll pll_inst1(
    31. .inclk0(fpga_clk),
    32. .c0(clk_16M),
    33. .c1(clk_4M),
    34. .c2(clk_1M)
    35. );
    36. //10分频
    37. fenpin_10 fenpin_10_inst1(
    38. .clk(clk_4M),
    39. .rst_n(rst_n),
    40. .outClk(clk_400k)
    41. );
    42. fenpin_10 fenpin_10_inst2(
    43. .clk(clk_1M),
    44. .rst_n(rst_n),
    45. .outClk(clk_100k)
    46. );
    47. fenpin_10 fenpin_10_inst3(
    48. .clk(clk_16M),
    49. .rst_n(rst_n),
    50. .outClk(clk_1600k)
    51. );
    52. fenpin_10_108du fenpin_10_108du_inst1(
    53. .clk(clk_4M),
    54. .rst_n(rst_n),
    55. .outClk(clk_400k108)
    56. );
    57. fenpin_10_108du fenpin_10_108du_inst2(
    58. .clk(clk_1M),
    59. .rst_n(rst_n),
    60. .outClk(clk_100k108)
    61. );
    62. //设置IIC总线时钟 400kb/s 或 100kb/s
    63. assign IIC_scl = rate_sel ? clk_400k : clk_100k ;
    64. assign sda_clk = rate_sel ? clk_1600k : clk_400k ;
    65. assign drive_clk = rate_sel ? clk_400k108 : clk_100k108 ;
    66. //**********************************//
    67. wire [7:0]rd_data ;
    68. wire ack_result ;
    69. wire [7:0]command ;
    70. wire [7:0]wr_data ;
    71. IIC_drive IIC_drive_inst(
    72. .fpga_clk(fpga_clk) ,//50MHz
    73. .drive_clk(drive_clk),
    74. .rst_n(rst_n) ,
    75. .wr_sig(write_pulse) ,
    76. .rd_sig(read_pulse) ,
    77. .wr_data(write_data) ,
    78. .rd_data(rd_data) ,
    79. .IIC_id(IIC_id) ,
    80. .wr_addr(wr_addr) ,
    81. .rd_addr(rd_addr) ,
    82. .trans_done(trans_done) ,
    83. .ack_result(ack_result) ,
    84. .cmd(command) ,
    85. .read_data(read_data) ,
    86. .write_data(wr_data)
    87. );
    88. IIC_underlying_logic IIC_underlying_logic_inst(
    89. .sda_clk(sda_clk) ,//
    90. .rst_n(rst_n) ,
    91. .cmd(command) ,
    92. .wr_data(wr_data) ,
    93. .rd_data(rd_data) ,
    94. .ack_result(ack_result) ,
    95. .trans_done(trans_done) ,
    96. .IIC_sda(IIC_sda)
    97. );
    98. endmodule
    1. module IIC_drive(
    2. input fpga_clk ,//50MHz
    3. input drive_clk,
    4. input rst_n ,
    5. input wr_sig ,
    6. input rd_sig ,
    7. input [7:0]wr_data ,
    8. input [7:0]rd_data ,
    9. input [7:0]IIC_id ,
    10. input [15:0]wr_addr ,
    11. input [15:0]rd_addr ,
    12. input trans_done ,
    13. input ack_result ,
    14. output reg [7:0]cmd ,
    15. output reg [7:0]read_data ,
    16. output reg [7:0]write_data
    17. );
    18. reg rd_pulse_reg ;
    19. reg wr_pulse_reg ;
    20. always@(posedge fpga_clk)
    21. begin
    22. wr_pulse_reg <= wr_sig ;
    23. rd_pulse_reg <= rd_sig ;
    24. end
    25. reg read_sig ;
    26. reg write_sig ;
    27. always@(posedge fpga_clk or negedge rst_n)
    28. if(!rst_n)
    29. begin
    30. read_sig <= 0 ;
    31. end
    32. else if( rd_pulse_reg == 0 && rd_sig == 1 )
    33. read_sig <= 1 ;
    34. else if( trans_done )
    35. read_sig <= 0 ;
    36. always@(posedge fpga_clk or negedge rst_n)
    37. if(!rst_n)
    38. begin
    39. write_sig <= 0 ;
    40. end
    41. else if( wr_pulse_reg == 0 && wr_sig == 1 )
    42. write_sig <= 1 ;
    43. else if(trans_done)
    44. write_sig <= 0 ;
    45. //定义状态机7个状态参数
    46. localparam idel_state = 8'b10000000 ; //空闲态:释放sda控制权
    47. localparam start_bit_state = 8'b01000000 ; //产生起始位
    48. localparam wr_data_state = 8'b00100000 ; // 写数据
    49. localparam rd_data_state = 8'b00010000 ; // 读数据
    50. localparam ack_check_state = 8'b00001000 ; //检查响应
    51. localparam stop_bit_state = 8'b00000100 ; //产生停止位
    52. localparam gen_ack_state = 8'b00000010 ; //产生停止位
    53. localparam gen_noack_state = 8'b00000001 ; //产生停止位
    54. //写逻辑
    55. reg [5:0]write_cnt ;
    56. //读逻辑
    57. reg [5:0]read_cnt ;
    58. always@(posedge drive_clk or negedge rst_n)
    59. if(!rst_n)
    60. begin
    61. cmd <= idel_state ;
    62. write_cnt <= 0 ;
    63. write_data <= 0 ;
    64. read_cnt <= 0 ;
    65. read_data <= 0 ;
    66. end
    67. else if( write_sig == 1 )
    68. begin
    69. write_cnt <= write_cnt + 1 ;
    70. if(ack_result)
    71. begin
    72. cmd <= ack_check_state ;
    73. write_cnt <= 0 ;
    74. end
    75. else
    76. begin
    77. if(write_cnt == 1)
    78. cmd <= idel_state ;//进入空闲态
    79. else if ( write_cnt == 2 )
    80. begin
    81. cmd <= start_bit_state ;//产生起始位
    82. end
    83. else if ( write_cnt == 3 )
    84. begin
    85. cmd <= wr_data_state ;
    86. write_data <= IIC_id ;//发送IIC器件ID
    87. end
    88. else if ( write_cnt == 11)
    89. begin
    90. cmd <= ack_check_state ;
    91. end
    92. else if ( write_cnt == 12 )
    93. begin
    94. cmd <= wr_data_state ;
    95. write_data <= wr_addr[15:8] ;//写存储器高位地址
    96. end
    97. else if ( write_cnt == 20 )
    98. begin
    99. cmd <= ack_check_state ;
    100. end
    101. else if ( write_cnt == 21 )
    102. begin
    103. cmd <= wr_data_state ;
    104. write_data <= wr_addr[7:0] ;//写存储器低位地址
    105. end
    106. else if ( write_cnt == 29 )
    107. begin
    108. cmd <= ack_check_state ;
    109. end
    110. else if ( write_cnt == 30 )
    111. begin
    112. cmd <= wr_data_state ;
    113. write_data <= wr_data ; //写数据
    114. end
    115. else if ( write_cnt == 38 )
    116. begin
    117. cmd <= ack_check_state ;
    118. end
    119. else if ( write_cnt == 39 )
    120. begin
    121. cmd <= stop_bit_state ;//产生停止位
    122. end
    123. else if ( write_cnt == 40 )
    124. begin
    125. cmd <= idel_state ;//进入空闲态
    126. write_cnt <= 0 ;
    127. end
    128. end
    129. end
    130. else if( read_sig == 1 )
    131. begin
    132. read_cnt <= read_cnt + 1 ;
    133. if(ack_result)
    134. begin
    135. cmd <= ack_check_state ;
    136. read_cnt <= 0 ;
    137. end
    138. else
    139. begin
    140. if(read_cnt == 1)
    141. cmd <= idel_state ;//进入空闲态
    142. else if ( read_cnt == 2 )
    143. begin
    144. cmd <= start_bit_state ;//产生起始位
    145. end
    146. else if ( read_cnt == 3 )
    147. begin
    148. cmd <= wr_data_state ;
    149. write_data <= IIC_id ;//发送IIC器件ID 和写指令
    150. end
    151. else if ( read_cnt == 11)
    152. begin
    153. cmd <= ack_check_state ;
    154. end
    155. else if ( read_cnt == 12 )
    156. begin
    157. cmd <= wr_data_state ;
    158. write_data <= rd_addr[15:8] ;//写存储器高位地址
    159. end
    160. else if ( read_cnt == 20 )
    161. begin
    162. cmd <= ack_check_state ;
    163. end
    164. else if ( read_cnt == 21 )
    165. begin
    166. cmd <= wr_data_state ;
    167. write_data <= rd_addr[7:0] ;//写存储器低位地址
    168. end
    169. else if ( read_cnt == 29 )
    170. begin
    171. cmd <= ack_check_state ;
    172. end
    173. else if ( read_cnt == 30 )
    174. begin
    175. cmd <= start_bit_state ;
    176. end
    177. else if ( read_cnt == 31 )
    178. begin
    179. cmd <= wr_data_state ;
    180. write_data <= ( IIC_id | 6'b000001 ) ;//写IIC器件ID 和读指令
    181. end
    182. else if ( read_cnt == 39 )
    183. begin
    184. cmd <= ack_check_state ;
    185. end
    186. else if ( read_cnt == 40 )
    187. begin
    188. cmd <= rd_data_state ;
    189. end
    190. else if ( read_cnt == 48 )
    191. begin
    192. cmd <= gen_noack_state ;
    193. end
    194. else if ( read_cnt == 49 )
    195. begin
    196. cmd <= stop_bit_state ;//产生停止位
    197. read_data <= rd_data ; //寄存读数据
    198. end
    199. else if ( read_cnt == 50 )
    200. begin
    201. cmd <= idel_state ;//进入空闲态
    202. write_cnt <= 0 ;
    203. end
    204. end
    205. end
    206. else
    207. begin
    208. cmd <= idel_state ;
    209. write_cnt <= 0 ;
    210. read_cnt <= 0 ;
    211. end
    212. endmodule
    1. module IIC_underlying_logic(
    2. input sda_clk ,//
    3. input rst_n ,
    4. input [7:0]cmd ,
    5. input [7:0]wr_data ,
    6. output reg [7:0]rd_data ,
    7. output reg ack_result ,
    8. output reg trans_done ,
    9. inout IIC_sda
    10. );
    11. //********* IIC_sda设计 ************//
    12. reg sda_out ;
    13. reg sda_ctrl;
    14. assign IIC_sda = sda_ctrl ? ( sda_out ? 1'bz : 1'b0 ) : 1'bz ; //三态门,高阻态时,要么由总线上的上拉电阻拉至高电平,即输出/输出1;要么由从机拉至0,输入0;
    15. //定义状态机7个状态参数
    16. localparam idel_state = 8'b10000000 ; //空闲态:释放sda控制权
    17. localparam start_bit_state = 8'b01000000 ; //产生起始位
    18. localparam wr_data_state = 8'b00100000 ; // 写数据
    19. localparam rd_data_state = 8'b00010000 ; // 读数据
    20. localparam ack_check_state = 8'b00001000 ; //检查响应
    21. localparam stop_bit_state = 8'b00000100 ; //产生停止位
    22. localparam gen_ack_state = 8'b00000010 ; //产生停止位
    23. localparam gen_noack_state = 8'b00000001 ; //产生停止位
    24. reg [7:0]rw_state ;//实时状态
    25. always@( posedge sda_clk or negedge rst_n )
    26. if(!rst_n)
    27. begin
    28. rw_state <= idel_state ;
    29. end
    30. else
    31. begin
    32. if( cmd & idel_state )
    33. rw_state <= idel_state ;
    34. else if ( cmd & start_bit_state )
    35. rw_state <= start_bit_state ;
    36. else if ( cmd & wr_data_state )
    37. rw_state <= wr_data_state ;
    38. else if ( cmd & rd_data_state )
    39. rw_state <= rd_data_state ;
    40. else if ( cmd & ack_check_state )
    41. rw_state <= ack_check_state ;
    42. else if ( cmd & stop_bit_state )
    43. rw_state <= stop_bit_state ;
    44. else if ( cmd & gen_ack_state )
    45. rw_state <= gen_ack_state ;
    46. else if ( cmd & gen_noack_state )
    47. rw_state <= gen_noack_state ;
    48. end
    49. reg [5:0]cnt ;
    50. always@( posedge sda_clk or negedge rst_n )
    51. if(!rst_n)
    52. begin
    53. sda_out <= 1'b1 ;
    54. sda_ctrl<= 1'b0 ;
    55. trans_done <= 0 ;
    56. ack_result <= 0 ;
    57. rd_data <= 0 ;
    58. cnt <= 1'b0 ;
    59. end
    60. else
    61. begin
    62. case(rw_state)
    63. idel_state:
    64. begin
    65. cnt <= 1'b0 ;
    66. sda_ctrl<= 1'b0 ;//释放sda控制权
    67. sda_out <= 1'b1 ;
    68. trans_done <= 0 ;
    69. ack_result <= 0 ;
    70. end
    71. start_bit_state:
    72. begin
    73. sda_ctrl<= 1'b1 ;//获取sda控制权
    74. if(cnt == 2 )
    75. begin
    76. sda_out <= 1'b0 ;
    77. end
    78. cnt <= cnt + 1 ;
    79. if(cnt == 3 )
    80. cnt <= 0 ;
    81. end
    82. wr_data_state:
    83. begin
    84. sda_ctrl<= 1'b1 ;//获取sda控制权
    85. if( cnt == 0 )
    86. sda_out <= wr_data[7] ;
    87. else if ( cnt == 4 )
    88. sda_out <= wr_data[6] ;
    89. else if ( cnt == 8 )
    90. sda_out <= wr_data[5] ;
    91. else if ( cnt == 12 )
    92. sda_out <= wr_data[4] ;
    93. else if ( cnt == 16 )
    94. sda_out <= wr_data[3] ;
    95. else if ( cnt == 20 )
    96. sda_out <= wr_data[2] ;
    97. else if ( cnt == 24 )
    98. sda_out <= wr_data[1] ;
    99. else if ( cnt == 28 )
    100. sda_out <= wr_data[0] ;
    101. cnt <= cnt +1 ;
    102. if( cnt == 31 )
    103. cnt <= 0 ;
    104. end
    105. rd_data_state:
    106. begin
    107. sda_ctrl<= 1'b0 ;//释放sda控制权
    108. if( cnt == 2 )
    109. rd_data[7] <= IIC_sda ;
    110. else if ( cnt == 6 )
    111. rd_data[6] <= IIC_sda ;
    112. else if ( cnt == 10 )
    113. rd_data[5] <= IIC_sda ;
    114. else if ( cnt == 14 )
    115. rd_data[4] <= IIC_sda ;
    116. else if ( cnt == 18 )
    117. rd_data[3] <= IIC_sda ;
    118. else if ( cnt == 22 )
    119. rd_data[2] <= IIC_sda ;
    120. else if ( cnt == 26 )
    121. rd_data[1] <= IIC_sda ;
    122. else if ( cnt == 30 )
    123. rd_data[0] <= IIC_sda ;
    124. cnt <= cnt +1 ;
    125. if( cnt == 31 )
    126. cnt <= 0 ;
    127. end
    128. ack_check_state:
    129. begin
    130. sda_ctrl<= 1'b0 ;//释放sda控制权
    131. if( cnt == 2 )
    132. ack_result <= IIC_sda ;
    133. cnt <= cnt + 1 ;
    134. if( cnt == 3 )
    135. cnt <= 0 ;
    136. end
    137. stop_bit_state:
    138. begin
    139. sda_ctrl<= 1'b1 ;//获取sda控制权
    140. sda_out <= 1'b0 ;
    141. if( cnt == 2 )
    142. begin
    143. sda_ctrl<= 1'b0 ;//释放sda控制权
    144. end
    145. cnt <= cnt + 1 ;
    146. if(cnt == 3 )
    147. begin
    148. cnt <= 0 ;
    149. trans_done <= 1 ;
    150. end
    151. end
    152. gen_ack_state:
    153. begin
    154. sda_ctrl<= 1'b1 ;//获取sda控制权
    155. sda_out <= 1'b0 ;
    156. cnt <= cnt + 1 ;
    157. if(cnt == 3 )
    158. cnt <= 0 ;
    159. end
    160. gen_noack_state:
    161. begin
    162. sda_ctrl<= 1'b1 ;//获取sda控制权
    163. sda_out <= 1'b1 ;
    164. cnt <= cnt + 1 ;
    165. if(cnt == 3 )
    166. cnt <= 0 ;
    167. end
    168. default: ;
    169. endcase
    170. end
    171. endmodule
    1. `timescale 1ns/1ns
    2. module IIC_design_tb();
    3. reg fpga_clk ;
    4. reg rst_n ;
    5. reg rate_sel ;
    6. reg read_pulse ;
    7. reg write_pulse ;
    8. reg [7:0]write_data ;
    9. reg [7:0]IIC_id ;
    10. reg [15:0]wr_addr ;
    11. reg [15:0]rd_addr ;
    12. wire trans_done ;
    13. wire [7:0]read_data ;
    14. wire IIC_scl ;
    15. wire IIC_sda ;
    16. pullup(IIC_sda) ;
    17. IIC_design IIC_design_inst(
    18. .fpga_clk(fpga_clk),//50MHz
    19. .rst_n(rst_n) ,
    20. .rate_sel(rate_sel) ,
    21. .read_pulse(read_pulse) ,
    22. .write_pulse(write_pulse) ,
    23. .write_data(write_data) ,
    24. .IIC_id(IIC_id) ,
    25. .wr_addr(wr_addr) ,
    26. .rd_addr(rd_addr) ,
    27. .trans_done(trans_done) ,
    28. .read_data(read_data) ,
    29. .IIC_scl(IIC_scl) ,
    30. .IIC_sda(IIC_sda)
    31. );
    32. M24LC04B M24LC04B_inst(
    33. .A0(0),
    34. .A1(0),
    35. .A2(0),
    36. .WP(0),
    37. .SDA(IIC_sda),
    38. .SCL(IIC_scl),
    39. .RESET(~rst_n)
    40. );
    41. initial fpga_clk = 1 ;
    42. always #10 fpga_clk = ! fpga_clk ;
    43. initial begin
    44. rst_n = 0 ;
    45. rate_sel = 1 ;
    46. read_pulse = 0 ;
    47. write_pulse = 0 ;
    48. write_data = 0 ;
    49. IIC_id = 0 ;
    50. wr_addr = 0 ;
    51. rd_addr = 0 ;
    52. #200 ;
    53. rst_n = 1 ;
    54. #1000 ;
    55. IIC_id = 8'b10100110 ;
    56. wr_addr = 16'd1 ;
    57. write_data = 8'd20 ;
    58. write_pulse = 1 ;
    59. #100 ;
    60. write_pulse = 0 ;
    61. #125000 ;
    62. rd_addr = 16'd1 ;
    63. read_pulse = 1 ;
    64. #100 ;
    65. read_pulse = 0 ;
    66. #250000 ;
    67. end
    68. endmodule

    4.板级验证

            为验证代码可行性,设计如下验证程序 : fpga通过IIC协议向内部EEPROM存储器的某个单元写入数据,并能读取出来。

            设置两个按键:key1 产生写信号write_sig ;key2 产生写信号read_sig ;

            通过 in-systerm probes and sources 功能验证:

            引脚分配要注意IIC_scl和IIC_sda的引脚要根据手册对应连接,且IIC_id也要对应(这里eeprom存储器M24LC04B对应的器件ID为A0)

    1. module IIC_eeprom(
    2. input clk ,
    3. input rst_n ,
    4. input [1:0]key ,
    5. output IIC_scl ,
    6. inout IIC_sda
    7. );
    8. wire [7:0]read_data ;
    9. wire [15:0]addr ;
    10. wire [7:0]write_data ;
    11. wire write_sig ;
    12. wire read_sig ;
    13. //assign addr = 16'd1 ;
    14. //assign write_data = 8'd250 ;
    15. IIC_design IIC_design_inst(
    16. .fpga_clk(clk),//50MHz
    17. .rst_n(rst_n) ,
    18. .rate_sel(1) ,
    19. .read_pulse(~read_sig) ,
    20. .write_pulse(~write_sig) ,
    21. .write_data(write_data) ,
    22. .IIC_id(8'ha0) ,
    23. .wr_addr(addr) ,
    24. .rd_addr(addr) ,
    25. .trans_done() ,
    26. .read_data(read_data) ,
    27. .IIC_scl(IIC_scl) ,
    28. .IIC_sda(IIC_sda)
    29. );
    30. buttopn_debounde buttopn_debounde_inst1(
    31. .clk(clk),
    32. .tx(key[1]),
    33. .reset(rst_n),
    34. .bd_tx(write_sig)
    35. );
    36. buttopn_debounde buttopn_debounde_inst2(
    37. .clk(clk),
    38. .tx(key[0]),
    39. .reset(rst_n),
    40. .bd_tx(read_sig)
    41. );
    42. insys_wrdata insys_wrdata_inst(
    43. .probe(), // probes.probe
    44. .source(write_data) // 向addr写入数据
    45. );
    46. insys_rddata insys_rddata_inst(
    47. .probe(read_data), // probes.probe
    48. .source(addr) // 手动输入地址,读取addr的数据
    49. );
    50. endmodule

    初始状态:

    修改addr:

    向addr写入数据66:

    从addr读出数据:

    串口-fpga-eeprom芯片

    功能:

    上位机(PC)通过uart串口协议与FPGA通信,发送想要向eeprom芯片写入的地址和数据,以及想要读取某地址的数据。FPGA通过IIC协议与eeprom芯片M24LC04B通信,读写数据。

    EEPROM的全称是“电可擦除可编程只读存储器”,即Electrically Erasable Programmable Read-Only Memory。是相对于紫外擦除的rom来讲的。但是今天已经存在多种EEPROM的变种,变成了一类存储器的统称。这种rom的特点是可以随机访问和修改任何一个字节,可以往每个bit中写入0或者1。这是最传统的一种EEPROM,掉电后数据不丢失,可以保存100年,可以擦写100w次。具有较高的可靠性,但是电路复杂/成本也高。因此目前的EEPROM都是几十千字节到几百千字节的,绝少有超过512K的。

    flash属于广义的EEPROM,因为它也是电擦除的rom。但是为了区别于一般的按字节为单位的擦写的EEPROM,我们都叫它flash。flash做的改进就是擦除时不再以字节为单位,而是以块为单位,一次简化了电路,数据密度更高,降低了成本。上M的rom一般都是flash。

    要求:

    ① 要有一定的抗干扰能力

    ②能舍弃传输错误的数据,又不丢失正确的数据

    方法:

    ① 定义一个传输帧(协议),有帧头帧尾(最好定义为5 或 A 这两个电平不断翻转的),这个帧包括地址、器件ID、读写数据等。

    ② 利用移位寄存器,每接受一个新的数据,就对移位寄存器内所有数据进行一次帧判别,这样就不会丢失。

    实现:

    定义帧格式如下:

    帧头 IIC_id haddr laddr option data 帧尾

    帧头定义为:AA AA 帧尾定义为:55

    option定义: 读(5A);写(A5)

    整帧:

    AA AA ID haddr laddr r/w data 55

    设计:

    每次uart接受模块接受到一个新数据,就通过移位寄存器保存下来,并进行整帧检测,如果符合帧格式,就产生读/写脉冲,通过IIC模块对eeprom存储器进行读写操作,然后通过uart发送模块传输到上位机。

    代码:

    1. module uart_IIC_eeprom(
    2. input fpga_clk ,
    3. input rst_n ,
    4. input uart_rx ,
    5. output rx_done ,
    6. output uart_tx ,
    7. output IIC_scl ,
    8. inout IIC_sda
    9. );
    10. //receive uart data
    11. //wire rx_done ;
    12. wire [7:0]rx_data ;
    13. uart_receive uart_rec_inst(
    14. .clk(fpga_clk) ,
    15. .reset(rst_n) ,
    16. .baud_rate('d5) ,//115200
    17. .uart_rx(uart_rx),
    18. .data(rx_data) ,
    19. .rx_done(rx_done)
    20. );
    21. //Shift register
    22. reg [7:0]rec_shift_reg[7:0] ;
    23. reg rw_flag ;
    24. wire iic_done ;
    25. always@(posedge fpga_clk or negedge rst_n)
    26. if(!rst_n)
    27. begin
    28. rec_shift_reg[0] <= 0 ;
    29. rec_shift_reg[1] <= 0 ;
    30. rec_shift_reg[2] <= 0 ;
    31. rec_shift_reg[3] <= 0 ;
    32. rec_shift_reg[4] <= 0 ;
    33. rec_shift_reg[5] <= 0 ;
    34. rec_shift_reg[6] <= 0 ;
    35. rec_shift_reg[7] <= 0 ;
    36. rw_flag <= 0 ;
    37. end
    38. else if(rx_done)
    39. begin
    40. rec_shift_reg[0] <= rec_shift_reg[1] ;
    41. rec_shift_reg[1] <= rec_shift_reg[2] ;
    42. rec_shift_reg[2] <= rec_shift_reg[3] ;
    43. rec_shift_reg[3] <= rec_shift_reg[4] ;
    44. rec_shift_reg[4] <= rec_shift_reg[5] ;
    45. rec_shift_reg[5] <= rec_shift_reg[6] ;
    46. rec_shift_reg[6] <= rec_shift_reg[7] ;
    47. rec_shift_reg[7] <= rx_data ;
    48. rw_flag <= 1 ;
    49. end
    50. else if(iic_done)
    51. rw_flag <= 0 ;
    52. reg [7:0]IIC_id ;
    53. reg [15:0]write_addr ;
    54. reg [15:0]read_addr ;
    55. reg [7:0]write_data ;
    56. reg [7:0]read_data ;
    57. wire [7:0]rd_data ;
    58. reg write_pulse ;
    59. reg read_pulse ;
    60. IIC_design IIC_rw(
    61. .fpga_clk(fpga_clk),//50MHz
    62. .rst_n(rst_n) ,
    63. .rate_sel(1) ,
    64. .read_pulse(read_pulse) ,
    65. .write_pulse(write_pulse) ,
    66. .write_data(write_data) ,
    67. .IIC_id(IIC_id) ,
    68. .wr_addr(write_addr) ,
    69. .rd_addr(read_addr) ,
    70. .trans_done(iic_done) ,
    71. .read_data(rd_data) ,
    72. .IIC_scl(IIC_scl) ,
    73. .IIC_sda(IIC_sda)
    74. );
    75. wire uart_send_done ;
    76. reg uart_send_en ;
    77. uart_send uart_send(
    78. .clk(fpga_clk),
    79. .reset(rst_n),
    80. .data(read_data),
    81. .send_en(uart_send_en),
    82. .baud_rate(5),//115200
    83. .uart_tx(uart_tx),
    84. .tx_done(uart_send_done)
    85. );
    86. //Parsing frames
    87. localparam frames_header = 8'hAA ;
    88. localparam frames_tail = 8'h55 ;
    89. localparam read_option = 8'h5A ;
    90. localparam write_option = 8'hA5 ;
    91. reg [15:0]wr_cnt ;
    92. reg [15:0]rd_cnt ;
    93. always@(posedge fpga_clk or negedge rst_n)
    94. if(!rst_n)
    95. begin
    96. IIC_id <= 0 ;
    97. write_addr <= 0 ;
    98. read_addr <= 0 ;
    99. write_data <= 0;
    100. read_data <= 0 ;
    101. write_pulse <= 0 ;
    102. read_pulse <= 0 ;
    103. //uart_send_en <= 0 ;
    104. wr_cnt <= 0 ;
    105. rd_cnt <= 0 ;
    106. end
    107. else if( rw_flag )
    108. begin
    109. if ( (rec_shift_reg[0] == frames_header) && (rec_shift_reg[1] == frames_header) && (rec_shift_reg[7] == frames_tail) )
    110. begin
    111. IIC_id <= rec_shift_reg[2] ;
    112. if(rec_shift_reg[5] == write_option )
    113. begin
    114. write_addr <= { rec_shift_reg[3] , rec_shift_reg[4] } ;
    115. write_data <= rec_shift_reg[6] ;
    116. wr_cnt <= wr_cnt + 1 ;
    117. //write signal
    118. if( wr_cnt < 3 )
    119. write_pulse <= 1 ;
    120. else if(iic_done)
    121. begin
    122. wr_cnt <= 0 ;
    123. end
    124. else
    125. write_pulse <= 0 ;
    126. end
    127. else if ( rec_shift_reg[5] == read_option )
    128. begin
    129. read_addr <= { rec_shift_reg[3] , rec_shift_reg[4] } ;
    130. //read signal
    131. rd_cnt <= rd_cnt + 1 ;
    132. if(iic_done)
    133. begin
    134. read_data <= rd_data ;
    135. read_pulse <= 0 ;
    136. //uart_send_en <= 1 ;
    137. rd_cnt <= 0 ;
    138. end
    139. //else if(uart_send_done)
    140. // uart_send_en <= 0 ;
    141. else if(rd_cnt <3)
    142. read_pulse <= 1 ;
    143. else
    144. read_pulse <= 0 ;
    145. end
    146. end
    147. end
    148. else
    149. begin
    150. write_pulse <= 0 ;
    151. read_pulse <= 0 ;
    152. //uart_send_en <= 0 ;
    153. wr_cnt <= 0 ;
    154. rd_cnt <= 0 ;
    155. end
    156. always@(posedge fpga_clk or negedge rst_n)
    157. if(!rst_n)
    158. uart_send_en <= 0 ;
    159. else if(iic_done && (rec_shift_reg[5] == read_option) )
    160. uart_send_en <= 1 ;
    161. else if(uart_send_done)
    162. uart_send_en <= 0 ;
    163. endmodule

    板级验证成功。

    可是可是,这样用分频器设计出来的sda_clk和drive_clk 时钟质量都很差,因为该时钟是从寄存器发出的信号,不是利用fpga的全局时钟资源,所以到达其他不同的寄存器的延时会因为布线长度的不同而不一样。这里上板验证可以成功,因为本设计对时钟质量要求不高。

    重新设计时,应该用fpga_clk或者PLL出来的时钟来设计。

    优化设计

    思路

            为了提高时钟质量,这里改变设计思路,选择用PLL出来的10MHz时钟作为驱动时钟,如图,只要把状态的转移设置从时钟下降沿开始,就可以兼顾起始位状态需要在scl高电平改变数据,读写数据需要在scl低电平修改数据的问题了。

    1. module IIC_design(
    2. input fpga_clk ,
    3. input rst_n ,
    4. input read_pulse ,
    5. input write_pulse ,
    6. input [7:0]write_data ,
    7. input [7:0]IIC_id ,
    8. input [15:0]wr_addr ,
    9. input [15:0]rd_addr ,
    10. output trans_done ,
    11. output [7:0]read_data ,
    12. output IIC_scl ,
    13. inout IIC_sda
    14. );
    15. wire [7:0]wr_data ;
    16. wire [7:0]rd_data ;
    17. wire [7:0]command ;
    18. IIC_drive IIC_drive_inst(
    19. .fpga_clk(fpga_clk) ,
    20. .rst_n(rst_n) ,
    21. .write_sig(write_pulse) ,
    22. .read_sig(read_pulse) ,
    23. .IIC_id(IIC_id) ,
    24. .wr_addr(wr_addr) ,
    25. .rd_addr(rd_addr) ,
    26. .wr_data(write_data) ,
    27. .rd_data(rd_data) ,
    28. .trans_done(trans_done) ,
    29. .read_data(read_data) ,
    30. .write_data(wr_data) ,
    31. .cmd(command)
    32. );
    33. IIC_underlying_logic IIC_underlying_logic_inst(
    34. .fpga_clk(fpga_clk) ,
    35. .rst_n(rst_n) ,
    36. .write_data(wr_data) ,
    37. .cmd(command),
    38. .read_data(rd_data) ,
    39. .trans_done(trans_done) ,
    40. .ack_result() ,
    41. .IIC_scl(IIC_scl) ,
    42. .IIC_sda(IIC_sda)
    43. );
    44. endmodule

    1. module IIC_drive(
    2. input fpga_clk ,
    3. input rst_n ,
    4. input write_sig ,
    5. input read_sig ,
    6. input [7:0]IIC_id ,
    7. input [15:0]wr_addr ,
    8. input [15:0]rd_addr ,
    9. input [7:0]wr_data ,
    10. input [7:0]rd_data ,
    11. input trans_done ,
    12. output reg[7:0]read_data ,
    13. output reg[7:0]write_data ,
    14. output reg [7:0]cmd
    15. );
    16. wire clk_10m ;
    17. //PLL
    18. pll pll_inst(
    19. .inclk0(fpga_clk),
    20. .c0(clk_10m)
    21. );
    22. // IIC rate : 400k clk : 10Mhz
    23. localparam cnt400k_max = 10000000 / 400000 ;
    24. reg [15:0]IIC_cnt ;
    25. always@(posedge clk_10m or negedge rst_n)
    26. if(!rst_n)
    27. IIC_cnt <= 0 ;
    28. else if( IIC_cnt >= 24 )
    29. IIC_cnt <= 0 ;
    30. else
    31. IIC_cnt <= IIC_cnt + 1 ;
    32. //定义状态机8个状态参数
    33. localparam idel_state = 8'b10000000 ; //空闲态:释放sda控制权
    34. localparam start_bit_state = 8'b01000000 ; //产生起始位
    35. localparam wr_data_state = 8'b00100000 ; // 写数据
    36. localparam rd_data_state = 8'b00010000 ; // 读数据
    37. localparam ack_check_state = 8'b00001000 ; //检查响应
    38. localparam stop_bit_state = 8'b00000100 ; //产生停止位
    39. localparam gen_ack_state = 8'b00000010 ; //产生响应位
    40. localparam gen_noack_state = 8'b00000001 ; //产生非响应位
    41. //移位寄存器。寄存读写信号
    42. reg rd_pulse_reg ;
    43. reg wr_pulse_reg ;
    44. always@(posedge fpga_clk)
    45. begin
    46. wr_pulse_reg <= write_sig ;
    47. rd_pulse_reg <= read_sig ;
    48. end
    49. reg rd_sig ;
    50. reg wr_sig ;
    51. always@(posedge fpga_clk or negedge rst_n)
    52. if(!rst_n)
    53. rd_sig <= 0 ;
    54. else if( rd_pulse_reg == 0 && read_sig == 1 )
    55. rd_sig <= 1 ;
    56. else if( trans_done )
    57. rd_sig <= 0 ;
    58. always@(posedge fpga_clk or negedge rst_n)
    59. if(!rst_n)
    60. wr_sig <= 0 ;
    61. else if( wr_pulse_reg == 0 && write_sig == 1 )
    62. wr_sig <= 1 ;
    63. else if( trans_done )
    64. wr_sig <= 0 ;
    65. //读写逻辑
    66. reg [5:0]write_cnt ;
    67. reg [5:0]read_cnt ;
    68. always@(posedge clk_10m or negedge rst_n)
    69. if(!rst_n)
    70. begin
    71. cmd <= idel_state ;
    72. write_data <= 0 ;
    73. read_data <= 0 ;
    74. write_cnt <= 0 ;
    75. read_cnt <= 0 ;
    76. end
    77. else if( wr_sig )
    78. begin
    79. if(write_cnt == 0)
    80. cmd <= idel_state ; //空闲态
    81. else if ( write_cnt == 1 )
    82. begin
    83. cmd <= start_bit_state ;//产生起始位
    84. end
    85. else if ( write_cnt == 2 )
    86. begin
    87. cmd <= wr_data_state ;
    88. write_data <= IIC_id ;//发送IIC器件ID
    89. end
    90. else if ( write_cnt == 10)
    91. begin
    92. cmd <= ack_check_state ;
    93. end
    94. else if ( write_cnt == 11 )
    95. begin
    96. cmd <= wr_data_state ;
    97. write_data <= wr_addr[15:8] ;//写存储器高位地址
    98. end
    99. else if ( write_cnt == 19 )
    100. begin
    101. cmd <= ack_check_state ;
    102. end
    103. else if ( write_cnt == 20 )
    104. begin
    105. cmd <= wr_data_state ;
    106. write_data <= wr_addr[7:0] ;//写存储器低位地址
    107. end
    108. else if ( write_cnt == 28 )
    109. begin
    110. cmd <= ack_check_state ;
    111. end
    112. else if ( write_cnt == 29 )
    113. begin
    114. cmd <= wr_data_state ;
    115. write_data <= wr_data ; //写数据
    116. end
    117. else if ( write_cnt == 37 )
    118. begin
    119. cmd <= ack_check_state ;
    120. end
    121. else if ( write_cnt == 38 )
    122. begin
    123. cmd <= stop_bit_state ;//产生停止位
    124. end
    125. if(IIC_cnt == 23)
    126. write_cnt <= write_cnt + 1 ;
    127. if(write_cnt >= 39)
    128. begin
    129. write_cnt <= 0 ;
    130. cmd <= idel_state ; //空闲态
    131. end
    132. end
    133. else if( rd_sig )
    134. begin
    135. if(read_cnt == 0)
    136. cmd <= idel_state ; //空闲态
    137. else if ( read_cnt == 1 )
    138. begin
    139. cmd <= start_bit_state ;//产生起始位
    140. end
    141. else if ( read_cnt == 2 )
    142. begin
    143. cmd <= wr_data_state ;
    144. write_data <= IIC_id ;//发送IIC器件ID
    145. end
    146. else if ( read_cnt == 10)
    147. begin
    148. cmd <= ack_check_state ;
    149. end
    150. else if ( read_cnt == 11 )
    151. begin
    152. cmd <= wr_data_state ;
    153. write_data <= rd_addr[15:8] ;//写存储器高位地址
    154. end
    155. else if ( read_cnt == 19 )
    156. begin
    157. cmd <= ack_check_state ;
    158. end
    159. else if ( read_cnt == 20 )
    160. begin
    161. cmd <= wr_data_state ;
    162. write_data <= rd_addr[7:0] ;//写存储器低位地址
    163. end
    164. else if ( read_cnt == 28 )
    165. begin
    166. cmd <= ack_check_state ;
    167. end
    168. else if( read_cnt == 29 )
    169. cmd <= start_bit_state ;
    170. else if ( read_cnt == 30 )
    171. begin
    172. cmd <= wr_data_state ;
    173. write_data <= (IIC_id | 8'b00000001 ) ; //写IIC器件ID 和读指令
    174. end
    175. else if ( read_cnt == 38 )
    176. begin
    177. cmd <= ack_check_state ;
    178. end
    179. else if ( read_cnt == 39 )
    180. begin
    181. cmd <= rd_data_state ;//
    182. end
    183. else if ( read_cnt == 47 )
    184. begin
    185. cmd <= gen_noack_state ;//
    186. end
    187. else if ( read_cnt == 48 )
    188. begin
    189. cmd <= stop_bit_state ;
    190. read_data <= rd_data ;
    191. end
    192. if(IIC_cnt == 23)
    193. read_cnt <= read_cnt + 1 ;
    194. if ( read_cnt == 49 )
    195. begin
    196. cmd <= idel_state ;
    197. read_cnt <= 0 ;
    198. end
    199. end
    200. else
    201. begin
    202. cmd <= idel_state ;
    203. write_cnt <= 0 ;
    204. read_cnt <= 0 ;
    205. end
    206. endmodule
    1. module IIC_underlying_logic(
    2. input fpga_clk ,
    3. input rst_n ,
    4. input [7:0]write_data ,
    5. input [7:0]cmd ,
    6. output reg [7:0]read_data ,
    7. output reg trans_done ,
    8. output reg ack_result ,
    9. output reg IIC_scl ,
    10. inout IIC_sda
    11. );
    12. wire clk_10m ;
    13. //PLL
    14. pll pll_inst(
    15. .inclk0(fpga_clk),
    16. .c0(clk_10m)
    17. );
    18. // IIC rate : 100k 400k clk : 10Mhz
    19. localparam cnt400k_max = 10000000 / 400000 ;
    20. localparam cnt100k_max = 10000000 / 100000 ;
    21. reg [15:0]IIC_cnt ;
    22. always@(posedge clk_10m or negedge rst_n)
    23. if(!rst_n)
    24. IIC_cnt <= 0 ;
    25. else if( IIC_cnt >= 24 )
    26. IIC_cnt <= 0 ;
    27. else
    28. IIC_cnt <= IIC_cnt + 1 ;
    29. //IIC SCLK
    30. always@(posedge clk_10m or negedge rst_n)
    31. if(!rst_n)
    32. IIC_scl <= 0 ;
    33. else if( IIC_cnt == 24 | IIC_cnt == 11 )
    34. IIC_scl <= !IIC_scl ;
    35. //IIC SDATA
    36. reg sda_ctrl ;
    37. reg sda_out ;
    38. assign IIC_sda = sda_ctrl ? ( sda_out ? 1'bz : 1'b0 ) : 1'bz ; //三态门,高阻态时,要么由总线上的上拉电阻拉至高电平,即输出/输出1;要么由从机拉至0,输入0;
    39. //定义状态机8个状态参数
    40. localparam idel_state = 8'b10000000 ; //空闲态:释放sda控制权
    41. localparam start_bit_state = 8'b01000000 ; //产生起始位
    42. localparam wr_data_state = 8'b00100000 ; // 写数据
    43. localparam rd_data_state = 8'b00010000 ; // 读数据
    44. localparam ack_check_state = 8'b00001000 ; //检查响应
    45. localparam stop_bit_state = 8'b00000100 ; //产生停止位
    46. localparam gen_ack_state = 8'b00000010 ; //产生响应位
    47. localparam gen_noack_state = 8'b00000001 ; //产生非响应位
    48. //状态机实时状态
    49. wire [7:0]rw_state;
    50. assign rw_state = cmd ;
    51. reg [8:0]state_cnt ;
    52. always@(posedge clk_10m or negedge rst_n)
    53. if(!rst_n)
    54. begin
    55. sda_ctrl <= 0 ;
    56. sda_out <= 1 ;
    57. trans_done <= 0 ;
    58. ack_result <= 0 ;
    59. read_data <= 0 ;
    60. state_cnt <= 0 ;
    61. end
    62. else
    63. begin
    64. case(rw_state)
    65. idel_state :
    66. begin
    67. sda_ctrl <= 0 ;
    68. trans_done <= 0 ;
    69. ack_result <= 0 ;
    70. state_cnt <= 0 ;
    71. end
    72. start_bit_state :
    73. begin
    74. sda_ctrl <= 1 ;
    75. state_cnt <= state_cnt + 1 ;
    76. if( state_cnt >= 17 )
    77. sda_out <= 0 ;
    78. else
    79. sda_out <= 1 ;
    80. if ( state_cnt >= 24 )
    81. state_cnt <= 0 ;
    82. end
    83. wr_data_state :
    84. begin
    85. sda_ctrl <= 1 ;
    86. state_cnt <= state_cnt + 1 ;
    87. if( state_cnt == 5 )
    88. sda_out <= write_data[7] ;
    89. else if( state_cnt == 30 )
    90. sda_out <= write_data[6] ;
    91. else if( state_cnt == 55 )
    92. sda_out <= write_data[5] ;
    93. else if( state_cnt == 80 )
    94. sda_out <= write_data[4] ;
    95. else if( state_cnt == 105 )
    96. sda_out <= write_data[3] ;
    97. else if( state_cnt == 130 )
    98. sda_out <= write_data[2] ;
    99. else if( state_cnt == 155 )
    100. sda_out <= write_data[1] ;
    101. else if( state_cnt == 180 )
    102. sda_out <= write_data[0] ;
    103. if( state_cnt >= 199 )
    104. state_cnt <= 0 ;
    105. end
    106. rd_data_state :
    107. begin
    108. sda_ctrl <= 0 ;
    109. state_cnt <= state_cnt + 1 ;
    110. if( state_cnt == 15 )
    111. read_data[7] <= IIC_sda ;
    112. else if( state_cnt == 40 )
    113. read_data[6] <= IIC_sda ;
    114. else if( state_cnt == 65 )
    115. read_data[5] <= IIC_sda ;
    116. else if( state_cnt == 90 )
    117. read_data[4] <= IIC_sda ;
    118. else if( state_cnt == 115 )
    119. read_data[3] <= IIC_sda ;
    120. else if( state_cnt == 140 )
    121. read_data[2] <= IIC_sda ;
    122. else if( state_cnt == 165 )
    123. read_data[1] <= IIC_sda ;
    124. else if( state_cnt == 190 )
    125. read_data[0] <= IIC_sda ;
    126. if( state_cnt >= 199 )
    127. state_cnt <= 0 ;
    128. end
    129. ack_check_state :
    130. begin
    131. sda_ctrl <= 0 ;//释放sda控制权
    132. state_cnt <= state_cnt + 1 ;
    133. if( state_cnt == 1 )
    134. ack_result <= IIC_sda ;
    135. if(state_cnt >= 24)
    136. state_cnt <= 0 ;
    137. end
    138. stop_bit_state :
    139. begin
    140. sda_ctrl<= 1'b1 ;//获取sda控制权
    141. sda_out <= 1'b0 ;
    142. state_cnt <= state_cnt + 1 ;
    143. if( state_cnt >= 17 )
    144. sda_out <= 1 ;
    145. else
    146. sda_out <= 0 ;
    147. if ( state_cnt >=24 )
    148. begin
    149. state_cnt <= 0 ;
    150. trans_done <= 1 ;
    151. end
    152. end
    153. gen_ack_state :
    154. begin
    155. sda_ctrl<= 1'b1 ;//获取sda控制权
    156. state_cnt <= state_cnt + 1 ;
    157. if( state_cnt >= 10 )
    158. sda_out <= 1'b0 ;
    159. if(state_cnt >= 24 )
    160. state_cnt <= 0 ;
    161. end
    162. gen_noack_state :
    163. begin
    164. sda_ctrl<= 1'b1 ;//获取sda控制权
    165. state_cnt <= state_cnt + 1 ;
    166. if( state_cnt >= 10 )
    167. sda_out <= 1'b1 ;
    168. if(state_cnt >= 24 )
    169. state_cnt <= 0 ;
    170. end
    171. default: ;
    172. endcase
    173. end
    174. endmodule
    1. //***************** ******************//
    2. // 串口波特率:115200 //
    3. // 通信帧格式: //
    4. // AA AA ID HA LA RW DA 55 //
    5. // 其中,AA AA 为帧头;55为帧尾; //
    6. // ID为IIC器件ID,HA、LA为存储器地址 //
    7. // RW为读写控制,读:5A ;写:A5 //
    8. // DA为要写入的数据,读的时候可为任意值 //
    9. // ********************************* //
    10. module uart_IIC_eeprom(
    11. input fpga_clk ,
    12. input rst_n ,
    13. input uart_rx ,
    14. output rx_done ,
    15. output uart_tx ,
    16. output IIC_scl ,
    17. inout IIC_sda
    18. );
    19. //receive uart data
    20. //wire rx_done ;
    21. wire [7:0]rx_data ;
    22. uart_receive uart_rec_inst(
    23. .clk(fpga_clk) ,
    24. .reset(rst_n) ,
    25. .baud_rate('d5) ,//115200
    26. .uart_rx(uart_rx),
    27. .data(rx_data) ,
    28. .rx_done(rx_done)
    29. );
    30. //Shift register
    31. reg [7:0]rec_shift_reg[7:0] ;
    32. reg rw_flag ;
    33. wire iic_done ;
    34. always@(posedge fpga_clk or negedge rst_n)
    35. if(!rst_n)
    36. begin
    37. rec_shift_reg[0] <= 0 ;
    38. rec_shift_reg[1] <= 0 ;
    39. rec_shift_reg[2] <= 0 ;
    40. rec_shift_reg[3] <= 0 ;
    41. rec_shift_reg[4] <= 0 ;
    42. rec_shift_reg[5] <= 0 ;
    43. rec_shift_reg[6] <= 0 ;
    44. rec_shift_reg[7] <= 0 ;
    45. rw_flag <= 0 ;
    46. end
    47. else if(rx_done)
    48. begin
    49. rec_shift_reg[0] <= rec_shift_reg[1] ;
    50. rec_shift_reg[1] <= rec_shift_reg[2] ;
    51. rec_shift_reg[2] <= rec_shift_reg[3] ;
    52. rec_shift_reg[3] <= rec_shift_reg[4] ;
    53. rec_shift_reg[4] <= rec_shift_reg[5] ;
    54. rec_shift_reg[5] <= rec_shift_reg[6] ;
    55. rec_shift_reg[6] <= rec_shift_reg[7] ;
    56. rec_shift_reg[7] <= rx_data ;
    57. rw_flag <= 1 ;
    58. end
    59. else if(iic_done)
    60. rw_flag <= 0 ;
    61. reg [7:0]IIC_id ;
    62. reg [15:0]write_addr ;
    63. reg [15:0]read_addr ;
    64. reg [7:0]write_data ;
    65. reg [7:0]read_data ;
    66. wire [7:0]rd_data ;
    67. reg write_pulse ;
    68. reg read_pulse ;
    69. IIC_design IIC_rw(
    70. .fpga_clk(fpga_clk),//50MHz
    71. .rst_n(rst_n) ,
    72. .read_pulse(read_pulse) ,
    73. .write_pulse(write_pulse) ,
    74. .write_data(write_data) ,
    75. .IIC_id(IIC_id) ,
    76. .wr_addr(write_addr) ,
    77. .rd_addr(read_addr) ,
    78. .trans_done(iic_done) ,
    79. .read_data(rd_data) ,
    80. .IIC_scl(IIC_scl) ,
    81. .IIC_sda(IIC_sda)
    82. );
    83. wire uart_send_done ;
    84. reg uart_send_en ;
    85. uart_send uart_send(
    86. .clk(fpga_clk),
    87. .reset(rst_n),
    88. .data(read_data),
    89. .send_en(uart_send_en),
    90. .baud_rate(5),//115200
    91. .uart_tx(uart_tx),
    92. .tx_done(uart_send_done)
    93. );
    94. //Parsing frames
    95. localparam frames_header = 8'hAA ;
    96. localparam frames_tail = 8'h55 ;
    97. localparam read_option = 8'h5A ;
    98. localparam write_option = 8'hA5 ;
    99. reg [15:0]wr_cnt ;
    100. reg [15:0]rd_cnt ;
    101. always@(posedge fpga_clk or negedge rst_n)
    102. if(!rst_n)
    103. begin
    104. IIC_id <= 0 ;
    105. write_addr <= 0 ;
    106. read_addr <= 0 ;
    107. write_data <= 0;
    108. read_data <= 0 ;
    109. write_pulse <= 0 ;
    110. read_pulse <= 0 ;
    111. wr_cnt <= 0 ;
    112. rd_cnt <= 0 ;
    113. end
    114. else if( rw_flag )
    115. begin
    116. if ( (rec_shift_reg[0] == frames_header) && (rec_shift_reg[1] == frames_header) && (rec_shift_reg[7] == frames_tail) )
    117. begin
    118. IIC_id <= rec_shift_reg[2] ;
    119. if(rec_shift_reg[5] == write_option )
    120. begin
    121. write_addr <= { rec_shift_reg[3] , rec_shift_reg[4] } ;
    122. write_data <= rec_shift_reg[6] ;
    123. wr_cnt <= wr_cnt + 1 ;
    124. //write signal
    125. if( wr_cnt < 3 )
    126. write_pulse <= 1 ;
    127. else if(iic_done)
    128. begin
    129. wr_cnt <= 0 ;
    130. end
    131. else
    132. write_pulse <= 0 ;
    133. end
    134. else if ( rec_shift_reg[5] == read_option )
    135. begin
    136. read_addr <= { rec_shift_reg[3] , rec_shift_reg[4] } ;
    137. //read signal
    138. rd_cnt <= rd_cnt + 1 ;
    139. if(iic_done)
    140. begin
    141. read_data <= rd_data ;
    142. read_pulse <= 0 ;
    143. rd_cnt <= 0 ;
    144. end
    145. else if(rd_cnt <3)
    146. read_pulse <= 1 ;
    147. else
    148. read_pulse <= 0 ;
    149. end
    150. end
    151. end
    152. else
    153. begin
    154. write_pulse <= 0 ;
    155. read_pulse <= 0 ;
    156. wr_cnt <= 0 ;
    157. rd_cnt <= 0 ;
    158. end
    159. always@(posedge fpga_clk or negedge rst_n)
    160. if(!rst_n)
    161. uart_send_en <= 0 ;
    162. else if(iic_done && (rec_shift_reg[5] == read_option) )
    163. uart_send_en <= 1 ;
    164. else if(uart_send_done)
    165. uart_send_en <= 0 ;
    166. endmodule
    1. module uart_receive(
    2. clk ,
    3. reset ,
    4. baud_rate ,
    5. uart_rx,
    6. data ,
    7. rx_done
    8. );
    9. input clk ;
    10. input reset ;
    11. input [2:0]baud_rate ;
    12. input uart_rx ;
    13. output reg [7:0]data ;
    14. output reg rx_done ;
    15. reg [2:0]r_data[7:0] ;
    16. reg [2:0]sta_bit ;
    17. reg [2:0]sto_bit ;
    18. reg [17:0]bit_tim ;
    19. always@(baud_rate)
    20. begin
    21. case(baud_rate)
    22. 3'd0 : bit_tim = 1000000000/300/20 ;
    23. 3'd1 : bit_tim = 1000000000/1200/20 ;
    24. 3'd2 : bit_tim = 1000000000/2400/20 ;
    25. 3'd3 : bit_tim = 1000000000/9600/20 ;
    26. 3'd4 : bit_tim = 1000000000/19200/20 ;
    27. 3'd5 : bit_tim = 1000000000/115200/20 ;
    28. default bit_tim = 1000000000/9600/20 ;
    29. endcase
    30. end
    31. wire [17:0]bit_tim_16 ;
    32. assign bit_tim_16 = bit_tim / 16;
    33. wire [8:0]bit16_mid ;
    34. assign bit16_mid = bit_tim_16 / 2 ;
    35. reg [1:0]edge_detect ;
    36. always @( posedge clk or negedge reset )
    37. begin
    38. if (!reset )
    39. edge_detect <= 2'd0 ;
    40. else
    41. begin
    42. edge_detect[0] <= uart_rx ;
    43. edge_detect[1] <= edge_detect[0] ;
    44. end
    45. end
    46. wire byte_sta_neg ;
    47. assign byte_sta_neg = ( edge_detect == 2'b10 ) ? 1 : 0 ;
    48. reg receive_en ;
    49. reg [17:0]div_cnt ;
    50. reg [7:0]bit16_cnt ;
    51. always @( posedge clk or negedge reset )
    52. begin
    53. if (!reset )
    54. receive_en <= 1'd0 ;
    55. else if ( byte_sta_neg )
    56. receive_en <= 1'd1 ;
    57. else if ( (rx_done) || (sta_bit >= 3'd4 ))
    58. receive_en <= 1'd0 ;
    59. else if ( ( bit16_cnt == 8'd159 ) && (div_cnt == bit_tim_16 - 1'd1 ) )
    60. receive_en <= 1'd0 ;
    61. end
    62. always@( posedge clk or negedge reset )
    63. begin
    64. if ( ! reset )
    65. div_cnt <= 18'd0 ;
    66. else if (receive_en)
    67. begin
    68. if ( div_cnt == bit_tim_16 - 1'd1 )
    69. div_cnt <= 18'd0 ;
    70. else
    71. div_cnt <= div_cnt + 1'b1 ;
    72. end
    73. else
    74. div_cnt <= 18'd0 ;
    75. end
    76. reg bit16_pulse ;
    77. always@( posedge clk or negedge reset )
    78. begin
    79. if ( ! reset )
    80. bit16_pulse <= 18'd0 ;
    81. else if (receive_en)
    82. if ( div_cnt == bit16_mid )
    83. bit16_pulse <= 1'd1 ;
    84. else
    85. bit16_pulse <= 1'd0 ;
    86. else
    87. bit16_pulse <= 1'd0 ;
    88. end
    89. always@( posedge clk or negedge reset )
    90. begin
    91. if ( ! reset )
    92. bit16_cnt <= 8'd0 ;
    93. else if (receive_en)
    94. begin
    95. if (( bit16_cnt == 8'd159 ) && (div_cnt == bit_tim_16 - 1'd1 ))
    96. bit16_cnt <= 8'd0 ;
    97. else if ( div_cnt == bit_tim_16 - 1'd1 )
    98. bit16_cnt <= bit16_cnt + 1'b1 ;
    99. end
    100. end
    101. always@(posedge clk or negedge reset)
    102. begin
    103. if(!reset)
    104. begin
    105. sta_bit <= 3'd0 ;
    106. r_data[0] <= 3'd0 ;
    107. r_data[1] <= 3'd0 ;
    108. r_data[2] <= 3'd0 ;
    109. r_data[3] <= 3'd0 ;
    110. r_data[4] <= 3'd0 ;
    111. r_data[5] <= 3'd0 ;
    112. r_data[6] <= 3'd0 ;
    113. r_data[7] <= 3'd0 ;
    114. sto_bit <= 3'd0 ;
    115. end
    116. else if (bit16_pulse)
    117. case(bit16_cnt)
    118. 0:
    119. begin
    120. sta_bit <= 3'd0 ;
    121. r_data[0] <= 3'd0 ;
    122. r_data[1] <= 3'd0 ;
    123. r_data[2] <= 3'd0 ;
    124. r_data[3] <= 3'd0 ;
    125. r_data[4] <= 3'd0 ;
    126. r_data[5] <= 3'd0 ;
    127. r_data[6] <= 3'd0 ;
    128. r_data[7] <= 3'd0 ;
    129. sto_bit <= 3'd0 ;
    130. end
    131. 5,6,7,8,9,10,11 : sta_bit <= sta_bit + uart_rx ;
    132. 21,22,23,24,25,26,27 : r_data[0] <= r_data[0] + uart_rx ;
    133. 37,38,39,41,42,43,44 : r_data[1] <= r_data[1] + uart_rx ;
    134. 53,54,55,56,57,58,59 : r_data[2] <= r_data[2] + uart_rx ;
    135. 69,70,71,72,73,74,75 : r_data[3] <= r_data[3] + uart_rx ;
    136. 85,86,87,88,89,90,91 : r_data[4] <= r_data[4] + uart_rx ;
    137. 101,102,103,104,105,106,107 : r_data[5] <= r_data[5] + uart_rx ;
    138. 117,118,119,120,121,122,123 : r_data[6] <= r_data[6] + uart_rx ;
    139. 133,134,135,136,137,138,139 : r_data[7] <= r_data[7] + uart_rx ;
    140. 149,150,151,152,153,154,155 : sto_bit <= sto_bit + uart_rx ;
    141. default ;
    142. endcase
    143. end
    144. always@( posedge clk or negedge reset )
    145. begin
    146. if ( ! reset )
    147. rx_done <= 8'd0 ;
    148. else if ( ( bit16_cnt == 8'd159 ) && (div_cnt == bit_tim_16 - 1'd1 ) )
    149. rx_done <= 8'd1 ;
    150. else if (rx_done <= 8'd1 )
    151. rx_done <= 8'd0 ;
    152. end
    153. always@( posedge clk or negedge reset )
    154. begin
    155. if ( ! reset )
    156. data <= 8'd0 ;
    157. //else if ( rx_done )
    158. else if(( bit16_cnt == 8'd159 ) && (div_cnt == bit_tim_16 - 1'd1 ))
    159. begin
    160. data[0] = ( r_data[0] >3 ) ? 1 : 0 ;
    161. data[1] = ( r_data[1] >3 ) ? 1 : 0 ;
    162. data[2] = ( r_data[2] >3 ) ? 1 : 0 ;
    163. data[3] = ( r_data[3] >3 ) ? 1 : 0 ;
    164. data[4] = ( r_data[4] >3 ) ? 1 : 0 ;
    165. data[5] = ( r_data[5] >3 ) ? 1 : 0 ;
    166. data[6] = ( r_data[6] >3 ) ? 1 : 0 ;
    167. data[7] = ( r_data[7] >3 ) ? 1 : 0 ;
    168. end
    169. //else if ( receive_en )
    170. //data <= 8'd0 ;
    171. end
    172. endmodule
    1. module uart_send(
    2. clk,
    3. reset,
    4. data,
    5. send_en,
    6. baud_rate,
    7. uart_tx,
    8. tx_done
    9. );
    10. input clk;
    11. input reset;
    12. input [7:0]data;
    13. input send_en;
    14. input [2:0]baud_rate;
    15. output reg uart_tx;
    16. output reg tx_done;
    17. reg [17:0]bit_tim;
    18. always@(baud_rate) //
    19. begin
    20. case(baud_rate)
    21. 3'd0 : bit_tim = 1000000000/300/20 ;
    22. 3'd1 : bit_tim = 1000000000/1200/20 ;
    23. 3'd2 : bit_tim = 1000000000/2400/20 ;
    24. 3'd3 : bit_tim = 1000000000/9600/20 ;
    25. 3'd4 : bit_tim = 1000000000/19200/20 ;
    26. 3'd5 : bit_tim = 1000000000/115200/20 ;
    27. default bit_tim = 1000000000/9600/20 ;
    28. endcase
    29. end
    30. reg [17:0]counter1 ;
    31. always@(posedge clk or negedge reset)
    32. begin
    33. if(!reset)
    34. counter1 <=17'b0 ;
    35. else if (send_en )
    36. begin
    37. if( counter1 == bit_tim - 1'b1 )
    38. counter1 <= 17'b0 ;
    39. else
    40. counter1 <= counter1 + 1'b1 ;
    41. end
    42. else counter1 <= 17'b0 ;
    43. end
    44. reg [3:0]counter2 ;
    45. always@(posedge clk or negedge reset)
    46. begin
    47. if(!reset)
    48. counter2 <= 4'b0 ;
    49. else if ( send_en )
    50. begin
    51. if(counter2 == 0)
    52. counter2 <= counter2 +1'b1 ;
    53. else if( counter1 == bit_tim - 1'b1 )
    54. counter2 <= counter2 + 4'b1 ;
    55. else
    56. counter2 <= counter2 ;
    57. end
    58. else
    59. counter2 <= 4'b0 ;
    60. end
    61. always@(posedge clk or negedge reset)
    62. begin
    63. if(!reset)
    64. begin
    65. uart_tx <= 4'b1 ;
    66. end
    67. else if ( send_en )
    68. case(counter2)
    69. 0:begin uart_tx <= 1'b1 ; end
    70. 1:uart_tx <= 1'b0 ;
    71. 2:uart_tx <= data[0] ;
    72. 3:uart_tx <= data[1] ;
    73. 4:uart_tx <= data[2] ;
    74. 5:uart_tx <= data[3] ;
    75. 6:uart_tx <= data[4] ;
    76. 7:uart_tx <= data[5] ;
    77. 8:uart_tx <= data[6] ;
    78. 9:uart_tx <= data[7] ;
    79. 10:uart_tx <= 1'b1 ;
    80. 11:begin uart_tx <= 1'b1 ; end
    81. default uart_tx <= 1'b1 ;
    82. endcase
    83. else
    84. uart_tx <= 1'b1 ;
    85. end
    86. always@(posedge clk or negedge reset)
    87. begin
    88. if(!reset)
    89. tx_done <= 1'b0 ;
    90. else if (send_en )
    91. begin
    92. if( counter2 == 0 )//
    93. tx_done <= 1'b0 ;
    94. else if ( counter2 == 11 )
    95. tx_done <= 1'b1 ;
    96. end
    97. else if (tx_done == 1'b1)
    98. tx_done <= 1'b0 ;
    99. end
    100. endmodule

    完结!只要串口按照帧格式发送数据就可以遵循IIC协议对AC609板子的eeprom存储器进行读写数据了,其他的IIC器件则修改对应IIC_id即可。

  • 相关阅读:
    代码随想录-021-349.两个数组的交集
    删除数据库
    【0119】PostgreSQL FMS(Free Space Map)
    21.9 Python 使用Selenium库
    【计算机毕业设计】校园二手市场平台+vue源码
    Validform验证时可以为空,否则按照指定格式验证
    kafka 自定义Interceptor(通过拦截器对消息进行定制化处理)
    dreamweaver个人网页设计作业 学生个人网页猫眼电影 WEB静态网页作业模板 大学生个人主页博客网页代码 dw个人网页作业成品
    工业无线网关在实际生产中的应用效果和价值-天拓四方
    快速入门安装及使用&git与svn的区别&常用命令
  • 原文地址:https://blog.csdn.net/qq_44366923/article/details/133561521