SPI总线接口的verilog的实现
- 格式:pdf
- 大小:541.16 KB
- 文档页数:6
十二SPI总线接口的verilog的实现1.实验目的项目中使用的许多器件需要SPI接口进行配置,比如PLL:ADF4350,AD:AD9627,VGA:AD8372等,本实验根据SPI协议,编写了一个简单的SPI读写程序,可以进行32位数据的读写,可以设置SPI SCLK相对于主时钟的分频比。
2.实验原理SPI总线系统是一种同步串行外设接口,它可以使MCU与各种外围设备以串行方式进行通信以交换信息。
外围设置FLASHRAM、网络控制器、LCD显示驱动器、A/D转换器和MCU等。
SPI总线系统可直接与各个厂家生产的多种标准外围器件直接接口,该接口一般使用4条线:串行时钟线(SCK)、主机输入/从机输出数据线MISO、主机输出/从机输入数据线MOST和低电平有效的从机选择线SS(有的SPI接口芯片带有中断信号线INT、有的SPI接口芯片没有主机输出/从机输入数据线MOSI)。
SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4根线,事实上3根也可以(单向传输时)。
也是所有基于SPI的设备共有的,它们是SDI(数据输入),SDO(数据输出),SCK(时钟),CS(片选)。
(1)MOSI –主设备数据输出,从设备数据输入(2)MISO –主设备数据输入,从设备数据输出(3)SCLK –时钟信号,由主设备产生(4)CS –从设备使能信号,由主设备控制其中CS是控制芯片是否被选中的,也就是说只有片选信号为预先规定的使能信号时(高电位或低电位),对此芯片的操作才有效。
这就允许在同一总线上连接多个SPI设备成为可能。
接下来就是负责通讯的3根线了。
通讯是通过数据交换完成的,这里先要知道SPI是串行通讯协议,也就是说数据是一位一位的传输的。
这就是SCK时钟线存在的原因,由SCK 提供时钟脉冲,SDI,SDO则基于此脉冲完成数据传输。
数据输出通过 SDO线,数据在时钟上升沿或下降沿时改变,在紧接着的下降沿或上升沿被读取。
完成一位数据传输,输入也使用同样原理。
这样,至少8次时钟信号的改变(上沿和下沿为一次),就可以完成8位数据的传输。
图2-12-1 SPI的环形总线结构如图2-12-1所示,当第一个上升沿来的时候,SPIDATA1将最高位1移除,并将所有数据左移1位,这时MOSI线为高电平,而SPIDATA2将最高位0移出,并将所有数据左移1位,这样MISO线为低电平。
然后当下降沿到来的时候,SPIDATA1将锁存MISO线上的电平,并将其移入其最低位,同样的,SPIDATA2 将锁存MOSI 线上的电平,并将其移入最低位。
经过8 个脉冲后,两个移位寄存器就实现了数据的交换,也就是完成了一次SPI 的时序。
3.实验内容3.1 spi_master模型设计1) 新建一个模型,名为spi_master,类型为module,具备6 输入1 输出3输入输出,每个引脚的属性和名称如下图2-12-2所示。
图2-12-2 spi_master的引脚属性图2-12-3 spi_master的界面图2) 添加代码。
点击模型下方的Code添加代码。
代码: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:beginout_data = out_buffer;end // read data received by SPI interface2'b01: beginout_data = {31'b0, busy};end // read 'busy' flag of SPI interface2'b10: beginout_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: beginin_buffer = in_data;busy = 1'b1;end //write in_data to SPI Buffer and let 'busy' flag on 2'b10: beginclkdiv = in_data;end //write 'clkdiv' number to SPIendcaseendendelse beginclkcount = clkcount + 1;if(clkcount >= clkdiv) //every clkdiv*period(clk) time send one bit by SPI beginclkcount = 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;3)保存模型(存储文件夹路径不能有空格和中文),运行并检查有无错误输出。
3.2 spi_master_tb测试文件的设计1)新建一个6 输入1 输出3输入输出的spi_master_tb测试文件,记得将Module Type设置为“testbench”,各个引脚配置如图2-12-4所示。
图2-12-4 spi_master_tb的引脚属性2)另存为测试文件。
将测试文件保存到上面创建的模型所在的文件夹下。
3)添加模型。
在Toolbox工具箱的Current栏里,会出现模型,单击该模型并在spi_master_tb 上添加,并连接引脚,如下图2-12-5所示:图2-12-5 spi_master_tb的界面图4) 输入激励。
点击测试模块下方的“Code”,输入激励算法。
激励代码用$finish 结束。
测试代码:integer counter = 0;initial begin // Initaddr = 0;in_data = 0;rd = 0;wr = 0;cs = 0;clk = 0;#20; // 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)beginaddr = 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;rd = 0;end#20 $finish;endalways #10 clk = ~clk;5)执行仿真并查看波形。
查看输出信息。
检查没有错误之后查看波形。
点击右侧Workspace 中的信号,进行添加并查看分析仿真结果。
如图2-12-6所示:图2-12-6 spi_master_tb的仿真波形4.问题与思考本实验只是在SPI协议的基础上编写的一个简单的SPI读写程序,可以进行32位数据的读写,同时可以设置SPI SCLK相对于主时钟的分频比。
在本次试验的基础上尝试设计具备全部功能的MC68HC11A8单片机的SPI接口,具有12种速率选择并支持四种传输模式。