MCU集成-常用接口spi

 spi介绍

SPI 通讯协议

优点

全双工串行通信
简单的硬件结构
高速数据传输速率(相比UART、IIC)
灵活的数据传输方式,不限于8位,可以是任意大小的字

缺点

仅支持一个主设备
引脚略多(相比UART、IIC)
没有硬件从机应答信号(主机可能在不知情的情况下无处发送)

spi结构和模式

SPI 通讯设备的通讯模式是主从通讯模式,通讯双方有主从之分,根据从机设备的数量,SPI 通讯设备之间的连接方式可分为一主一从和一主多从

        SPI总线传输只需要4根线就能完成,这四根线的作用分别如下:

SCK (Serial Clock):时钟信号线,用于同步通讯数据。由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不同
MOSI (Master Output, Slave Input):主设备输出/从设备输入引脚。主机的数据从这条信号线输出,从机由这条信号线读入主机发送的数据,数据方向由主机到从机
MISO (Master Input,Slave Output):主设备输入/从设备输出引脚。主机从这条信号线读入数据,从机的数据由这条信号线输出到主机,数据方向由从机到主机
CS (Chip Select):片选信号线。当有多个 SPI 从 设备与 SPI 主机相连时,设备的其它信号线 SCK、MOSI 及 MISO 同时并联到相同的 SPI 总线上,即无论有多少个从设备,都共同使用这 3 条总线;而每个从设备都有独立的片选信号线,即有多少个从设备,就有多少条片选信号线。相当于由SPI构成的通信系统中,通过CS片选信号来决定通信的从机设备是哪一台。通信期间低电平有效,表示对应从机被选中。

        SPI总线传输一共有4种模式,这4种模式分别由时钟极性(CPOL,Clock Polarity)和时钟相位(CPHA,Clock Phase)来定义其中CPOL参数规定了SCK时钟信号空闲状态的电平,CPHA规定了数据是在SCK时钟的上升沿被采样还是下降沿被采样。

模式0:CPOL= 0,CPHA=0。SCK串行时钟线空闲是为低电平,数据在SCK时钟的上升沿被采样,数据在SCK时钟的下降沿切换
模式1:CPOL= 0,CPHA=1。SCK串行时钟线空闲是为低电平,数据在SCK时钟的下降沿被采样,数据在SCK时钟的上升沿切换
模式2:CPOL= 1,CPHA=0。SCK串行时钟线空闲是为高电平,数据在SCK时钟的下降沿被采样,数据在SCK时钟的上升沿切换
模式3:CPOL= 1,CPHA=1。SCK串行时钟线空闲是为高电平,数据在SCK时钟的上升沿被采样,数据在SCK时钟的下降沿切换

SPI协议通信过程


        下面以模式 0 为例,讲解一下 SPI 基本的通讯过程:

        MOSI 与 MISO 的信号只在 CS_N 为低电平的时候才有效,在 SCK 的每个时钟周期 MOSI 和 MISO 传输一位数据。 

      在图中的标号1处,CS_N 信号线由高变低,是 SPI 通讯的起始信号。在图中的标号6处,CS_N 信号由低变高,是 SPI 通讯的停止信号,表示本次通讯结束,从机的选中状态被取消。

        SPI 使用 MOSI 及 MISO 信号线来传输数据,使用 SCK 信号线进行数据同步。MOSI 及 MISO 数据线在 SCK 的每个时钟周期传输一位数据,且数据输入输出是同时进行的。数据传输时,MSB 先行或 LSB 先行并没有作硬性规定但要保证两个 SPI 通讯设备之间使用同样的协定,一般都会采用MSB 先行模式MOSI 及 MISO 的数据在 SCK 的下降沿期间变化输出, 在 SCK 的上升沿时被采样。即在 SCK 的上升沿时刻,MOSI 及 MISO 的数据有效,高电平时表示数据“1”,为低电平时表示数据“0”。在其它时刻,数据无效,MOSI 及 MISO 为下一次表示数据做准备。SPI 每次数据传输可以 8 位或 16 位为单位,每次传输的单位数不受限制。

spi单元

实现目标:MSB 先行;仅限模式0;每次传输8位(1个BYTE)。

