SPI-Verilog

SPI通讯协议(简介)

SPl ( Serial Peripheral Interface,串行外围设备接口)通讯协议,是Motorola公司提出的一步串行接口技术,是一种高速、全双工、同步通信总线,在芯片中只占用四根管脚用来控制及数据传输。

应用:EEPROM、Flash、RTC、ADC、DSP等。

优缺点︰全双工通信,通讯方式较为简单,相对数据传输速率较快﹔没有应答机制确认数据是否接收,在数据可靠性上有一定缺陷(与l2C相比)。

SPI物理层连接

第一种:单对单(一主一从)

第二种:单对多(一主多从)

spi四根管脚的作用:

SCK(Serial Clock):时钟信号线,用于同步通讯数据;

MOSI (Master Output, Slave lnput):主设备输出/从设备输入引脚

MISO (Master Input,Slave Output):主设备输入/从设备输出引脚

cs (Chip Select):片选信号线,也称为CS_N。

SPI协议层

SPI串行同步时钟可以设置为不同的极性(Clock Polarity ,CPOL)与相位(Clock Phase ,CPHA)。

 

时钟的极性(CPOL):用来决定在总线空闲时,同步时钟(SCK)信号线上的电位是高电平还是低电平。当时钟极性为0时(CPOL=0),SCK信号线在空闲时为低电平;当时钟极性为1时(CPOL=1),SCK信号线在空闲时为高电平;

 

时钟的相位(CPHA):用来决定何时进行信号采样。当时钟相位为1时(CPHA=1),在SCK信号线的第二个跳变沿进行采样;这里的跳变沿究竟是上升沿还是下降沿?取决于时钟的极性。当时钟极性为0时,取下降沿;当时钟极性为1时,取上升沿。或者说是当时钟极性为0时,在奇数边沿取信号,当时钟极性为1时,在偶数边沿取信号。

根据CPOL和CPHA不同SPI具有四种工作模式,如下图:

SPI应用举例-AD7793芯片

芯片手册(网上查)

AD7793芯片是采用4线spi接口用于转换把传输进来的模拟信号转换为数字信号。

AD7793芯片读写时序

 

AD7793芯片写时序

 

 

 

 AD7793芯片读时序

 

 

 AD7793芯片工作主要引脚说明

AD7792/AD7793的串行接口由四个信号组成:CS、DIN、SCLK和DOUT/RDY。

DIN线路用于将数据传输至片内寄存器中,DOUT/RDY则用于从片内寄存器中获取数据。SCLK是器件的串行时钟输入,所有数据传输(无论是DIN上还是DOUT/RDY上)均与SCLK信号相关。

DOUT/RDY引脚也可输出数据就绪信号;当输出寄存器中有新数字字可用时,该线路变为低电平。对数据寄存器的读操作完成时,该线路复位为高电平。数据寄存器更新之前,该线路也会变为高电平,提示在此时不应对器件进行读操作,以确保在更新数据寄存器的过程中不会发生数据读取操作。

AD7793芯片一般工作流程

读写大体流程是:
1、先向通讯寄存器写入地址和读写请求;
2、然后写入或者读取指定bit位宽的数据;
一般流程:
1、读ID寄存器,判断芯片是否符合要求;
2、选择输入通道
3、配置需要增益;
4、进行校准设置
5、发起单次或者连续AD转换读取

 

举例:AD7793控制模块(仅供参考,程序以及过程未验证)

 

输入:

Sys_clk:系统时钟

Sys_rst_n:复位信号

Key_flag:开始信号

MISO(DUT):AD7793(从机)传给主机的信号

 

输出:

Sclk: AD7793的钟控制信号

Cs:AD7793的片选信号

MOSI(DIN):主机输入从机输出

adc_convertion_data:转换完成的数据

AD7793控制模块状态图:

AD7793控制模块时序图

中间变量说明:

State:状态变量;

cnt_sclk:系统时钟的计数变量,主要用于生成sclk时钟用于驱动AD7793,根据手册知道sclk时钟最小为20;所以计数到10时钟翻转;

