当前位置:   article > 正文

Verilog(3)I2C通信协议_用verilog写一个i2cmaster和slave的模型

用verilog写一个i2cmaster和slave的模型
  1. I2C接口简介

1.1:

I2C全称:Inter-Integrated Circuit,是一种同步、半双工的通信总线。

同步:发送接收端要严格同步,一般有同步时钟线。

半双工:I2C只有一条数据线,所以master发数据与收数据不能同时进行。

图中是2个master+2个slave的示意,同一时刻只有一个master与一个slave通信。若想实现这个效果:

1.多个master-slave 时钟、数据线连在一起,需要实现信号的“线与”逻辑(所以SDA、SCL 被设计为漏极开路结构,外加上拉电阻实现“线与”)。

2.需要实现 “时钟同步”和“总线仲裁”,引脚在输出信号的同时还能对引脚上的电平进行检测,检测是否与刚才输出一致,为 “时钟同步”和“总线仲裁”提供硬件基础。

3.I2C在读写时需要带上设备地址,这样不使用多的信号线就可指定特定的slave(而SPI通信需要多的片选线)。

1.2:

写过程:

  1. Master发起START

  1. Master发送I2C addr(7bit)和W(写)操作0(1bit),等待ACK

  1. Slave发送ACK

  1. Master发送reg addr(8bit),等待ACK

  1. Slave发送ACK

  1. Master发送data(8bit),即要写入寄存器中的数据,等待ACK

  1. Slave发送ACK
    第6步和第7步可以重复多次,即顺序写多个寄存器

  1. Master发起STOP结束传输

读过程:

  1. Master发起START

  1. Master发送I2C addr(7bit)和r(读)操作1(1bit),等待ACK

  1. Slave发送ACK

  1. Slave发送data(8bit),即寄存器里的值

  1. Master发送ACK
    第7步和第8步可以重复多次,即顺序读多个寄存器

  1. 当master接收完想要的数据后,由Master发送NACK,告知slave停止发送数据

  1. Master发送STOP结束传输

1.3:

1.总线空闲状态:

SDA和SCL同时处于高电平时,规定为总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。

2.总线START:

SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。

3.总线STOP:

SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据。

4.总线Restart:

SCL为高电平时,SDA由高电平向低电平跳变,本质上也是START信号,用在完整I2C读过程中的读阶段,在首次发送停止信号之前,master通过发送Restart信号,可以转换与当前slave的通信模式(从写模式到读模式),或是切换到与另一个slave通信。

5.数据阶段:

在IIC总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在SCL串行时钟的配合下,在SDA上逐位地串行传送每一位数据。进行数据传送时,在SCL呈现高电平期间,SDA上的电平必须保持稳定。只有在SCL为低电平期间,才允许SDA上的电平改变状态。简单的说就是,数据在SCL下降会被采样,所以SDA需要在SCL为高电平时保持稳定。

6. ACK与NACK信号:

IIC总线上的所有数据都是以8位字节传送的,发送器每发送一个字节,就在第9个时钟脉冲期间释放数据线,由接收器反馈一个应答信号。应答信号为低电平时,规定为有效应答位(ACK简称应答位),表示接收器已经成功地接收了该字节;应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。

这段话再说细一点:

在写阶段,master写了一字节数据,在第9个时钟脉冲期间释放数据线,由slave反馈应答信号,ACK(低)表示数据成功接收,NACK(高)表示该字节没有接收成功;

在读阶段,master向slave收数据,slave写了一字节数据,在第9个时钟脉冲期间释放数据线,由master反馈应答信号,ACK(低)表示数据成功接收,NACK(高)表示该字节没有接收成功。

还有一种特殊情况:当master决定不再接收数据时,应向slave发送NACK信号,高速slave不再发送。

以下情况会导致出现NACK位:

1.接收器没有发送机响应的地址,接收端没有任何ACK发送给发送器

2.由于接收器正在忙碌处理实时程序导致接无法接收或者发送
传输过程中,接收机器别不了发送机的数据或命令

