SPI协议-序列机控制ADC128S022

本文主要是学习SPI通信时记下的笔记

 SPI通信协议原理:

        串行外设接口(SPI)是微控制器和外围IC(如传感器、ADCDAC、移位寄存器、SRAM等)之间使用最广泛的接口之一

        SPI是一种同步、全双工、主从式接口。来自主机或从机的数据在时钟上升沿或下降沿同步。主机和从机可以同时传输数据。SPI接口可以是3线式或4线式。

SPI四种通信模式:

        SPI通信有4种不同的模式,不同的从设备可能在出厂是就是配置为某种模式,这是不能改变的;但我们的通信双方必须是工作在同一模式下,所以我们可以对我们的主设备的SPI模式进行配置,通过CPOL(时钟极性)和CPHA(时钟相位)来控制我们主设备的通信模式,具体如下

          Mode0CPOL=0CPHA=0

          Mode1:CPOL=0CPHA=1

          Mode2:CPOL=1CPHA=0

          Mode3:CPOL=1CPHA=1

        时钟极性CPOL是用来配置SCLK的电平出于哪种状态时是空闲态或者有效态,时钟相位CPHA用来配置数据采样是在第几个边沿

        CPOL=0,CPHA=0:此时空闲态时,SCLK处于低电平,数据采样是在第1边沿,
SCLK由低电平到高电平的跳变,所以数据采样是在上升沿,数据发送是在下降沿。

        CPOL=0,CPHA=1:此时空闲态时,SCLK处于低电平,数据发送是在第1个边沿
SCLK由低电平到高电平的跳变,所以数据采样是在下降沿,数据发送是在上升沿。

         CPOL=1,CPHA=0:此时空闲态时,SCLK处于高电平,数据采集是在第1个边沿
SCLK由高电平到低电平的跳变,所以数据采集是在下降沿,数据发送是在上升沿。

         CPOL=1,CPHA=1:此时空闲态时,SCLK处于高电平,数据发送是在第1个边沿
SCLK由高电平到低电平的跳变,所以数据采集是在上升沿,数据发送是在下降沿。

采用ADC128S022仿真SPI通信接口:

DIN数据只会在低5个SCLK下降时刻进行输入,等数据稳定后在第6个上升沿时进行采样,后面的数据同理。 

DOUT数据第9时刻输出,在第10个时刻等输出数据稳定后再采样后统一传给r_data寄存器。

更细致的时序图:

驱动该adc使用的线性序列机编写:

//
// Company: 
// Engineer: 
// 
// Create Date: 2025/04/01 15:33:36
// Design Name: 
// Module Name: adc128_dri
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//