`timescale 1ns/1ns		//时间单位/精度	
// 模式0
module spi_drive
(
// 系统接口
    input               sys_clk		, 			// 全局时钟50MHz
    input               sys_rst_n	, 			// 复位信号,低电平有效
// 用户接口	
    input               spi_start	,			// 发送传输开始信号,一个高电平
    input              	spi_end		,			// 发送传输结束信号,一个高电平
    input        [7:0]  data_send   , 			// 要发送的数据
    output  reg  [7:0]  data_rec  	, 			// 接收到的数据
    output  reg         send_done	, 			// 主机发送一个字节完毕标志位    
    output  reg         rec_done	, 			// 主机接收一个字节完毕标志位    
// SPI物理接口
    input               spi_miso	, 			// SPI串行输入,用来接收从机的数据
    output  reg         spi_sclk	, 			// SPI时钟
    output  reg         spi_cs    	, 			// SPI片选信号,低电平有效
    output  reg         spi_mosi				// SPI输出,用来给从机发送数据          
);
 
reg	[1:0]	cnt;								//4分频计数器
reg	[3:0]	bit_cnt_send;						//发送计数器
reg	[3:0]	bit_cnt_rec;						//接收计数器
reg			spi_end_req;						//结束请求
 
//4分频计数器
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		cnt <= 2'd0;						
	else if(!spi_cs)begin
		if(cnt == 2'd3)
			cnt <= 2'd0;
		else
		cnt <= cnt + 1'b1;		
	end
	else 
		cnt <= 2'd0;	
end
// 生成spi_sclk时钟
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		spi_sclk <= 1'b0;			//模式0默认为低电平					
	else if(!spi_cs)begin			//在SPI传输过程中
		if(cnt == 2'd0 )
			spi_sclk <= 1'b0;
		else if (cnt == 2'd2)
			spi_sclk <= 1'b1;
		else 
			spi_sclk <= spi_sclk;	
	end
	else 
		spi_sclk <= 1'b0;			//模式0默认为低电平		
end
// 生成片选信号spi_cs
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		spi_cs <= 1'b1;				//默认为高电平						
	else if(spi_start)				//开始SPI准备传输,拉低片选信号
		spi_cs <= 1'b0;
	//收到了SPI结束信号,且结束了最近的一个BYTE
	else if(spi_end_req && (cnt == 2'd1 && bit_cnt_rec == 4'd0))
		spi_cs <= 1'b1;				//拉高片选信号,结束SPI传输
end
// 生成结束请求信号(捕捉spi_end信号)
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		spi_end_req <= 1'b0;		//默认不使能					
	else if(spi_cs)					
		spi_end_req <= 1'b0;		//结束SPI传输后拉低请求
	else if(spi_end)				
		spi_end_req <= 1'b1;		//接收到SPI结束信号后就把结束请求拉高
end
// 发送数据过程--------------------------------------------------------------------
 
// 发送数据
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		spi_mosi <= 1'b0;						//模式0空闲
		bit_cnt_send <= 4'd0;
	end
	else if(cnt == 2'd0 && !spi_cs)begin		//模式0的上升沿
		spi_mosi <= data_send[7-bit_cnt_send];	//发送数据移位
		if(bit_cnt_send == 4'd7)				//发送完8bit
			bit_cnt_send <= 4'd0;
		else
			bit_cnt_send <= bit_cnt_send + 1'b1;	
	end
	else if(spi_cs)begin						//非传输时间段
		spi_mosi <= 1'b0;						//模式0空闲
		bit_cnt_send <= 4'd0;
	end
	else begin
		spi_mosi <= spi_mosi;
		bit_cnt_send <= bit_cnt_send;
	end
end
// 发送数据标志
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		send_done <= 1'b0;			
	else if(cnt == 2'd0 && bit_cnt_send == 4'd7)		//发送完了8bit数据
		send_done <= 1'b1;								//拉高一个周期,表示发送完成	
	else 
		send_done <= 1'b0;			
end
 
// 接收数据过程--------------------------------------------------------------------
 
// 接收数据spi_miso
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		data_rec <= 8'd0;		
		bit_cnt_rec <= 4'd0;
	end
	else if(cnt == 2'd2 && !spi_cs)begin				//模式0的上升沿
		data_rec[7-bit_cnt_rec] <= 	spi_miso;			//移位接收
		if(bit_cnt_rec == 4'd7)							//接收完了8bit
			bit_cnt_rec <= 4'd0;
		else
			bit_cnt_rec <= bit_cnt_rec + 1'b1;	
	end
	else if(spi_cs)begin								
		bit_cnt_rec <= 4'd0;
	end
	else begin
		data_rec <= data_rec;
		bit_cnt_rec <= bit_cnt_rec;
	end
end
// 接收数据标志
always @(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		rec_done <= 1'b0;									
	else if(cnt == 2'd2 && bit_cnt_rec == 4'd7)			//接收完了8bit
		rec_done <= 1'b1;								//拉高一个周期,表示接收完成			
	else 
		rec_done <= 1'b0;					
end
 
endmodule

注意:

1. spi设计成4分频是因为实现比较简单。

2. spi是数据交换器,但是只有主发给从的数据的时候,或者说只想把主机数据发给从机,主机不需要从机的数据的时候,从机也需要返回0。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值