基于SPI协议的ADC

ADC128S102时序图

alt text

对应操作

alt text alt text alt text

设计思路

线性序列机结构

  1. 定义单元,定义两个计数器,用以表示时刻0-34
  2. 对照时刻表,在0-34时刻,驱动信号进行相应变化
  3. 驱动部分,负责根据时刻表中各个信号的值,在对应时间点驱动信号变化

线性序列机的好处是在每个时刻做的事情都是已知的

结构图

alt text

驱动代码

ADC测试(top)

module ADC128S102_test(
    clk,
    rst_n,
    key,
    led,
    addr,
    ADC_SCLK,
    ADC_CS_N,
    ADC_DIN,
    ADC_DOUT,
    DIO,
    SRCLK,
    RCLK
);

    input clk;
    input rst_n;
    input key;
    output reg led;
    input [2:0]addr;
    output ADC_SCLK;
    output ADC_CS_N;
    output ADC_DIN;
    input ADC_DOUT;
    output DIO;
    output SRCLK;
    output RCLK;

    wire [11:0]data;
    wire conv_done;
    wire conv_go;
    wire Key_P_Flag;
    wire [31:0] disp_data;

    wire [3:0]ge;
    wire [3:0]shi;
    wire [3:0]bai;
    wire [3:0]qian;

    assign ge=data%10;//个
    assign shi=data/10%10;//十
    assign bai=data/100%10;//百
    assign qian=data/1000;//千

    assign disp_data={20'd0,qian,bai,shi,ge};
    assign conv_go=Key_P_Flag;

    key_filter key_filter(
        .Clk(clk),
        .Rst_n(rst_n),
        .key_in(key),
        .key_flag(Key_P_Flag),
        .key_state()
    );

    ADC128S102_Driver ADC128S102_Driver(
        .clk(clk),
        .rst_n(rst_n),
        .conv_go(conv_go),
        .addr(addr),
        .conv_done(conv_done),
        .data(data),
        .ADC_SCLK(ADC_SCLK),
        .ADC_CS_N(ADC_CS_N),
        .ADC_DIN(ADC_DIN),
        .ADC_DOUT(ADC_DOUT)
    );

    hex8_hc595_test hex8_hc595_test(
        .clk(clk),
        .rst_n(rst_n),
        .data(disp_data),
        .DIO(DIO),
        .SRCLK(SRCLK),
        .RCLK(RCLK)
    );

    always@(posedge clk or negedge rst_n)
        if(!rst_n)
            led<=0;
        else if(conv_done)
            led<=~led;

endmodule

ADC驱动

module ADC128S102_Driver(
    clk,
    rst_n,
    conv_go,
    addr,
    conv_done,
    data,
    ADC_SCLK,
    ADC_CS_N,
    ADC_DIN,
    ADC_DOUT
);

    input clk;
    input rst_n;
    input conv_go;
    input [2:0]addr;
    
    output reg  conv_done;
    output reg[11:0] data;
    output reg ADC_SCLK;
    output reg ADC_CS_N;
    output reg ADC_DIN;
    input ADC_DOUT;

    parameter CLOCK_FREQ=50_000_000;
    parameter SCLK_FREQ=12_500_000;
    parameter MCNT_DIV_CNT=CLOCK_FREQ/(SCLK_FREQ*2)-1;

    reg conv_en;
    reg [5:0]lsm_cnt;//序列计数器(LSM:线性序列机)
    reg [11:0] data_r;//序列机的执行单元
    reg[2:0] r_addr;


    reg [7:0]div_cnt;//最小时间单位计数器
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            div_cnt<=0;
        else if(conv_en)begin
            if(div_cnt==MCNT_DIV_CNT)
                div_cnt<=0;
            else
                div_cnt<=div_cnt+1'd1;
        end
        else
            div_cnt<=0;
    end


    // reg [5:0]lsm_cnt;//序列计数器(LSM:线性序列机)
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)
            lsm_cnt<=0;
        else if(div_cnt==MCNT_DIV_CNT)begin
            if(lsm_cnt==34)
                lsm_cnt<=0;
            else
                lsm_cnt<=lsm_cnt+1'd1;
        end
        else
            lsm_cnt<=lsm_cnt;
    end

    // reg [11:0] data_r;//序列机的执行单元
    // reg[2:0] r_addr;
    always@(posedge clk)
        if(conv_go)
            r_addr<=addr;
        else
            r_addr<=r_addr;

    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)begin
            data_r<=12'd0;
            ADC_SCLK<=1'd1;
            ADC_DIN<=1'd1;
            ADC_CS_N<=1'd1;
        end
        else if (div_cnt==MCNT_DIV_CNT)begin
            case(lsm_cnt)
                0:begin ADC_CS_N<=1'd1;ADC_SCLK<=1'd1;end
                1:begin ADC_CS_N<=1'd0; end
                2:begin ADC_SCLK<=1'd0; end
                3:begin ADC_SCLK<=1'd1; end
                4:begin ADC_SCLK<=1'd0; end
                5:begin ADC_SCLK<=1'd1; end
                6:begin ADC_SCLK<=1'd0;ADC_DIN<=r_addr[2]; end
                7:begin ADC_SCLK<=1'd1; end
                8:begin ADC_SCLK<=1'd0;ADC_DIN<=r_addr[1]; end
                9:begin ADC_SCLK<=1'd1; end
                10:begin ADC_SCLK<=1'd0;ADC_DIN<=addr[0]; end
                11:begin ADC_SCLK<=1'd1;data_r[11]<=ADC_DOUT; end
                12:begin ADC_SCLK<=1'd0; end
                13:begin ADC_SCLK<=1'd1;data_r[10]<=ADC_DOUT; end
                14:begin ADC_SCLK<=1'd0; end
                15:begin ADC_SCLK<=1'd1;data_r[9]<=ADC_DOUT; end
                16:begin ADC_SCLK<=1'd0; end
                17:begin ADC_SCLK<=1'd1;data_r[8]<=ADC_DOUT; end
                18:begin ADC_SCLK<=1'd0; end
                19:begin ADC_SCLK<=1'd1;data_r[7]<=ADC_DOUT; end
                20:begin ADC_SCLK<=1'd0; end
                21:begin ADC_SCLK<=1'd1;data_r[6]<=ADC_DOUT; end
                22:begin ADC_SCLK<=1'd0; end
                23:begin ADC_SCLK<=1'd1;data_r[5]<=ADC_DOUT; end
                24:begin ADC_SCLK<=1'd0; end
                25:begin ADC_SCLK<=1'd1;data_r[4]<=ADC_DOUT; end
                26:begin ADC_SCLK<=1'd0; end
                27:begin ADC_SCLK<=1'd1;data_r[3]<=ADC_DOUT; end
                28:begin ADC_SCLK<=1'd0; end
                29:begin ADC_SCLK<=1'd1;data_r[2]<=ADC_DOUT; end
                30:begin ADC_SCLK<=1'd0; end
                31:begin ADC_SCLK<=1'd1;data_r[1]<=ADC_DOUT; end
                32:begin ADC_SCLK<=1'd0; end
                33:begin ADC_SCLK<=1'd1;data_r[0]<=ADC_DOUT; end
                34:begin ADC_CS_N<=1'd1; end
                default:begin ADC_CS_N<=1'd1; end
            endcase
        end
    end

    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)begin
            data<=0;
            conv_done<=0;
        end
        else if(lsm_cnt==34 && div_cnt==MCNT_DIV_CNT)begin
            conv_done<=1;
            data<=data_r;
        end
        else begin
            data<=data;
            conv_done<=0;
        end
    end

    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)
            conv_en<=0;
        else if(conv_go)
            conv_en<=1;
        else if(lsm_cnt==34 && div_cnt==MCNT_DIV_CNT)
            conv_en<=0;
        else
            conv_en<=conv_en;
    end 


