通信协议-IIC Verilog HDL实现 发表于 2017-06-08 | 分类于 FPGA , 通信协议 字数统计: 1.5k | 阅读时长 ≈ 8 该程序使用Verilog通过状态机实现IIC读写AT24C02。123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406module iic_com( clk, rst_n, sw1, sw2, // SW1写入AT240C2 scl, sda, //时钟 数据 dis_data //输出变量用数码管显示);input clk;input rst_n;input sw1,sw2;output scl;inout sda;output [7:0] dis_data;reg sw1_r, sw2_r;reg[19:0] cnt_20ms;always @ (posedge clk or negedge rst_n) if(!rst_n) cnt_20ms <= 20'd0; else cnt_20ms <= cnt_20ms + 1'b1; //不断计数always @(posedge clk or negedge rst_n) if(!rst_n) begin sw1_r <= 1'b1; sw2_r <= 1'b1; end else if(cnt_20ms == 20'hfffff) begin sw1_r <= sw1; //20ms进行一次锁存 sw2_r <= sw2; end//以下的程序将输入时钟进行分频,并且应用于SCL输出上reg[2:0] cnt; // cnt=0:scl上升沿,cnt=1:scl高电平中间,cnt=2:scl下降沿,cnt=3:scl低电平中间reg[8:0] cnt_delay; //500循环计数,产生iic所需要的时钟reg scl_r; //时钟脉冲寄存器always @ (posedge clk or negedge rst_n) if(!rst_n) cnt_delay <= 9'd0; else if(cnt_delay == 9'd499) cnt_delay <= 9'd0; //计数到10us为scl的周期,即100KHz else cnt_delay <= cnt_delay+1'b1; //时钟计数always @ (posedge clk or negedge rst_n) begin if(!rst_n) cnt <= 3'd5; else begin case (cnt_delay) 9'd124: cnt <= 3'd1; //cnt=1:scl高电平中间,用于数据采样 9'd249: cnt <= 3'd2; //cnt=2:scl下降沿 9'd374: cnt <= 3'd3; //cnt=3:scl低电平中间,用于数据变化 9'd499: cnt <= 3'd0; //cnt=0:scl上升沿 default: cnt <= 3'd5; endcase endend`define SCL_POS (cnt == 3'd0)`define SCL_HIG (cnt == 3'd1)`define SCL_NEG (cnt == 3'd2)`define SCL_LOW (cnt == 3'd3)always @(posedge clk or negedge rst_n) if(!rst_n) scl_r <= 1'b0; else if(cnt == 3'd0) scl_r <= 1'b1; //scl信号设置为高 else if(cnt == 3'd2) scl_r <= 1'b0; //scl信号设置为低assign scl = scl_r; //模块对外输出SCL信号//具体对24C02的具体操作`define DEVICE_READ 8'b1010_0001`define DEVICE_WRITE 8'b1010_0000`define WRITE_DATA 8'b0001_0001`define BYTE_ADDR 8'b0000_0011reg[7:0] db_r; //在IIC上传送的数据寄存器reg[7:0] read_data; //读出的EEPROM的数据//读写时序parameter IDLE = 4'd0;parameter START1 = 4'd1;parameter ADD1 = 4'd2;parameter ACK1 = 4'd3;parameter ADD2 = 4'd4;parameter ACK2 = 4'd5;parameter START2 = 4'd6;parameter ADD3 = 4'd7;parameter ACK3 = 4'd8;parameter DATA = 4'd9;parameter ACK4 = 4'd10;parameter STOP1 = 4'd11;parameter STOP2 = 4'd12;reg[3:0] cstate; //状态寄存器reg sda_r; //输出数据寄存器reg sda_link; //输出数据sda信号inout方向控制位 1 输入 0 输出reg[3:0] num;always@(posedge clk or negedge rst_n)begin if(!rst_n) begin cstate <= IDLE; sda_r <= 1'b1; sda_link <= 1'b0; num <= 4'd0; read_data <= 8'b0000_0000; end else case(cstate) IDLE: begin sda_link <= 1'b1; sda_r <= 1'b1; if(!sw1_r || sw2_r) begin db_r <= `DEVICE_WRITE; cstate <= START1; end else cstate <= IDLE; end START1: //先给出开始信号 begin if(`SCL_HIG) begin sda_link <= 1'b1; //设置sda为output sda_r <= 1'b0; //拉低sda,产生起始信号 cstate <= ADD1; //进入下一阶段 num <= 4'd0; end else cstate <= START1; //等待达到 SCL高电平中间 end ADD1: begin if(`SCL_LOW) //低电平变换数据 begin if(num == 4'd8) begin num <= 4'd0; sda_r <= 1'b1; sda_link <= 1'b0; //sda设置为高阻态(input) cstate <= ACK1; end else begin cstate <= ADD1; num <= num + 1'b1; case(num) 4'd0: sda_r <= db_r[7]; 4'd1: sda_r <= db_r[6]; 4'd2: sda_r <= db_r[5]; 4'd3: sda_r <= db_r[4]; 4'd4: sda_r <= db_r[3]; 4'd5: sda_r <= db_r[2]; 4'd6: sda_r <= db_r[1]; 4'd7: sda_r <= db_r[0]; default: ; endcase end end else cstate <= ADD1; end ACK1: begin if(`SCL_NEG) //该器件不考虑应答,直接认为已经响应,接下来传输写入的地址 begin cstate <= ADD2; db_r <= `BYTE_ADDR; end else cstate <= ACK1; end ADD2: //写入数据的地址 begin if(`SCL_LOW) begin if(num == 4'd8) begin num <= 4'd0; sda_r <= 1'b1; sda_link <= 1'b0; //设置为高阻态,为解说应答做准备 cstate <= ACK2; end else begin sda_link <= 1'b1; num <= num + 1'b1; case(num) 4'd0: sda_r <= db_r[7]; 4'd1: sda_r <= db_r[6]; 4'd2: sda_r <= db_r[5]; 4'd3: sda_r <= db_r[4]; 4'd4: sda_r <= db_r[3]; 4'd5: sda_r <= db_r[2]; 4'd6: sda_r <= db_r[1]; 4'd7: sda_r <= db_r[0]; default: ; endcase cstate <= ADD2; end end else cstate <= ADD2; end ACK2: begin if(`SCL_NEG) begin if(!sw1_r) //根据按键判断是读还是写 begin cstate <= DATA; db_r <= `WRITE_DATA; end else if(!sw2_r) begin db_r <= `DEVICE_READ; cstate <= START2; end end else cstate <= START2; end START2: begin if(`SCL_LOW) begin sda_link <= 1'b1; sda_r <= 1'b1; cstate <= START2; end else if(`SCL_HIG) begin sda_r <= 1'b0; cstate <= ADD3; end else cstate <= START2; end ADD3: //写入要读的地址 begin if(`SCL_LOW) begin if(num == 4'd8) begin num <= 4'd0; sda_r <= 1'b1; sda_link <= 1'b0; cstate <= ACK3; end else begin case(num) 4'd0: sda_r <= db_r[7]; 4'd1: sda_r <= db_r[6]; 4'd2: sda_r <= db_r[5]; 4'd3: sda_r <= db_r[4]; 4'd4: sda_r <= db_r[3]; 4'd5: sda_r <= db_r[2]; 4'd6: sda_r <= db_r[1]; 4'd7: sda_r <= db_r[0]; default: ; endcase cstate <= ADD3; end end else cstate <= ADD3; end ACK3: begin if(`SCL_NEG) begin cstate <= DATA; sda_link <= 1'b0; end else cstate <= ACK3; end DATA: begin if(!sw2_r) //读操作 begin if(num <= 4'd7) begin cstate <= DATA; if(`SCL_HIG) begin num <= num + 1'b1; case(num) 4'd0: read_data[7] <= sda; //依次在高电平保存数据 4'd1: read_data[6] <= sda; 4'd2: read_data[5] <= sda; 4'd3: read_data[4] <= sda; 4'd4: read_data[3] <= sda; 4'd5: read_data[2] <= sda; 4'd6: read_data[1] <= sda; 4'd7: read_data[0] <= sda; default: ; endcase end end else if((`SCL_LOW) && (num == 4'd8)) begin num <= 4'd0; cstate <= ACK4; end else cstate <= DATA; end else if(!sw1_r) //写操作 begin sda_link <= 1'b1; if(num <= 4'd7) begin cstate <= DATA; if(`SCL_LOW) begin sda_link <= 1'b1; num <= num + 1'b1; case(num) 4'd0: sda_r <= db_r[7]; 4'd1: sda_r <= db_r[6]; 4'd2: sda_r <= db_r[5]; 4'd3: sda_r <= db_r[4]; 4'd4: sda_r <= db_r[3]; 4'd5: sda_r <= db_r[2]; 4'd6: sda_r <= db_r[1]; 4'd7: sda_r <= db_r[0]; default: ; endcase end end else if((`SCL_LOW) && (num == 4'd8)) begin num <= 4'd0; sda_r <= 1'b1; sda_link <= 1'b0; cstate <= ACK4; end else cstate <= ACK4; end end ACK4: begin if(`SCL_NEG) begin cstate <= STOP1; end else cstate <= ACK4; end STOP1: begin if(`SCL_LOW) begin sda_link <= 1'b1; sda_r <= 1'b0; cstate <= STOP1; end else if(`SCL_HIG) begin sda_r <= 1'b1; cstate <= STOP2; end else cstate <= STOP1; end STOP2: begin if(`SCL_LOW) sda_r <= 1'b1; else if(cnt_20ms == 20'hffff0) cstate <= IDLE; else cstate <= STOP2; end default: cstate <= IDLE; endcaseendassign sda = sda_link ? sda_r:1'bz;assign dis_data = read_data;endmodule 版权声明:本文为博主原创文章,转载需声明为转载内容并添加原文地址。 原文地址:https://coderdock.com 欢迎您扫一扫上面的微信公众号,订阅我的公众号