Cnt_bit:bit计数器,用于计算传输的bit个数;

Cnt_byte:字节计数器,用于计算传输的字节个数;

miso_flag:有效数据标志位,用于提示有效数据生成

data:用于有效数据保存

data_vld:有效数据接收完成标志

 

1、复位------------>向通信寄存器写入001_0000表示写配置寄存器---------->配置寄存器0x6e10----------->通信寄存器写0000_1000

 

 

 

 

模式寄存器写入0x200a---------------->通信寄存器写0101_1100设置为连续读取模式------------------->等待转换完成

 

rdy_n为0-------------->读取转换结果

代码(仅供参考未-不要指望完全对,我是一个新手)

 

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2023/03/03 19:42:16
// Design Name: 
// Module Name: AD7793
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module AD7793(
	input wire sys_clk,
	input wire sys_rst_n,
	input wire key_flag,
	input wire miso_dout,

	output reg sclk,
	output reg cs,
	output reg mosi_din,
	output reg [23:0] adc_convertion_data
    );
parameter IDLE = 4'b0001;
parameter WR = 4'b0010;
parameter WAIT = 4'b0100;
parameter READ = 4'b1000;

reg [3:0] state;
reg [3:0] cnt_clk;
reg [2:0] cnt_bit;
reg [2:0] cnt_byte;
reg miso_flag;
reg data_vld;
reg [23:0] data;