endmodule

74HC595驱动

module HC595_driver(clk,rst_n,seg,sel,DIO,SRCLK,RCLK);

    input clk;
    input rst_n;
    input [7:0]seg;
    input [7:0]sel;

    output reg DIO;//数据
    output reg SRCLK;//时钟
    output reg RCLK;//锁存

    reg [2:0]div_cnt;
    reg [4:0]cnt;


    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)begin
          div_cnt<=0;
          cnt<=0;
        end
        else if(div_cnt==5)begin
            div_cnt<=0;
            cnt<=cnt+1;
          end
        else begin
              div_cnt<=div_cnt+1;
          end
    end

    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)begin
          DIO<=0;
          RCLK<=0;
          SRCLK<=0;
        end
        else begin
            case(cnt)
                0:begin DIO<=seg[7];SRCLK<=0; RCLK<=1;end
                1:begin SRCLK<=1;RCLK<=0;     end
                2:begin DIO<=seg[6];SRCLK<=0; end
                3:begin SRCLK<=1;             end
                4:begin DIO<=seg[5];SRCLK<=0; end
                5:begin SRCLK<=1;             end
                6:begin DIO<=seg[4];SRCLK<=0; end
                7:begin SRCLK<=1;             end
                8:begin DIO<=seg[3];SRCLK<=0; end
                9:begin SRCLK<=1;             end
                10:begin DIO<=seg[2];SRCLK<=0;end
                11:begin SRCLK<=1;            end
                12:begin DIO<=seg[1];SRCLK<=0;end
                13:begin SRCLK<=1;            end
                14:begin DIO<=seg[0];SRCLK<=0;end
                15:begin SRCLK<=1;            end

                16:begin DIO<=sel[7];SRCLK<=0;end
                17:begin SRCLK<=1;            end
                18:begin DIO<=sel[6];SRCLK<=0;end
                19:begin SRCLK<=1;            end
                20:begin DIO<=sel[5];SRCLK<=0;end
                21:begin SRCLK<=1;            end
                22:begin DIO<=sel[4];SRCLK<=0;end
                23:begin SRCLK<=1;            end
                24:begin DIO<=sel[3];SRCLK<=0;end
                25:begin SRCLK<=1;            end
                26:begin DIO<=sel[2];SRCLK<=0;end
                27:begin SRCLK<=1;            end
                28:begin DIO<=sel[1];SRCLK<=0;end
                29:begin SRCLK<=1;            end
                30:begin DIO<=sel[0];SRCLK<=0;end
                31:begin SRCLK<=1;            end
            endcase
        end
        
    end