3.接收器无法接收

4.master接收完成读取数据后,要发送NACK结束告知slave。
当master接收到slave的NACK信号后,可以STOP这次传输,也可以重新START。
所以:NACK并不只是表示字节没有成功接收,也可以表示master告诉slave不再需要发送数据。

  1. I2C程序

  1. `timescale 1ns / 1ps
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 2023/02/23 16:16:18
  7. // Design Name:
  8. // Module Name: i2c_dri
  9. // Project Name:
  10. // Target Devices:
  11. // Tool Versions:
  12. // Description:
  13. //
  14. // Dependencies:
  15. //
  16. // Revision:
  17. // Revision 0.01 - File Created
  18. // Additional Comments:
  19. //
  20. //
  21. module i2c_dri
  22. #(
  23. parameter SLAVE_ADDR = 7'b1010000,
  24. parameter CLK_FREQ = 26'd50_000_000,
  25. parameter I2C_FREQ = 18'd250_000
  26. )
  27. (
  28. input clk ,
  29. input rst_n ,
  30. input i2c_exec ,
  31. input i2c_rh_wl ,
  32. input bit_ctrl ,
  33. input [15:0] i2c_addr ,
  34. input [7:0] i2c_data_w ,
  35. output reg [7:0] i2c_data_r ,
  36. output reg i2c_done ,
  37. output reg i2c_ack ,
  38. output reg scl ,
  39. inout sda ,
  40. output reg dri_clk
  41. );
  42. localparam st_idle = 8'b0000_0001; //空闲状态
  43. localparam st_sladdr = 8'b0000_0010; //发送器件地址
  44. localparam st_addr16 = 8'b0000_0100; //发送16位地址
  45. localparam st_addr8 = 8'b0000_1000; //发送8位地址
  46. localparam st_data_wr = 8'b0001_0000; //写数据
  47. localparam st_addr_rd = 8'b0010_0000; //读地址
  48. localparam st_data_rd = 8'b0100_0000; //读数据
  49. localparam st_stop = 8'b1000_0000; //结束i2c操作
  50. reg sda_dir ;
  51. reg sda_out ;
  52. reg st_done ;//状态结束
  53. reg wr_flag ;//写标志
  54. reg [6:0] cnt ;
  55. reg [7:0] cur_state ;
  56. reg [7:0] next_state ;
  57. reg [15:0] addr_t ;
  58. reg [7:0] data_r ;
  59. reg [7:0] data_wr_t ;
  60. reg [9:0] clk_cnt ;
  61. wire sda_in ;
  62. wire [8:0] clk_divide ;
  63. assign sda = sda_dir ? sda_out : 1'bz;
  64. assign sda_in = sda;
  65. assign clk_divide = (CLK_FREQ/I2C_FREQ) >> 2'd2;
  66. //I2C的SCL四倍频
  67. always@(posedge clk or negedge rst_n)begin
  68. if(!rst_n)begin
  69. dri_clk <= 1'b0;
  70. clk_cnt <= 10'd0;
  71. end
  72. else if(clk_cnt == clk_divide[8:1] - 1'd1)begin
  73. clk_cnt <= 10'd0;
  74. dri_clk <= ~dri_clk;
  75. end
  76. else
  77. clk_cnt <= clk_cnt + 1'd1;
  78. end
  79. //状态机状态转移
  80. always@(posedge dri_clk or negedge rst_n)begin
  81. if(!rst_n)
  82. cur_state <= st_idle;
  83. else
  84. cur_state <= next_state;
  85. end
  86. //状态机状态转移条件
  87. always@(*)begin
  88. next_state = st_idle;
  89. case(cur_state)
  90. st_idle: begin
  91. if(i2c_exec == 1'b1)
  92. next_state = st_sladdr;
  93. else
  94. next_state = st_idle;
  95. end
  96. st_sladdr:begin
  97. if(st_done)begin
  98. if(bit_ctrl)
  99. next_state = st_addr16;
  100. else
  101. next_state = st_addr8;
  102. end
  103. else
  104. next_state = st_sladdr;
  105. end
  106. st_addr16:begin
  107. if(st_done)begin
  108. next_state = st_addr8;
  109. end
  110. else begin
  111. next_state = st_addr16;
  112. end
  113. end
  114. st_addr8:begin
  115. if(st_done)begin
  116. if(wr_flag == 1'b0)
  117. next_state = st_data_wr;
  118. else
  119. next_state = st_addr_rd;
  120. end
  121. else begin
  122. next_state = st_addr8;
  123. end
  124. end
  125. st_data_wr:begin
  126. if(st_done)
  127. next_state = st_stop;
  128. else
  129. next_state = st_data_wr;
  130. end
  131. st_addr_rd:begin
  132. if(st_done)begin
  133. next_state = st_data_rd;
  134. end
  135. else begin
  136. next_state = st_addr_rd;
  137. end
  138. end
  139. st_data_rd:begin
  140. if(st_done)
  141. next_state = st_stop;
  142. else
  143. next_state = st_data_rd;
  144. end
  145. st_stop:begin
  146. if(st_done)
  147. next_state = st_idle;
  148. else
  149. next_state = st_stop;
  150. end
  151. default:next_state = st_idle;
  152. endcase
  153. end
  154. //时序电路描述状态输出
  155. always@(posedge dri_clk or negedge rst_n)begin
  156. if(!rst_n)begin
  157. scl <=1'b1;
  158. sda_out <=1'b1;
  159. sda_dir <=1'b1;
  160. i2c_done <=1'b0;
  161. i2c_ack <=1'b0;
  162. cnt <=1'b0;
  163. st_done <=1'b0;
  164. data_r <=1'b0;
  165. i2c_data_r <=1'b0;
  166. wr_flag <=1'b0;
  167. addr_t <=1'b0;
  168. data_wr_t <=1'b0;
  169. end
  170. else begin
  171. st_done <=1'b0;
  172. cnt<= cnt+1'b1;
  173. case(cur_state)
  174. st_idle:begin
  175. scl <=1'b1;
  176. sda_out <=1'b1;
  177. sda_dir <=1'b1;
  178. i2c_done<=1'b0;
  179. cnt <=1'b0;
  180. if(i2c_exec)begin
  181. wr_flag <=i2c_rh_wl;
  182. addr_t <=i2c_addr;
  183. data_wr_t <=i2c_data_w;
  184. i2c_ack <=1'b0;
  185. end
  186. end
  187. st_sladdr:begin
  188. case(cnt)
  189. 7'd1 : sda_out <= 1'b0;
  190. 7'd3 : scl <= 1'b0;
  191. 7'd4 : sda_out <= SLAVE_ADDR[6];
  192. 7'd5 : scl <=1'b1;
  193. 7'd7 : scl <=1'b0;
  194. 7'd8 : sda_out <= SLAVE_ADDR[5];
  195. 7'd9 : scl <= 1'b1;
  196. 7'd11: scl <= 1'b0;
  197. 7'd12: sda_out <= SLAVE_ADDR[4];
  198. 7'd13: scl <= 1'b1;
  199. 7'd15: scl <= 1'b0;
  200. 7'd16: sda_out <= SLAVE_ADDR[3];
  201. 7'd17: scl <= 1'b1;
  202. 7'd19: scl <= 1'b0;
  203. 7'd20: sda_out <= SLAVE_ADDR[2];
  204. 7'd21: scl <= 1'b1;
  205. 7'd23: scl <= 1'b0;
  206. 7'd24: sda_out <= SLAVE_ADDR[1];
  207. 7'd25: scl <= 1'b1;
  208. 7'd27: scl <= 1'b0;
  209. 7'd28: sda_out <= SLAVE_ADDR[0];
  210. 7'd29: scl <= 1'b1;
  211. 7'd31: scl <= 1'b0;
  212. 7'd32: sda_out <= 1'b0; //0:写
  213. 7'd33: scl <= 1'b1;
  214. 7'd35: scl <= 1'b0;
  215. 7'd36: begin
  216. sda_dir <= 1'b0;
  217. sda_out <= 1'b1;
  218. end
  219. 7'd37: scl <= 1'b1;
  220. 7'd38: begin
  221. st_done <= 1'b1;
  222. if(sda_in==1'b1)
  223. i2c_ack <= 1'b1;
  224. end
  225. 7'd39:begin
  226. scl <= 1'b0;
  227. cnt <= 1'b0;
  228. end
  229. default : ;
  230. endcase
  231. end
  232. st_addr16:begin
  233. case(cnt)
  234. 7'd0:begin
  235. sda_dir <= 1'b1;
  236. sda_out <= addr_t[15];
  237. end
  238. 7'd1: scl <= 1'b1;
  239. 7'd3: scl <= 1'b0;
  240. 7'd4: sda_out <= addr_t[14];
  241. 7'd5: scl <= 1'b1;
  242. 7'd7: scl <= 1'b0;
  243. 7'd8: sda_out <= addr_t[13];
  244. 7'd9: scl <= 1'b1;
  245. 7'd11:scl <= 1'b0;
  246. 7'd12:sda_out <= addr_t[12];
  247. 7'd13:scl <= 1'b1;
  248. 7'd15:scl <= 1'b0;
  249. 7'd16:sda_out <= addr_t[11];
  250. 7'd17:scl <= 1'b1;
  251. 7'd19:scl <= 1'b0;
  252. 7'd20:sda_out <= addr_t[10];
  253. 7'd21:scl <= 1'b1;
  254. 7'd23:scl <= 1'b0;
  255. 7'd24:sda_out <= addr_t[9];
  256. 7'd25:scl <= 1'b1;
  257. 7'd27:scl <= 1'b0;
  258. 7'd28:sda_out <= addr_t[8];
  259. 7'd29:scl <= 1'b1;
  260. 7'd31:scl <= 1'b0;
  261. 7'd32:begin
  262. sda_dir <= 1'b0;
  263. sda_out <= 1'b1;
  264. end
  265. 7'd34:begin
  266. st_done <= 1'b1;
  267. if(sda_in == 1'b1)
  268. i2c_ack <= 1'b1;
  269. end
  270. 7'd35:begin
  271. scl <= 1'b0;
  272. cnt <= 1'b0;
  273. end
  274. default: ;
  275. endcase
  276. end
  277. st_addr8:begin
  278. case(cnt)
  279. 7'd0:begin
  280. sda_dir <= 1'b1;
  281. sda_out <= addr_t[7];
  282. end
  283. 7'd1: scl <= 1'b1;
  284. 7'd3: scl <= 1'b0;
  285. 7'd4: sda_out <= addr_t[6];
  286. 7'd5: scl <= 1'b1;
  287. 7'd7: scl <= 1'b0;
  288. 7'd8: sda_out <= addr_t[5];
  289. 7'd9: scl <= 1'b1;
  290. 7'd11:scl <= 1'b0;
  291. 7'd12:sda_out <= addr_t[4];
  292. 7'd13:scl <= 1'b1;
  293. 7'd15:scl <= 1'b0;
  294. 7'd16:sda_out <= addr_t[3];
  295. 7'd17:scl <= 1'b1;
  296. 7'd19:scl <= 1'b0;
  297. 7'd20:sda_out <= addr_t[2];
  298. 7'd21:scl <= 1'b1;
  299. 7'd23:scl <= 1'b0;
  300. 7'd24:sda_out <= addr_t[1];
  301. 7'd25:scl <= 1'b1;
  302. 7'd27:scl <= 1'b0;
  303. 7'd28:sda_out <= addr_t[0];
  304. 7'd29:scl <= 1'b1;
  305. 7'd31:scl <= 1'b0;
  306. 7'd32:begin
  307. sda_dir <= 1'b0;
  308. sda_out <= 1'b1;
  309. end
  310. 7'd34:begin
  311. st_done <= 1'b1;
  312. if(sda_in == 1'b1)
  313. i2c_ack <= 1'b1;
  314. end
  315. 7'd35:begin
  316. scl <= 1'b0;
  317. cnt <= 1'b0;
  318. end
  319. default: ;
  320. endcase
  321. end
  322. st_data_wr:begin
  323. case(cnt)
  324. 7'd0:begin
  325. sda_out <= data_wr_t[7];
  326. sda_dir <= 1'b1;
  327. end
  328. 7'd1: scl <= 1'b1;
  329. 7'd3: scl <= 1'b0;
  330. 7'd4: sda_out <= data_wr_t[6];
  331. 7'd5 : scl <= 1'b1;
  332. 7'd7 : scl <= 1'b0;
  333. 7'd8 : sda_out <= data_wr_t[5];
  334. 7'd9 : scl <= 1'b1;
  335. 7'd11: scl <= 1'b0;
  336. 7'd12: sda_out <= data_wr_t[4];
  337. 7'd13: scl <= 1'b1;
  338. 7'd15: scl <= 1'b0;
  339. 7'd16: sda_out <= data_wr_t[3];
  340. 7'd17: scl <= 1'b1;
  341. 7'd19: scl <= 1'b0;
  342. 7'd20: sda_out <= data_wr_t[2];
  343. 7'd21: scl <= 1'b1;
  344. 7'd23: scl <= 1'b0;
  345. 7'd24: sda_out <= data_wr_t[1];
  346. 7'd25: scl <= 1'b1;
  347. 7'd27: scl <= 1'b0;
  348. 7'd28: sda_out <= data_wr_t[0];
  349. 7'd29: scl <= 1'b1;
  350. 7'd31: scl <= 1'b0;
  351. 7'd32: begin
  352. sda_dir <= 1'b0;
  353. sda_out <= 1'b1;
  354. end
  355. 7'd33: scl <= 1'b1;
  356. 7'd34: begin //从机应答
  357. st_done <= 1'b1;
  358. if(sda_in == 1'b1) //高电平表示未应答
  359. i2c_ack <= 1'b1; //拉高应答标志位
  360. end
  361. 7'd35: begin
  362. scl <= 1'b0;
  363. cnt <= 1'b0;
  364. end
  365. default : ;
  366. endcase
  367. end
  368. st_addr_rd: begin
  369. case(cnt)
  370. 7'd0 : begin
  371. sda_dir <= 1'b1;
  372. sda_out <= 1'b1;
  373. end
  374. 7'd1 : scl <= 1'b1;
  375. 7'd2 : sda_out <= 1'b0;
  376. 7'd3 : scl <= 1'b0;
  377. 7'd4 : sda_out <= SLAVE_ADDR[6];
  378. 7'd5 : scl <= 1'b1;
  379. 7'd7 : scl <= 1'b0;
  380. 7'd8 : sda_out <= SLAVE_ADDR[5];
  381. 7'd9 : scl <= 1'b1;
  382. 7'd11: scl <= 1'b0;
  383. 7'd12: sda_out <= SLAVE_ADDR[4];
  384. 7'd13: scl <= 1'b1;
  385. 7'd15: scl <= 1'b0;
  386. 7'd16: sda_out <= SLAVE_ADDR[3];
  387. 7'd17: scl <= 1'b1;
  388. 7'd19: scl <= 1'b0;
  389. 7'd20: sda_out <= SLAVE_ADDR[2];
  390. 7'd21: scl <= 1'b1;
  391. 7'd23: scl <= 1'b0;
  392. 7'd24: sda_out <= SLAVE_ADDR[1];
  393. 7'd25: scl <= 1'b1;
  394. 7'd27: scl <= 1'b0;
  395. 7'd28: sda_out <= SLAVE_ADDR[0];
  396. 7'd29: scl <= 1'b1;
  397. 7'd31: scl <= 1'b0;
  398. 7'd32: sda_out <= 1'b1;
  399. 7'd33: scl <= 1'b1;
  400. 7'd35: scl <= 1'b0;
  401. 7'd36: begin
  402. sda_dir <= 1'b0;
  403. sda_out <= 1'b1;
  404. end
  405. 7'd37: scl <=1'b1;
  406. 7'd38: begin
  407. st_done <= 1'b1;
  408. if(sda_in == 1'b1)
  409. i2c_ack <= 1'b1;
  410. end
  411. 7'd39: begin
  412. scl <= 1'b0;
  413. cnt <= 1'b0;
  414. end
  415. default : ;
  416. endcase
  417. end
  418. st_data_rd:begin
  419. case(cnt)
  420. 7'd0: sda_dir <= 1'b0;
  421. 7'd1: begin
  422. data_r[7] <= sda_in;
  423. scl <= 1'b1;
  424. end
  425. 7'd3: scl <= 1'b0;
  426. 7'd5: begin
  427. data_r[6] <= sda_in;
  428. scl <= 1'b1;
  429. end
  430. 7'd7: scl <= 1'b0;
  431. 7'd9: begin
  432. data_r[5] <= sda_in;
  433. scl <= 1'b1;
  434. end
  435. 7'd11: scl <= 1'b0;
  436. 7'd13: begin
  437. data_r[4] <= sda_in;
  438. scl <= 1'b1 ;
  439. end
  440. 7'd15: scl <= 1'b0;
  441. 7'd17: begin
  442. data_r[3] <= sda_in;
  443. scl <= 1'b1 ;
  444. end
  445. 7'd19: scl <= 1'b0;
  446. 7'd21: begin
  447. data_r[2] <= sda_in;
  448. scl <= 1'b1 ;
  449. end
  450. 7'd23: scl <= 1'b0;
  451. 7'd25: begin
  452. data_r[1] <= sda_in;
  453. scl <= 1'b1 ;
  454. end
  455. 7'd27: scl <= 1'b0;
  456. 7'd29: begin
  457. data_r[0] <= sda_in;
  458. scl <= 1'b1 ;
  459. end
  460. 7'd31: scl <= 1'b0;
  461. 7'd32: begin
  462. sda_dir <= 1'b1;
  463. sda_out <= 1'b1;
  464. end
  465. 7'd33: scl <= 1'b1;
  466. 7'd34: st_done <= 1'b1;
  467. 7'd35:begin
  468. scl <= 1'b0;
  469. cnt <= 1'b0;
  470. i2c_data_r <= data_r;
  471. end
  472. default : ;
  473. endcase
  474. end
  475. st_stop:begin
  476. case(cnt)
  477. 7'd0:begin
  478. sda_dir <= 1'b1;
  479. sda_out <= 1'b0;
  480. end
  481. 7'd1: scl <= 1'b1;
  482. 7'd3: sda_out <= 1'b1;
  483. 7'd15: st_done <= 1'b1;
  484. 7'd16:begin
  485. cnt <=1'b0;
  486. i2c_done<=1'b1;
  487. end
  488. default : ;
  489. endcase
  490. end
  491. endcase
  492. end
  493. end
  494. endmodule

分频系数就是scl一个周期有几个clk,dri_clk=4*scl,所以分频系数再除以四(等价于右移两位)。

  1. //I2C的SCL四倍频
  2. always@(posedge clk or negedge rst_n)begin
  3. if(!rst_n)begin
  4. dri_clk <= 1'b0;
  5. clk_cnt <= 10'd0;
  6. end
  7. else if(clk_cnt == clk_divide[8:1] - 1'd1)begin
  8. clk_cnt <= 10'd0;
  9. dri_clk <= ~dri_clk;
  10. end
  11. else
  12. clk_cnt <= clk_cnt + 1'd1;
  13. end

参考:I2C详解 - sureZ_ok - 博客园 (cnblogs.com)

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

闽ICP备14008679号