[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通信需要一个片选信号来选择要与主设备通信的外设。
可以通过一个时序控制器实现片选信号的生成。
在与一些外设通信时,使能片选信号,否则禁用片选信号。
用Verilog语言写的CPLD和MCU通讯的SPI接口程序2010-04-12 21:32近日,在调试Altera的MAXII系列的一款CPLD,做了一个SPI接口同MCU通讯,MCU做主机通过SPI对CPLD做读写操作,经过测试验证,效果不错。
程序代码如下,独立模块,可以根据实际应用直接实例化使用。
说明: 代码中的"\* ... *\"注释不能在网页上显示出来,源文件可在链接博客中直接下载.*************************************************************************** spi** Filename : spi.v* Programmer: jose.huang* Project :* Version : V1.0* TOP MODULE: SpiModule.v* Describel : spi总线,数据移位处理模块;* 发送,接收均为8位数据;* 接收时,上升沿移入数据;* 发送时,上升沿来之前放上数据* 用于SPI从动模式* ************************************************************************* Date Comment Author Email TEL* 08-31 original jose.huang work_email@* ------- ----*************************************************************************** module spi (rst,clk,sdi,sdo,sck,cs,OData,IData,ReceiveFlag,TransFlag,TransEndFlag);input rst; // 异步清零input sdi; // spi data inputinput sck; // spi clk, MAX 25MHzinput cs; // spi enableinput clk; // cpld main clk,MIN 50MHzinput[7:0] IData; // Input 8bit Data want to transmit to mcuinput TransFlag; // 发送标志output reg sdo; // spi data outputoutput reg[7:0] OData; // Receive 8bit Data 命令字或数据output reg ReceiveFlag; // 收到8bit Data 标志output reg TransEndFlag; // 发送结束标志reg[2:0] CPLDPort;reg[2:0] TempPort;reg[2:0] BufferPort; // sampling CPLD IO Data to BufferPortreg[7:0] samplnum;always@(posedge clk)beginCPLDPort[0] <= sdi;CPLDPort[1] <= sck;CPLDPort[2] <= cs;endalways@(posedge clk or negedge rst)beginif(!rst) // 异步清零beginsamplnum <= 8'b0;endelse if(TempPort == CPLDPort)beginsamplnum <= samplnum + 1'b1;endelsebeginsamplnum <= 8'b0;TempPort <= CPLDPort;endendalways@(posedge clk or negedge rst)beginif(!rst) // 异步清零beginBufferPort[0] <= 1'b0; //CPLDPort[0] <= sdi; BufferPort[1] <= 1'b0; //CPLDPort[1] <= sck; BufferPort[2] <= 1'b1; //CPLDPort[2] <= cs;endelsebeginif(samplnum > 10) // set by cpld main clk and spi clk beginBufferPort <= TempPort;endelse BufferPort <= BufferPort;endendwire wsdi;wire wsck;wire wcs;assign wsdi = BufferPort[0]; //CPLDPort[0] <= sdi; assign wsck = BufferPort[1]; //CPLDPort[1] <= sck; assign wcs = BufferPort[2]; //CPLDPort[2] <= cs;// 计数reg[7:0] ShiftCounter;reg[7:0] ClrFlagCounter;reg Bwsck;always@(posedge clk or negedge rst or posedge wcs) beginif(!rst) // 异步清零beginShiftCounter <= 8'b0;ReceiveFlag <= 1'b0;TransEndFlag <= 1'b0; // shift状态清接收标志Bwsck <= 1'b0;endelse if(wcs) // 结束异步清零beginShiftCounter <= 8'b0;Bwsck <= 1'b0;if(ClrFlagCounter == 10)beginReceiveFlag <= 1'b0; // SPI操作结束清接收标志TransEndFlag <= 1'b0; // shift状态清接收标志endelse ClrFlagCounter <= ClrFlagCounter + 1'b1;endelsebeginBwsck <= wsck;ClrFlagCounter <= 8'b0;if(wsck && (!Bwsck)) // 上升沿处理beginif(ShiftCounter == 7)beginShiftCounter <= 8'b0;if(!TransFlag) // receive data from mcu ReceiveFlag <= 1'b1; // 收到8bit dataelseTransEndFlag <= 1'b1; // 发送完成endelsebeginShiftCounter <= ShiftCounter + 1'b1;ReceiveFlag <= 1'b0; // shift状态清接收标志TransEndFlag <= 1'b0; // shift状态清接收标志endendendend// 移位always@(posedge clk or negedge rst or posedge wcs) beginif(!rst)sdo <= 1'b0;else if(wcs)sdo <= 1'b0;else if(wsck && (!Bwsck)) //上升沿处理beginif(!TransFlag) // receive data from mcubeginOData <= {OData[6:0],wsdi};endelse // transmit data to mcubeginsdo <= IData[7-ShiftCounter];endendendendmodule波形图。
Verilog--SPI协议Verilog -- SPI协议简介SPI是⼀种全双⼯通信,并且是⼀种同步传输⽅式(slave的接收clk需要master给出)SPI总线是⼀种4线总线,因其硬件功能很强,所以与SPI有关的软件就相当简单,使中央处理器(Central Processing Unit,CPU)有更多的时间处理其他事务。
正是因为这种简单易⽤的特性,越来越多的芯⽚集成了这种通信协议,⽐如AT91RM9200。
SPI是⼀种⾼速、⾼效率的串⾏接⼝技术。
通常由⼀个主模块和⼀个或多个从模块组成,主模块选择⼀个从模块进⾏同步通信,从⽽完成数据的交换。
SPI是⼀个环形结构,通信时需要⾄少4根线(事实上在单向传输时3根线也可以)。
SPI的通信原理很简单,它以主从⽅式⼯作,这种模式通常有⼀个主设备和⼀个或多个从设备,需要⾄少4根线,事实上3根也可以(单向传输时)。
也是所有基于SPI的设备共有的,它们是MISO(主设备数据输⼊)、MOSI(主设备数据输出)、SCLK(时钟)、CS(⽚选)。
(1)MISO– Master Input Slave Output,主设备数据输⼊,从设备数据输出;(2)MOSI– Master Output Slave Input,主设备数据输出,从设备数据输⼊;(3)SCLK – Serial Clock,时钟信号,由主设备产⽣;(4)CS – Chip Select,从设备使能信号,由主设备控制。
其中,CS是从芯⽚是否被主芯⽚选中的控制信号,也就是说只有⽚选信号为预先规定的使能信号时(⾼电位或低电位),主芯⽚对此从芯⽚的操作才有效。
这就使在同⼀条总线上连接多个SPI设备成为可能。
(以上来⾃百度百科)SPI最⼤传输速率SPI是⼀种事实标准,由Motorola开发,并没有⼀个官⽅标准。
已知的有的器件SPI已达到50Mbps。
具体到产品中SPI的速率主要看主从器件SPI控制器的性能限制。
电子与电气工程系课程设计、专题(综合)实验报告课题名称__串行接口IP核的设计与验证(spi)_专业____ 电子信息工程________班级_____ 08电子1班__________学号__0806012103_ 0806012104_姓名___ 高江柯____吴冠雄__ ______成绩________________________指导教师_______袁江南____________2011年 6 月 15 日串行接口IP核的设计与验证(SPI)(FPGA作为主机)一、实验目的:通过本实验的学习,使学生掌握使用VHDL 设计一个实用数字系统的能力,以及单片机串行接口编程等知识,训练 VHDL以及单片机的编程与综合使用能力,培养工程设计的基本技能,为今后毕业设计以及实际工作奠定基础。
二、实验原理SPI 接口是在CPU 和外围低速器件之间进行同步串行数据传输,在主器件的移位脉冲下,数据按位传输,高位在前,地位在后,为全双工通信,数据传输速度总体来说比I2C 总线要快,速度可达到几Mbps。
SPI 接口是以主从方式工作的,这种模式通常有一个主器件和一个或多个从器件,其接口包括以下四种信号:(1)MOSI –主器件数据输出,从器件数据输入(2)MISO –主器件数据输入,从器件数据输出(3)SCLK –时钟信号,由主器件产生(4)/CS –从器件使能信号,由主器件控制在点对点的通信中,SPI 接口不需要进行寻址操作,且为全双工通信,显得简单高效。
SPI接口在内部硬件实际上是两个简单的移位寄存器,传输的数据为8位,在主器件产生的从器件使能信号和移位脉冲下,按位传输,高位在前,低位在后。
下图所示,在SCLK 的下降沿上数据改变,同时一位数据被存入移位寄存器。
三、实验步骤:1、查找关于SPI的资料,认识理解SPI。
2、根据SPI传输数据的时序图构建出SPI的框图。
3、利用自顶向下的方法根据框图分模块进行程序的编写。
FPGA作为从机与STM32进行SPI协议通信Verilog实现一.SPI协议简要介绍SPI,是英语Serial Peripheral Interface的缩写,顾名思义就是串行外围设备接口。
SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议。
SPI总线是Motorola公司推出的三线同步接口,同步串行3线方式进行通信:一条时钟线SCK,一条数据输入线MOSI,一条数据输出线MISO;用于 CPU 与各种外围器件进行全双工、同步串行通讯。
SPI主要特点有:可以同时发出和接收串行数据;可以当作主机或从机工作;提供频率可编程时钟;发送结束中断标志;写冲突保护;总线竞争保护等。
SPI总线有四种工作方式(SP0, SP1, SP2, SP3),其中使用的最为广泛的是SPI0和SPI3方式。
SPI模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以进行配置,时钟极性(CPOL)对传输协议没有重大的影响。
如果CPOL=0,串行同步时钟的空闲状态为低电平;如果CPOL=1,串行同步时钟的空闲状态为高电平。
时钟相位(CPHA)能够配置用于选择两种不同的传输协议之一进行数据传输。
如果 CPHA=0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如果CPHA=1,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样。
SPI主模块和与之通信的外设时钟相位和极性应该一致。
以下是SPI时序图:主要讲解一下广泛使用的两种方式设置:SPI0方式:CPOL=0,CPHA=0;SCK空闲状态为低电平,第一个跳变沿(上升沿)采样数据,无论对Master还是Slaver都是如此。
SPI3方式:CPOL=1,CPHA=1;SCK空闲状态为高电平,第二个跳变沿(上升沿采样数据,无论对Master还是Slaver都是如此。
基于FPGA的SPI接口设计SPI是一种在FPGA和其他芯片之间传输数据的简单有效的接口方式。
SPI项目第一部分:什么是SPI第二部分:SPI的简单实现第三部分:应用第一部分:什么是SPISPI是允许一个器件同其他一个或多个器件进行通讯的简单接口。
SPI是什么样的?首先让我们来看看两个芯片之间的SPI接口是如何连接的。
在两个芯片时间通讯时,SPI需要4条连线。
正如你所看到的,他们是SCK、MISO、MOSI以及SSEL。
其中一个芯片叫做主控芯片,另一个叫从芯片。
SPI基础基本特点:1.同步2.串行3.全双工4.非即插即用5.一主多从更多细节:1.同步时钟有主控芯片产生,每个时钟传输一位数据2.数据在传输前,首先许要进行并转串,才能用一条线传输3.两条数据线,一条输入、一条输出4.主从双方有关于SPI传输的先验知识,如比特顺序、数据长度等5.数据传输有主控芯片发起,每次只与一个从芯片通讯SPI是一种同步全双工的通讯接口,每个时钟在两条数据线上各传输一比特数据。
简单的传输假设在主从芯片之间进行的是8位长度的,高位数据在前的SPI传输,则单个字节的传输在波形上看起来是这样的。
MOSI是主输出线,而MISO则是从输出线。
由于SPI是全双工的,所以在时钟沿上两条线同时传输数据。
MOSI将数据从主控芯片传输至从芯片,MISO则将从芯片的数据传输到主控芯片。
详细的说是这样的:1,首先主控芯片使能相应的SSEL信号,通知相应的从芯片数据传输要开始了;2,主控芯片产生8个SPI时钟周期,并将数据在每个时钟沿发送出去,同时从芯片在也每个时钟沿将数据发送到MISO线上。
3,主控芯片撤销SSEL信号,一次SPI传输结束多个从芯片的情况通过扩展SSEL信号,一个主控芯片可以和多个从芯片进行SPI通讯。
下图是有三个从芯片的情况:主控芯片有3条SSEL线,每次只使能条,和其中一个从芯片进行SPI通讯。
由于所有芯片的MISO都连接在一起,所以不允许同时有多个从芯片驱动MISO线。
SPI接口的verilog实现SPI接口的verilog实现项目中使用的许多器件需要SPI接口进行配置,比如PLL:ADF4350,AD:AD9627,VGA:AD8372等,根据SPI协议,站长编写了一个简单的SPI读写程序,可以进行32为数据的读写(读者可以修改程序中数字使其变成16位或8位读写,也可以将读写位数参数化),可以设置SPI SCLK 相对于主时钟的分频比。
【SPI程序】/* SPI interface module V1.0 一些语句做了简单的英文注释*/ module spi_master(addr, in_data, out_data, rd, wr, cs, clk, miso, mosi, sclk);input wire [1:0] addr;input wire [31:0] in_data;output reg [31:0] out_data;input wire rd;input wire wr;input wire cs;input wire clk;inout miso;inout mosi;inout sclk;reg sclk_buffer = 0;reg mosi_buffer = 0;reg busy = 0;reg [31:0] in_buffer = 0;reg [31:0] out_buffer = 0;reg [7:0] clkcount = 0;reg [7:0] clkdiv = 0;reg [6:0] count = 0;always@(cs or rd or addr or out_buffer or busy or clkdiv)beginout_data = 32'bx;if(cs && rd)//selected and readbegincase(addr)2'b00: begin out_data = out_buffer; end // read data received by SPI interface2'b01: begin out_data = {31'b0, busy}; end // read 'busy' flag of SPI interface2'b10: begin out_data = clkdiv; end // read 'clkdiv' number of SPIendcaseendendalways@(posedge clk)beginif(!busy) //SPI interface is not busybeginif(cs && wr) //selected and writebegincase(addr)2'b00: begin in_buffer = in_data; busy = 1'b1; end //write in_data to SPI Buffer and let 'busy' flag on 2'b10: begin clkdiv = in_data; end //write 'clkdiv' number to SPIendcaseendendelsebeginclkcount = clkcount + 1;if(clkcount >= clkdiv) //every clkdiv*period(clk) time send one bit by SPIbeginclkcount = 0;if((count % 2) == 0) // change data in negtive sclkbeginmosi_buffer = in_buffer[31];in_buffer = in_buffer << 1;endif(count > 0 && count < 65) //32 periodsbeginsclk_buffer = ~sclk_buffer;endcount = count + 1;if(count > 65)begincount = 0;busy = 1'b0;endendendendalways@(posedge sclk_buffer)beginout_buffer = out_buffer << 1;out_buffer[0] = miso; //read data from pin 'miso'endassign sclk = sclk_buffer;assign mosi = mosi_buffer;endmodule【SPI程序testbench】/*SPI module testbench V1.0Loujianquan2009.7.20*/`timescale 1ns/1nsmodule spi_master_tb();reg [1:0] addr;reg [31:0] in_data;wire [31:0] out_data;reg rd;reg wr;reg cs;reg clk;tri1 miso;wire mosi;wire sclk;spi_master uut(addr, in_data, out_data, rd, wr, cs, clk, miso, mosi, sclk); integer counter = 0;initialbegin// Initaddr = 0;in_data = 0;rd = 0;wr = 0;cs = 0;clk = 0;// Set CLK_DIVaddr = 2;in_data = 0;wr = 1;cs = 1;#20;addr = 0;in_data = 0;wr = 0;cs = 0;#20;// Output from 0 to 255for(counter = 0; counter < 256; counter = counter + 1) begin addr = 0;in_data = counter;wr = 1;cs = 1;#20;addr = 0;in_data = 0;wr = 0;cs = 0;#20;// Poll Busy signaladdr = 1;cs = 1;rd = 1;#20;while(out_data[0] == 1'b1)begin#20;endcs = 0;#20;end$stop;endalways begin clk = ~clk; #10; end endmodule 【仿真波形】ISE仿真结果。
/****************************************************************************** ****************** SPI MASTER* January 2007******************************************************************************* *****************/`timescale 10ns/1nsmodule SPI_Master ( miso, mosi, sclk, ss, data_bus, CS, addr, pro_clk, WR, RD);inout [7:0] data_bus; // 8 bit bidirectional data businput pro_clk; // Host Processor clockinput miso; // Master in slave outinput [1:0] addr; // A1 and A0, lower bits of address businput CS; // Chip Selectinput WR, RD; // Write and read enablesoutput mosi; // Master out slave inoutput sclk; // SPI clockoutput [7:0] ss; // 8 slave select linesreg [7:0] shift_register; // Shift registerreg [7:0] txdata; // Transmit bufferreg [7:0] rxdata; // Receive bufferreg [7:0] data_out; // Data output registerreg [7:0] data_out_en; // Data output enablereg [7:0] control, status; // Control Register COntrols things like ss, CPOL, CPHA, clock divider// Status Register is a dummy register never used.reg [7:0] clk_divide; // Clock divide counterreg [3:0] count; // SPI word length counterreg sclk;reg slave_cs; // Slave cs flagreg mosi; // Master out slave inreg spi_word_send; // Will send a new spi word.wire [7:0] data_bus;wire [7:0] data_in = data_bus;wire spi_clk_gen;wire [2:0] divide_factor = control[2:0];wire CPOL = control[3];wire CPHA = control[4];wire [7:0]ss;/* Slave Select lines */assign ss[7] = ~( control[7] & control[6] & control[5] & (~slave_cs)); assign ss[6] = ~( control[7] & control[6] & ~control[5] & (~slave_cs)); assign ss[5] = ~( control[7] & ~control[6] & control[5] & (~slave_cs)); assign ss[4] = ~( control[7] & ~control[6] & ~control[5] & (~slave_cs)); assign ss[3] = ~(~control[7] & control[6] & control[5] & (~slave_cs)); assign ss[2] = ~(~control[7] & control[6] & ~control[5] & (~slave_cs)); assign ss[1] = ~(~control[7] & ~control[6] & control[5] & (~slave_cs)); assign ss[0] = ~(~control[7] & ~control[6] & ~control[5] & (~slave_cs));/* clock divide */assign spi_clk_gen = clk_divide[divide_factor];/* Clock Divider */always @ (negedge pro_clk) beginclk_divide = clk_divide + 1;end/* Reading the miso line and shifting */always @ (posedge (sclk ^ (CPHA ^ CPOL)) or posedge spi_word_send) begin if (spi_word_send) beginshift_register[7:0] = txdata;end else beginshift_register = shift_register << 1;shift_register[0] <= miso;endend/* Writing the mosi */always @ (negedge (sclk ^ (CPHA ^ CPOL)) or posedge spi_word_send) begin if (spi_word_send) beginmosi = txdata[7];end else beginmosi = shift_register[7];endend/* Contolling the interrupt bit in the status bit */always @ (posedge slave_cs or posedge spi_word_send) begin if (spi_word_send) beginstatus[0] = 0;end else beginstatus = 8'h01;rxdata = shift_register; // updating read buffer endend/* New SPI wrod starts when the transmit buffer is updated */ always @ (posedge pro_clk) beginif (spi_word_send) beginslave_cs <= 0;end else if ((count == 8) & ~(sclk ^ CPOL)) beginslave_cs <= 1;endend/* New Spi word is intiated when transmit buffer is updated */ always @ (posedge pro_clk) beginif (CS & WR & addr[1] & ~addr[0]) beginspi_word_send <=1;end else beginspi_word_send <=0;endend/* Generating the SPI clock */always @ (posedge spi_clk_gen) beginif (~slave_cs) beginsclk = ~sclk;end else if (~CPOL) beginsclk = 0;end else beginsclk = 1;endend/* Counting SPI word length */always @ (posedge sclk or posedge slave_cs) beginif (slave_cs) begincount = 0;end else begincount = count + 1;endend/* Reading, writing SPI registers */always @ (posedge pro_clk) beginif (CS) begincase (addr)2'b00 : if (WR) control <= data_in;2'b01 : if (RD) data_out <= status; // Void2'b10 : if (WR) txdata <= data_in;2'b11 : if (RD) data_out <= rxdata;endcaseendend/* Controlling the data out enable */always @ (RD or data_out) beginif (RD)data_out_en = data_out;elsedata_out_en = 8'bz;endassign data_bus = data_out_en;initialbeginmosi = 0;//sclk = 0;control = 0;count = 0;slave_cs = 1;txdata = 0;rxdata = 0;clk_divide = 0;data_out = 0;endendmodule/********************************************** END ******************************************************************/。