[FPGA][Verilog][SPI]简单的读写SPI接口EEPROM-93C46程序
- 格式:pdf
- 大小:195.00 KB
- 文档页数:9
FPGA读写EEPROMFPGA读写EEPROMmodulei2c(clk,rst,data_in,scl,sda,wr_input,rd_input,lowbit,en,seg_data);input clk,rst;output scl;//I2C时钟线inout sda;//I2C数据线input[3:0] data_in;//拨码开关输入想写入EEPROM的数据input wr_input;//要求写的输入input rd_input;//要求读的输入output lowbit; //输出一个低电平给矩阵键盘的某一行output[1:0] en;//数码管使能output[7:0] seg_data;//数码管段数据reg[7:0] seg_data;reg scl;reg[1:0] en;reg[7:0] seg_data_buf;reg[11:0] cnt_scan;reg sda_buf;//sda输入输出数据缓存reg link; //sda输出标志reg phase0,phase1,phase2,phase3;//一个scl时钟周期的四个相位阶段,将一个scl周期分为4段//phase0对应scl的上升沿时刻,phase2对应scl的下降沿时刻,phase1对应从scl高电平的中间时刻,phase2对应从scl低电平的中间时刻,reg[7:0] clk_div;//分频计数器reg[1:0] main_state;reg[2:0] i2c_state;//对i2c操作的状态reg[3:0] inner_state;//i2c每一操作阶段内部状态reg[19:0] cnt_delay;//按键延时计数器reg start_delaycnt;//按键延时开始reg[7:0] writeData_reg,readData_reg;//要写的数据的寄存器和读回数据的寄存器reg[7:0] addr;//被操作的EEPROM字节的地址parameter div_parameter=100;// 分频系数,AT24C02最大支持400K时钟速率parameter start=4'b0000, //开始first=4'b0001, //第1位second=4'b0010,//第2位third=4'b0011, //第3位fourth=4'b0100, //第4位fifth=4'b0101, //第5位sixth=4'b0110, //第6位seventh=4'b0111, //第7位eighth=4'b1000, //第8位ack=4'b1001, //确认位stop=4'b1010; //结束位parameter ini=3'b000, //初始化EEPROM状态sendaddr=3'b001, //发送地址状态write_data=3'b010, //写数据状态?read_data=3'b011, //读数据状态read_ini=6'b100; //发送读信息状态assign lowbit=0;assign sda=(link)? sda_buf:1'bz;always@(posedge clk or negedge rst) beginif(!rst)cnt_delay<=0;else beginif(start_delaycnt) beginif(cnt_delay!=20'd800000)cnt_delay<=cnt_delay+1;elsecnt_delay<=0;endendendalways@(posedge clk or negedge rst) begin if(!rst) beginclk_div<=0;phase0<=0;phase1<=0;phase2<=0;phase3<=0;endelse beginif(clk_div!=div_parameter-1)clk_div<=clk_div+1;elseclk_div<=0;if(phase0)phase0<=0;else if(clk_div==99)phase0<=1;if(phase1)phase1<=0;else if(clk_div==24)phase1<=1;if(phase2)phase2<=0;else if(clk_div==49)phase2<=1;if(phase3)phase3<=0;else if(clk_div==74)phase3<=1;endend///////////////////////////EEPROM操作部分///////////// always@(posedge clk or negedge rst) beginif(!rst) beginstart_delaycnt<=0;main_state<=2'b00;i2c_state<=ini;inner_state<=start;scl<=1;sda_buf<=1;link<=0;writeData_reg<=5;readData_reg<=0;addr<=10;endelse begincase(main_state)2'b00: begin //等待读写要求writeData_reg<=data_in;scl<=1;sda_buf<=1;link<=0;inner_state<=start;i2c_state<=ini;if((cnt_delay==0)&&(!wr_input||!rd_input)) start_delaycnt<=1;else if(cnt_delay==20'd800000) beginstart_delaycnt<=0;if(!wr_input)main_state<=2'b01;else if(!rd_input)main_state<=2'b10;endend2'b01: begin //向EEPROM写入数据if(phase0) scl<=1;else if(phase2)scl<=0;case(i2c_state)ini: begin //初始化EEPROM case(inner_state)start: beginif(phase1) beginlink<=1;sda_buf<=0;endif(phase3&&link) begininner_state<=first;sda_buf<=1;link<=1;endendfirst:if(phase3) beginlink<=1;inner_state<=second; end second:if(phase3) beginsda_buf<=1;link<=1;inner_state<=third; end third:if(phase3) beginsda_buf<=0;link<=1;inner_state<=fourth; end fourth:if(phase3) beginsda_buf<=0;link<=1;inner_state<=fifth; end fifth:if(phase3) beginsda_buf<=0;link<=1;inner_state<=sixth; end sixth:if(phase3) beginsda_buf<=0;link<=1;inner_state<=seventh; end seventh:if(phase3) beginlink<=1;inner_state<=eighth; endeighth:if(phase3) beginlink<=0;inner_state<=ack;endack: beginif(phase0)sda_buf<=sda;if(phase1) beginif(sda_buf==1)main_state<=3'b000;endif(phase3) beginlink<=1;sda_buf<=addr[7];inner_state<=first;i2c_state<=sendaddr;endendendcaseendsendaddr: begin //送相应字节的地址case(inner_state) first:if(phase3) beginlink<=1;sda_buf<=addr[6]; inner_state<=second; end second:if(phase3) beginlink<=1;sda_buf<=addr[5]; inner_state<=third; end third:if(phase3) beginlink<=1;sda_buf<=addr[4]; inner_state<=fourth; end fourth:if(phase3) beginlink<=1;sda_buf<=addr[3];inner_state<=fifth; endfifth:if(phase3) beginlink<=1;sda_buf<=addr[2]; inner_state<=sixth; end sixth:if(phase3) beginlink<=1;sda_buf<=addr[1]; inner_state<=seventh; end seventh:if(phase3) beginlink<=1;sda_buf<=addr[0]; inner_state<=eighth; end eighth:if(phase3) beginlink<=0;inner_state<=ack;endack: beginif(phase0)sda_buf<=sda;if(phase1) beginif(sda_buf==1)main_state<=3'b000;endif(phase3) beginlink<=1;sda_buf<=writeData_reg[7]; inner_state<=first;i2c_state<=write_data;endendendcaseendwrite_data: begin //写入数据case(inner_state) first:if(phase3) beginlink<=1;sda_buf<=writeData_reg[6]; inner_state<=second; endsecond:if(phase3) beginlink<=1;sda_buf<=writeData_reg[5]; inner_state<=third; endthird:if(phase3) beginlink<=1;sda_buf<=writeData_reg[4]; inner_state<=fourth; endfourth:if(phase3) beginlink<=1;sda_buf<=writeData_reg[3]; inner_state<=fifth; endfifth:if(phase3) beginlink<=1;sda_buf<=writeData_reg[2]; inner_state<=sixth; endsixth:if(phase3) beginlink<=1;sda_buf<=writeData_reg[1]; inner_state<=seventh; endseventh:if(phase3) beginlink<=1;sda_buf<=writeData_reg[0]; inner_state<=eighth; endeighth:if(phase3) beginlink<=0;inner_state<=ack;endack: beginif(phase0)sda_buf<=sda;if(phase1) beginif(sda_buf==1)main_state<=2'b00; endelse if(phase3) begin link<=1;sda_buf<=0;inner_state<=stop; endendstop: beginif(phase1)sda_buf<=1;if(phase3)main_state<=2'b00; endendcasedefault:main_state<=2'b00; endcaseend2'b10: begin //读EEPROMif(phase0)scl<=1;else if(phase2)scl<=0;case(i2c_state)ini: begin //初始化EEPROM case(inner_state) start: beginif(phase1) beginlink<=1;sda_buf<=0;endif(phase3&&link) begin inner_state<=first; sda_buf<=1;link<=1;endfirst:if(phase3) beginsda_buf<=0;link<=1;inner_state<=second; end second:if(phase3) beginsda_buf<=1;link<=1;inner_state<=third; end third:if(phase3) beginsda_buf<=0;link<=1;inner_state<=fourth; end fourth:if(phase3) beginsda_buf<=0;link<=1;inner_state<=fifth; end fifth:if(phase3) beginsda_buf<=0;link<=1;inner_state<=sixth; end sixth:if(phase3) beginsda_buf<=0;link<=1;inner_state<=seventh; endseventh:if(phase3) beginsda_buf<=0;link<=1;inner_state<=eighth; end。
基于FPGA的SPI总线接口的实现0 引言串行接口已成为当前传输接口的发展趋势,原因在于串行的高速率传输性能和较简单的线路连接。
在已知的外围器件连接端口中,有USB,wishbone 和并行端口。
其中SPI 接口总线基于串行传输的思想,已经制定成为标准,成为常用的外围器件连接方式。
针对FLASH 这种常用的外围存储器件,有多种接口可供选择,然而具有SPI 接口的FLASH 芯片硬件连接方便,通过FPGA 编程可以便捷地实现FLASH 的存取功能。
因此基于FPGA 的具有SPI 总线接口的FLASH 功能实现为工程设计提供了一种原型,为进一步的工程开发奠定了基础。
1 SPI 总线介绍1.1 SPI 总线简介同步外设接口(serial peripheral,interface,SPI)是由摩托罗拉公司开发的全双工同步串行总线。
SPT 是一种串行同步通信协议,由1 个主设备和1 个或多个从设备组成,主设备启动一个与从设备的同步通信,从而完成数据的交换。
1.2 SPI 总线接口及时序SPI 接口由SDI(串行数据输入),SDO(串行数据输出),SCK(串行移位时钟),CS(从使能信号)四种信号构成,CS 决定了惟一的与主设备通信的从设备,如没有CS 信号,则只能存在一个从设备,主设备通过产生移位时钟来发起通信。
通信时,数据由SDO 输出,SDI 输入,数据在时钟的上升沿或下降沿从SDO 输出,在紧接着的下降沿或上升沿由SDI 读入,这样经过8/16 次时钟改变,完成8/16 位数据的传输。
在SPI 传输中,数据是同步进行发送和接收的。
数据传输的时钟基于来自主处理器的时钟脉冲,摩托罗拉没有定义任何通用SPI 时钟规范。
然而,最常用的时钟设置基于时钟极性(CPOL)和时钟相位(CPHA)两个参数;CPOL 定义SPI 串行时钟的活动状态,而CPHA 定义相对于数据位的时钟相位。
CPOL 和CPHA 的设置决定了数据取样的时钟沿。
使用Verilog来实现EEPROM的读写,进行一个简单的I2C实战应用 I2C在芯片的配置中应用还是很多的,比如摄像头、VGA转HDMI转换芯片,之前博主分享过一篇I2C协议的基础学习IIC协议学习笔记,这篇就使用Verilog来实现EEPROM的读写,进行一个简单的I2C实战应用。
EEPROM 我使用的这个芯片是AT24C32,它手册上还有一种AT24C64,其实操作都是一样的,只是内存大小不同,AT24C32是32k(4096x8)AT24C64是64k(8192x8), SCL设置为频率200Khz SCL clk posedge data输入EEPROM SCL clk negedge data输出EEPROM SDA 双向Pin A2,A1,A0 Device Addr default all 0,只操作一片可悬空引脚。
WP 接地正常读写,WP接Vcc写操作被禁止 字节寻址地址,是由12(AT24C32)或13bit(AT24C64)的地址组成,需要操作16位字地址高3或4位忽略即可。
Device Address 8’hA0写器件地址,8’hA1读器件地址 写字节操作 随机读字节操作 我这个芯片是双字节数据地址,所以在写数据地址时要写两次,先是高字节后是低字节。
开始结束标志 这个I2C总线的时序是一致的。
EEPROM应答 输出应答sclk的第九个周期给出,低电平应答。
如果主机没有收到应答,需要重新配置。
数据传输时序 sda数据线在scl时钟的下降沿中间变化,可以避免产生误触开始结束标志。
I2C Design i2c_start为高电平有效,传输完成后会产生一个i2c_done结束标志,表示操作完成。
I2C状态转移图 I2C写操作 (1)产生start位 (2)传送器件地址ID_Address,器件地址的最后一位为数据的传输方向位,R/W,低电平0表示主机往从机写数据(W),1表示主机从从机读数据(R)。
SPI总线的原理与Verilog设计实现一、软件平台与(硬件)平台软件平台:1、(操作系统):Windows-8.12、开发套件:ISE14.73、(仿真)工具:Model(Sim)-10.4-SE硬件平台:1、(FPGA)型号:Xilinx公司的XC6SLX45-2CSG3242、Flash型号:WinBond公司的W25Q128BV Qual SPI Flash存储器二、原理介绍SPI(Serial Peripheral Interface,串行外围设备(接口)),是Motorola公司提出的一种同步串行(接口技术),是一种高速、全双工、同步(通信)总线,在(芯片)中只占用四根管脚用来控制及数据传输,广泛用于EEP(ROM)、Flash、RTC((实时时钟))、(ADC)((数模转换器))、(DSP)((数字信号)(处理器))以及数字信号解码器上。
SPI通信的速度很容易达到好几兆bps,所以可以用SPI总线传输一些未压缩的(音频)以及压缩的(视频)。
下图是只有2个chip利用SPI总线进行通信的结构图时序图如下所示:从上面的时序图可以很清楚的看出,当ROM的地址加1以后,ROM的数据是滞后了一个时钟才输出的,而ROM数据输出的时刻(这个时候ROM的输出数据并没有稳定)刚好是spi_module模块发送下个数据最高位的时刻,那么这就有可能导致数据发送错误,从以上时序图就可以看出8’h33和8’h24两个数据正确发送了,但是8’h98这个数据就发送错误了。
为了解决这个问题,其实只需要把spi_module模块的发送状态机在加一个冗余状态就行了,spi_module模块的发送状态机一共有0~15总共16个状态,那么我在加一个冗余状态,这个状态执行的操作和最后那个状态执行的操作完全相同,这样就预留了一个时钟的时间用来预先设置好要发送的数据,这样的效果是发送数据的最后一个bit实际上占用了3个时钟周期,其中第一个时钟周期把O_tx_done 拉高,后两个时钟周期把O_tx_done拉低。
SPI串行总线接口的Verilog实现摘要:集成电路设计越来越向系统级的方向发展,并且越来越强调模块化的设计。
SPI(Serial Peripheral Bus)总线是Motorola公司提出的一个同步串行外设接口,容许CPU 与各种外围接口器件以串行方式进行通信、交换信息。
本文简述了SPI总线的特点,介绍了其4条信号线,SPI串行总线接口的典型应用。
重点描述了SPI串行总线接口在一款802.11b芯片中的位置,及该接口作为基带和射频的通讯接口所完成的功能,并给出了用硬件描述语言Verilog HDL 实现该接口的部分程序。
该实现已经在Modelsim 中完成了仿真, 并经过了FPGA 验证, 最后给出了仿真和验证的结果。
在SOC设计中,利用EDA 工具设计芯片实现系统功能已经成为支撑电子设计的通用平台.并逐步向支持系统级的设计方向发展。
而且,在设计过程中,越来越强调模块化设计。
SPI总线是Motorola公司提出的一个同步串行外设接口,具有接口线少、通讯效率高等特点。
本文给出的是利用Verilog HDL实现的SPI总线模块,该模块是802.11b无线局域网芯片中一个子模块,该模块完成了芯片中基带(base band)与RF的通讯工作.1 SPI总线接口概述SPI(Serial Parallel Bus)总线是Motorola公司提出的一个同步串行外设接口,允许CPU 与各种外围接口器件(包括模/数转换器、数/模转换器、液晶显示驱动器等)以串行方式进行通信、交换信息。
他使用4条线:串行时钟线(SCK)、主机输入/从机输出线(MISO)、主机输出/从机输入线(MOSI)、低电平有效的使能信号线(CS)。
这样,仅需3~4根数据线和控制线即可扩展具有SPI接口的各种I/O 器件其典型结构如图1所示。
SPI总线具有以下特点:(1)连线较少,简化电路设计。
并行总线扩展方法通常需要8根数据线、8~16根地址线、2~3根控制线。
FPGA实现SPIFPGA(Field Programmable Gate Array)是一种可编程逻辑器件,可以实现不同的数字电路功能。
SPI(Serial Peripheral Interface)是一种同步串行通信协议,常用于连接外围设备和主控制器。
在本文中,将介绍如何使用FPGA实现SPI。
1.确定硬件资源:首先,需要确定FPGA中可用的IO资源。
SPI需要至少4个IO口,分别是主设备的时钟引脚(SCK),主设备输出的数据引脚(MOSI),主设备输入的数据引脚(MISO)和片选引脚(SS)。
根据所用的FPGA型号,可以查找对应的引脚定义。
2.确定SPI时序:SPI的时序是非常重要的,不同设备可能有不同的时序规范。
一般情况下,SPI的时序包括时钟下降沿数据采样、时钟上升沿数据输出等。
SPI的时序图可以在设备的数据手册中找到。
3. 编写SPI控制器:SPI控制器可以用硬件描述语言如VHDL或Verilog编写。
控制器的功能包括生成时钟、控制数据的发送和接收、以及处理片选信号。
a.时钟生成:SPI通信需要一个时钟信号来驱动数据的传输。
可以通过计数器模块来生成控制器的时钟信号。
计数器的频率一般是SPI时钟频率的若干倍。
b. 数据发送:对于主设备(Master),要发送数据给外设,可以使用移位寄存器(Shift Register)来存储要发送的数据。
可以使用计数器生成移位寄存器的时钟信号,通过串行输入数据,并在时钟的上升沿时将数据发送到MOSI引脚。
c.数据接收:对于主设备,要接收外设发送的数据,可以使用另一个移位寄存器来接收MISO引脚传输的数据。
可以使用计数器生成移位寄存器的时钟信号,通过MISO引脚接收数据,并在时钟的下降沿时将数据存储到接收寄存器。
d.片选控制:SPI通信需要一个片选信号来选择要与主设备通信的外设。
可以通过一个时序控制器实现片选信号的生成。
在与一些外设通信时,使能片选信号,否则禁用片选信号。