//状态机的生成
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(~sys_rst_n) 
		state <= IDLE;
	else
		 case(state)
		 	IDLE:if (cs == 1'b0)
		 			state <= WR;
		 		else
		 			state <= IDLE;
		 	WR  :if ((cnt_clk == 4'd9)&&(cnt_bit == 3'd7)&&(cnt_byte == 3'd6))
		 			state <= WAIT; 
		 		else
		 			state <= WR;
		 	WAIT:if (miso_dout == 1'b0)
		 			state <= READ;
		 		else if(cs == 1'b1)
		 			state <= IDLE;
		 		else
		 			state <= WAIT;
		 	READ:if (cs == 1'b1)
		 			state <= IDLE;
		 		else if((state == READ)&&(cnt_clk == 4'd9)&&(cnt_bit == 3'd7)&&(cnt_byte == 3'd2))
		 			state <= WAIT;
		 		else
		 			state <= READ;
		 	default:state <= IDLE;
		 endcase
	
end

//片选
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(~sys_rst_n)
		cs <= 1'b1; 
	else if(key_flag == 1'b1)
		cs <= ~cs; 
	else
		cs <= cs;
end

//系统时钟计数
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(~sys_rst_n)
		cnt_clk <= 4'd0;
	else if(cnt_clk == 4'd9)
		cnt_clk <= 4'd0;
	else if(state == WAIT)
		cnt_clk <= 4'd0;
	else if(cs == 1'b1)
		cnt_clk <= 4'd0;
	else
		cnt_clk <= cnt_clk + 1'b1;
end

//bit计数
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(~sys_rst_n)
		cnt_bit <= 3'd0;
	else if(cnt_clk == 4'd9)
		cnt_bit <= cnt_bit + 1'b1;
	else
		cnt_bit <= cnt_bit;
end

//字节计数器
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(~sys_rst_n)
		cnt_byte <= 3'd0;
	else if((cnt_byte == 3'd6)&&(cnt_bit == 4'd7)&&(cnt_clk == 4'd9))
		cnt_byte <= 3'd0;
	else if((cnt_bit == 4'd7)&&(cnt_clk == 4'd9))
		cnt_byte <= cnt_byte + 1'b1;
	else
		cnt_byte <= cnt_byte;
end

//sclk产生
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(~sys_rst_n)
		sclk <= 1'd1;
	else if(state == WAIT)
		sclk <= 1'd1;
	else if(cs == 1'b1)
		sclk <= 1'd1;
	else if(cnt_clk == 4'd4 ||cnt_clk == 4'd9)
		sclk = ~sclk;
	else
		sclk <=sclk;
end

//mosi的输出波形
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(~sys_rst_n)
		mosi_din <= 1'b1;
	else if((state == WR)&&(cnt_clk == 4'd4)&&(cnt_bit == 3'd0)&&(cnt_byte == 3'd0))
		mosi_din <= 1'b0;
	else if((state == WR)&&(cnt_clk == 4'd4)&&(cnt_bit == 3'd3)&&(cnt_byte == 3'd0))
		mosi_din <= 1'b1;
	else if((state == WR)&&(cnt_clk == 4'd4)&&(cnt_bit == 3'd4)&&(cnt_byte == 3'd0))
		mosi_din <= 1'b0;
	else if(((state == WR)&&(cnt_byte == 3'd1)&&(cnt_clk == 4'd4))&&(cnt_bit == 3'd1||cnt_bit == 3'd4))
		mosi_din <= 1'b1;
	else if(((state == WR)&&(cnt_byte == 3'd1)&&(cnt_clk == 4'd4))&&(cnt_bit == 3'd3||cnt_bit == 3'd7))
		mosi_din <= 1'b0;
	else if(((state == WR)&&(cnt_byte == 3'd2)&&(cnt_clk == 4'd4))&&(cnt_bit == 3'd3))
		mosi_din <= 1'b1;
	else if(((state == WR)&&(cnt_byte == 3'd2)&&(cnt_clk == 4'd4))&&(cnt_bit == 3'd4))
		mosi_din <= 1'b0;
	else if(((state == WR)&&(cnt_byte == 3'd3)&&(cnt_clk == 4'd4))&&(cnt_bit == 3'd4))
		mosi_din <= 1'b1;
	else if(((state == WR)&&(cnt_byte == 3'd3)&&(cnt_clk == 4'd4))&&(cnt_bit == 3'd5))
		mosi_din <= 1'b0;
	else if(((state == WR)&&(cnt_byte == 3'd5)&&(cnt_clk == 4'd4))&&(cnt_bit == 3'd4))
		mosi_din <= 1'b1;
	else if(((state == WR)&&(cnt_byte == 3'd6)&&(cnt_clk == 4'd4))&&(cnt_bit == 3'd0||cnt_bit == 3'd2||cnt_bit == 3'd6))
		mosi_din <= 1'b0;
	else if(((state == WR)&&(cnt_byte == 3'd6)&&(cnt_clk == 4'd4))&&(cnt_bit == 3'd1||cnt_bit == 3'd3))
		mosi_din <= 1'b1;
	else if(cs == 1'd1)
		mosi_din <= 1'b1;
	else if (miso_dout == 1'd0)
		mosi_din <= 1'b0;
	else
		mosi_din <= mosi_din;
end

//miso_flag 接收的bit奇数
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(~sys_rst_n)
		miso_flag <=1'b0;
	else if((state == READ)&&(cnt_clk == 4'd8))
		miso_flag <=1'b1;
	else
		miso_flag <=1'b0;
end

//串并转化标识符
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(~sys_rst_n)
		data <= 24'd0;
	else if(miso_flag <=1'b1)
		data <={data,miso_dout};
	else
		data <=data;
end

//串并转换完成标识符
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(~sys_rst_n)
		data_vld <= 1'b0;
	else if(state == READ && cnt_bit == 3'd7 && cnt_clk == 4'd9 && cnt_byte == 3'd2)
		data_vld <= 1'b1;
	else
		data_vld <= 1'b0;
end

//有效转换数据
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(~sys_rst_n)
		adc_convertion_data <= 24'd0;
	else if(data_vld == 1'b1)
		adc_convertion_data <= data;
	else
		adc_convertion_data <= adc_convertion_data ;
end


endmodule

  

 

 

 

 

 

 

 

 

图源以及参考(17. 基于spi协议的flash驱动控制 — [野火]FPGA Verilog开发实战指南——基于Altera EP4CE10 征途Pro开发板 文档 (embedfire.com)

 

 

posted @ 2023-03-08 20:49  太平洋3333  阅读(647)  评论(0编辑  收藏  举报