显示页面 讨论 修订记录 反向链接 本页面只读。您可以查看源文件,但不能更改它。如果您觉得这是系统错误,请联系管理员。 ======基于STEP FPGA的PCF8591的ADC(I2C)功能驱动====== 本节将和大家一起使用FPGA驱动底板上的PCF8591的ADC采样(I2C)功能。 ====硬件说明==== ------- PCF8591是集成了4路ADC和1路DAC的芯片,使用I2C总线通信。 \\ I2C总线是由Philips公司开发的一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。主器件用于启动总线传送数据,并产生时钟以开放传送的器件,此时任何被寻址的器件均被认为是从器件。如果主机要发送数据给从器件,则主机首先寻址从器件,然后主动发送数据至从器件,最后由主机终止数据传送;如果主机要接收从器件的数据,首先由主器件寻址从器件,然后主机接收从器件发送的数据,最后由主机终止接收过程。这里不做过多的讲解,硬件连接如下: {{ :i2c总线框架.jpg?800 |}} \\ 本设计的硬件连接如下 {{ :pcf8591硬件连接.jpg?1000 |}} \\ 本设计中FPGA作为I2C主设备,PCF8591作为I2C从设备,从设备的地址由固定地址和可编程地址组成,我们的外设底板已将可编程地址A0、A1、A2接地,所以7位地址为7'h48,加上最低位的读写控制,所以给PCF8591写数据时的寻址地址为8'h90,对PCF8591读数据时的寻址地址为8'h91。如下 {{ :pcf8591_i2c地址.jpg?800 |}} \\ PCF8591集成了很多功能,当需要不同的功能时要对PCF8591做相应的配置,配置数据存储在名为CONTROL BYTE的寄存器中,下图展示了寄存器中部分bit的功能,详细请参考PCF8591的datasheet,本设计中我们只使用通道1的ADC功能,配置数据为8'h01。 {{ :pcf8591_控制字.jpg?800 |}} \\ 本设计中我们需要两次通信, * 第一次为配置数据,具体为:开始--写寻址--读响应--写配置数据--读响应--结束 * 第二次为读ADC数据,具体为:开始--读寻址--读响应--[读ADC数据--写响应--]循环读 第二次的时序如下图: {{ :pcf8591_adc时序.jpg?1000 |}} \\ 通过上面的介绍大家应该对如何驱动PCF8591进行ADC采样有了整体的概念,还有一些细节就是I2C通信的时序明细,如下图 {{ :pcf8591_时序控制.jpg?800 |}} {{ :pcf8591_时序控制2.jpg?800 |}} ====Verilog代码==== ------ <code verilog> // -------------------------------------------------------------------- // >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<< // -------------------------------------------------------------------- // Module: Uart_bus // // Author: Step // // Description: The module for uart communication // // Web: www.stepfapga.com // // -------------------------------------------------------------------- // Code Revision History : // -------------------------------------------------------------------- // Version: |Mod. Date: |Changes Made: // V1.0 |2016/04/20 |Initial ver // -------------------------------------------------------------------- module Uart_Bus # ( parameter BPS_PARA = 1250 //当使用12MHz时钟时波特率参数选择1250对应9600的波特率 ) ( input clk_in, //系统时钟 input rst_n_in, //系统复位,低有效 input rs232_rx, //FPGA中UART接收端,分配给UART模块中的发送端TXD output rs232_tx //FPGA中UART发送端,分配给UART模块中的接收端RXD ); /////////////////////////////////UART接收功能模块例化//////////////////////////////////// wire bps_en_rx,bps_clk_rx; wire [7:0] rx_data; //UART接收波特率时钟控制模块 例化 Baud # ( .BPS_PARA (BPS_PARA ) ) Baud_rx ( .clk_in (clk_in ), //系统时钟 .rst_n_in (rst_n_in ), //系统复位,低有效 .bps_en (bps_en_rx ), //接收时钟使能 .bps_clk (bps_clk_rx ) //接收时钟输出 ); //UART接收数据模块 例化 Uart_Rx Uart_Rx_uut ( .clk_in (clk_in ), //系统时钟 .rst_n_in (rst_n_in ), //系统复位,低有效 .bps_en (bps_en_rx ), //接收时钟使能 .bps_clk (bps_clk_rx ), //接收时钟输入 .rs232_rx (rs232_rx ), //UART接收输入 .rx_data (rx_data ) //接收到的数据 ); /////////////////////////////////UART发送功能模块例化//////////////////////////////////// wire bps_en_tx,bps_clk_tx; //UART发送波特率时钟控制模块 例化 Baud # ( .BPS_PARA (BPS_PARA ) ) Baud_tx ( .clk_in (clk_in ), //系统时钟 .rst_n_in (rst_n_in ), //系统复位,低有效 .bps_en (bps_en_tx ), //发送时钟使能 .bps_clk (bps_clk_tx ) //发送时钟输出 ); //UART发送数据模块 例化 Uart_Tx Uart_Tx_uut ( .clk_in (clk_in ), //系统时钟 .rst_n_in (rst_n_in ), //系统复位,低有效 .bps_en (bps_en_tx ), //发送时钟使能 .bps_clk (bps_clk_tx ), //发送时钟输入 .rx_bps_en (bps_en_rx ), //因需要自收自发,使用接收时钟使能判定:接收到新的数据,需要发送 .tx_data (rx_data ), //需要发出的数据 .rs232_tx (rs232_tx ) //UART发送输出 ); endmodule </code> \\ <code verilog> // -------------------------------------------------------------------- // >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<< // -------------------------------------------------------------------- // Module: Baud // // Author: Step // // Description: Beat for uart transfer and receive baud rate // // Web: www.stepfapga.com // // -------------------------------------------------------------------- // Code Revision History : // -------------------------------------------------------------------- // Version: |Mod. Date: |Changes Made: // V1.0 |2016/04/20 |Initial ver // -------------------------------------------------------------------- module Baud # ( parameter BPS_PARA = 1250 //当使用12MHz时钟时波特率参数选择1250对应9600的波特率 ) ( input clk_in, //系统时钟 input rst_n_in, //系统复位,低有效 input bps_en, //接收或发送时钟使能 output reg bps_clk //接收或发送时钟输出 ); reg [12:0] cnt; //计数器计数满足波特率时钟要求 always @ (posedge clk_in or negedge rst_n_in) begin if(!rst_n_in) cnt <= 1'b0; else if((cnt >= BPS_PARA-1)||(!bps_en)) //当时钟信号不使能(bps_en为低电平)时,计数器清零并停止计数 cnt <= 1'b0; //当时钟信号使能时,计数器对系统时钟计数,周期为BPS_PARA个系统时钟周期 else cnt <= cnt + 1'b1; end //产生相应波特率的时钟节拍,接收模块将以此节拍进行UART数据接收 always @ (posedge clk_in or negedge rst_n_in) begin if(!rst_n_in) bps_clk <= 1'b0; else if(cnt == (BPS_PARA>>1)) //BPS_PARA右移一位等于除2,因计数器终值BPS_PARA为数据更替时间点,所以计数器中值时为数据最稳定时间点 bps_clk <= 1'b1; else bps_clk <= 1'b0; end endmodule </code> \\ <code verilog> // -------------------------------------------------------------------- // >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<< // -------------------------------------------------------------------- // Module: Uart_Rx // // Author: Step // // Description: The receive module of uart interface // // Web: www.stepfapga.com // // -------------------------------------------------------------------- // Code Revision History : // -------------------------------------------------------------------- // Version: |Mod. Date: |Changes Made: // V1.0 |2016/04/20 |Initial ver // -------------------------------------------------------------------- module Uart_Rx ( input clk_in, //系统时钟 input rst_n_in, //系统复位,低有效 output reg bps_en, //接收时钟使能 input bps_clk, //接收时钟输入 input rs232_rx, //UART接收输入 output reg [7:0] rx_data //接收到的数据 ); reg rs232_rx0,rs232_rx1,rs232_rx2; //多级延时锁存去除亚稳态 always @ (posedge clk_in or negedge rst_n_in) begin if(!rst_n_in) begin rs232_rx0 <= 1'b0; rs232_rx1 <= 1'b0; rs232_rx2 <= 1'b0; end else begin rs232_rx0 <= rs232_rx; rs232_rx1 <= rs232_rx0; rs232_rx2 <= rs232_rx1; end end //检测UART接收输入信号的下降沿 wire neg_rs232_rx = rs232_rx2 & rs232_rx1 & (~rs232_rx0) & (~rs232_rx); reg [3:0] num; //接收时钟使能信号的控制 always @ (posedge clk_in or negedge rst_n_in) begin if(!rst_n_in) bps_en <= 1'b0; else if(neg_rs232_rx && (!bps_en)) //当空闲状态(bps_en为低电平)时检测到UART接收信号下降沿,进入工作状态(bps_en为高电平),控制时钟模块产生接收时钟 bps_en <= 1'b1; else if(num==4'd9) //当完成一次UART接收操作后,退出工作状态,恢复空闲状态 bps_en <= 1'b0; end reg [7:0] rx_data_r; //当处于工作状态中时,按照接收时钟的节拍获取数据 always @ (posedge clk_in or negedge rst_n_in) begin if(!rst_n_in) begin num <= 4'd0; rx_data <= 8'd0; rx_data_r <= 8'd0; end else if(bps_en) begin if(bps_clk) begin num <= num+1'b1; if(num<=4'd8) rx_data_r[num-1]<=rs232_rx; //先接受低位再接收高位,8位有效数据 end else if(num == 4'd9) begin //完成一次UART接收操作后,将获取的数据输出 num <= 4'd0; rx_data <= rx_data_r; end end end endmodule </code> \\ <code verilog> // -------------------------------------------------------------------- // >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<< // -------------------------------------------------------------------- // Module: Uart_Tx // // Author: Step // // Description: The transfer module of uart interface // // Web: www.stepfapga.com // // -------------------------------------------------------------------- // Code Revision History : // -------------------------------------------------------------------- // Version: |Mod. Date: |Changes Made: // V1.0 |2016/04/20 |Initial ver // -------------------------------------------------------------------- module Uart_Tx ( input clk_in, //系统时钟 input rst_n_in, //系统复位,低有效 output reg bps_en, //发送时钟使能 input bps_clk, //发送时钟输入 input rx_bps_en, //因需要自收自发,使用接收时钟使能判定:接收到新的数据,需要发送 input [7:0] tx_data, //需要发出的数据 output reg rs232_tx //UART发送输出 ); reg rx_bps_en_r; //延时锁存接收时钟使能信号 always @ (posedge clk_in or negedge rst_n_in) begin if(!rst_n_in) rx_bps_en_r <= 1'b0; else rx_bps_en_r <= rx_bps_en; end //检测接收时钟使能信号的下降沿,因为下降沿代表接收数据的完成,以此作为发送信号的激励 wire neg_rx_bps_en = rx_bps_en_r & (~rx_bps_en); reg [3:0] num; reg [9:0] tx_data_r; //根据接收数据的完成,驱动发送数据操作 always @ (posedge clk_in or negedge rst_n_in) begin if(!rst_n_in) begin bps_en <= 1'b0; tx_data_r <= 8'd0; end else if(neg_rx_bps_en)begin bps_en <= 1'b1; //当检测到接收时钟使能信号的下降沿,表明接收完成,需要发送数据,使能发送时钟使能信号 tx_data_r <= {1'b1,tx_data,1'b0}; end else if(num==4'd10) begin bps_en <= 1'b0; //一次UART发送需要10个时钟信号,然后结束 end end //当处于工作状态中时,按照发送时钟的节拍发送数据 always @ (posedge clk_in or negedge rst_n_in) begin if(!rst_n_in) begin num <= 1'b0; rs232_tx <= 1'b1; end else if(bps_en) begin if(bps_clk) begin num <= num + 1'b1; rs232_tx <= tx_data_r[num]; end else if(num>=4'd10) num <= 4'd0; end end endmodule </code> ====小结==== ------ 本节主要为大家讲解了UART通信的原理及软件设计,需要大家掌握的同时自己创建工程,通过整个设计流程,生成FPGA配置文件加载测试。 \\ 如果你对Diamond软件的使用不了解,请参考这里:[[lattice_diamond的使用|Diamond的使用]]。 ====相关资料==== ------ \\ 使用[[STEP-MXO2第二代]]的UART通信程序: 后续会有下载连接 待更新 \\ 使用[[STEP-MAX10]]的UART通信程序: 后续会有下载连接 待更新 \\