FPGA SPI 驱动程序

这篇具有很好参考价值的文章主要介绍了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)状态控制程序

其主要是以空状态,写状态,读状态三个状态顺序执行的。文章来源地址https://www.toymoban.com/news/detail-691575.html

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 SPI 驱动程序的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【最通用版FPGA 实现 SPI 驱动】

    最近研究了一下SPI协议的FPGA实现,发现网上很多大佬分享的方法都是针对某一特定的flash芯片或者某一传感器芯片来设计电路结构的。所以想根据SPI(Serial Peripheral Interface)的基本通讯协议实现一个通用版的SPI Master驱动。SPI在嵌入式领域是一个很成熟且应用非常广泛的通信协

    2024年02月05日
    浏览(32)
  • RN8209 RN8302 spi驱动和校表程序STM32

    2024年01月25日
    浏览(25)
  • FPGA驱动SPI接口的LCD(三)——LCD的初始化

    LCD初始化函数 void LCD_Init(void);  首先是LCD的复位 void LCD_RESET(void) {     LCD_RST_CLR;        //拉低复位引脚     Delay_Ms(100);        //延时100ms         LCD_RST_SET;        //拉高复位引脚     Delay_Ms(50);        //延时50ms } 向LCD屏幕写入一个8位命令 void LCD_WR_REG(u8 data) {     

    2024年02月01日
    浏览(31)
  • FPGA实现基于SPI协议的Flash驱动控制(全擦除、页擦除、读数据、页写、连续写—地址写)

    本论文使用Verilog HDL硬件描述语言,结合野火可以FPGA征途Pro开发板,实现了SPI通信协议的全擦除,扇区擦除,读数据,页写,连续写的驱动设计。在 Altera Cyclone Ⅳ 芯片上采用“自顶向下”的模块化设计思想及 Verilog HDL 硬件描述语言,设计并实现串行外设接口(SPI)。在 Qu

    2024年02月12日
    浏览(34)
  • Linux驱动开发—最详细应用程序调用驱动程序解析

    Linux下进行驱动开发,完全将驱动程序与应用程序隔开,中间通过 C标准库函数 以及 系统调用 完成驱动层和应用层的数据交换。 驱动加载成功以后会在“/dev”目录下生成一个相应的文件,应用程序通过 对“/dev/xxx” (xxx 是具体的驱动文件名字) 的文件进行相应的操作 即可实

    2024年02月16日
    浏览(34)
  • windows驱动开发7:应用程序和驱动程序的通信

    一、基础介绍 1.1 设备与驱动的关系 设备由驱动去创建,访问一个设备,是首先得访问驱动。如果驱动在卸载的时候没有删除符号,r3下也是不能去访问设备的。 驱动程序和系统其他组件之间的交互是通过给设备发送或者接受发给设备的请求来交互的。换句话说,一个没有任

    2023年04月08日
    浏览(31)
  • Linux 驱动开发基础知识——Hello驱动程序(一)

     个人名片: 🦁作者简介:一名喜欢分享和记录学习的在校大学生 🐯个人主页:妄北y 🐧个人QQ:2061314755 🐻个人邮箱:2061314755@qq.com 🦉个人WeChat:Vir2021GKBS 🐼 本文由妄北y原创,首发CSDN 🎊🎊🎊 🐨座右铭:大多数人想要改造这个世界,但却罕有人想改造自己。 专栏导

    2024年01月19日
    浏览(32)
  • Linux 驱动开发基础知识——认识LED驱动程序 (二)

     个人名片: 🦁作者简介:一名喜欢分享和记录学习的在校大学生 🐯个人主页:妄北y 🐧个人QQ:2061314755 🐻个人邮箱:2061314755@qq.com 🦉个人WeChat:Vir2021GKBS 🐼 本文由妄北y原创,首发CSDN 🎊🎊🎊 🐨座右铭:大多数人想要改造这个世界,但却罕有人想改造自己。 专栏导

    2024年01月21日
    浏览(31)
  • FPGA程序烧录方式:JTAG调试与SPI固化

    用JTAG方式烧写后,已经在FPGA中没有存储了,相当于这时候已经对FPGA做了编程,断电后FPGA需要重新烧写才能用。 按照严格来讲那个不是程序,可以说是在FPGA上直接编程,可以说存在整个芯片中。 在用JTAG烧录的时候下载进去的是二进制文件bit类型的,而在vivado中bit文件可能

    2024年02月08日
    浏览(44)
  • 嵌入式Linux驱动开发 02:将驱动程序添加到内核中

    在上一篇文章 《嵌入式Linux驱动开发 01:基础开发与使用》 中我们已经实现了最基础的驱动功能。在那篇文章中我们的驱动代码是独立于内核代码存放的,并且我们的驱动编译后也是一个独立的模块。在实际使用中将驱动代码放在内核代码中,并将驱动编译到内核中也是比较

    2023年04月09日
    浏览(57)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包