精华内容
下载资源
问答
  • spi时序

    2020-04-26 23:58:01
    SPI时序总结 ​ 关于具体的在fpga上实现spi传输时序的原理部分各种论坛文章已经记录的很清楚了,但我发现无论是我手里黑金开发板送的例程还是网上能搜到的都不是很全面。我自己结合了黑金的例程和CSDN上某位博主的...

    SPI时序总结

    ​ 关于具体的在fpga上实现spi传输时序的原理部分各种论坛文章已经记录的很清楚了,但我发现无论是我手里黑金开发板送的例程还是网上能搜到的都不是很全面。我自己结合了黑金的例程和CSDN上某位博主的帖子写了一个相对效率和代码可读性来说都相对较高的一个SPI时序电路。

    代码解读

    SPI时序最普遍的方法是状态机+移位寄存器,黑金例程采用的是一段式状态机。

    case(state)
    		IDLE:
    			if(wr_req == 1'b1)
    				next_state <= DCLK_IDLE;
    			else
    				next_state <= IDLE;
    		DCLK_IDLE:
    			//half a SPI clock cycle produces a clock edge
    			if(clk_cnt == clk_div)
    				next_state <= DCLK_EDGE;
    			else
    				next_state <= DCLK_IDLE;
    		DCLK_EDGE:
    			//a SPI byte with a total of 16 clock edges
    			if(clk_edge_cnt == 5'd15)
    				next_state <= LAST_HALF_CYCLE;
    			else
    				next_state <= DCLK_IDLE;
    		//this is the last data edge		
    		LAST_HALF_CYCLE:
    			if(clk_cnt == clk_div)
    				next_state <= ACK;
    			else
    				next_state <= LAST_HALF_CYCLE; 
    		//send one byte complete		
    		ACK:
    			next_state <= ACK_WAIT;
    		//wait for one clock cycle, to ensure that the cancel request signal
    		ACK_WAIT:
    			next_state <= IDLE;
    		default:
    			next_state <= IDLE;
        
    always@(*)
    begin
    	case(state)
    		IDLE:
    			if(wr_req == 1'b1)
    				next_state <= DCLK_IDLE;
    			else
    				next_state <= IDLE;
    		DCLK_IDLE:
    			//half a SPI clock cycle produces a clock edge
    			if(clk_cnt == clk_div)
    				next_state <= DCLK_EDGE;
    			else
    				next_state <= DCLK_IDLE;
    		DCLK_EDGE:
    			//a SPI byte with a total of 16 clock edges
    			if(clk_edge_cnt == 5'd15)
    				next_state <= LAST_HALF_CYCLE;
    			else
    				next_state <= DCLK_IDLE;
    		//this is the last data edge		
    		LAST_HALF_CYCLE:
    			if(clk_cnt == clk_div)
    				next_state <= ACK;
    			else
    				next_state <= LAST_HALF_CYCLE; 
    		//send one byte complete		
    		ACK:
    			next_state <= ACK_WAIT;
    		//wait for one clock cycle, to ensure that the cancel request signal
    		ACK_WAIT:
    			next_state <= IDLE;
    		default:
    			next_state <= IDLE;
    	endcase
    end
    
    always@(posedge sys_clk or posedge rst)
    begin
    	if(rst)
    		DCLK_reg <= 1'b0;
    	else if(state == IDLE)
    		DCLK_reg <= CPOL;
    	else if(state == DCLK_EDGE)
    		DCLK_reg <= ~DCLK_reg;//SPI clock edge
    end
    //SPI clock wait counter
    always@(posedge sys_clk or posedge rst)
    begin
    	if(rst)
    		clk_cnt <= 16'd0;
    	else if(state == DCLK_IDLE || state == LAST_HALF_CYCLE) 
    		clk_cnt <= clk_cnt + 16'd1;
    	else
    		clk_cnt <= 16'd0;
    end
    //SPI clock edge counter
    always@(posedge sys_clk or posedge rst)
    begin
    	if(rst)
    		clk_edge_cnt <= 5'd0;
    	else if(state == DCLK_EDGE)
    		clk_edge_cnt <= clk_edge_cnt + 5'd1;
    	else if(state == IDLE)
    		clk_edge_cnt <= 5'd0;
    end
    //SPI data output
    always@(posedge sys_clk or posedge rst)
    begin
    	if(rst)
    		MOSI_shift <= 8'd0;
    	else if(state == IDLE && wr_req)
    		MOSI_shift <= data_in;
    	else if(state == DCLK_EDGE)
    		if(CPHA == 1'b0 && clk_edge_cnt[0] == 1'b1)
    			MOSI_shift <= {MOSI_shift[6:0],MOSI_shift[7]};
    		else if(CPHA == 1'b1 && (clk_edge_cnt != 5'd0 && clk_edge_cnt[0] == 1'b0))
    			MOSI_shift <= {MOSI_shift[6:0],MOSI_shift[7]};
    end
    //SPI data input
    always@(posedge sys_clk or posedge rst)
    begin
    	if(rst)
    		MISO_shift <= 8'd0;
    	else if(state == IDLE && wr_req)
    		MISO_shift <= 8'h00;
    	else if(state == DCLK_EDGE)
    		if(CPHA == 1'b0 && clk_edge_cnt[0] == 1'b0)
    			MISO_shift <= {MISO_shift[6:0],MISO};
    		else if(CPHA == 1'b1 && (clk_edge_cnt[0] == 1'b1))
    			MISO_shift <= {MISO_shift[6:0],MISO};
    end 
    

    设计思路

    如果理解了spi传输时序,程序应不难理解。但可以看到黑金的例程中采用了大量的alwayls块对各个状态时电路的连接做了描述但很显然这种方式并不能很清晰的表达各个状态的时序时如何变更的。而且电路中用了大量的if语句这样会综合出大量的数据选择器来消耗fpga内部的逻辑资源。收到论坛博主启发我想采用两段式状态机来实现spi的时序。例化模块的处理方式。

    也就是状态转移状态机+处理使能状态机的模式

    顶层模块

    module spi_master//与例程同名
    (
    	input clk,
    	input rst_n,
    	output mosi,
    	input miso,
    	input cpol,
    	input cpha,
    	input ctrcs_n,
    	input	wr_en,
    	input [7:0]data_in,
        input [15:0]clk_div,//时钟阈值
    	
    	output cs_n,
    	output sclk,
    	output wr_ack,//spi完成应答,在应答状态使能
        output [7:0]data_out
    	
    );
    
    /*
    状态转移寄存器状态
    */
    localparam IDLE					=3'd0;
    localparam SCLK_EDGE				=3'd1;
    localparam SCLK_IDLE				=3'd2;
    localparam ACK						=3'd3;//应答状态,发送完成
    localparam LAST_HALF_CYCLE		=3'd4;//最后半个周期,具体参考黑金例程理解
    localparam ACK_WAIT				=3'd5;
    
    
    reg[2:0] state;
    reg[2:0] next_state;
    /*
    使能状态机的使能寄存器
    */
    reg en_sclk_load;
    reg en_shift_load;
    reg en_sclk;
    reg en_clk_cnt;
    reg en_edge;
    reg en_start;
    
    wire[15:0]clk_cnt;
    wire[4:0] clk_edge_cnt;
    
    
    assign wr_ack=(state==ACK);
    assign cs_n=ctrcs_n;
    
    
    
    sclk s0(
    	.clk			(clk),
    	.rst_n		(rst_n),
    	.cpol			(cpol),
    	.en			(en_sclk),
    	.en_load		(en_sclk_load),
    	.sclk_reg	(sclk)
    );
    
    counter c0(
    	.clk				(clk),
    	.rst_n			(rst_n),
    	.en_clk_cnt		(en_clk_cnt),
    	.en_edge			(en_edge),
    	.clk_cnt			(clk_cnt),
    	.clk_edge_cnt	(clk_edge_cnt)
    );
    
    shifter sh0(	
    	.clk				(clk),
    	.rst_n			(rst_n),
    	.data_in			(data_in),
    	.clk_edge_cnt	(clk_edge_cnt),
    	.en_load			(en_shift_load),
    	.en				(en_start),
    	.cpha				(cpha),
    	.miso				(miso),
    	.mosi				(mosi),
    	.data_out		(data_out)
    );
    /*
    状态转移寄存器,用来控制spi的各个阶段,
    以wr_en使能开始状态转移
    在edge态和idle态进行转换来实现数据发送
    */
    always@(posedge clk or negedge rst_n)
    begin
    	if(!rst_n)
    		state <= IDLE;
    	else
    		state <= next_state;
    end
    
    always@(*)
    begin
    	case(state)
    	
    		IDLE:
    			if(wr_en == 1'b1)
    				next_state <= SCLK_IDLE;
    			else
    				next_state <= state;
    			
    		SCLK_IDLE:
    			if(clk_cnt==clk_div)
    				next_state <= SCLK_EDGE;
    			else
    				next_state <= state;
    				
    		SCLK_EDGE:
    			if(clk_edge_cnt == 5'd15)
    				next_state <= LAST_HALF_CYCLE;
    			else
    				next_state <= SCLK_IDLE;
    		
    		LAST_HALF_CYCLE:
    			if(clk_cnt == clk_div)
    				next_state <= ACK;
    			else
    				next_state <= LAST_HALF_CYCLE; 
    				
    		ACK:
    			next_state <= IDLE;
    		
    		ACK_WAIT:
    			next_state <= IDLE;
    			
    		default:
    			next_state <= IDLE;
    			
    	endcase					
    end
    /*
    使能状态机
    */
    always@(*)
    begin
    	case(state)
    	
    		IDLE:
    			begin
    				if(wr_en==1'b1)					
    					en_sclk_load<= 1'b1;
    				else
                        /*
    						数据装载
    					*/
    					en_sclk_load<= 1'b0;
    					
    				en_shift_load 	<= 1'b1;
    				en_sclk			<= 1'b0;
    				en_clk_cnt		<= 1'b0;
    				en_edge			<=	1'b0;		
    				en_start			<= 1'b0;
    
    			end
    			
    		SCLK_IDLE://计数状态
    			begin
    				en_sclk_load 	<= 1'b0;
    				en_sclk			<= 1'b0;
    				en_shift_load 	<= 1'b0;
    				en_clk_cnt		<= 1'b1;
    				en_edge			<=	1'b0;		
    				en_start			<= 1'b0;				
    			end
    			
    		SCLK_EDGE://以时钟沿为控制的移位寄存器
    			begin
    				en_sclk_load 	<= 1'b0;
    				en_sclk			<= 1'b1;
    				en_shift_load 	<= 1'b0;
    				en_clk_cnt		<= 1'b0;
    				en_edge			<=	1'b1;		
    				en_start			<= 1'b1;	
    			end
    		LAST_HALF_CYCLE:
    			begin
    				en_sclk_load 	<= 1'b0;
    				en_sclk			<= 1'b0;
    				en_shift_load 	<= 1'b0;
    				en_clk_cnt		<= 1'b1;
    				en_edge			<=	1'b0;		
    				en_start			<= 1'b0;
    			end
    		ACK://应答状态代表数据已完成
    		begin
    				en_sclk_load 	<= 1'b0;
    				en_shift_load 	<= 1'b0;
    				en_sclk			<= 1'b0;
    				en_clk_cnt		<= 1'b0;
    				en_edge			<=	1'b0;		
    				en_start			<= 1'b0;	
    		end
    		ACK_WAIT:
    		begin
    				en_sclk_load 	<= 1'b0;
    				en_sclk			<= 1'b0;
    				en_shift_load 	<= 1'b0;
    				en_clk_cnt		<= 1'b0;
    				en_edge			<=	1'b0;		
    				en_start			<= 1'b0;	
    		end
    	default:begin
    				en_sclk_load 	<= 1'b1;
    				en_shift_load 	<= 1'b1;
    				en_clk_cnt		<= 1'b0;
    				en_edge			<=	1'b0;		
    				en_start			<= 1'b0;
    				end
    	endcase	
    end
    
    
    
    
    endmodule
    
    
    
    
    

    关于调用的模块就不在这里贴了,看顶层的调用大家应该也知道子模块的功能。都很简单就不一一列举了

    遇到的问题

    因为时间过去蛮久的了好多问题都解决了,就说几个现在还记忆犹新的吧

    1.首先在时序要求比较严格的电路一定要控制好各个时序是否正确,我认为状态机其实就是一个时序管理,只有把状态机的时序调到清晰易懂,时序也自然容易出来。这种请何况在我现在做的一个pi算法中尤为明显

    2在设计移位子模块时有一个小地方

    `

    lways@(posedge clk or negedge rst_n)
    begin
    	if(!rst_n)
    		mosi_shift <= 8'b0;
    		
    	else if(en_load==1'b1)
    		mosi_shift <= data_in;
    			
    	else if(en==1'b1 && en_load==1'b0)
    			begin
    			if(cpha == 1'b0 && clk_edge_cnt[0] == 1'b1)
    				mosi_shift <= {mosi_shift[6:0],mosi_shift[7]};
    				
    			else if(cpha == 1'b1 && (clk_edge_cnt != 5'd0 && clk_edge_cnt[0] == 1'b0))
    				mosi_shift <= {mosi_shift[6:0],mosi_shift[7]};
    			end
    				
    	else
    		mosi_shift <= mosi_shift;//这句话一开始我写的是零但应该是进行数据所存,查了好久才查到	
    

    `

    END

    因为最近事情好多,这个详细写起来也太麻烦了所以就没有详细写,其实有关前面几个小项目也应该好好总结一下了。希望五一的时候能把之前的坑都填一填

    1出租车计费器

    2 flash的时序控制

    3 iic

    4 spi时序的完善和testbenchi的建立

    5 fpga实现pi算法

    发现这些如果不总结会遗忘的好快,而且不总结经常是自己踩得坑又踩一遍

    展开全文
  • SPI 时序

    2020-12-08 17:01:29
    一、SPI为3线通讯的串行总线 (1)、时钟线规则,包括空闲电平-CPOL(高和低)和采样沿-CPHA(前沿和后沿) 二、数据线(MISO采样时要求数据稳定) ...

    一、SPI为3线通讯的串行总线

       (1)时钟线规则,包括空闲电平-CPOL(高和低)采样沿-CPHA(前沿和后沿) 2个采样沿分别给两根数据

                

                       

     

    二、数据线(MISO采样时要求数据稳定,正好对着MISO数据线的稳定期间)

     

     

     

     

    展开全文
  • SPI时序

    2019-08-14 15:39:16
    SPI :Serial Peripheral Interface 是一种高速的、全双工、同步通信总线。 应用:单片机和EEPROM、实时时钟、数字信号处理器 组成 标准的SPI由四根线组成 SSEL:片选,也写做SCS,从设备片选使能信号。(需要看设备...

    简介

    SPI :Serial Peripheral Interface 是一种高速的、全双工、同步通信总线。
    应用:单片机和EEPROM、实时时钟、数字信号处理器

    组成

    标准的SPI由四根线组成
    SSEL:片选,也写做SCS,从设备片选使能信号。(需要看设备是高电平还是低电平使能)
    SCLK:时钟,也写作SCK,由主机产生,和SCL类似
    MOSI:主机输出从机输入,Master Output/Slave Input,主机给从机发送指令或者数据的通道。
    MISO:主机输入从机输出,Master Input/Slave Output,主机读取从机的状态或者数据的通道

    读写数据四种模式

    CPOL:Clock Polarity,即时钟极性。空闲状态为高电平,则CPOL=1,空闲状态为低电平,则CPOL=0。
    CPHA:Clock Phase,即时钟相位。CPHA=1表示数据的输出是在一个时钟周期的第一个沿。CPHA=0表示数据的采样是在一个时钟周期的第一个沿。

    同步通信特点:所有数据的变化和采样都是伴随着时钟沿进行的,即数据总是在时钟边沿附近变化或者取样。

    在这里插入图片描述在这里插入图片描述

    展开全文
  • SPI总线协议及SPI时序图详解 SPI总线协议及SPI时序图详解
  • spi时序,spi应用

    2013-05-26 11:18:36
    spi应用,spi时序,spi浅析,主要是spi的时序图
  • 了解SPI是他的时序是怎么样子的,对SPI时序图详解及SPI总线协议
  • SPI时序详解

    2018-11-10 18:03:42
    SPI时序详解

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

    也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

                   

    SPI时序详解

                 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时序详解---SPI接口在模式0下输出第一位数据的时刻SPI接口有四种不同的数据传输时序,取决于CPOL和CPHL这两位的组合。图1中表现了这四种时序,时序与CPOL、CPHL的关系也可以从图中看出。

    图1
              CPOL是用来决定SCK时钟信号空闲时的电平,CPOL=0,空闲电平为低电平,CPOL=1时,空闲电平为高电平。CPHA是用来决定采样时刻的,CPHA=0,在每个周期的第一个时钟沿采样,CPHA=1,在每个周期的第二个时钟沿采样。由于我使用的器件工作在模式0这种时序(CPOL=0,CPHA=0),所以将图1简化为图2,只关注模式0的时序。

    图2
              我们来关注SCK的第一个时钟周期,在时钟的前沿采样数据(上升沿,第一个时钟沿),在时钟的后沿输出数据(下降沿,第二个时钟沿)。首先来看主器件,主器件的输出口(MOSI)输出的数据bit1,在时钟的前沿被从器件采样,那主器件是在何时刻输出bit1的呢?bit1的输出时刻实际上在SCK信号有效以前,比SCK的上升沿还要早半个时钟周期。bit1的输出时刻与SSEL信号没有关系。再来看从器件,主器件的输入口MISO同样是在时钟的前沿采样从器件输出的bit1的,那从器件又是在何时刻输出bit1的呢。从器件是在SSEL信号有效后,立即输出bit1,尽管此时SCK信号还没有起效。

    从这张图就可以很清楚的看出主从器件的bit1是怎样输出的





               

    给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow

    这里写图片描述
    展开全文
  • spi时序verilog实现

    2017-12-02 17:17:53
    用verilog语言实现一个简单的spi时序逻辑,代码风格简洁规范。
  • SPI(Serial Peripheral Interface),顾名思义就是串行外围设备接口。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线(MISO, MOSI, CLK, CS)可以不用CS片选引脚也是三线式,SPI有...
  • SPI总线协议及SPI时序图详解含实例
  • verilog 四线spi时序

    2020-02-20 17:05:36
    verilog 四线spi时序,已验证,spi的时钟速率可配置,支持读和写,其中写数据共24位,前16位为寄存器地址,后八位是要写入的内容,上升沿采样写入;读数据前16位为要读取的寄存器的地址,上升沿读取,read_data 是该...
  • 详细讲解SPI总线协议,图解介绍SPI时序
  • spi时序

    2008-05-24 14:20:41
    这是液晶屏的spi时序
  • LMK04828 SPI时序控制

    千次阅读 2020-07-15 11:54:29
    查看LMK04828芯片手册,获取lmk04828芯片spi时序及相关寄存器设置 https://pdf1.alldatasheet.com/datasheet-pdf/view/932720/TI1/LMK04828.html lmk04828芯片的寄存器配置可下载TI相关配置软件TICS PRO进行模拟配置...
  • YDOOK:ESP8266: SPI 时序特征图 © YDOOK JY Lin 文章目录YDOOK:ESP8266: SPI 时序特征图© YDOOK JY Lin
  • SPI时序解析

    2017-06-12 13:25:16
    SPI时序详解  SPI总线是Motorola公司推出的三线同步接口,同步串行3线方式进行通信:一条时钟线SCK,一条数据输入线MOSI,一条数据输出线MISO;用于 CPU与各种外围器件进行全双工、同步串行通讯。SPI主要特点有:...
  • 在使用STM32驱动NRF2401时需注意,NRF2401的spi时序要求。空闲时,spi应为低电平,这点一定要注意。否则芯片一直检测不到nrf2401.
  • 在ARM Linux下使用GPIO模拟SPI时序详解.pdf

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,248
精华内容 499
关键字:

spi时序