当前位置: 首页 > news >正文

10-verilog的EEPROM驱动-单字节读写

verilog的EEPROM驱动-单字节读写

1.读写

module i2c_dri(                                                            input                clk        ,    input                rst_n      ,   //i2c interface                      input                i2c_rw         ,  //0是写,1是读input                i2c_begin      ,  //I2C触发执行信号input        [15:0]  i2c_addr       ,  //I2C器件内地址input        [ 7:0]  i2c_data_w     ,  //I2C要写的数据output  reg  [ 7:0]  i2c_data_r     ,  //I2C读出的数据output  reg          i2c_busy       ,  //I2C忙信号output  reg          i2c_done   ,  //I2C一次操作完成output  reg          scl            ,  //I2C的SCL时钟信号inout                sda   ,             //I2C的SDA信号output       reg    led2    ,   //output reg            dri_clk    //I2C数据(SDA)方向控制                                 );
parameter   SLAVE_ADDR = 7'b1010000   ;  //EEPROM从机地址
parameter   CLK_FREQ   = 26'd50_000_000; //模块输入的时钟频率
parameter   I2C_FREQ   = 18'd250_000;     //IIC_SCL的时钟频率//reg define
reg    [ 9:0]  clk_cnt   ; //分频时钟计数
reg    [ 7:0]  data_wr_t ; //I2C需写的数据的临时寄存
reg    [15:0]  addr_t    ; //读写地址临时寄存
reg    [ 7:0]  data_r    ; //数据
reg    [ 7:0]  data_w    ; //数据
reg            r_or_w   ;   //读写
reg            sda_dir   ; //I2C数据(SDA)方向控制
reg            sda_out   ; //reg     [ 3:0]         flow_cnt    ;        // 流转计数
//wire define
wire          sda_in     ; //SDA输入信号
wire   [8:0]  clk_divide ; //模块驱动时钟的分频系数assign  sda     = sda_out ;     //SDA数据输出或高阻
assign  clk_divide = (CLK_FREQ/I2C_FREQ) >> 2'd2;//模块驱动时钟的分频系数//生成I2C的SCL的四倍频率的驱动时钟用于驱动i2c的操作
always @(posedge clk or negedge rst_n) beginif(!rst_n) begindri_clk <=  1'b0;clk_cnt <= 10'd0;   endelse if(clk_cnt == clk_divide[8:1] - 1'd1) beginclk_cnt <= 10'd0;dri_clk <= ~dri_clk;endelseclk_cnt <= clk_cnt + 1'b1;
end
//////////////////////////////////
reg    [ 7:0]  data_temp    ; //数据
reg     flow_cnt_ok ;        // 流转计数
reg    [ 6:0]  cnt       ; //计数
////////////////////////////////////////////////
/*
写
0:空闲
1:加载器件地址
2:写字节
3:加载16为地址高字节
4:写字节
5:加载16为地址低字节
6:写字节
7:加载数据
8:写字节
9:发送停止位读
0:空闲
1:加载器件地址
2:写字节
3:加载16为地址高字节
4:写字节
5:加载16为地址低字节
6:写字节
10:加载器件地址+读
8:写字节
11:读数据
12:发送停止*/
always @(posedge dri_clk or negedge rst_n) beginif(!rst_n) beginscl <=  1'b1;sda_out <=  1'b1;cnt<=  7'b0;flow_cnt<=4'b0;i2c_busy<=1'b0;flow_cnt_ok<= 1'b0;cnt<= 1'b0;end////////////////////////////////////////////else if (flow_cnt_ok&&(r_or_w==0))begin flow_cnt<=((flow_cnt>=4'd9)? (4'd0):(flow_cnt+4'd1));//状态技术+1i2c_done<=1'b0;flow_cnt_ok<= 1'b0;end else if (flow_cnt_ok&&(r_or_w==1))begin if(flow_cnt<=4'd5)flow_cnt<=flow_cnt+1'b1;else if (flow_cnt==4'd6)flow_cnt<=4'd10;else if (flow_cnt==4'd10)flow_cnt<=4'd8;else if (flow_cnt==4'd8)flow_cnt<=4'd11;     else if (flow_cnt==4'd11)flow_cnt<=4'd12;else if (flow_cnt==4'd12)flow_cnt<=4'd0;i2c_done<=1'b0;flow_cnt_ok<= 1'b0;end ///////////////////////////////////////////////else if(flow_cnt == 4'b0) beginif(i2c_begin==1'b1&&i2c_busy==1'b0) beginr_or_w<=i2c_rw;addr_t<=i2c_addr;data_w<=i2c_data_w;i2c_busy<=1'b1;flow_cnt_ok<=1'b1;endelse flow_cnt = 4'b0;endelse if(flow_cnt == 4'd1) begin//加载写字节data_temp<={SLAVE_ADDR,1'b0};flow_cnt_ok<=1'b1;endelse if(flow_cnt == 4'd3) begin//加载eeprom地址data_temp<=addr_t[15:8];flow_cnt_ok<=1'b1;endelse if(flow_cnt == 4'd5) begin//加载低8位地址data_temp<=addr_t[7:0];flow_cnt_ok<=1'b1;endelse if(flow_cnt == 4'd7) begin//加载要写入的数据data_temp<=data_w[7:0];flow_cnt_ok<=1'b1;endelse if(flow_cnt == 4'd9) begin//发送STOPcnt     <= cnt +1'b1 ;case(cnt)7'd1 : sda_out <= 1'b0;          //开始I2C7'd2 : scl <= 1'b1;              7'd3 : sda_out <= 1'b1; //传送器件地址7'd4 : begini2c_busy<=1'b0;i2c_done<=1'b1;scl <= 1'b1;sda_out <= 1'b1;flow_cnt_ok<=1'b1;cnt <= 1'b0;enddefault :  ;   endcaseendelse if(flow_cnt == 4'd2||flow_cnt == 4'd4||flow_cnt == 4'd6||flow_cnt == 4'd8) begin//发送一个字节cnt     <= cnt +1'b1 ;case(cnt)//7'd0 :begin scl <= 1'b1;sda_out <= 1'b1;end7'd1 : sda_out <= 1'b0;          //开始I2C7'd3 : scl <= 1'b0;              7'd4 : sda_out <= data_temp[7]; //传送器件地址7'd5 : scl <= 1'b1;              7'd7 : scl <= 1'b0;              7'd8 : sda_out <= data_temp[6]; 7'd9 : scl <= 1'b1;              7'd11: scl <= 1'b0;              7'd12: sda_out <= data_temp[5]; 7'd13: scl <= 1'b1;              7'd15: scl <= 1'b0;              7'd16: sda_out <= data_temp[4]; 7'd17: scl <= 1'b1;              7'd19: scl <= 1'b0;              7'd20: sda_out <= data_temp[3]; 7'd21: scl <= 1'b1;              7'd23: scl <= 1'b0;              7'd24: sda_out <= data_temp[2]; 7'd25: scl <= 1'b1;              7'd27: scl <= 1'b0;              7'd28: sda_out <= data_temp[1]; 7'd29: scl <= 1'b1;              7'd31: scl <= 1'b0;              7'd32: sda_out <= data_temp[0];          //0:写7'd33: scl <= 1'b1;              7'd35: scl <= 1'b0;              7'd36: sda_out <= 1'bz;  //            7'd37: scl     <= 1'b1;    7'd38: begin                     //从机应答 if(sda == 1'b1) begin          //高电平表示未应答cnt <= 1'b0;flow_cnt = 4'b0;end    end                                          7'd39: begin                     scl <= 1'b0;                 cnt <= 1'b0;flow_cnt_ok<=1'b1;enddefault :  ;    endcaseendelse if(flow_cnt == 4'd10) begin//加载要读的芯片地址data_temp<={SLAVE_ADDR,1'b1};flow_cnt_ok<=1'b1;scl <= 1'b1;//添加起始位endelse if(flow_cnt == 4'd12) begin//发送STOPcnt     <= cnt +1'b1 ;case(cnt)7'd1 : sda_out <= 1'b0;          //开始I2C7'd2 : scl <= 1'b1;              7'd3 : sda_out <= 1'b1; //传送器件地址7'd4 : begini2c_busy<=1'b0;i2c_done<=1'b1;scl <= 1'b1;sda_out <= 1'b1;flow_cnt_ok<=1'b1;cnt <= 1'b0;enddefault :  ;   endcaseendelse if(flow_cnt == 4'd11) begin//加载要读的芯片地址cnt     <= cnt +1'b1 ;case(cnt)7'd1 : sda_out <= 1'bz;          //开始I2C7'd3 : scl <= 1'b0;              7'd5 : scl <= 1'b1; 7'd6 : data_r[7]<=sda;7'd7 : scl <= 1'b0;              7'd9 : scl <= 1'b1;  7'd10 : data_r[6]<=sda;7'd11: scl <= 1'b0;              7'd13: scl <= 1'b1;    7'd14 : data_r[5]<=sda;7'd15: scl <= 1'b0;              7'd17: scl <= 1'b1;              7'd18: data_r[4]<=sda;7'd19: scl <= 1'b0;              7'd21: scl <= 1'b1;              7'd22: data_r[3]<=sda;7'd23: scl <= 1'b0;              7'd25: scl <= 1'b1;              7'd26: data_r[2]<=sda;7'd27: scl <= 1'b0;              7'd29: scl <= 1'b1;              7'd30:data_r[1]<=sda;7'd31: scl <= 1'b0;              7'd33: scl <= 1'b1;              7'd34: data_r[0]<=sda;7'd35: scl <= 1'b0;  7'd36: sda_out <= 1'b0;           7'd37: scl <= 1'b1; 7'd39: scl <= 1'b0; 7'd40: sda_out <= 1'b1; 7'd42: begin                                  cnt <= 1'b0;flow_cnt_ok<=1'b1;enddefault :  ;  endcase endelse ;
end
endmodule

