FPGA SPI 驱动程序

1.引言

此驱动程序已经完成很久了,花了2个星期的时间,主要是提升程序运行的效率。最近整理文件的时候又看到了,记录一下。

2.程序框架分解

module adc7254_Ctrl(
    input               sys_clk,    //system clkc 50M
    input               reset_n,    //reset flag
    input               iData_a_in,      //ADC to fpga 
    input               iData_b_in, 
	 
    output               sclk_out,   //to ADC 
    output              cs_out,     //to ADC  
    output              sdin,    //to ADC
	 output 		 [11:0]	oData_a, //get data
	 output		 [11:0]	oData_b	//get data
);
wire clk_200M;                   //PLL驱动
AD_PLL AD_PLL_inst(
	.inclk0(sys_clk),
	.c0(Clk_200M),
	.c1(sclk_out)
);
wire En_conv,En_send;            //定义ADC发送和接收程序的状态切换时间
adc_test adc_test_inst(
    .iRst_n(reset_n),
    .iDclk(sclk_out),
    .iSend_down(Send_down),

    .oAdc_rst_n(adc_rst_n),
    .oEn_conv(En_conv),
    .oEn_send(En_send)
);
wire Send_down;                   //ADC指令发送部分
adc_in_send adc_in_send_inst
(
    .iClk_200M(Clk_200M),
    .iRst_n(adc_rst_n),
    .iDcLK(sclk_out),
    .iEn_send(En_send),
    .oSDATA(sdin),
    .oSend_down(Send_down)
); 
wire Conv_down;
adc_out_conv adc_out_conv_inst				//ADC数据采样部分
(
	.iClk_200M		( Clk_200M  	    ),
	.iRst_n		   ( adc_rst_n 	 ),
	.iEn_conv		( En_conv 		),
	.iDcLK			( sclk_out 		),
	.iData_a_in		( iData_a_in 	),
	.iData_b_in		( iData_b_in 	),
	
	.oData_a		( oData_a	 	),	
	.oData_b		( oData_b 		),
    .oConv_down     ( Conv_down     )
);
assign cs_out = Conv_down & Send_down;       //状态完成
endmodule

3.子任务分解

(1)状态控制程序

其主要是以空状态,写状态,读状态三个状态顺序执行的。

module adc_test(
    input iRst_n,
    input iDclk,
    input iSend_down,

    output oAdc_rst_n,
    output reg oEn_send,
    output reg oEn_conv
);

reg [1:0] state;
reg [5:0] sclk_cnt;
localparam state_IDLE  = 2'd0;
localparam state_Write = 2'd1;
localparam state_Read  = 2'd3;