//这个驱动是SPI接口的
module adc128_dri(
         input clk,  	//50Mhz
         input rst_n,
         input start,
         input [2:0] channel,

         //*****ADC128S022*****//
         output reg DIN,
         output reg CS_N,
         output reg SCLK,
         input DOUT,
         
         output reg done,
         output reg [11:0] data
);
	reg        en;
	reg [2:0] r_channel;	
	reg [4:0] cnt;
	reg cnt_flag;
	reg [5:0] SCLK_CNT;
	reg [11:0] r_data;

	//转换使能
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			en <= 0;
		else if(start)
			en <= 1;
		else
			en <= en;
	end
	//r_channel
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			r_channel <=0;
		else if(start)
			r_channel <= channel;
		else
			r_channel <= r_channel;
	end

	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			cnt<=0;
		else if(en)begin
			if(cnt == 5'd9)
				cnt <= 5'd0;
			else
				cnt <= cnt + 1'b1;
		end
		else 
			cnt <= 5'd0; 
	end

	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			cnt_flag <= 1'b0;
		else if(cnt == 5'd9)
			cnt_flag <= 1'b1;
		else 
			cnt_flag <= 1'b0;
	end

//***********SCLK_CNT**********//
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			SCLK_CNT <= 6'd0;
		else if(en)begin
		if(SCLK_CNT ==6'd33)
			SCLK_CNT <= 6'd0; //分频计数器
		else if(cnt_flag)
			SCLK_CNT <= SCLK_CNT + 6'd1;
		else 
			SCLK_CNT <= SCLK_CNT;
		end
		else
			SCLK_CNT <= 0;
	end

	//*******线性序列机,数据发送\接收********//
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
			SCLK <= 1'b1;
			CS_N <= 1'b1;
			DIN   <= 1'b1;
		end
		else if(en)begin
			case(SCLK_CNT)
				6'd0:begin CS_N <= 1'b0;end
				6'd1:begin SCLK <= 1'b0; DIN <=1'b0;end	
				6'd2:begin SCLK <= 1'b1;end 
				6'd3:begin SCLK <= 1'b0;end
				6'd4:begin SCLK <= 1'b1;end
				6'd5:begin SCLK <= 1'b0;DIN <= r_channel[2];end  //通道第一个是最高位
				6'd6:begin SCLK <= 1'b1;end
				6'd7:begin SCLK <= 1'b0;DIN <= r_channel[1];end
				6'd8:begin SCLK <= 1'b1;end
				6'd9:begin SCLK <= 1'b0;DIN <= r_channel[0];end //奇数位输入进去
				6'd10,6'd12,6'd14,6'd16,6'd18,6'd20,6'd22,6'd24,6'd26,6'd28,6'd30,6'd32:
					begin SCLK <= 1'b1;r_data <= {r_data[10:0],DOUT};end  //紧接着偶数倍采样进去
				6'd11,6'd13,6'd15,6'd17,6'd19,6'd21,6'd23,6'd25,6'd27,6'd29,6'd31:
					begin SCLK <= 1'b0;end  //控制SCLK的时序
				6'd33:begin CS_N <= 1'b1;end
			default:begin CS_N <= 1'b1;end	//SCLK <= 1'b1;
			endcase
		end
		else begin
			SCLK <= 1'b1;
			CS_N <= 1'b1;
			DIN   <= 1'b1;
		end
	end

//*********done***********//
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			done <= 1'b0;
		else if(SCLK_CNT == 6'd33)
			done <= 1'b1;
		else 
			done <= 1'b0;
	end
//*********data***********//
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			data <= 12'b0;
		else if(SCLK_CNT == 6'd33)
			data <= r_data;
		else 
			data <= data;
	end
endmodule

 仿真:SCLK时钟输入为400ns一周期,即2.5MHz,符合0.8~3.2MHz的时钟频率要求。

再CS_N有效期内各状态符合逻辑设定 

tb:

`timescale 1ns/1ns

`define sin_data_file "C:/Users/LISON/Desktop/SPI/SPI/sim/sin_12bit.txt"

module tb_adc18s022;

	reg clk;
	reg rst_n;
	reg start;
	reg [2:0]channel;
	
	wire SCLK;
	wire DIN;
	wire CS_N;
	reg DOUT;
	
	wire done;
	wire [11:0]data;
	
	reg [11:0]memory[4095:0];//测试波形数据存储空间
	
	reg [11:0]address;//存储器地址 

	adc128_dri SPI_inst(
		.clk(clk),
		.rst_n(rst_n),
		.start(start),
		.channel(channel),
	
	//========ADC128S022===========//
		.SCLK(SCLK),
		.DIN(DIN),
		.CS_N(CS_N),
		.DOUT(DOUT),
	
		.done(done),
		.data(data)
);
	
	initial clk = 1'b1;
	always#10 clk = ~clk;

	initial $readmemh(`sin_data_file,memory);//读取原始波形数据读到memory中

	integer i;
	
	initial begin
		rst_n = 1'b0;
		channel = 'd0;
		start = 1'b0;
		DOUT = 1'b0;
		address = 0;
		#100;
		rst_n = 1'b1;
		#100;
		channel = 3;
		for(i=0;i<3;i=i+1)begin
			for(address=0;address<4095;address=address+1)begin
				start = 1;
				#20;
				start = 0;
				gene_DOUT(memory[address]);	//依次将存储器中存储的波形读出,按照ADC的转换结果输出方式送到DOUT信号线上
				@(posedge done);	//等待转换完成信号
				#200;
			end
		end
		#20000;
		$stop;
	end	

		
	//将并行数据按照ADC的数据输出格式,送到DOUT信号线上,供控制模块采集读取
	task gene_DOUT;
		input [15:0]vdata;
		reg [4:0]cnt;
		begin
			cnt = 0;
			wait(!CS_N);
			while(cnt<16)begin
				@(negedge SCLK) DOUT = vdata[15-cnt];   //高位到低位的串行输出
				cnt = cnt + 1'b1;
			end
		end
	endtask
	

endmodule 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值