[FPGA][Verilog][SPI]简单的读写SPI接口EEPROM-93C46程序
EEPROM_SPI

EEPROM和SPI的使用
目标: 1、能熟练使用EEPROM数写数据 2、能熟练使用SPI接口实现数据的传输
EEPROM&&SPI
任务1:在EEPROM任意位置写入一个数据,再读取写入的数据 思考1:EEPROM是什么?PIC中的EEPROM有多大? 步骤:1、上网查阅资料 2、查阅数据手册 EEPROM (Electrically Erasable Programmable Read-Only Memory),电 可擦可编程只 读存储器--一 种掉电后数据 不丢失的存储 芯片。
• 4、WR=1 //启动写操作
EEPROM&&SPI
读数据
• void EEPROM_R() • { • EEADR=0X01; //地址 • RD=1; //使能读操作 • SHU=EEDATA;//读数据 • }
EEPROM&&SPI
任务2:连续写入200个数据,读取任意位置的数据
void EEPROM_R() { Unsigend char i; for(i=0;i<20;i++) { EEADR=0X01; RD=1; SHU[i]=EEDATA; } }
EEPROM&&SPI
时钟 数据输出
EEPROM&&SPI
• SPI初始化 • void spi_initatal() • {
SSPSTAT=0XC0; //时钟下降沿发送数据 SSPCON1=0X30;//SSPEEEPROM&&SPI
发送数据
• SSPBUF= data; //发送数据 • do { • ; • }while(SSPIF==0); //等待发送完毕 • SSPIF=0;
FPGA读写EEPROM

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总线接口的实现

基于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实战应用

使用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)。
使用Verilog来实现EEPROM的读写,进行一个简单的I2C实战应用

这个 I2C 总线的时序是一致的。 EEPROM 应答 输出应答 sclk 的第九个周期给出,低电平应答。如果主机没有收到应答, 需要重新配置。 数据传输时序 sda 数据线在 scl 时钟的下降沿中间变化,可以避免产生误触开始结束标 志。 I2C Design i2c_start 为高电平有效,传输完成后会产生一个 i2c_done 结束标志,表示 操作完成。 I2C 状态转移图 I2C 写操作
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 读器件地址 写字节操作 随机读字节操作 我这个芯片是双字节数据地址,所以在写数据地址时要写两次,先是高字 节后是低字节。 开始结束标志
(1)产生 start 位 (2)传送器件地址 ID_Address,器件地址的最后一位为数据的传输方向 位,R/W,低电平 0 表示主机往从机写数据(W),1 表示主机从从机读数据 (R)。这里按照手册给出的操作图,应该是 W 即低电平。ACK 应答,应答 是从机发送给主机的应答,这里不用管。 (3)传送写入器件寄存器地址,即数据要写入的位置。同样 ACK 应答 不用管。 (4)传送要写入的数据。ACK 应答不用管。 (5)产生 stop 信号。 I2C 读操作 (1)产生 start 信号
其他部分没啥好说的根据时序图写就行了,需要注意的一点是我们应该在 sclk 的高电平的中间采样数据,在 sclk 低电平的中间改变数据,当 sclk 为高 电平的时候,sda 为出现下降沿为 start 位, sda 出现上升沿为 stop 位,所以 在 sclk 为高电平的时候 sda 应该保持稳定不能随意乱动。这就又回到了数据 传输有效的条件,只有在 sclk 为低电平期间,才允许数据变化,在高电平期 间,不允许数据变化,否则就会出现起始位或结束位。 EEPROM 有个仿真模型,在夏雨闻老师的书里面就有,这个模型默认是 200khz 的 sclk 驱动,仿真的时候可以将时间参数改小,我这里也分享出来。 仿真模型代码点击阅读原文可以查看。 根据仿真模型仿真的话基本不会有什幺问题,需要注意的是操作的完成标 志。从仿真上看到输入读写都没问题,但是 stop 标志没产生好,仿真看到读 写操作没问题,但实际还是不行的,需要严格按照 EEPROM 的手册操作时序 进行,差一点就不行。 我最后使用拨码开关作为读写使能,数码管显示读出来的输出,最后实现 了对指定存储地址读写数据。
[FPGA][Verilog][SPI]简单的读写SPI接口EEPROM-93C46程序
![[FPGA][Verilog][SPI]简单的读写SPI接口EEPROM-93C46程序](https://img.taocdn.com/s3/m/1221e485ec3a87c24028c428.png)
Write19: begin mo <= 0; cs <= 0; end
Read0: cs <= 0; Read1://110+add(7bit) begin cs <= 1; mo <= 1; end Read2: mo <= 1; Read3: mo <= 0;//110 Read4: mo <= 0; Read5: mo <= 1; Read6: mo <= 1; Read7: mo <= 1; Read8: mo <= 1; Read9: mo <= 1; Read10: mo <= 1; Read11: begin mo <= 0; led[7] <= mi; end Read12: led[7] <= mi; Read13: led[6] <= mi; Read14: led[5] <= mi; Read15:
从开始读数据手册,到研究时序,到编写 Verilog 程序,到仿真调试时序,整整 花了有 3-4 天时间。 最后时序已经完全正确, 却读不出任何数据,经过一个晚上的排查才发现是开发 板上的 DI DO SK CS 标号标错了,泪奔~~ 本来我想写一个完整的 SPI 接口出来,想了几天都没有头绪,最后还是写了一个 最简单的写数据读数据的小程序,如果做成接口也勉强可以用。 程序的功能很简单,往地址 0111111 的位置写了 00001111 的数据,地址都还没 有做成接口,固定在程序里面的。 具体用了一个状态机共 53 个状态,每一个状态都是一个 SCK 信号的处理,当然 有分为三个大状态,分别为 ENWR、WRITE、READ 93C46 要首先写 ENWR 信号才能写入数据,具体还得研究数据手册 通过这次 93C46 和上次写 18B20 的经历, 我感觉到数据手册的确是相当的重要的, 需要仔细推敲,分析每一个时序图!下次要做 I2C 接口的 24C02,1、2、3 线就 都学过拉。 当然作为初学者程序是写的那是超级的烂,欢迎拍砖
SPI总线的原理与Verilog设计实现

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串行总线接口的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实现SPI

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通信需要一个片选信号来选择要与主设备通信的外设。
可以通过一个时序控制器实现片选信号的生成。
在与一些外设通信时,使能片选信号,否则禁用片选信号。
SPI协议的Verilog 实现

// //接收数据存入 out_data
6
begin case(addr) 1'b0: begin in_buffer = in_data; busy = 1'b1; end 转入工作状态 1'b1: begin busy = 1'b0;end endcase end end else begin if(cs) begin clkcount = clkcount + 1'b1; if(clkcount >= 8'b10) // 控制 SCK 周期 begin clkcount = 0; if((count % 2) == 0) //待发数据存入缓存区,
DataPort[5]-DataPort[0] : busy、sdo 、addr、cs、wr、rd DataPort[21]-DataPort[14] :待发送 8bit 数据 DataPort[13]-DataPort[6] : 接收的 8bit 数据
3
� 总结
完成时间:12.4--12.12 前期:该阶段主要是熟悉 SPI 工作原理,进一步掌握和认识 SPI 通信协议。Chipscore 的使 用之前未曾接触, 在这一阶段, 我先通过简单编程结合开发板抓取数据进行分析达到 对其的基本掌握。 中期:在熟悉 SPI 的工作原理之后,开始尝试编写代码。写了两三次代码,效果均不理想, 很多问题在编写代码的时候没有考虑清楚,导致到了仿真阶段结果与预期有所差距, 且代码冗长复杂。参考了一些资料,效果也不是很好,特别是数据传输暂停部分, 很 多都省略了。不过借鉴别人写的代码也让我收获了不少编写的经验,有些情况下, 运 用不同的逻辑思维可以让代码更简洁、 更具有健壮性。 当然期间也遇到了一些自己无 法解决的问题,非常感谢福星学长耐心的指导,让我学到了不少知识和经验。 后期:该阶段主要是对代码进行再修改、波形仿真以及抓数据调试。 问题及分析: 小问题遇到不少,不过大多都通过 error 的提示,或者上网搜索,找到了问题的原因, 并予以解决。也有软件上的原因,比如:第一次装 ISE 的时候可能没有安装好,上板调试的 时候,cable 不能识别。经过测试分析发现 ISE 里的 drive 没有装上。考虑到这样一个问题的 出现可能还会附带有一些软件上的漏洞,重装了一遍 ISE,问题解决。 使用 chipscope 的时候,芯片配置不对连接失败,查阅该电路板的资料,重新配置,问 题解决。在 chipscope 里面有些触发信号找不到,经分析是被优化了,通过简单修改代码避 免它被优化后,问题解决。运行 chipscope 后发现 waveform 始终没反应,经过一番分析, 认 为时钟线的引脚配置有问题,重换一个时钟信号线 I/O 引脚,问题解决。解决后发现抓取的 波形没有明显的高低跳变,经分析可能是参考时钟选取不对,重选后问题解决。
用Verilog语言写的CPLD和MCU通讯的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协议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控制器的性能限制。
SPI接口的Verilog语言实现

电子与电气工程系课程设计、专题(综合)实验报告课题名称__串行接口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、利用自顶向下的方法根据框图分模块进行程序的编写。
Verilog实现FPGA作为从机与STM32进行SPI协议通信

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接口设计

基于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接口的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接口的verilog实现

/****************************************************************************** ****************** 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 ******************************************************************/。
基于verilog的SPI设计

武汉理工大学本科学生毕业设计(论文)开题报告目录摘要 (I)Abstract (II)1 绪论 (3)1.1课题研究背景 (3)1.2 SPI研究目的及意义 (4)1.3 本章小结 (4)2 SPI原理分析 (5)2.1 SPI介绍 (5)2.2 SPI工作模式 (6)2.3 SPI传输模式 (6)2.4 SPI协议 (7)2.5 本章小结 (8)3 方案论证 (10)3.1在51系列单片机系统中实现 (10)3.2 用可编程逻辑器件设计SPI (11)3.3 本章小结 (11)4 SPI的电路设计 (12)4.1 SPI设计系统的功能 (12)4.2 SPI各部分具体实现 (12)4.2.2 SPI系统中所用的寄存器 (13)4.2.3 SPI速率控制 (14)4.2.4 SPI控制状态机 (14)4.2.5 SPI程序设计流程图 (15)4.3 SPI仿真及开发板上调试验证分析 (16)4.3.1 仿真分析 (16)4.3.2开发板上调试 (18)4.4 本章小结 (20)5 论文总结 (21)致谢 (22)参考文献 (23)附录1 (24)附录2 (28)摘要随着专用集成电路(ASIC)设计技术的进步以及超大规模集成电路(VLSI)工艺技术的飞速发展,以及其价格的日益降低,采用FPGA编程的硬件电路来实现诸如SPI接口也日益切实可行,相对软件实现具有更好的优点。
SPI接口是一种常用的标准接口,由于其使用简单方便且节省系统资源,很多芯片都支持该接口,SPI接口主要应用在EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间等等。
由于SPI接口是一种事实标准,并没有标准协议,大部分厂家都是参照Motorola的SPI接口定义来设计的,但正因为没有确切的版本协议,不同厂家产品的SPI接口在技术上存在一定的差别,容易引起歧义,有的甚至无法互联(需要用软件进行必要的修改)。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
盯着这张鬼时序图看了一天,唉,累 //=============================================================== module spi93c46 (rst, clk, sck, mo, mi, cs, led, write, read); input rst, clk, mi, write, read; output sck, cs, mo; output [7:0] led; reg [7:0] led; reg cs; reg mo; assign sck = clk;
Write19: begin mo <= 0; cs <= 0; end
Read0: cs <= 0; Read1://110+add(7bit) begin cs <= 1; mo <= 1; end Read2: mo <= 1; Read3: mo <= 0;//110 Read4: mo <= 0; Read5: mo <= 1; Read6: mo <= 1; Read7: mo <= 1; Read8: mo <= 1; Read9: mo <= 1; Read10: mo <= 1; Read11: begin mo <= 0; led[7] <= mi; end Read12: led[7] <= mi; Read13: led[6] <= mi; Read14: led[5] <= mi; Read15:
= Read16; = Read17; = Read18; = Read19; = Idle;
//第三个进程,同步时序 always 模块,格式化描述次态寄存器输出 always @ (negedge clk) case(next_state) Idle: cs <= 0; Ewen0: cs <= 0; Ewen1: begin cs <= 1; mo <= 1; end Ewen2: mo <= 0; Ewen3: mo <= 0; Ewen4: mo <= 1; Ewen5: mo <= 1;//以下四个为无用信号 Ewen6: mo <= 0; Ewen7: mo <= 0; Ewen8: mo <= 0; Ewen9: mo <= 0; Ewen10: cs <= 0;
从开始读数据手册,到研究时序,到编写 Verilog 程序,到仿真调试时序,整整 花了有 3-4 天时间。 最后时序已经完全正确, 却读不出任何数据,经过一个晚上的排查才发现是开发 板上的 DI DO SK CS 标号标错了,泪奔~~ 本来我想写一个完整的 SPI 接口出来,想了几天都没有头绪,最后还是写了一个 最简单的写数据读数据的小程序,如果做成接口也勉强可以用。 程序的功能很简单,往地址 0111111 的位置写了 00001111 的数据,地址都还没 有做成接口,固定在程序里面的。 具体用了一个状态机共 53 个状态,每一个状态都是一个 SCK 信号的处理,当然 有分为三个大状态,分别为 ENWR、WRITE、READ 93C46 要首先写 ENWR 信号才能写入数据,具体还得研究数据手册 通过这次 93C46 和上次写 18B20 的经历, 我感觉到数据手册的确是相当的重要的, 需要仔细推敲,分析每一个时序图!下次要做 I2C 接口的 24C02,1、2、3 线就 都学过拉。 当然作为初学者程序是写的那是超级的烂,欢迎拍砖
//状态机 parameter Idle = 32'd1, Ewen0=32'd10, Ewen1=32'd11, Ewen2=32'd12, Ewen3=32'd13, Ewen4=32'd14, Ewen5=32'd15, Ewen6=32'd16,Ewen7=32'd17, Ewen8=32'd18, Ewen9=32'd19, Ewen10=32'd110, Ewen11=32'd111, Write0=32'd20,Write1=32'd21,Write2=32'd22,Write3=32'd23,Write4 =32'd24, Write5=32'd25,Write6=32'd26,Write7=32'd27,Write8=32'd28,Write9 =32'd29, Write10=32'd120,Write11=32'd121,Write12=32'd122,Write13=32'd12 3,Write14=32'd124, Write15=32'd125,Write16=32'd126,Write17=32'd127,Write18=32'd12 8,Write19=32'd129,
Ewen11: cs <= 0; Write0: cs <= 0; Write1: begin cs <= 1; mo <= 1; end Write2: mo <= 0; Write3: mo <= 1;//101+add(7bit) Write4: mo <= 0; Write5: mo <= 1; Write6: mo <= 1; Write7: mo <= 1; Write8: mo <= 1; Write9: mo <= 1; Write10: mo <= 1; Write11: //data 8bit mo <= 0; Write12: mo <= 0; Write13: mo <= 0; Write14: mo <= 0; Write15: mo <= 1; Write16: mo <= 1; Write17: mo <= 1; Write18: mo <= 1;
led[4] <= Read16: led[3] <= Read17: led[2] <= Read18: led[1] <= Read19: begin led[0] <= cs <= 0; end endcase endmodule
mi; mi; mi; mi;
mi;
Read0: next_state Read1: next_state Read2: next_state Read3: next_state Read4: next_state Read5: next_state Read6: next_state Read7: next_state Read8: next_state Read9: next_state Read10: next_state Read11: next_state Read12: next_state Read13: next_state Read14: next_state Read15:
Read0=32'd30, Read1=32'd31, Read2=32'd32, Read3=32'd33, Read4=32'd34, Read5=32'd35, Read6=32'd36, Read7=32'd37, Read8=32'd38, Read9=32'd39, Read10=32'd130, Read11=32'd131, Read12=32'd132, Read13=32'd133, Read14=32'd134, Read15=32'd135, Read16=32'd136, Read17=32'd137, Read18=32'd138, Read19=32'd139; reg[31:0] current_state; reg[31:0] next_state; //第一个进程 always @(posedge clk ) if(rst) current_state <= Idle; else current_state <= next_state;//注意,使用的是非阻塞赋值
= Ewen6; = Ewen7; = Ewen8; = Ewen9; = Ewen10; = Ewen11; = Write0;
= Write1; = Write2; = Write3; = Write4; = Write5; = Write6; = Write7; = Write8; = Write9; = Write10; = Write11; = Write12; = Write13; = Write14;
= Ewen1; = Ewen2; = Ewen3; = Ewen4; = Ewen5;
Ewen5: next_state Ewen6: next_state Ewen7: next_state Ewen8: next_state Ewen9: next_state Ewen10: next_state Ewen11: next_state Write0: next_state Write1: next_state Write2: next_state Write3: next_state Write4: next_state Write5: next_state Write6: next_state Write7: next_state Write8: next_state Write9: next_state Write10: next_state Write11: next_state Write12: next_state Write13: next_state Write14:
next_state Write15: next_state Write16: next_state Write17: next_state Write18: next_state Write19: next_state