assign oAdc_rst_n = (iRst_n & state);
always @(posedge iDclk or negedge iRst_n) begin
    if(!iRst_n)begin
        sclk_cnt <= 6'd0;
        state <= state_IDLE;
		  oEn_conv = 1'd0;
        oEn_send = 1'd0;  
    end else begin   
        case(state)
            state_IDLE:
                begin
                    if (sclk_cnt > 6'd30 ) begin
                        sclk_cnt <= 6'd0;
                        state <= state_Write;
                        oEn_conv = 1'd0;
                        oEn_send = 1'd1;
                    end else begin
                        sclk_cnt <= sclk_cnt + 1'd1;
                        state <= state_IDLE; 
                        oEn_conv = 1'd0;
                        oEn_send = 1'd0;   
                    end    
                end
            state_Write:
                begin
                    if (iSend_down == 1'd1 && sclk_cnt > 6'd30 ) begin
                        sclk_cnt <= 0;
                        state <= state_Read;
                        oEn_conv = 1'd1;
                        oEn_send = 1'd0;
                    end else begin
                        sclk_cnt <= sclk_cnt + 1'd1;
                        state <= state_Write;
                        oEn_conv = 1'd0;
                        oEn_send = 1'd1;
                    end
                end
            state_Read:
                begin
                    state <= state_Read;
                    sclk_cnt <= 1'd0;
                    oEn_conv = 1'd1;
                    oEn_send = 1'd0;
                end
        endcase
    end
end

endmodule

(2)写命令程序部分

module adc_in_send(
    input iClk_200M,     //200M
    input iRst_n,

    input iDcLK,    //最小T>60ns 
    input iEn_send,	 
	 output  oSDATA,
    output oSend_down     
  );
  
  //==================使能接收标志位en==================//
  //一旦启动不会突然停止除非复位信号到来
  reg en;
  reg [5:0] sclk_cnt;
  always @(posedge iDcLK or negedge iRst_n ) begin
    if (!iRst_n) begin
        en <= 1'd0;
        sclk_cnt <= 6'd0;
    end else if ( iEn_send == 1'd1 && sclk_cnt == 6'd0 ) begin
        en <= 1'd1;
        sclk_cnt <= 6'd32;
    end else if ( sclk_cnt > 6'd1 ) begin
        en <= en;
        sclk_cnt <= sclk_cnt - 1'd1;
    end else if (oSend_down == 1'd1 && sclk_cnt == 6'd1 ) begin
        en <= 1'd0;
        sclk_cnt <= sclk_cnt - 1'd1;
    end else begin
        en <= en;
        sclk_cnt <= sclk_cnt;
    end
  end
//==================使能接收标志位en==================//
//==================SDATA输出操作=========================//
reg [15:0]CFR_16bit_data = 16'h8840;	//需要写入寄存器中的数据
assign oSDATA = (en > 1'd0) ? ((sclk_cnt > 6'd17) ? CFR_16bit_data[sclk_cnt-6'd17] : 0 ): 0;
//==================SDATA操作=========================//
//==================oSend_down操作======================//
assign oSend_down = (sclk_cnt > 6'd1) ? 0 : 1;
//==================oSend_down操作======================//
endmodule

(3)读数据程序部分

module adc_out_conv(
    input iClk_200M,     //200M
    input iRst_n,
    input iData_a_in,
    input iData_b_in,  
    input iDcLK,    //最小T=60ns 
    input iEn_conv,
	 
    output reg [11:0] oData_a,
    output reg [11:0] oData_b,
    output  oConv_down     //T>70ns
  );
//下降沿接收
//==================使能接收标志位en==================//
//一旦启动不会突然停止除非复位信号到来
reg en;//接收使能标志位
reg [5:0] sclk_cnt;
always @(posedge iDcLK or negedge iRst_n ) begin
  if (!iRst_n) begin
    en <= 1'd0;
    sclk_cnt <= 5'd0;
  end else if (iEn_conv == 1'd1   &&  sclk_cnt == 4'd0) begin
    en <= 1'd1;
    sclk_cnt <= 6'd17;
  end else if (sclk_cnt > 4'd1) begin
	 en <= en;
	 sclk_cnt  <= sclk_cnt - 1'd1;
  end else if (oConv_down == 1'd1 &&  sclk_cnt == 4'd1)begin
    en <= 1'd0;
    sclk_cnt <= sclk_cnt - 1'd1;
  end else begin
    en <= en;
    sclk_cnt <= sclk_cnt;
  end
end
//==================使能接收标志位en==================//
//==================dclk时钟采样==================//
reg	[6:0] dclk;
always@(posedge iClk_200M or negedge iRst_n) begin
	 if(!iRst_n) begin
      dclk <= 7'd0;
    end	else if(!en) begin
		dclk <= 7'd0;
	 end  else begin
      dclk <= {dclk[5:0],iDcLK};
    end
			
	end
//==================dclk时钟采样==================//
//==================状态切换==================//
    reg [1:0] state;
    parameter state_IDLE = 2'd0;
    parameter state_Read = 2'd1;
    parameter state_Write = 2'd2;
      always@(posedge iClk_200M or negedge iRst_n)  begin
          if(!iRst_n ) begin
            state <= state_IDLE;
          end else if(!en) begin
              state <= state_IDLE;
            end else if(dclk[1] == 1 & dclk[2] == 0) begin
            state <= state_Read;
          end else if (dclk[1] == 0 & dclk[2] == 1) begin
            state <= state_Write;
          end else begin
            state <=state;
          end            
        end
//==================状态切换==================//
//==================data串行转并行==================//
reg [2:0] Data_a_in_temp,Data_b_in_temp;//保证7次采样有4次为1
reg [11:0]  Data_a_temp,Data_b_temp;
always@(posedge iClk_200M or negedge iRst_n)
begin
    if(!iRst_n ) begin
        Data_a_temp    <= 12'd0;
        Data_b_temp    <= 12'd0;
        Data_a_in_temp <= 3'd0;
        Data_b_in_temp <= 3'd0;
    end else if(sclk_cnt > 6'd16)begin
                Data_a_in_temp <= 3'd0;
                Data_b_in_temp <= 3'd0;
                Data_a_temp    <= 12'd0;
                Data_b_temp    <= 12'd0;
    end else if(sclk_cnt > 6'd3 ) begin
        if(state == state_Read && dclk[6] == 0 ) begin
                Data_a_in_temp <= Data_a_in_temp + iData_a_in;
                Data_b_in_temp <= Data_b_in_temp + iData_b_in;
                Data_a_temp    <= Data_a_temp;
                Data_b_temp    <= Data_b_temp;
        end else if(state == state_Write && dclk[0]!=dclk[1])begin
                Data_a_in_temp <= 3'd0;
                Data_b_in_temp <= 3'd0;
                Data_a_temp    <= {Data_a_temp[10:0],Data_a_in_temp[2]};
                Data_b_temp    <= {Data_b_temp[10:0],Data_b_in_temp[2]};
        end else begin
                Data_a_in_temp <= Data_a_in_temp;
                Data_b_in_temp <= Data_b_in_temp;
                Data_a_temp    <= Data_a_temp;
                Data_b_temp    <= Data_b_temp;
        end
    end else begin
                Data_a_in_temp <= Data_a_in_temp;
                Data_b_in_temp <= Data_b_in_temp;
                Data_a_temp    <= Data_a_temp;
                Data_b_temp    <= Data_b_temp;
    end
end
//==================data串行转并行==================//
//==================oConv_down操作======================//
assign oConv_down = (sclk_cnt > 6'd1) ? 0 : 1;
//==================oConv_down操作======================//
//==================数据按帧输出==================//
always@(posedge iClk_200M or negedge iRst_n)
begin
  if(!iRst_n )
    begin
      oData_a <= 12'd0;
      oData_b <= 12'd0;
    end
  else if( oConv_down == 1'd1)
    begin
      oData_a <= Data_a_temp;
      oData_b <= Data_b_temp;
    end
  else
    begin
      oData_a <= oData_a;
      oData_b <= oData_b;
    end
end
//==================数据按帧输出==================//
endmodule

你可能感兴趣的:(FPGA,fpga开发)