8.23 正点原子领航者V1开发板学习之EEPROM

                                                        一、EEPROM和ICC介绍

1.串口通信中UART 和IIC区别

UART是有两个数据项RX ,TX,属于全双工,无时钟线异步串行通信

IIC 有两根线SCL时钟线和SDA数据线,由于只有一条数据线,是同步半双工通信。

2. AT24C64 芯片手册  器件操作时序

                                                                IIC通信协议

 SDA是在SCL低电平时改变,当SCL为高电平时SDA保持数据稳定。

SCL为高电平时,SDA由高拉低是开始标志,传输完数据后由低拉高是结束标志。

 这个是应答位 输出完8bit数据后,输出一个低电平。当输出一直为高电平有可能是数据出错,SCL 频率太高等。

3.器件地址

        

 前四位是固定值,A2A1A0是可配置地址输入,最多供8个器件EEPROM组合,最后一位是读写控制位。1读0写。本次实验配置A2A1A0为000。

4.写操作

字节写

 这个图略去了SCL时钟波形,根据上述器件操作时序可知,当SCL高电平时SDA拉低则表示数据START,然后在每个SCL低电平期间,数据改变一共输出8位1010A2A1A0+读写位,最后在SCL高电平期间输出一个响应位即SDA拉低。

硬件上就是开始到器件地址位由FPGA来控制,然后由EEPROM响应是否输出低电平响应。

字节地址位,*表示不关心位,+表示32k不关心,芯片是64k的所以要看设置为BIT_CTRL=1。

AT24C64 存储容量 为 64Kbit,内部分成 256 页,每页 32 字节,共有 8192 个字节,且其读写操作都是以字节为基本单位。可 以把 AT24C64 看作一本书,那么这本书有 256 页,每页有 32 行,每行有 8 个字,总共有 256*32*8=65536 个字,对应着 AT24C64 的 64*1024=65536 个 bit。8192=2^13,所以用13位来表示字节地址,当然每8位后还是有一个ACK。识别完器件地址和字节地址信息后,就开始写数据了,随后输出一个ACK和停止位。

页写

 DATA一共写32个字节数据,然后再输出ACK和停止位。

显然如果数据多,用页写操作效率远大于字节写。

5.读操作

EEPROM支持三种读操作,当前地址读,随机地址读和顺序地址读。

顺序读中,读操作和写操作有一个很不同的地方。读到某一页最后字节会跳转到下一页,一直读到最后一页最后一个字节然后跳转到第一页第一个字节。写操作则是写到当前页最后一个字节时跳到当前页第一个字节。

当前地址读好理解,最后不需要ACK信号。 

 任意地址读则需要在都之前进行一次哑写,写入地址,然后读出该地址,同样没有ACK信号。

由于需要哑写,所以耗费时间

 在当前地址读基础上主机不返回STOP

本次实验选择字节写和随机读的方式,也是为了后面实验直接调用本模块。

6.实验IIC读写协议选取

字节写

 随机读

                                                          二、实验

1.实验任务

        先给EEPROM   地址0-255写入数据0-255.之后再读取数据,若读取正确LED常亮否则闪烁。

 模块框架中就两条线SCL和SDA

2.原理图

 图中iic_sda是INOUT信号。

i2c_dri 为 I2C 驱动模块,用来驱动 I2C 的读写操作。当 FPGA 通过 EEPROM 读写模块 e2prom_rw 向 EEPROM 读写数据时,拉高 i2c 触发控制信号 i2c_exec 以使能 I2C 驱动模块,并使用读写控制信号 i2c_rh_wl 控制读写操作,当 i2c_rh_wl 为低电平时,I2C 驱动模块 i2c_dri 执行写操作,当 i2c_rh_wl 为高电平时,I2C 驱动模块 i2c_dri 执行读操作。此外,e2prom_rw 模块通过 i2c_addr 接口向 i2c_dri 模块输入器件字地址,通 过 i2c_data_w接口向 i2c_dri模块输入写的数据,并通过 i2c_data_r 接口读取 i2c_dri模块读到的数据。rw_done 信号是读写测试完成的标志,rw_result 是读写测试的结果。

3.i2c_dri程序设计

sda信号设计:它是一个INOUT信号,EEPROM是半双工,它只有一条数据线。所以用三态门设计读写分开操作

reg            sda_dir   ; //I2C数据(SDA)方向控制
wire          sda_in     ; //SDA输入信号

assign  sda     = sda_dir ?  sda_out : 1'bz;     //SDA数据输出或高阻
assign  sda_in  = sda ;                          //SDA数据输入

dri_clk的设计:

驱动时钟dri要驱动SCL,SDA所以时钟频率要高,毕竟SDA在SCL低电平时候变化,所以dri_clk频率最好是SCL4倍以上。

 先求分频系数,系统时钟50MHZ除以I2C的时钟,可得I2C时钟分频系数,用一个计数器计数到这么多跳变一次的时钟就是I2C所需要的时钟频率的两倍。而dri_clk频率是其四倍,右移两位即除4。(计数为I2C的四分之一就跳变就是频率为其4倍)

assign  clk_divide = (CLK_FREQ/I2C_FREQ) >> 2'd2;//模块驱动时钟的分频系数

计数器模块如下:

//生成I2C的SCL的四倍频率的驱动时钟用于驱动i2c的操作
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        dri_clk <=  1'b0;
        clk_cnt <= 10'd0;
    end
    else if(clk_cnt == clk_divide[8:1] - 1'd1) begin
        clk_cnt <= 10'd0;
        dri_clk <= ~dri_clk;
    end
    else
        clk_cnt <= clk_cnt + 1'b1;
end

 要注意取[8:1]也是除2操作,因为分频系数只是求到那个跳变的计数,即一个周期的一半,故还要再除2,这样才是一个周期。减一1是从0开始计数。(当然这个仅适用于偶分频)

IP核也可以分频但是资源利用率会上升。

状态机的设计:

 初始状态设为idle。

用三段式来设计

//localparam define
localparam  st_idle     = 8'b0000_0001; //空闲状态
localparam  st_sladdr   = 8'b0000_0010; //发送器件地址(slave address)
localparam  st_addr16   = 8'b0000_0100; //发送16位字地址
localparam  st_addr8    = 8'b0000_1000; //发送8位字地址
localparam  st_data_wr  = 8'
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值