endmodule

数码管动态扫描

module seg(clk,rst_n,data,sel,seg);//hex

    input clk;
    input rst_n;
    input [31:0]data;

    output reg[7:0]sel;//位选
    output reg[7:0]seg;//段选

    reg [15:0] cnt_1ms;
    reg clk_1ms;
    reg [2:0]sel_cnt;
    reg [3:0]data_temp;

    always @(posedge clk or negedge rst_n) begin//1ms分频
        if(!rst_n)begin
          cnt_1ms<=0;
          clk_1ms<=0;
        end
        else begin
          if(cnt_1ms==50000/2-1)begin
            cnt_1ms<=0;
            clk_1ms<=~clk_1ms;
          end
          else begin
              cnt_1ms<=cnt_1ms+1;
          end
        end
        
    end

    always @(posedge clk_1ms or negedge rst_n) begin//1ms计时
        if(!rst_n)begin
          sel_cnt<=0;
        end
        else begin
            if(sel_cnt==8)begin
              sel_cnt<=0;
            end
            else begin
              sel_cnt<=sel_cnt+1;
            end
        end
    end


    always @(posedge clk_1ms) begin//动态扫描
        case(sel_cnt)
            0:begin sel<=8'b0000_0001;data_temp<=data[3:0];end
            1:begin sel<=8'b0000_0010;data_temp<=data[7:4];end
            2:begin sel<=8'b0000_0100;data_temp<=data[11:8];end
            3:begin sel<=8'b0000_1000;data_temp<=data[15:12];end
            4:begin sel<=8'b0001_0000;data_temp<=data[19:16];end
            5:begin sel<=8'b0010_0000;data_temp<=data[23:20];end
            6:begin sel<=8'b0100_0000;data_temp<=data[27:24];end
            7:begin sel<=8'b1000_0000;data_temp<=data[31:28];end
        endcase
    end


    always @(posedge clk_1ms or negedge rst_n) begin
        case(data_temp)
            0:seg<=8'b1100_0000;//0
            1:seg<=8'b1111_1001;//1
            2:seg<=8'b1010_0100;//2
            3:seg<=8'b1011_0000;//3
            4:seg<=8'b1001_1001;//4
            5:seg<=8'b1001_0010;//5
            6:seg<=8'b1000_0010;//6
            7:seg<=8'b1111_1000;//7
            8:seg<=8'b1000_0000;//8
            9:seg<=8'b1001_0000;//9
            10:seg<=8'b1000_1000;//a
            11:seg<=8'b1000_0011;//b
            12:seg<=8'b1100_0110;//c
            13:seg<=8'b1010_0001;//d
            14:seg<=8'b1000_0110;//e
            15:seg<=8'b1000_1110;//f
        endcase
    end
