差别
这里会显示出您选择的修订版和当前版本之间的差别。
后一修订版 | 前一修订版 | ||
adc数模转换模块 [2017/06/23 15:19] anran 创建 |
adc数模转换模块 [2022/07/20 10:26] (当前版本) zhijun [小结] |
||
---|---|---|---|
行 1: | 行 1: | ||
- | ======基于STEP FPGA的矩阵按键驱动====== | + | ======基于STEP FPGA的PCF8591的ADC(I2C)功能驱动====== |
- | 本节将和大家一起使用FPGA驱动底板上的UART接口通信。 | + | 本节将和大家一起使用[[FPGA]]驱动底板上的PCF8591的[[ADC]]采样([[I2C]])功能。 |
====硬件说明==== | ====硬件说明==== | ||
+ | |||
------- | ------- | ||
- | 通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),通常称作UART,是一种通用串行数据总线,用于异步通信。该总线双向通信,可以实现全双工传输和接收。 | + | PCF8591是集成了4路[[ADC]]和1路[[DAC]]的芯片,使用[[I2C]]总线通信。 |
\\ | \\ | ||
- | 异步通信以一个字符为传输单位,通信中两个字符间的时间间隔多少是不固定的,然而在同一个字符中的两个相邻位间的时间间隔是固定的。两个相邻位间的时间间隔与UART通信的波特率有关,波特率用来表征UART通信中数据传输的速率,即每秒钟传送的二进制位数。例如数据传送速率为120字符/秒,而每一个字符为10位(1个起始位,7个数据位,1个校验位,1个结束位),则其传送的波特率为10×120=1200字符/秒=1200波特。 | + | [[I2C]]总线是由Philips公司开发的一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。主器件用于启动总线传送数据,并产生时钟以开放传送的器件,此时任何被寻址的器件均被认为是从器件。如果主机要发送数据给从器件,则主机首先寻址从器件,然后主动发送数据至从器件,最后由主机终止数据传送;如果主机要接收从器件的数据,首先由主器件寻址从器件,然后主机接收从器件发送的数据,最后由主机终止接收过程。这里不做过多的讲解,硬件连接如下: |
+ | {{ :i2c总线框架.jpg?800 |}} | ||
\\ | \\ | ||
- | {{ :uart时序.jpg?800 |}} | + | 本设计的硬件连接如下 |
+ | {{ :pcf8591硬件连接.jpg?1000 |}} | ||
\\ | \\ | ||
- | * 起始位:先发出一个逻辑”0”信号,表示传输字符的开始。 | + | 本设计中FPGA作为[[I2C]]主设备,PCF8591作为[[I2C]]从设备,从设备的地址由固定地址和可编程地址组成,我们的外设底板已将可编程地址A0、A1、A2接地,所以7位地址为7'h48,加上最低位的读写控制,所以给PCF8591写数据时的寻址地址为8'h90,对PCF8591读数据时的寻址地址为8'h91。如下 |
- | * 数据位:可以是5~8位逻辑”0”或”1”。如ASCII码(7位),扩展BCD码(8位)。小端传输 | + | {{ :pcf8591_i2c地址.jpg?800 |}} |
- | * 校验位:数据位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验) | + | |
- | * 停止位:它是一个字符数据的结束标志。可以是1位、1.5位、2位的高电平。 | + | |
- | * 空闲位:处于逻辑“1”状态,表示当前线路上没有资料传送。 | + | |
\\ | \\ | ||
- | 我们这里使用的时序为去掉校验位的时序 | + | PCF8591集成了很多功能,当需要不同的功能时要对PCF8591做相应的配置,配置数据存储在名为CONTROL BYTE的寄存器中,下图展示了寄存器中部分bit的功能,详细请参考PCF8591的datasheet,本设计中我们只使用通道1的ADC功能,配置数据为8'h01。 |
- | {{ :uart4.png?600 |}} | + | {{ :pcf8591_控制字.jpg?800 |}} |
\\ | \\ | ||
- | 本设计共有四个模块,一个top模块,一个baud模块,一个接收模块和一个发送模块,大家可以根据自己的需求进行调整。 | + | 本设计中我们需要两次通信, |
+ | * 第一次为配置数据,具体为:开始--写寻址--读响应--写配置数据--读响应--结束 | ||
+ | * 第二次为读ADC数据,具体为:开始--读寻址--读响应--[读ADC数据--写响应--]循环读 | ||
+ | 第二次的时序如下图: | ||
+ | {{ :pcf8591_adc时序.jpg?1000 |}} | ||
+ | \\ | ||
+ | 通过上面的介绍大家应该对如何驱动PCF8591进行[[ADC]]采样有了整体的概念,还有一些细节就是[[I2C]]通信的时序明细,如下图 | ||
+ | {{ :pcf8591_时序控制.jpg?800 |}} | ||
+ | {{ :pcf8591_时序控制2.jpg?800 |}} | ||
====Verilog代码==== | ====Verilog代码==== | ||
+ | |||
------ | ------ | ||
<code verilog> | <code verilog> | ||
行 28: | 行 38: | ||
// >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<< | // >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<< | ||
// -------------------------------------------------------------------- | // -------------------------------------------------------------------- | ||
- | // Module: Uart_bus | + | // Module: ADC_I2C |
// | // | ||
// Author: Step | // Author: Step | ||
// | // | ||
- | // Description: The module for uart communication | + | // Description: ADC_I2C |
- | // | + | |
- | // Web: www.stepfapga.com | + | |
// | // | ||
+ | // Web: www.stepfpga.com | ||
+ | // | ||
// -------------------------------------------------------------------- | // -------------------------------------------------------------------- | ||
// Code Revision History : | // Code Revision History : | ||
// -------------------------------------------------------------------- | // -------------------------------------------------------------------- | ||
// Version: |Mod. Date: |Changes Made: | // Version: |Mod. Date: |Changes Made: | ||
- | // V1.0 |2016/04/20 |Initial ver | + | // V1.1 |2016/10/30 |Initial ver |
// -------------------------------------------------------------------- | // -------------------------------------------------------------------- | ||
- | module Uart_Bus # | + | module ADC_I2C |
( | ( | ||
- | parameter BPS_PARA = 1250 //当使用12MHz时钟时波特率参数选择1250对应9600的波特率 | + | input clk_in, //系统时钟 |
- | ) | + | input rst_n_in, //系统复位,低有效 |
- | ( | + | output scl_out, //I2C总线SCL |
- | input clk_in, //系统时钟 | + | inout sda_out, //I2C总线SDA |
- | input rst_n_in, //系统复位,低有效 | + | output reg adc_done, //ADC采样完成标志 |
- | input rs232_rx, //FPGA中UART接收端,分配给UART模块中的发送端TXD | + | output reg [7:0] adc_data //ADC采样数据 |
- | 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 ) //接收到的数据 | + | |
); | ); | ||
+ | parameter CNT_NUM = 15; | ||
- | /////////////////////////////////UART发送功能模块例化//////////////////////////////////// | + | localparam IDLE = 3'd0; |
- | wire bps_en_tx,bps_clk_tx; | + | localparam MAIN = 3'd1; |
- | + | localparam START = 3'd2; | |
- | //UART发送波特率时钟控制模块 例化 | + | localparam WRITE = 3'd3; |
- | Baud # | + | localparam READ = 3'd4; |
- | ( | + | localparam STOP = 3'd5; |
- | .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数据接收 | + | //根据PCF8591的datasheet,I2C的频率最高为100KHz, |
- | always @ (posedge clk_in or negedge rst_n_in) | + | //我们准备使用4个节拍完成1bit数据的传输,所以需要400KHz的时钟触发完成该设计 |
- | begin | + | //使用计数器分频产生400KHz时钟信号clk_400khz |
- | if(!rst_n_in) | + | reg clk_400khz; |
- | bps_clk <= 1'b0; | + | reg [9:0] cnt_400khz; |
- | else if(cnt == (BPS_PARA>>1)) //BPS_PARA右移一位等于除2,因计数器终值BPS_PARA为数据更替时间点,所以计数器中值时为数据最稳定时间点 | + | always@(posedge clk_in or negedge rst_n_in) begin |
- | bps_clk <= 1'b1; | + | if(!rst_n_in) begin |
- | else | + | cnt_400khz <= 10'd0; |
- | bps_clk <= 1'b0; | + | clk_400khz <= 1'b0; |
+ | end else if(cnt_400khz >= CNT_NUM-1) begin | ||
+ | cnt_400khz <= 10'd0; | ||
+ | clk_400khz <= ~clk_400khz; | ||
+ | end else begin | ||
+ | cnt_400khz <= cnt_400khz + 1'b1; | ||
+ | end | ||
end | end | ||
+ | |||
+ | reg [7:0] adc_data_r; | ||
+ | reg scl_out_r; | ||
+ | reg sda_out_r; | ||
+ | reg [2:0] cnt; | ||
+ | reg [3:0] cnt_main; | ||
+ | reg [7:0] data_wr; | ||
+ | reg [2:0] cnt_start; | ||
+ | reg [2:0] cnt_write; | ||
+ | reg [4:0] cnt_read; | ||
+ | reg [2:0] cnt_stop; | ||
+ | reg [2:0] state; | ||
- | endmodule | + | always@(posedge clk_400khz or negedge rst_n_in) begin |
- | </code> | + | if(!rst_n_in) begin //如果按键复位,将相关数据初始化 |
- | \\ | + | scl_out_r <= 1'd1; |
- | <code verilog> | + | sda_out_r <= 1'd1; |
- | // -------------------------------------------------------------------- | + | cnt <= 1'b0; |
- | // >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<< | + | cnt_main <= 4'd0; |
- | // -------------------------------------------------------------------- | + | cnt_start <= 3'd0; |
- | // Module: Uart_Rx | + | cnt_write <= 3'd0; |
- | // | + | cnt_read <= 5'd0; |
- | // Author: Step | + | cnt_stop <= 1'd0; |
- | // | + | adc_done <= 1'b0; |
- | // Description: The receive module of uart interface | + | adc_data <= 1'b0; |
- | // | + | state <= IDLE; |
- | // Web: www.stepfapga.com | + | end else begin |
- | // | + | case(state) |
- | // -------------------------------------------------------------------- | + | IDLE:begin //软件自复位,主要用于程序跑飞后的处理 |
- | // Code Revision History : | + | scl_out_r <= 1'd1; |
- | // -------------------------------------------------------------------- | + | sda_out_r <= 1'd1; |
- | // Version: |Mod. Date: |Changes Made: | + | cnt <= 1'b0; |
- | // V1.0 |2016/04/20 |Initial ver | + | cnt_main <= 4'd0; |
- | // -------------------------------------------------------------------- | + | cnt_start <= 3'd0; |
- | module Uart_Rx | + | cnt_write <= 3'd0; |
- | ( | + | cnt_read <= 5'd0; |
- | input clk_in, //系统时钟 | + | cnt_stop <= 1'd0; |
- | input rst_n_in, //系统复位,低有效 | + | adc_done <= 1'b0; |
- | + | state <= MAIN; | |
- | output reg bps_en, //接收时钟使能 | + | end |
- | input bps_clk, //接收时钟输入 | + | MAIN:begin |
- | + | if(cnt_main >= 4'd6) cnt_main <= 4'd6; //对MAIN中的子状态执行控制cnt_main | |
- | input rs232_rx, //UART接收输入 | + | else cnt_main <= cnt_main + 1'b1; |
- | output reg [7:0] rx_data //接收到的数据 | + | case(cnt_main) |
- | ); | + | 4'd0: begin state <= START; end //I2C通信时序中的START |
- | + | 4'd1: begin data_wr <= 8'h90; state <= WRITE; end //A0,A1,A2都接了GND,写地址为8'h90 | |
- | reg rs232_rx0,rs232_rx1,rs232_rx2; | + | 4'd2: begin data_wr <= 8'h00; state <= WRITE; end //control byte为8'h00,采用4通道ADC中的通道0 |
- | //多级延时锁存去除亚稳态 | + | 4'd3: begin state <= STOP; end //I2C通信时序中的START |
- | always @ (posedge clk_in or negedge rst_n_in) begin | + | 4'd4: begin state <= START; end //I2C通信时序中的STOP |
- | if(!rst_n_in) begin | + | 4'd5: begin data_wr <= 8'h91; state <= WRITE; end //A0 A1 A2都接了GND,读地址为8'h91 |
- | rs232_rx0 <= 1'b0; | + | 4'd6: begin state <= READ; adc_done <= 1'b0; end //读取ADC的采样数据 |
- | rs232_rx1 <= 1'b0; | + | 4'd7: begin state <= STOP; adc_done <= 1'b1; end //I2C通信时序中的STOP,读取完成标志 |
- | rs232_rx2 <= 1'b0; | + | 4'd8: begin state <= MAIN; end //预留状态,不执行 |
- | end else begin | + | default: state <= IDLE; //如果程序失控,进入IDLE自复位状态 |
- | rs232_rx0 <= rs232_rx; | + | endcase |
- | rs232_rx1 <= rs232_rx0; | + | end |
- | rs232_rx2 <= rs232_rx1; | + | START:begin //I2C通信时序中的起始START |
- | end | + | if(cnt_start >= 3'd5) cnt_start <= 1'b0; //对START中的子状态执行控制cnt_start |
- | end | + | else cnt_start <= cnt_start + 1'b1; |
- | + | case(cnt_start) | |
- | //检测UART接收输入信号的下降沿 | + | 3'd0: begin sda_out_r <= 1'b1; scl_out_r <= 1'b1; end //将SCL和SDA拉高,保持4.7us以上 |
- | wire neg_rs232_rx = rs232_rx2 & rs232_rx1 & (~rs232_rx0) & (~rs232_rx); | + | 3'd1: begin sda_out_r <= 1'b1; scl_out_r <= 1'b1; end //clk_400khz每个周期2.5us,需要两个周期 |
- | + | 3'd2: begin sda_out_r <= 1'b0; end //SDA拉低到SCL拉低,保持4.0us以上 | |
- | reg [3:0] num; | + | 3'd3: begin sda_out_r <= 1'b0; end //clk_400khz每个周期2.5us,需要两个周期 |
- | //接收时钟使能信号的控制 | + | 3'd4: begin scl_out_r <= 1'b0; end //SCL拉低,保持4.7us以上 |
- | always @ (posedge clk_in or negedge rst_n_in) begin | + | 3'd5: begin scl_out_r <= 1'b0; state <= MAIN; end //clk_400khz每个周期2.5us,需要两个周期,返回MAIN |
- | if(!rst_n_in) | + | default: state <= IDLE; //如果程序失控,进入IDLE自复位状态 |
- | bps_en <= 1'b0; | + | endcase |
- | else if(neg_rs232_rx && (!bps_en)) //当空闲状态(bps_en为低电平)时检测到UART接收信号下降沿,进入工作状态(bps_en为高电平),控制时钟模块产生接收时钟 | + | end |
- | bps_en <= 1'b1; | + | WRITE:begin //I2C通信时序中的写操作WRITE和相应判断操作ACK |
- | else if(num==4'd9) //当完成一次UART接收操作后,退出工作状态,恢复空闲状态 | + | if(cnt <= 3'd6) begin //共需要发送8bit的数据,这里控制循环的次数 |
- | bps_en <= 1'b0; | + | if(cnt_write >= 3'd3) begin cnt_write <= 1'b0; cnt <= cnt + 1'b1; end |
- | end | + | else begin cnt_write <= cnt_write + 1'b1; cnt <= cnt; end |
- | + | end else begin | |
- | reg [7:0] rx_data_r; | + | if(cnt_write >= 3'd7) begin cnt_write <= 1'b0; cnt <= 1'b0; end //两个变量都恢复初值 |
- | //当处于工作状态中时,按照接收时钟的节拍获取数据 | + | else begin cnt_write <= cnt_write + 1'b1; cnt <= cnt; end |
- | always @ (posedge clk_in or negedge rst_n_in) begin | + | end |
- | if(!rst_n_in) begin | + | case(cnt_write) |
- | num <= 4'd0; | + | //按照I2C的时序传输数据 |
- | rx_data <= 8'd0; | + | 3'd0: begin scl_out_r <= 1'b0; sda_out_r <= data_wr[7-cnt]; end //SCL拉低,并控制SDA输出对应的位 |
- | rx_data_r <= 8'd0; | + | 3'd1: begin scl_out_r <= 1'b1; end //SCL拉高,保持4.0us以上 |
- | end else if(bps_en) begin | + | 3'd2: begin scl_out_r <= 1'b1; end //clk_400khz每个周期2.5us,需要两个周期 |
- | if(bps_clk) begin | + | 3'd3: begin scl_out_r <= 1'b0; end //SCL拉低,准备发送下1bit的数据 |
- | num <= num+1'b1; | + | //获取从设备的响应信号并判断 |
- | if(num<=4'd8) | + | 3'd4: begin sda_out_r <= 1'bz; end //释放SDA线,准备接收从设备的响应信号 |
- | rx_data_r[num-1]<=rs232_rx; //先接受低位再接收高位,8位有效数据 | + | 3'd5: begin scl_out_r <= 1'b1; end //SCL拉高,保持4.0us以上 |
- | end else if(num == 4'd9) begin //完成一次UART接收操作后,将获取的数据输出 | + | 3'd6: begin if(sda_out) state <= IDLE; else state <= state; end //获取从设备的响应信号并判断 |
- | num <= 4'd0; | + | 3'd7: begin scl_out_r <= 1'b0; state <= MAIN; end //SCL拉低,返回MAIN状态 |
- | rx_data <= rx_data_r; | + | default: state <= IDLE; //如果程序失控,进入IDLE自复位状态 |
+ | endcase | ||
+ | end | ||
+ | READ:begin //I2C通信时序中的读操作READ和返回ACK的操作 | ||
+ | if(cnt <= 3'd6) begin //共需要接收8bit的数据,这里控制循环的次数 | ||
+ | if(cnt_read >= 3'd3) begin cnt_read <= 1'b0; cnt <= cnt + 1'b1; end | ||
+ | else begin cnt_read <= cnt_read + 1'b1; cnt <= cnt; end | ||
+ | end else begin | ||
+ | if(cnt_read >= 3'd7) begin cnt_read <= 1'b0; cnt <= 1'b0; end //两个变量都恢复初值 | ||
+ | else begin cnt_read <= cnt_read + 1'b1; cnt <= cnt; end | ||
+ | end | ||
+ | case(cnt_read) | ||
+ | //按照I2C的时序接收数据 | ||
+ | 3'd0: begin scl_out_r <= 1'b0; sda_out_r <= 1'bz; end //SCL拉低,释放SDA线,准备接收从设备数据 | ||
+ | 3'd1: begin scl_out_r <= 1'b1; end //SCL拉高,保持4.0us以上 | ||
+ | 3'd2: begin adc_data_r[7-cnt] <= sda_out; end //读取从设备返回的数据 | ||
+ | 3'd3: begin scl_out_r <= 1'b0; end //SCL拉低,准备接收下1bit的数据 | ||
+ | //向从设备发送响应信号 | ||
+ | 3'd4: begin sda_out_r <= 1'b0; adc_done <= 1'b1; adc_data <= adc_data_r; end //发送响应信号,将前面接收的数据锁存 | ||
+ | 3'd5: begin scl_out_r <= 1'b1; end //SCL拉高,保持4.0us以上 | ||
+ | 3'd6: begin scl_out_r <= 1'b1; adc_done <= 1'b0; end //SCL拉高,保持4.0us以上 | ||
+ | 3'd7: begin scl_out_r <= 1'b0; state <= MAIN; end //SCL拉低,返回MAIN状态 | ||
+ | default: state <= IDLE; //如果程序失控,进入IDLE自复位状态 | ||
+ | endcase | ||
+ | end | ||
+ | STOP:begin //I2C通信时序中的结束STOP | ||
+ | if(cnt_stop >= 3'd5) cnt_stop <= 1'b0; //对STOP中的子状态执行控制cnt_stop | ||
+ | else cnt_stop <= cnt_stop + 1'b1; | ||
+ | case(cnt_stop) | ||
+ | 3'd0: begin sda_out_r <= 1'b0; end //SDA拉低,准备STOP | ||
+ | 3'd1: begin sda_out_r <= 1'b0; end //SDA拉低,准备STOP | ||
+ | 3'd2: begin scl_out_r <= 1'b1; end //SCL提前SDA拉高4.0us | ||
+ | 3'd3: begin scl_out_r <= 1'b1; end //SCL提前SDA拉高4.0us | ||
+ | 3'd4: begin sda_out_r <= 1'b1; end //SDA拉高 | ||
+ | 3'd5: begin sda_out_r <= 1'b1; state <= MAIN; end //完成STOP操作,返回MAIN状态 | ||
+ | default: state <= IDLE; //如果程序失控,进入IDLE自复位状态 | ||
+ | endcase | ||
+ | end | ||
+ | default:; | ||
+ | endcase | ||
end | end | ||
end | end | ||
- | end | + | |
+ | assign scl_out = scl_out_r; //对SCL端口赋值 | ||
+ | assign sda_out = sda_out_r; //对SDA端口赋值 | ||
endmodule | endmodule | ||
- | |||
</code> | </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配置文件加载测试。 | + | 本节主要为大家讲解了使用[[I2C]]驱动PCF8591的[[ADC]]功能的原理及软件设计,需要大家掌握的同时自己创建工程,通过整个设计流程,生成[[FPGA]]配置文件加载测试。 |
\\ | \\ | ||
- | 如果你对Diamond软件的使用不了解,请参考这里:[[lattice_diamond的使用|Diamond的使用]]。 | + | 如果你对Diamond软件的使用不了解,请参考这里:[[lattice_fpga|Diamond的使用]]。 |
====相关资料==== | ====相关资料==== | ||
+ | |||
------ | ------ | ||
\\ | \\ | ||
- | 使用[[STEP-MXO2第二代]]的UART通信程序: 后续会有下载连接 待更新 | + | 使用[[STEP-MXO2第二代]]的PCF8591的[[ADC]]驱动程序: 后续会有下载连接 待更新 |
\\ | \\ | ||
- | 使用[[STEP-MAX10]]的UART通信程序: 后续会有下载连接 待更新 | + | 使用[[STEP-MAX10]]的PCF8591的[[ADC]]驱动程序: 后续会有下载连接 待更新 |
\\ | \\ |