2.例化


module e2prom_top(input               sys_clk    ,      //系统时钟input               sys_rst_n  ,      //系统复位//eeprom interfaceoutput              iic_scl    ,      //eeprom的时钟线sclinout               iic_sda    ,      //eeprom的数据线sdaoutput              iic_scl_b    ,      output              iic_sda_b   ,output              dri_clk   ,output       reg    led0    ,      output       reg    led1    ,    output              led2    ,    input               key0    ,input               key1
);assign iic_scl_b=iic_scl;
assign iic_sda_b=iic_sda;
//parameter define
parameter    SLAVE_ADDR = 7'b1010000    ; //器件地址(SLAVE_ADDR)
parameter    CLK_FREQ   = 26'd50_000_000; //i2c_dri模块的驱动时钟频率(CLK_FREQ)
parameter    I2C_FREQ   = 18'd250_000   ; //I2C的SCL时钟频率//*****************************************************
//**                    main code
//****************************************************//
reg  i2c_rw;
reg  i2c_begin;
reg  [15:0]  i2c_addr;
reg  [ 7:0]  i2c_data_w     ;  //I2C要写的数据
wire [ 7:0]  i2c_data_r     ;  //I2C读出的数据always @(posedge dri_clk or negedge sys_rst_n) begin if (!sys_rst_n)beginled0<=1'b0;i2c_begin<=1'b0;i2c_rw<=1'b0;//写数据i2c_addr<=16'h0000;i2c_data_w<=8'h00;endelse if (key0_flag&&key0_value&&(!i2c_busy))begini2c_begin<=1'b1;i2c_rw<=1'b0;//写数据i2c_addr<=16'h0001;i2c_data_w<=8'h12;led0<=~led0;endelse if (key1_flag&&key1_value&&(!i2c_busy))begini2c_begin<=1'b1;i2c_rw<=1'b1;//读数据i2c_addr<=16'h0001;i2c_data_w<=8'h12;led1<=~led1;endelsei2c_begin<=1'b0;
end//i2c驱动模块
i2c_dri #(.SLAVE_ADDR  (SLAVE_ADDR),  //EEPROM从机地址.CLK_FREQ    (CLK_FREQ  ),  //模块输入的时钟频率.I2C_FREQ    (I2C_FREQ  )   //IIC_SCL的时钟频率
) u_i2c_dri(.clk         (sys_clk   ),  .rst_n       (sys_rst_n ),  .i2c_rw      (i2c_rw   ),.i2c_begin   (i2c_begin   ),    //I2C触发执行信号.i2c_addr    (i2c_addr   ),     //I2C器件内地址.i2c_data_w  (i2c_data_w   ),  //I2C要写的数据.i2c_data_r  (i2c_data_r   ),  //I2C读出的数据.i2c_busy    (i2c_busy   ),     //I2C忙信号    .i2c_done    (i2c_done   ),     //I2C忙信号    .led2           (led2   ),     //I2C忙信号 .scl         (iic_scl   ),  //I2C的SCL时钟信号.sda         (iic_sda   ) , //I2C的SDA信号.dri_clk       (dri_clk)
);/*
always @(posedge dri_clk or negedge sys_rst_n) begin if (!sys_rst_n)led0<=1'b0;else if (key0_flag&&key0_value)begin//led0<=~led0;;endelse//led0<=led0;;
end always @(posedge dri_clk or negedge sys_rst_n) begin if (!sys_rst_n)led1<=1'b0;else if (key1_flag&&key1_value)led1<=~led1;elseled1<=led1;
end 
*/
wire key0_value;
wire key0_flag;
key_in u0_key_in(.sys_clk         (dri_clk   ),  .rst_n           (sys_rst_n ),  .key_in          (key0 ),         .key_flag        (key0_flag ),        .key_value       (key0_value )       
);key_in u1_key_in(.sys_clk         (dri_clk   ),  .rst_n           (sys_rst_n ),  .key_in          (key1 ),         .key_flag        (key1_flag ),        .key_value       (key1_value )       
);endmodule
module key_in(input            sys_clk,          //外部50M时钟input            rst_n,        //外部复位信号,低有效input            key_in,              //外部按键输入output reg       key_flag,         //按键数据有效信号output reg       key_value         //按键消抖后的数据  );reg [31:0] delay_cnt;
reg        key_reg;always @(posedge sys_clk or negedge rst_n) begin if (!rst_n) begin key_reg   <= 1'b1;delay_cnt <= 32'd0;endelse beginkey_reg <= key_in;				//非阻塞赋值,按键输入是wire类型的,随时可能有值的变化if(key_reg != key_in)          //一旦检测到按键状态发生变化(有按键被按下或释放)delay_cnt <= 32'd500_000;  //给延时计数器重新装载初始值(计数时间为10ms)else if(key_reg == key_in) begin  //在按键状态稳定时,计数器递减,开始10ms倒计时if(delay_cnt > 32'd0)delay_cnt <= delay_cnt - 1'b1;elsedelay_cnt <= delay_cnt;end           end   
end
always @(posedge sys_clk or negedge rst_n) begin if (!rst_n) begin key_flag  <= 1'b0;key_value <= 1'b1;          endelse beginif(delay_cnt == 32'd1) begin   //当计数器递减到1时,说明按键稳定状态维持了20mskey_flag  <= 1'b1;         //此时消抖过程结束,给出一个时钟周期的标志信号key_value <= key_in;          //并寄存此时按键的值endelse beginkey_flag  <= 1'b0;key_value <= key_value; end  end   
end
endmodule

3.波形

在这里插入图片描述
在这里插入图片描述

http://www.xdnf.cn/news/1308061.html

相关文章:

  • 罗技MX Anywhere 2S鼠标修复记录
  • 多机编队——(6)解决机器人跟踪过程中mpc控制转圈问题
  • AT89C52单片机介绍
  • CVE-2024-28752漏洞复现
  • mysql一启动就挂的解决
  • Javar如何用RabbitMQ订单超时处理
  • Docker部署 Neo4j Community【拒绝国内镜像拉取异常】
  • Vue组件生命周期钩子:深入理解组件的生命周期阶段
  • 论文学习24:Boundary-Sensitive Segmentation of SmallLiver Lesions
  • 服务器可以ping通,但部署的网站打不开
  • [Linux] Linux tar文档管理 系统间复制文档
  • Android 移动端 UI 设计:前端常用设计原则总结
  • 使用openssl创建自签名CA并用它签发服务器证书
  • c# WebAssembly,在网页上能运行多线程,异步,锁,原子加,减等代码吗
  • tailscale远程服务器连接局域网方案(解决境外服务器网速慢的问题)
  • OBOO鸥柏丨75寸/86平板企业办公会议触控一体机核心国产化品牌招投标参数
  • 企业运维规划及Linux介绍虚拟环境搭建
  • Jenkins Pipeline中参数化构建
  • 5 索引的操作
  • 惠普声卡驱动win10装机完成检测不到声卡
  • 每日任务day0816:小小勇者成长记之符文羊皮卷
  • ML307C 4G通信板:工业级DTU固件,多协议支持,智能配置管理
  • AI热点周报(8.10~8.16):AI界“冰火两重天“,GPT-5陷入热议,DeepSeek R2模型训练受阻?
  • c#Blazor WebAssembly在网页中多线程计算1000万次求余
  • MongoDB 聚合提速 3 招:$lookup 管道、部分索引、时间序列集合(含可复现实验与 explain 统计)
  • 完整设计 之 智能合约系统:主题约定、代理协议和智能合约 (临时命名)----PromptPilot (助手)答问之2
  • JavaEE 初阶第十九期:网络编程“通关记”(一)
  • 【Java学习】锁、线程死锁、线程安全2
  • 【C++】动态内存管理
  • 代码随想录Day52:图论(孤岛的总面积、沉没孤岛、水流问题、建造最大岛屿)