endmodule

74HC595驱动数码管通用测试代码

module hex8_hc595_test(clk,rst_n,data,DIO,SRCLK,RCLK);
    input clk;
    input rst_n;
    input [31:0]data;

    output DIO,SRCLK,RCLK;


    wire [7:0]sel;
    wire [7:0]seg;

   
    seg seg0(
        .clk(clk),
        .rst_n(rst_n),
        .data(data),
        .sel(sel),
        .seg(seg)
    );

    HC595_driver HC595_driver0(
        .clk(clk),
        .rst_n(rst_n),
        .seg(seg),
        .sel(sel),
        .DIO(DIO),
        .SRCLK(SRCLK),
        .RCLK(RCLK)
    );


endmodule

按键消抖模块

module key_filter(Clk,Rst_n,key_in,key_flag,key_state);

	input Clk;
	input Rst_n;
	input key_in;
	
	output reg key_flag;
	output reg key_state;
	
	localparam
		IDEL		= 4'b0001,
		FILTER0	= 4'b0010,
		DOWN		= 4'b0100,
		FILTER1 	= 4'b1000;
		
	reg [3:0]state;
	reg [19:0]cnt;
	reg en_cnt;	//使能计数寄存器
	
//对外部输入的异步信号进行同步处理
	reg key_in_sa,key_in_sb;
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)begin
		key_in_sa <= 1'b0;
		key_in_sb <= 1'b0;
	end
	else begin
		key_in_sa <= key_in;
		key_in_sb <= key_in_sa;	
	end
	
	reg key_tmpa,key_tmpb;
	wire pedge,nedge;
	reg cnt_full;//计数满标志信号
	
//使用D触发器存储两个相邻时钟上升沿时外部输入信号(已经同步到系统时钟域中)的电平状态
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)begin
		key_tmpa <= 1'b0;
		key_tmpb <= 1'b0;
	end
	else begin
		key_tmpa <= key_in_sb;
		key_tmpb <= key_tmpa;	
	end

//产生跳变沿信号	
	assign nedge = !key_tmpa & key_tmpb;
	assign pedge = key_tmpa & (!key_tmpb);
	
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)begin
		en_cnt <= 1'b0;
		state <= IDEL;
		key_flag <= 1'b0;
		key_state <= 1'b1;
	end
	else begin
		case(state)
			IDEL :
				begin
					key_flag <= 1'b0;
					if(nedge)begin
						state <= FILTER0;
						en_cnt <= 1'b1;
					end
					else
						state <= IDEL;
				end
					
			FILTER0:
				if(cnt_full)begin
					key_flag <= 1'b1;
					key_state <= 1'b0;
					en_cnt <= 1'b0;
					state <= DOWN;
				end
				else if(pedge)begin
					state <= IDEL;
					en_cnt <= 1'b0;
				end
				else
					state <= FILTER0;
					
			DOWN:
				begin
					key_flag <= 1'b0;
					if(pedge)begin
						state <= FILTER1;
						en_cnt <= 1'b1;
					end
					else
						state <= DOWN;
				end
			
			FILTER1:
				if(cnt_full)begin
					key_flag <= 1'b1;
					key_state <= 1'b1;
					en_cnt <= 1'b0;
					state <= IDEL;
				end
				else if(nedge)begin
					en_cnt <= 1'b0;
					state <= DOWN;
				end
				else
					state <= FILTER1;
			
			default:
				begin 
					state <= IDEL; 
					en_cnt <= 1'b0;		
					key_flag <= 1'b0;
					key_state <= 1'b1;
				end
				
		endcase	
	end
	
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		cnt <= 20'd0;
	else if(en_cnt)
		cnt <= cnt + 1'b1;
	else
		cnt <= 20'd0;
	
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		cnt_full <= 1'b0;
	else if(cnt == 999_999)
		cnt_full <= 1'b1;
	else
		cnt_full <= 1'b0;	

endmodule

RTL图

alt text

板级验证

按下按键,则数码管显示对应的采样数值


本文章使用limfx的vscode插件快速发布