基于紫光同创盘古50K的I2C时序编写
2026/4/30 13:37:40 网站建设 项目流程

顶层模块

module I2C_top( input clk, input rst_n, input key_3, input key_1, inout SCL, inout SDA, output [7:0] led ); wire wr_pulse, rd_pulse; wire i2c_busy, i2c_done; wire [7:0] read_result; // 物理引脚处理 wire sda_in, sda_out; wire scl_out; assign sda_in = SDA; assign SCL = scl_out ? 1'bz : 1'b0; assign SDA = sda_out ? 1'bz : 1'b0; // 互斥触发逻辑:如果忙碌,按键无效 wire safe_wr = wr_pulse && !i2c_busy; wire safe_rd = rd_pulse && !i2c_busy; i2c_master u_master( .clk(clk), .rst_n(rst_n), .wr_en(safe_wr), .rd_en(safe_rd), .dev_addr(8'hA0), // EEPROM 设备地址 .mem_addr(8'h00), // 我们固定读写 0x00 地址 .data_in(8'b10101010), // 测试写入的数据 .sda_in(sda_in), .deb_scl(scl_out), .deb_sda(sda_out), .data_out(read_result), .busy(i2c_busy), .done(i2c_done) ); key_debounce_write u_write_db ( .clk(clk), .rst_n(rst_n), .key_3(~key_3), .key_pulse(write_pulse) // 输出写脉冲 ); key_debounce_read u_read_db ( .clk(clk), .rst_n(rst_n), .key_1(~key_1), .key_pulse(read_pulse) // 输出写脉冲 ); endmodule

读写模块

module i2c_master( input clk, input rst_n, input wr_en, // 写使能脉冲 input rd_en, // 读使能脉冲 input [7:0] dev_addr, // 设备地址 (如 8'hA0) input [7:0] mem_addr, // 寄存器/内存地址 (如 8'h00) input [7:0] data_in, // 要写入的数据 input sda_in, output reg deb_scl, output reg deb_sda, output reg [7:0] data_out, // 读取到的数据 output reg busy, // 总线忙碌标志 output reg done // 操作完成脉冲 ); localparam IDLE = 0, START = 1, SEND_DEV = 2, // 发送设备地址 SEND_MEM = 3, // 发送内存地址 RESTART = 4, // 重复起始位(仅读取时需要) SEND_DEV_R = 5, // 再次发送设备地址(读模式) READ_DATA = 6, WRITE_DATA= 7, ACK = 8, STOP = 9; reg [3:0] state; reg [1:0] s_cnt; reg [3:0] bit_cnt; reg [7:0] clk_div; reg [7:0] shift_reg; reg is_rd; // 记录当前是读还是写 wire clk_en = (clk_div == 8'd30); always @(posedge clk or negedge rst_n) begin if(!rst_n) begin state <= IDLE; clk_div <= 0; deb_scl <= 1; deb_sda <= 1; busy <= 0; done <= 0; end else if(clk_en) begin clk_div <= 0; case(state) IDLE: begin done <= 0; deb_scl <= 1; deb_sda <= 1; if(wr_en || rd_en) begin busy <= 1; is_rd <= rd_en; state <= START; shift_reg <= {dev_addr[7:1], 1'b0}; // 初始都是写模式(为了发内存地址) end else busy <= 0; end START: begin deb_sda <= 0; state <= SEND_DEV; bit_cnt <= 0; end SEND_DEV, SEND_MEM, WRITE_DATA, SEND_DEV_R: begin s_cnt <= s_cnt + 1; case(s_cnt) 0: begin deb_scl <= 0; deb_sda <= shift_reg[7-bit_cnt]; end 1: begin deb_scl <= 1; end 2: begin deb_scl <= 1; end 3: begin deb_scl <= 0; s_cnt <= 0; if(bit_cnt == 7) begin bit_cnt <= 0; state <= ACK; end else bit_cnt <= bit_cnt + 1; end endcase end ACK: begin s_cnt <= s_cnt + 1; case(s_cnt) 0: begin deb_scl <= 0; deb_sda <= 1; end // 释放SDA 1: begin deb_scl <= 1; end 3: begin s_cnt <= 0; deb_scl <= 0; // 逻辑跳转 if(state == SEND_DEV) begin shift_reg <= mem_addr; state <= SEND_MEM; end else if(state == SEND_MEM) begin if(is_rd) state <= RESTART; else {shift_reg, state} <= {data_in, WRITE_DATA}; end else if(state == WRITE_DATA || state == READ_DATA) begin state <= STOP; end else if(state == SEND_DEV_R) begin state <= READ_DATA; end end endcase end RESTART: begin // 核心步骤:不发STOP直接再次START s_cnt <= s_cnt + 1; case(s_cnt) 0: begin deb_sda <= 1; end 1: begin deb_scl <= 1; end 2: begin deb_sda <= 0; end // 在SCL高电平时拉低SDA 3: begin s_cnt <= 0; deb_scl <= 0; shift_reg <= {dev_addr[7:1], 1'b1}; // 切换为读模式 state <= SEND_DEV_R; end endcase end READ_DATA: begin s_cnt <= s_cnt + 1; case(s_cnt) 0: begin deb_scl <= 0; deb_sda <= 1; end // 释放总线供从机输出 1: begin deb_scl <= 1; end 2: begin data_out[7-bit_cnt] <= sda_in; deb_scl <= 1; end 3: begin deb_scl <= 0; s_cnt <= 0; if(bit_cnt == 7) begin bit_cnt <= 0; state <= ACK; end else bit_cnt <= bit_cnt + 1; end endcase end STOP: begin s_cnt <= s_cnt + 1; case(s_cnt) 0: begin deb_sda <= 0; end 1: begin deb_scl <= 1; end 2: begin deb_sda <= 1; done <= 1; end 3: begin s_cnt <= 0; state <= IDLE; end endcase end endcase end else clk_div <= clk_div + 1; end endmodule

消抖模块

module key_debounce_write( input clk, input rst_n, input key_3, output key_pulse // 确保这是一个单周期脉冲 ); reg [19:0] cnt_key; reg key_r1, key_r2; // 两级寄存器同步,消除亚稳态 // 1. 同步异步信号 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin key_r1 <= 1'b1; key_r2 <= 1'b1; end else begin key_r1 <= key_3; key_r2 <= key_r1; // key_r2 是 key_3 稳定的一拍副本 end end // 2. 消抖计数 reg key_stable; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin cnt_key <= 20'd0; key_stable <= 1'b1; end else begin // 检测到变化(当前值与同步值不同) if(key_r1 != key_r2) begin cnt_key <= 20'd0; // 只要抖动,立刻清零 end else if(cnt_key < 20'd1_000_000) begin cnt_key <= cnt_key + 1'b1; // 稳定则累加 end else begin key_stable <= key_r2; // 只有连续稳定 20ms,才更新输出电平 end end end // 3. 边沿检测:产生单脉冲 reg key_stable_r; always @(posedge clk or negedge rst_n) begin if(!rst_n) key_stable_r <= 1'b1; else key_stable_r <= key_stable; end // 下降沿检测:当 stable 从 1 变到 0 的瞬间,产生一个高脉冲 assign key_pulse = key_stable_r && (!key_stable); endmodule

有大佬知道我这个模块的I2C时序发送为什么一直不成功吗 ?我使用按键发送一个信息到我的EPROM里面去 ,然后在去读取这个信息,读取完之后通过LED灯显示,但是目前这个数据在读写阶段存在问题

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询